use MHash to generate NT password hash

This commit is contained in:
Roland Gruber 2006-04-29 09:58:17 +00:00
parent 856f2ebcd8
commit 69d1af357b
11 changed files with 140 additions and 200 deletions

View File

@ -2,6 +2,8 @@
- security enhancements: session timeout, logging, host restrictions - security enhancements: session timeout, logging, host restrictions
- fixed bugs: - fixed bugs:
-> PDF creation bug when GID translation is activated (1477111) -> PDF creation bug when GID translation is activated (1477111)
-> allow "@" in passwords (1477878)
-> Samba 2/3: fixed NT hashes
12.04.2006 1.0.1 12.04.2006 1.0.1

View File

@ -0,0 +1,55 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Module HowTo - Defining required extensions</title>
<link rel="stylesheet" type="text/css" href="style/layout.css">
</head>
<body>
<div style="text-align: center;">
<h1>Module HowTo - Defining required extensions<br>
</h1>
<div style="text-align: left;"><br>
Your account module might require special PHP extensions. LAM can check
this for you and display an error message at the login page.<br>
<br>
</div>
<div style="text-align: left;">You will need to implement the function <span
style="font-weight: bold;">getRequiredExtensions()</span> or use <span
style="font-weight: bold;">meta['extensions']</span>.<br>
<br>
<span style="font-weight: bold; text-decoration: underline;">Example:</span><br
style="font-weight: bold; text-decoration: underline;">
<br>
The <span style="font-style: italic;">posixAccount</span> module needs
to generate password hashes. Therefore it needs the MHash extension.<br>
<br>
<table style="width: 100%; text-align: left;" class="mod-code"
border="0" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="vertical-align: top;">&nbsp;&nbsp;&nbsp; /**<br>
&nbsp;&nbsp;&nbsp; * Returns meta data that is interpreted by parent
class<br>
&nbsp;&nbsp;&nbsp; *<br>
&nbsp;&nbsp;&nbsp; * @return array array with meta data<br>
&nbsp;&nbsp;&nbsp; */<br>
&nbsp;&nbsp;&nbsp;<span style="font-weight: bold;"> function</span>
get_metaData() {<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $return = array();<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // PHP extensions<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;$return["extensions"] =
array("mhash");<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [...]<br>
</td>
</tr>
</tbody>
</table>
<br>
<br>
<br>
<span style="font-weight: bold;"></span>
<h2><span style="font-weight: bold;"></span></h2>
</div>
</div>
</body>
</html>

View File

@ -51,6 +51,8 @@ existing modules.<br>
<br> <br>
<h3><a href="mod_rdn.htm">4. Defining the RDN</a></h3> <h3><a href="mod_rdn.htm">4. Defining the RDN</a></h3>
<br> <br>
<h3><a href="mod_ext.htm">5. Defining required PHP extensions</a></h3>
<br>
<br> <br>
</div> </div>
</div> </div>

View File

@ -515,8 +515,22 @@ internal data structures.<br>
is an hash array (identifier =&gt; array(values))&nbsp; with all values is an hash array (identifier =&gt; array(values))&nbsp; with all values
of an account profile.<br> of an account profile.<br>
<br> <br>
<h3><br> <h3>2.1.20. getRequiredExtensions*</h3>
</h3> <br>
<table border="0" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="vertical-align: top; text-align: center;"><span
style="font-weight: bold;">function getRequiredExtensions()</span><br>
</td>
</tr>
</tbody>
</table>
<br>
This function returns a list of PHP extensions (e.g. mhash) which are
needed by this module.<br>
<br>
<br>
<br> <br>
<br> <br>
<h3>2.2. Functions which are called inside of an account container<br> <h3>2.2. Functions which are called inside of an account container<br>
@ -1442,6 +1456,14 @@ return value of get_uploadColumns().<br>
the the
return value of get_uploadPreDepends().<br> return value of get_uploadPreDepends().<br>
</span></span><br> </span></span><br>
<h3>6.15 getRequiredExtensions()<br>
</h3>
"extensions" =&gt; array()<br>
<span style="font-weight: bold;"><br>
<span style="font-style: italic;"></span></span><span
style="font-weight: bold;">Example:</span><span
style="font-style: italic; font-weight: bold;"> array('mhash')</span><br>
<br>
<span style="font-weight: bold;"></span><span style="font-weight: bold;"><span <span style="font-weight: bold;"></span><span style="font-weight: bold;"><span
style="font-style: italic;"></span></span><span style="font-style: italic;"></span></span><span
style="font-style: italic; font-weight: bold;"></span><span style="font-style: italic; font-weight: bold;"></span><span

View File

@ -557,8 +557,11 @@ class baseModule {
* The maximum length of the attributes is checked, too. * The maximum length of the attributes is checked, too.
* *
* @return mixed 0 if no errors/warnings occured, otherwise an array of status messages. * @return mixed 0 if no errors/warnings occured, otherwise an array of status messages.
*
* TODO: remove this function and move checks to posixAccount.inc
*/ */
function input_check() { function input_check() {
$messages = array();
// Do a check for every ldap attribute // Do a check for every ldap attribute
$attributes = array_keys($this->attributes); $attributes = array_keys($this->attributes);
for ($i=0; $i<count($attributes); $i++) { for ($i=0; $i<count($attributes); $i++) {
@ -676,6 +679,16 @@ class baseModule {
else return array(); else return array();
} }
/**
* Returns a list of required PHP extensions.
*
* @return array extensions
*/
function getRequiredExtensions() {
if (isset($this->meta['extensions']) && is_array($this->meta['extensions'])) return $this->meta['extensions'];
else return array();
}
} }

View File

@ -329,192 +329,7 @@ var $sbox = array(array(array(14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5
* @return string hash value * @return string hash value
*/ */
function nthash($password = "") { function nthash($password = "") {
$password = substr($password,0,128); return strtoupper(bin2hex(mhash(MHASH_MD4, iconv("UTF-8","UTF-16LE",$password))));
$password2 = "";
for ($i = 0; $i < strlen($password); $i++) $password2 .= $password[$i] . chr(0);
$password = $password2;
$hex = $this->mdfour($password);
for ($i = 0; $i < sizeof($hex); $i++) {
$hex[$i] = sprintf("%02X", $hex[$i]);
}
return join("", $hex);
}
# Support functions
# Ported from SAMBA/source/lib/md4.c:F,G and H respectfully
function F($X, $Y, $Z) {
$ret = (($X&$Y) | ((~((int)$X))&$Z));
if ($this->x($ret) > 4294967296) {
$ret = (2*4294967296) - $this->x($ret);
}
return $ret;
}
function G($X, $Y, $Z) {
return ($X&$Y) | ($X&$Z) | ($Y&$Z);
}
function H($X, $Y, $Z) {
return $X^$Y^$Z;
}
# Ported from SAMBA/source/lib/md4.c:mdfour
function mdfour($in) {
$in = unpack("C*",$in);
$in = array_values($in);
$b = sizeof($in) * 8;
$A = array(0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476);
while (sizeof($in) > 64 ) {
$M = $this->copy64($in);
$this->mdfour64($A[0], $A[1], $A[2], $A[3], $M);
$new_in = array();
for ($i = 64; $i < sizeof($in); $i++) $new_in[] = $in[$i];
$in = $new_in;
}
$buf = $in;
$buf[] = 0x80;
for ($i = sizeof($buf) - 1; $i < 127; $i++) $buf[] = 0;
if ( sizeof($in) <= 55 ) {
$temp = $this->copy4($b);
$buf[56] = $temp[0];
$buf[57] = $temp[1];
$buf[58] = $temp[2];
$buf[59] = $temp[3];
$M = $this->copy64($buf);
$this->mdfour64($A[0], $A[1], $A[2], $A[3], $M);
}
else {
$temp = $this->copy4($b);
$buf[120] = $temp[0];
$buf[121] = $temp[1];
$buf[122] = $temp[2];
$buf[123] = $temp[3];
$M = $this->copy64($buf);
$this->mdfour64($A[0], $A[1], $A[2], $A[3], $M);
$temp = array();
for ($i = 64; $i < sizeof($buf); $i++) $temp[] = $buf[$i];
$M = $this->copy64($temp);
$this->mdfour64($A[0], $A[1], $A[2], $A[3], $M);
}
$out = array();
$temp = $this->copy4($A[0]);
for ($i = 0; $i < 4; $i++) $out[] = $temp[$i];
$temp = $this->copy4($A[1]);
for ($i = 0; $i < 4; $i++) $out[] = $temp[$i];
$temp = $this->copy4($A[2]);
for ($i = 0; $i < 4; $i++) $out[] = $temp[$i];
$temp = $this->copy4($A[3]);
for ($i = 0; $i < 4; $i++) $out[] = $temp[$i];
return $out;
}
# Ported from SAMBA/source/lib/md4.c:copy4
function copy4($x) {
$out = array();
$out[0] = $x&0xFF;
$out[1] = $this->unsigned_shift_r($x, 8)&0xFF;
$out[2] = $this->unsigned_shift_r($x, 16)&0xFF;
$out[3] = $this->unsigned_shift_r($x, 24)&0xFF;
return $out;
}
# Ported from SAMBA/source/lib/md4.c:copy64
function copy64($in) {
for ($i = 0; $i < 16; $i++) {
$M[$i] = ($in[$i*4+3]<<24) | ($in[$i*4+2]<<16) | ($in[$i*4+1]<<8) | ($in[$i*4+0]<<0);
}
return $M;
}
# Ported from SAMBA/source/lib/md4.c:mdfour64
function mdfour64(&$A, &$B, &$C, &$D, $M) {
$X = array();
for ($i = 0; $i < 16; $i++) $X[] = $M[$i];
$AA=$A;
$BB=$B;
$CC=$C;
$DD=$D;
$this->ROUND1($A,$B,$C,$D, 0, 3, $X);
$this->ROUND1($D,$A,$B,$C, 1, 7, $X);
$this->ROUND1($C,$D,$A,$B, 2, 11, $X);
$this->ROUND1($B,$C,$D,$A, 3, 19, $X);
$this->ROUND1($A,$B,$C,$D, 4, 3, $X); $this->ROUND1($D,$A,$B,$C, 5, 7, $X);
$this->ROUND1($C,$D,$A,$B, 6, 11, $X); $this->ROUND1($B,$C,$D,$A, 7, 19, $X);
$this->ROUND1($A,$B,$C,$D, 8, 3, $X); $this->ROUND1($D,$A,$B,$C, 9, 7, $X);
$this->ROUND1($C,$D,$A,$B, 10, 11, $X); $this->ROUND1($B,$C,$D,$A, 11, 19, $X);
$this->ROUND1($A,$B,$C,$D, 12, 3, $X); $this->ROUND1($D,$A,$B,$C, 13, 7, $X);
$this->ROUND1($C,$D,$A,$B, 14, 11, $X); $this->ROUND1($B,$C,$D,$A, 15, 19, $X);
$this->ROUND2($A,$B,$C,$D, 0, 3, $X); $this->ROUND2($D,$A,$B,$C, 4, 5, $X);
$this->ROUND2($C,$D,$A,$B, 8, 9, $X); $this->ROUND2($B,$C,$D,$A, 12, 13, $X);
$this->ROUND2($A,$B,$C,$D, 1, 3, $X); $this->ROUND2($D,$A,$B,$C, 5, 5, $X);
$this->ROUND2($C,$D,$A,$B, 9, 9, $X); $this->ROUND2($B,$C,$D,$A, 13, 13, $X);
$this->ROUND2($A,$B,$C,$D, 2, 3, $X); $this->ROUND2($D,$A,$B,$C, 6, 5, $X);
$this->ROUND2($C,$D,$A,$B, 10, 9, $X); $this->ROUND2($B,$C,$D,$A, 14, 13, $X);
$this->ROUND2($A,$B,$C,$D, 3, 3, $X); $this->ROUND2($D,$A,$B,$C, 7, 5, $X);
$this->ROUND2($C,$D,$A,$B, 11, 9, $X); $this->ROUND2($B,$C,$D,$A, 15, 13, $X);
$this->ROUND3($A,$B,$C,$D, 0, 3, $X); $this->ROUND3($D,$A,$B,$C, 8, 9, $X);
$this->ROUND3($C,$D,$A,$B, 4, 11, $X); $this->ROUND3($B,$C,$D,$A, 12, 15, $X);
$this->ROUND3($A,$B,$C,$D, 2, 3, $X); $this->ROUND3($D,$A,$B,$C, 10, 9, $X);
$this->ROUND3($C,$D,$A,$B, 6, 11, $X); $this->ROUND3($B,$C,$D,$A, 14, 15, $X);
$this->ROUND3($A,$B,$C,$D, 1, 3, $X); $this->ROUND3($D,$A,$B,$C, 9, 9, $X);
$this->ROUND3($C,$D,$A,$B, 5, 11, $X); $this->ROUND3($B,$C,$D,$A, 13, 15, $X);
$this->ROUND3($A,$B,$C,$D, 3, 3, $X); $this->ROUND3($D,$A,$B,$C, 11, 9, $X);
$this->ROUND3($C,$D,$A,$B, 7, 11, $X); $this->ROUND3($B,$C,$D,$A, 15, 15, $X);
$A = $this->add32(array($A, $AA)); $B = $this->add32(array($B, $BB));
$C = $this->add32(array($C, $CC)); $D = $this->add32(array($D, $DD));
}
# Needed? because perl seems to choke on overflowing when doing bitwise
# operations on numbers larger than 32 bits. Well, it did on my machine =)
function add32($v) {
for ($i = 0; $i < sizeof($v); $i++) {
$v[$i] = array($this->unsigned_shift_r(($v[$i]&0xffff0000), 16), ($v[$i]&0xffff));
}
for ($i = 0; $i < sizeof($v); $i++) {
$sum[0] += $v[$i][0];
$sum[1] += $v[$i][1];
}
$sum[0] += ($sum[1]&0xffff0000)>>16;
$sum[1] &= 0xffff;
$sum[0] &= 0xffff;
$ret = ($sum[0]<<16) | $sum[1];
if ($this->x($ret) > 4294967296) {
$ret = (2*4294967296) - $this->x($ret);
}
return $ret;
}
# Ported from SAMBA/source/lib/md4.c:ROUND1
function ROUND1(&$a,$b,$c,$d,$k,$s,$X) {
$a = $this->md4lshift($this->add32(array($a, $this->F($b,$c,$d), $X[$k])), $s);
return $a;
}
# Ported from SAMBA/source/lib/md4.c:ROUND2
function ROUND2(&$a,$b,$c,$d,$k,$s,$X) {
$a = $this->md4lshift($this->add32(array($a, $this->G($b,$c,$d), $X[$k] + 0x5A827999)), $s);
return $a;
}
# Ported from SAMBA/source/lib/md4.c:ROUND3
function ROUND3(&$a,$b,$c,$d,$k,$s,$X) {
$a = $this->md4lshift($this->add32(array($a + $this->H($b,$c,$d) + $X[$k] + 0x6ED9EBA1)), $s);
return $a;
}
# Ported from SAMBA/source/lib/md4.c:lshift
# Renamed to prevent clash with SAMBA/source/libsmb/smbdes.c:lshift
function md4lshift($x, $s) {
$x &= 0xFFFFFFFF;
if ($this->x($x) > 4294967296) {
$x = (2*4294967296) - $this->x($x);
}
$ret = ((($x<<$s)&0xFFFFFFFF) | $this->unsigned_shift_r($x, (32-$s)));
if ($this->x($ret) > 4294967296) {
$ret = (2*4294967296) - $this->x($ret);
}
return $ret;
} }
/** /**

View File

@ -528,6 +528,27 @@ function doUploadPostActions($scope, $data, $ids, $failed) {
return $return; return $return;
} }
/**
* Returns true if the module is a base module
*
* @return array required extensions
*/
function getRequiredExtensions() {
$extList = array();
$scopes = $_SESSION['config']->get_ActiveTypes();
for ($i = 0; $i < sizeof($scopes); $i++) {
$mods = $_SESSION['config']->get_AccountModules($scopes[$i]);
for ($m = 0; $m < sizeof($mods); $m++) {
$module = new $mods[$m]($scopes[$i]);
$ext = $module->getRequiredExtensions();
for ($e = 0; $e < sizeof($ext); $e++) {
if (!in_array($ext[$e], $extList)) $extList[] = $ext[$e];
}
}
}
return $extList;
}
/** /**
* Takes a list of meta-HTML elements and prints the equivalent HTML output. * Takes a list of meta-HTML elements and prints the equivalent HTML output.
* *

View File

@ -125,6 +125,8 @@ class posixAccount extends baseModule {
$return["RDN"] = array("uid" => "normal", "cn" => "low"); $return["RDN"] = array("uid" => "normal", "cn" => "low");
// managed object classes // managed object classes
$return['objectClasses'] = array('posixAccount'); $return['objectClasses'] = array('posixAccount');
// PHP extensions
$return['extensions'] = array('mhash');
// profile checks // profile checks
$return['profile_checks']['posixAccount_homeDirectory'] = array('type' => 'ext_preg', 'regex' => 'homeDirectory', $return['profile_checks']['posixAccount_homeDirectory'] = array('type' => 'ext_preg', 'regex' => 'homeDirectory',
'error_message' => $this->messages['homeDirectory'][0]); 'error_message' => $this->messages['homeDirectory'][0]);
@ -583,7 +585,7 @@ class posixAccount extends baseModule {
$this->attributes['homeDirectory'][0] = $post['homeDirectory']; $this->attributes['homeDirectory'][0] = $post['homeDirectory'];
$this->attributes['loginShell'][0] = $post['loginShell']; $this->attributes['loginShell'][0] = $post['loginShell'];
if (isset($post['gecos'])) $this->attributes['gecos'][0] = $post['gecos']; if (isset($post['gecos'])) $this->attributes['gecos'][0] = $post['gecos'];
if ($post['createhomedir']) $this->createhomedir = true; if (isset($post['createhomedir'])) $this->createhomedir = true;
else $this->createhomedir = false; else $this->createhomedir = false;
if ($this->orig['uid'][0]!='' && $post['uid']!=$this->attributes['uid'][0]) if ($this->orig['uid'][0]!='' && $post['uid']!=$this->attributes['uid'][0])
$triggered_messages['uid'][] = $this->messages['uid'][0]; $triggered_messages['uid'][] = $this->messages['uid'][0];
@ -672,30 +674,31 @@ class posixAccount extends baseModule {
if ( !get_preg($this->attributes['homeDirectory'][0], 'homeDirectory' )) if ( !get_preg($this->attributes['homeDirectory'][0], 'homeDirectory' ))
$triggered_messages['homeDirecotry'][] = $this->messages['homeDirectory'][0]; $triggered_messages['homeDirecotry'][] = $this->messages['homeDirectory'][0];
} }
if (($post['userPassword_lock'] && $post['userPassword_invalid']) || ($post['userPassword_nopassword'] && $post['userPassword_invalid'])) { if ((isset($post['userPassword_lock']) && isset($post['userPassword_invalid'])) ||
(isset($post['userPassword_nopassword']) && isset($post['userPassword_invalid']))) {
// found invalid password parameter combination // found invalid password parameter combination
$triggered_messages['userPassword'][] = $this->messages['userPassword'][3]; $triggered_messages['userPassword'][] = $this->messages['userPassword'][3];
} }
else { else {
if ($post['userPassword_nopassword']) { if (isset($post['userPassword_nopassword'])) {
$this->userPassword_nopassword=true; $this->userPassword_nopassword=true;
$this->userPassword_invalid=false; $this->userPassword_invalid=false;
$this->attributes['userPassword'][0] = ''; $this->attributes['userPassword'][0] = '';
$post['userPassword2'] = ''; $post['userPassword2'] = '';
if ($post['userPassword_lock']) if (isset($post['userPassword_lock']))
$this->userPassword_lock=true; $this->userPassword_lock=true;
else $this->userPassword_lock=false; else $this->userPassword_lock=false;
} }
else { else {
$this->userPassword_nopassword=false; $this->userPassword_nopassword=false;
if ($post['userPassword_invalid']) { if (isset($post['userPassword_invalid'])) {
$this->userPassword_invalid=true; $this->userPassword_invalid=true;
$this->userPassword_lock=false; $this->userPassword_lock=false;
$post['userPassword2'] = ''; $post['userPassword2'] = '';
} }
else { else {
$this->userPassword_invalid=false; $this->userPassword_invalid=false;
if ($post['genpass']) $this->attributes['userPassword'][0] = genpasswd(); if (isset($post['genpass'])) $this->attributes['userPassword'][0] = genpasswd();
elseif ($_SESSION[$this->base]->isNewAccount) { elseif ($_SESSION[$this->base]->isNewAccount) {
if ($post['userPassword'] != $post['userPassword2']) if ($post['userPassword'] != $post['userPassword2'])
$triggered_messages['userPassword'][] = $this->messages['userPassword'][0]; $triggered_messages['userPassword'][] = $this->messages['userPassword'][0];
@ -703,7 +706,7 @@ class posixAccount extends baseModule {
if (!get_preg($this->attributes['userPassword'][0], 'password')) if (!get_preg($this->attributes['userPassword'][0], 'password'))
$triggered_messages['userPassword'][] = $this->messages['userPassword'][1]; $triggered_messages['userPassword'][] = $this->messages['userPassword'][1];
} }
if ($post['userPassword_lock']) $this->userPassword_lock=true; if (isset($post['userPassword_lock'])) $this->userPassword_lock=true;
else $this->userPassword_lock=false; else $this->userPassword_lock=false;
} }
} }
@ -841,7 +844,7 @@ class posixAccount extends baseModule {
function display_html_attributes(&$post) { function display_html_attributes(&$post) {
// check password format if called the first time // check password format if called the first time
if (!isset($this->userPassword_invalid)) { if (!isset($this->userPassword_invalid)) {
if ($this->attributes['userPassword'][0]) { if (isset($this->attributes['userPassword'][0])) {
if ($this->attributes['userPassword'][0] == '*') $this->userPassword_invalid = true; if ($this->attributes['userPassword'][0] == '*') $this->userPassword_invalid = true;
else $this->userPassword_invalid = false; else $this->userPassword_invalid = false;
if (pwd_is_enabled($this->attributes['userPassword'][0])) $this->userPassword_lock = false; if (pwd_is_enabled($this->attributes['userPassword'][0])) $this->userPassword_lock = false;

View File

@ -107,6 +107,8 @@ class sambaAccount extends baseModule {
$return['dependencies'] = array('depends' => array('posixAccount'), 'conflicts' => array()); $return['dependencies'] = array('depends' => array('posixAccount'), 'conflicts' => array());
// managed object classes // managed object classes
$return['objectClasses'] = array('sambaAccount'); $return['objectClasses'] = array('sambaAccount');
// PHP extensions
$return['extensions'] = array('mhash');
// profile options // profile options
if ($this->get_scope() == 'user') { if ($this->get_scope() == 'user') {
// set Unix password for Samba // set Unix password for Samba

View File

@ -126,6 +126,8 @@ class sambaSamAccount extends baseModule {
$return['dependencies'] = array('depends' => array('posixAccount'), 'conflicts' => array()); $return['dependencies'] = array('depends' => array('posixAccount'), 'conflicts' => array());
// managed object classes // managed object classes
$return['objectClasses'] = array('sambaSamAccount'); $return['objectClasses'] = array('sambaSamAccount');
// PHP extensions
$return['extensions'] = array('mhash');
// profile checks // profile checks
$return['profile_checks']['sambaSamAccount_smbhome'] = array( $return['profile_checks']['sambaSamAccount_smbhome'] = array(
'type' => 'ext_preg', 'type' => 'ext_preg',

View File

@ -179,10 +179,13 @@ function display_LoginPage($config_object) {
</table> </table>
<hr><br><br> <hr><br><br>
<?php <?php
// check if all password hashes are possible // check extensions
if ((! function_exists('mHash')) && (! function_exists('sha1'))) { $extList = getRequiredExtensions();
StatusMessage("INFO", "Your PHP does not support MHash or sha1(), you will only be able to use CRYPT/PLAIN/MD5/SMD5 for user passwords!", "Please install MHash or update to PHP >4.3."); for ($i = 0; $i < sizeof($extList); $i++) {
echo "<br><br>"; if (!extension_loaded($extList[$i])) {
StatusMessage("ERROR", _("A required extension is missing!"), $extList[$i]);
echo "<br>";
}
} }
?> ?>
<table width="650" align="center" border="2" rules="none" bgcolor="white"> <table width="650" align="center" border="2" rules="none" bgcolor="white">