[dev] Enhancement to smbldap driver for Passwd module

Kevin Glueck glueck-horde at viz.tamu.edu
Sun Jan 16 20:14:18 PST 2005


I recently turned to horde to be my central password change location for my
users.  When we got rid of the nt pdc and bdcs in favor of running the PDC
with samba, my dream of single-sign-on was oh so much closer to reality.
Here enters horde and the awesome code that's available.  Well, I'd had the
passwd module running with just the ldap backend driver, which was working
fine.  but when we wanted to do the central password changes, the smbldap
driver seemed the ideal thing to use.

Unfortunately, I discovered quickly as I was looking at the php code that
the NT and Lan Manager hashes were being generated by an exec() call to a
program that took the password on the commandline and spit out the hashes
with a colon in between them.  This caused some trouble for me because
there was text being read in that was being passed to the external program
without escaping shell characters before being called.  This had the effect
of when a character, such as a single quote, was used in the password, the
attempt to change the password returned errors...I didn't try a backtick,
but I'm sure that could be a rather fun experiment for an enterprising
individual.  Rather than escape the text, I thought incorporating the
hashing function inside the horde code was better (and likely much more
portable).

So, I started to look to see what I could do about it. lo and behold there
was a pear module Crypt_CHAP that would generate the hashes for me.  So,
I installed it and changed a couple of lines of code in the
lib/Drivers/smbldap.php code segment.  then I realized that the
sambaPwdLastSet and sambaPwdMustChange attributes also weren't getting
set.  So I added that code.

Since I had to add a new pear module, I shamelessly stole the
accounts/test.php code and modified it to be the passwd/test.php to check
for the PEAR module Crypt_CHAP and its php prerequisites mhash and mcrypt.

Features that are modified/added by the attached patches are:
 - Crypt_CHAP replaces external mkntpwd program to generate LM and NT
   hashes
 - added config attributes pw_set_attribute, pw_expire_attribute, and
   pw_expire_time to backends.php.dist
 - pw_set_attribute (sambaPwdLastSet) timestamp is updated when password
   changed
 - pw_expire_attribute (sambaPwdMustChange) timestamp is generated from
   current timestamp + pw_expire_time attribute * 86400
 - added test.php to test for additional modules needed

Oh, and these patches are against HEAD in CVS...

Something I didn't immediately see how to do (haven't looked extensively
as I wanted to get this up asap on my system) was how to have horde submit
all of the ldap updates in one ldap modify command.  I'll prolly look into
that next.  any pointers?  I'd also appreciate comments on the code...
is there a better/more efficient way to do this?

-- 
Kevin Glueck                                    Senior Systems Administrator
Visualization Laboratory                        Langford Arch Center C-416D
Texas A&M University
-------------- next part --------------
--- backends.php.dist.orig	2005-01-16 19:04:25.349000064 -0600
+++ backends.php.dist	2005-01-16 19:03:33.326908624 -0600
@@ -187,9 +187,11 @@
                        // looking for the userdn.
         'encryption' => 'crypt',
         'tls' => false, // make sure the host == cn in the server certificate
-	'mkntpwd_cmd' => '/usr/local/sbin/mkntpwd', //locations of the mkntpwd executable
 	'lm_attribute' => 'sambaLMPassword',
-	'nt_attribute' => 'sambaNTPassword'
+	'nt_attribute' => 'sambaNTPassword',
+	'pw_set_attribute' => 'sambaPwdLastSet',
+	'pw_expire_attribute' => 'sambaPwdMustChange',
+	'pw_expire_time' => 180 // number of days until samba password expires
     )
 );
 
-------------- next part --------------
--- smbldap.php.orig	2004-05-12 15:59:30.000000000 -0500
+++ smbldap.php	2005-01-16 21:54:32.409291040 -0600
@@ -41,7 +41,9 @@
         $this->_params['attribute']       = isset($params['attribute']) ? $params['attribute'] : 'userPassword';
         $this->_params['lm_attribute']    = isset($params['lm_attribute']) ? $params['lm_attribute'] : 'sambaLMPassword';
         $this->_params['nt_attribute']    = isset($params['nt_attribute']) ? $params['nt_attribute'] : 'sambaNTPassword';
-        $this->_params['mkntpwd_cmd']     = isset($params['mkntpwd_cmd']) ? $params['mkntpwd_cmd'] : '/usr/local/sbin/mkntpwd';
+        $this->_params['pw_set_attribute']    = isset($params['pw_set_attribute']) ? $params['pw_set_attribute'] : 'sambaPwdLastSet';
+        $this->_params['pw_expire_attribute'] = isset($params['pw_expire_attribute']) ? $params['pw_expire_attribute'] : 'sambaPwdMustChange';
+        $this->_params['pw_expire_time']      = isset($params['pw_expire_time']) ? $params['pw_expire_time'] : 2147483647;
     }
 
     /**
@@ -139,6 +141,8 @@
      */
     function changePassword($username, $old_password, $new_password)
     {
+        require_once 'Crypt/CHAP.php';
+
         // Get the user's dn.
         global $conf;
 
@@ -175,9 +179,13 @@
             return PEAR::raiseError(ldap_error($this->_ds));
         }
 
