Hello,
I wrote a backend for the password plugin that uses OpenLDAP's
ldappasswd(1). My motivation for this was to remove the requirement to
retrieve the user's full LDAP record, which our policy does not allow,
but this method is also easier to configure, obviates the need for php
to be able to produce the password hash, and supports a more complete
range of password storage and authentication options (e.g. SASL binds)
In particular, this might satisfy New Feature Request #1486349:
password plugin: using LDAP EXOP for changing passwords (RFC3062)
>From the comments:
* Advantages of this method:
* - No extra configuration if OpenLDAP/ldappasswd are already configured
* - Indifferent to password storage (attribute) and hashing details
* - Future-proof: supports everything ldappasswd(1) can do now, and later
* - TLS/SSF verification is done by OpenLDAP according to system settings
* - Uses PASSMOD extended operation; no need to retrieve full user record
Please review. If possible, I would like to see this in the main tree so
I don't have to maintain it locally
Patch attached
Cheers,
--
Dima
diff --git a/plugins/password/config.inc.php.dist b/plugins/password/config.inc.php.dist
index d979a19..669e15c 100644
--- a/plugins/password/config.inc.php.dist
+++ b/plugins/password/config.inc.php.dist
@@ -160,7 +160,7 @@ $config['password_ldap_adminPW'] = null;
// '%name' will be replaced by the current roundcube user's name part
// '%domain' will be replaced by the current roundcube user's domain part
// '%dc' will be replaced by domain name hierarchal string e.g. "dc=test,dc=domain,dc=com"
-// Exemple: 'uid=%login,ou=people,dc=exemple,dc=com'
+// Example: 'uid=%login,ou=people,dc=exemple,dc=com'
$config['password_ldap_userDN_mask'] = 'uid=%login,ou=people,dc=exemple,dc=com';
// LDAP search DN
@@ -238,6 +238,22 @@ $config['password_ldap_samba_pwattr'] = '';
$config['password_ldap_samba_lchattr'] = '';
+// ldappasswd(1) Driver options
+// --------------------------
+// TEMPLATE for executing the ldappasswd(1) utility
+// New password is passed on stdin, so you will likely want -T /dev/stdin
+// If %currpasspipe appears in the string, the current password is piped
+// in via fd 3 and the macro itself is replaced with /dev/fd/3
+// Macros from password_ldap_userDN_mask are also supported (e.g. %login, %dc)
+// *************************************************************************
+// *** COMPATIBILITY NOTICE - IF YOU INTEND TO PIPE THE CURRENT PASSWORD ***
+// *** This driver REQUIRES /dev/fd/N for N>2 ***
+// *** On BSD variants you may need to mount fdescfs(4) ***
+// *************************************************************************
+// WARNING: php likely invokes /bin/sh so be careful with shell metacharacters
+$config['password_ldappasswd_cmd'] = '/usr/local/bin/ldappasswd -T /dev/stdin -y %currpasspipe -D %dc %dc';
+
+
// DirectAdmin Driver options
// --------------------------
// The host which changes the password
diff --git a/plugins/password/drivers/ldappasswd.php b/plugins/password/drivers/ldappasswd.php
new file mode 100644
index 0000000..c819fcc
--- /dev/null
+++ b/plugins/password/drivers/ldappasswd.php
@@ -0,0 +1,110 @@
+<?php
+/*-
+ * Copyright (c) 2014, Dima Dorfman
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * Password change driver using the OpenLDAP ldappasswd(1) utility
+ *
+ * Advantages of this method:
+ * - No extra configuration if OpenLDAP/ldappasswd are already configured
+ * - Indifferent to password storage (attribute) and hashing details
+ * - Future-proof: supports everything ldappasswd(1) can do now, and later
+ * - TLS/SSF verification is done by OpenLDAP according to system settings
+ * - Uses PASSMOD extended operation; no need to retrieve full user record
+ *
+ * Disadvantages:
+ * - ldappasswd(1) must be installed and configured on the local system
+ *
+ **************************************************************************
+ *** COMPATIBILITY NOTICE ***
+ *** This implementation REQUIRES /dev/fd/N for N>2 ***
+ *** On BSD variants you may need to mount fdescfs(4) ***
+ **************************************************************************
+ *
+ * Unlike, e.g., GnuPG, ldappasswd(1) does not support passing a numeric fd
+ * from which to read a password. Since writing the password to a temporary
+ * file or passing on the command line is unacceptable, /dev/fd is the only
+ * secure method if we want a non-interactive option
+ */
+class rcube_ldappasswd_password
+{
+ public function save($currpass, $newpass)
+ {
+ if (!($cmd = self::expanded_cmd()))
+ return PASSWORD_ERROR;
+ $fds = array(0 => array('pipe', 'r'));
+ if ($cppipe = strpos($cmd, '%currpasspipe') !== false) {
+ $fds[3] = array('pipe', 'r');
+ $cmd = str_replace('%currpasspipe', '/dev/fd/3', $cmd);
+ }
+ $proc = proc_open($cmd, $fds, $pipes);
+ if (!is_resource($proc))
+ return PASSWORD_ERROR;
+ foreach($pipes as $pipe)
+ stream_set_blocking($pipe, 0);
+ fwrite($pipes[0], $newpass);
+ if ($cppipe)
+ fwrite($pipes[3], $currpass);
+ foreach($pipes as $pipe)
+ fclose($pipe);
+ if (($status = proc_close($proc)) == 0)
+ return PASSWORD_SUCCESS;
+ else {
+ rcube::raise_error(array(
+ 'code' => 600,
+ 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "ldappasswd exec failed, status=$status; $cmd"
+ ), true, false);
+ return PASSWORD_ERROR;
+ }
+ }
+
+ private function expanded_cmd()
+ {
+ $rcmail = rcmail::get_instance();
+ return self::substitute_vars(
+ $rcmail->config->get('password_ldappasswd_cmd'));
+ }
+
+ /**
+ * [Duplicated from ldap.php]
+ * XXX: Should this be called directly from ldap.php somehow? Our
+ * XXX: config comments promise compatibility and local macros are
+ * XXX: already implemented in another pass
+ * Substitute %login, %name, %domain, %dc in $str
+ * See plugin config for details
+ */
+ static function substitute_vars($str)
+ {
+ $str = str_replace('%login', $_SESSION['username'], $str);
+ $str = str_replace('%l', $_SESSION['username'], $str);
+
+ $parts = explode('@', $_SESSION['username']);
+
+ if (count($parts) == 2) {
+ $dc = 'dc='.strtr($parts[1], array('.' => ',dc=')); // hierarchal domain string
+
+ $str = str_replace('%name', $parts[0], $str);
+ $str = str_replace('%n', $parts[0], $str);
+ $str = str_replace('%dc', $dc, $str);
+ $str = str_replace('%domain', $parts[1], $str);
+ $str = str_replace('%d', $parts[1], $str);
+ }
+
+ return $str;
+ }
+}
_______________________________________________
Roundcube Development discussion mailing list
[email protected]
http://lists.roundcube.net/mailman/listinfo/dev