-        $cmd = $this->_params['mkntpwd_cmd'] . " " . $new_password;
-        $fullntpasswd = exec($cmd);
-        list($lmpasswd, $ntpasswd) = split(':',$fullntpasswd);
+        $hash = new Crypt_CHAP_MSv2;
+        $hash->password = $new_password;
+        $lmpasswd = strtoupper(bin2hex($hash->lmPasswordHash()));
+        $ntpasswd = strtoupper(bin2hex($hash->ntPasswordHash()));
+        $settime = time();
+        // 24 hours/day * 60 min/hour * 60 secs/min = 86400 seconds/day
+        $expiretime = $settime + ($this->_params['pw_expire_time'] * 86400);
 
         $new_lm_passwd[$this->_params['lm_attribute']] = $lmpasswd;
         if (!ldap_mod_replace($this->_ds, $userdn, $new_lm_passwd)) {
@@ -189,6 +197,16 @@
             return PEAR::raiseError(ldap_error($this->_ds));
         }
 
+        $new_set_time[$this->_params['pw_set_attribute']] = $settime;
+        if (!ldap_mod_replace($this->_ds, $userdn, $new_set_time)) {
+            return PEAR::raiseError(ldap_error($this->_ds));
+        }
+
+        $new_set_time[$this->_params['pw_expire_attribute']] = $expiretime;
+        if (!ldap_mod_replace($this->_ds, $userdn, $new_set_time)) {
+            return PEAR::raiseError(ldap_error($this->_ds));
+        }
+
         return true;
     }
 
-------------- next part --------------
<?php
/**
 * $Horde$
 *
 * Copyright 2002-2005 Brent J. Nordquist <bjn at horde.org>
 * Copyright 1999-2005 Charles J. Hagenbuch <chuck at horde.org>
 * Copyright 1999-2005 Jon Parise <jon at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL).  If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 */

/* Include Horde's core.php file. */
include_once '../lib/core.php';

/* We should have loaded the String class, from the Horde_Util
 * package, in core.php. If String:: isn't defined, then we're not
 * finding some critical libraries. */
if (!class_exists('String')) {
    echo '<br /><span style="color: red; font-size: 18px; font-weight: bold;">The Horde_Util package was not found. If PHP\'s error_reporting setting is high enough, there should be error messages printed above that may help you in debugging the problem. If you are simply missing these files, then you need to get the <a href="http://cvs.horde.org/cvs.php/framework">framework</a> module from <a href="http://horde.org/source/">Horde CVS</a>, and install the packages in it with the install-packages.php script.</span>';
    exit;
}

/* Initialize the Horde_Test:: class. */
if (!(@is_readable('../lib/Test.php'))) {
    echo 'ERROR: You must install Horde before running this script.';
    exit;
}
require_once '../lib/Test.php';
$horde_test = &new Horde_Test;

/* Accounts definitions. */
$module = 'Passwd';
require_once './lib/version.php';
$module_version = PASSWD_VERSION;

require TEST_TEMPLATES . 'header.inc';
require TEST_TEMPLATES . 'version.inc';

/* PHP module capabilities. */
$module_list = array(
    'mcrypt' => array(
        'descrip' => 'mcrypt',
        'error' => 'If you will be using the smbldap driver for password changes, PHP must have mcrypt support. Compile PHP <code>--with-mcrypt</code> before continuing.'
    ),
    'mhash' => array(
        'descrip' => 'mhash',
        'error' => 'If you will be using the smbldap driver for password changes, PHP must have mhash support. Compile PHP <code>--with-mhash</code> before continuing.'
    )
);


/* PEAR */
$pear_list = array(
    'Crypt_CHAP' => array(
        'path' => 'Crypt/CHAP.php',
        'error' => 'If you will be using the smbldap driver for password changes, then you must install the PEAR Crypt_CHAP module.'
    )
);

/* Get the status output now. */
$module_output = $horde_test->phpModuleCheck($module_list);

?>
<h1>PHP Module Capabilities</h1>
<ul>
    <?php echo $module_output ?>
</ul>

<h1>PEAR Modules</h1>
<ul>
    <?php echo $horde_test->PEARModuleCheck($pear_list) ?>
</ul>

<?php
require TEST_TEMPLATES . 'footer.inc';


More information about the dev mailing list