From: Ben Klang Date: Wed, 19 May 2010 01:06:25 +0000 (-0400) Subject: Import passwd from Horde CVS X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=2b06b89c4e2e1a154fb0c2e85139036ac036e74e;p=horde.git Import passwd from Horde CVS --- diff --git a/passwd/COPYING b/passwd/COPYING new file mode 100644 index 000000000..a6b67561a --- /dev/null +++ b/passwd/COPYING @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/passwd/README b/passwd/README new file mode 100644 index 000000000..0cc4bd741 --- /dev/null +++ b/passwd/README @@ -0,0 +1,87 @@ +What is Passwd? +=============== + +:Last update: $Date: 2008/10/09 17:12:03 $ +:Revision: $Revision: 1.5.2.2 $ +:Contact: sork@lists.horde.org + +.. contents:: Contents +.. section-numbering:: + +Passwd is the Horde password changing application. Right now, Passwd provides +fairly complete support for changing passwords via Poppassd, LDAP, Unix expect +scripts, the Unix smbpasswd command for SMB/CIFS passwords, Kolab, ADSI, Pine, +Serv-U FTP, VMailMgr, vpopmail, and SQL passwords. + +This software is OSI Certified Open Source Software. OSI Certified is a +certification mark of the `Open Source Initiative`_. + +.. _`Open Source Initiative`: http://www.opensource.org/ + + +Obtaining Passwd +------------------ + +Further information on Passwd and the latest version can be obtained at + + http://www.horde.org/passwd/ + + +Documentation +------------- + +The following documentation is available in the Passwd distribution: + +:README_: This file +:COPYING_: Copyright and license information +:`docs/CHANGES`_: Changes by release +:`docs/CREDITS`_: Project developers +:`docs/INSTALL`_: Installation instructions and notes +:`docs/TODO`_: Development TODO list + + +Installation +------------ + +Instructions for installing Passwd can be found in the file INSTALL_ in the +``docs/`` directory of the Passwd distribution. + + +Assistance +---------- + +If you encounter problems with Passwd, help is available! + +The Horde Frequently Asked Questions List (FAQ), available on the Web at + + http://www.horde.org/faq/ + +The Horde Project runs a number of mailing lists, for individual applications +and for issues relating to the project as a whole. Information, archives, and +subscription information can be found at + + http://www.horde.org/mail/ + +Lastly, Horde developers, contributors and users also make occasional +appearances on IRC, on the channel #horde on the freenode Network +(irc.freenode.net). + + +Licensing +--------- + +For licensing and copyright information, please see the file COPYING_ in the +Passwd distribution. + +Thanks, + +The Sork team + + +.. _README: ?f=README.html +.. _COPYING: http://www.horde.org/licenses/gpl.php +.. _docs/CHANGES: ?f=CHANGES.html +.. _docs/CREDITS: ?f=CREDITS.html +.. _INSTALL: +.. _docs/INSTALL: ?f=INSTALL.html +.. _docs/TODO: ?f=TODO.html diff --git a/passwd/config/.cvsignore b/passwd/config/.cvsignore new file mode 100644 index 000000000..e624d245b --- /dev/null +++ b/passwd/config/.cvsignore @@ -0,0 +1,4 @@ +backends.php +conf.php +conf.bak.php +hooks.php diff --git a/passwd/config/.htaccess b/passwd/config/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/passwd/config/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/passwd/config/backends.php.dist b/passwd/config/backends.php.dist new file mode 100644 index 000000000..b0600a60b --- /dev/null +++ b/passwd/config/backends.php.dist @@ -0,0 +1,548 @@ + 'Horde Authentication', + 'preferred' => '', + 'password policy' => array( + 'minLength' => 3, + 'maxLength' => 8, + 'maxSpace' => 0, + 'minUpper' => 1, + 'minLower' => 1, + 'minNumeric' => 1, + 'minSymbol' => 1 + ), + 'driver' => 'sql', + 'params' => array_merge($conf['sql'], + array('table' => 'horde_users', + 'user_col' => 'user_uid', + 'pass_col' => 'user_pass', + 'show_encryption' => false)), +); + +$backends['poppassd'] = array( + 'name' => 'Example Poppassd Server', + 'preferred' => '', + 'password policy' => array(), + 'driver' => 'poppassd', + 'params' => array( + 'host' => 'localhost', + 'port' => 106 + ) +); + +$backends['servuftp'] = array( + 'name' => 'Example Serv-U FTP Server', + 'preferred' => '', + 'password policy' => array(), + 'driver' => 'servuftp', + 'params' => array( + 'host' => 'localhost', + 'port' => 106, + 'timeout' => 30 + ) +); + +$backends['expect'] = array( + 'name' => 'Example Expect Script', + 'preferred' => '', + 'password policy' => array(), + 'driver' => 'expect', + 'params' => array( + 'program' => '/usr/bin/expect', + 'script' => dirname(__FILE__) . '/../scripts/passwd_expect', + 'params' => '-telnet -host localhost -output /tmp/passwd.log' + ) +); + +$backends['sudo_expect'] = array( + 'name' => 'Example Expect with Sudo Script', + 'preferred' => '', + 'password policy' => array(), + 'driver' => 'procopen', + 'params' => array( + 'program' => '/usr/bin/expect ' . dirname(__FILE__) . '/../scripts/passwd_expect -sudo' + ) +); + +$backends['smbpasswd'] = array( + 'name' => 'Example Samba Server', + 'preferred' => '', + 'password policy' => array(), + 'driver' => 'smbpasswd', + 'params' => array( + 'program' => '/usr/bin/smbpasswd', + 'host' => 'localhost' + ) +); + +// NOTE: to set the ldap userdn, see horde/config/hooks.php +$backends['ldap'] = array( + 'name' => 'Example LDAP Server', + 'preferred' => 'www.example.com', + 'password policy' => array( + 'minLength' => 3, + 'maxLength' => 8 + ), + 'driver' => 'ldap', + 'params' => array( + 'host' => 'localhost', + 'port' => 389, + 'basedn' => 'o=example.com', + 'uid' => 'uid', + // these attributes will enable shadow password policies. + // 'shadowlastchange' => 'shadowlastchange', + // 'shadowmin' => 'shadowmin', + // this will be appended to the username when looking for the userdn. + 'realm' => '', + 'encryption' => 'crypt', + // make sure the host == cn in the server certificate + 'tls' => false + ) +); + +// NOTE: to set the ldap userdn, see horde/config/hooks.php +$backends['ldapadmin'] = array( + 'name' => 'Example LDAP Server with Admin Bindings', + 'preferred' => 'www.example.com', + 'password policy' => array( + 'minLength' => 3, + 'maxLength' => 8 + ), + 'driver' => 'ldap', + 'params' => array( + 'host' => 'localhost', + 'port' => 389, + 'basedn' => 'o=example.com', + 'admindn' => 'cn=admin,o=example.com', + 'adminpw' => 'somepassword', + + // LDAP object key attribute + 'uid' => 'uid', + + // these attributes will enable shadow password policies. + // 'shadowlastchange' => 'shadowlastchange', + // 'shadowmin' => 'shadowmin', + 'attribute' => 'clearPassword', + + // this will be appended to the username when looking for the userdn. + 'realm' => '', + + // Use this filter when searching for the user's DN. + 'filter' => '', + + // Hash method to use when storing the password + 'encryption' => 'crypt', + + // Only applies to LDAP servers. If set, should be 0 or 1. See the LDAP + // documentation about the corresponding parameter REFERRALS. + // Windows 2003 Server require to set this parameter to 0 + //'referrals' => 0, + + + // Whether to enable TLS for this LDAP connection + // Note: make sure the host matches cn in the server certificate + 'tls' => false + ) +); + +// NOTE: to set the ldap userdn, see horde/config/hooks.php +// NOTE: to make work with samba 2.x schema you must change lm_attribute and +// nt_attribute +$backends['smbldap'] = array( + 'name' => 'Example Samba/LDAP Server', + 'preferred' => 'www.example.com', + 'password policy' => array( + 'minLength' => 3, + 'maxLength' => 8 + ), + 'driver' => 'smbldap', + 'params' => array( + 'host' => 'localhost', + 'port' => 389, + 'basedn' => 'o=example.com', + 'uid' => 'uid', + // This will be appended to the username when looking for the userdn. + 'realm' => '', + 'encryption' => 'crypt', + // Make sure the host == cn in the server certificate. + 'tls' => false, + // If any of the following attributes are commented out, they + // won't be set on the LDAP server. + 'lm_attribute' => 'sambaLMPassword', + 'nt_attribute' => 'sambaNTPassword', + 'pw_set_attribute' => 'sambaPwdLastSet', + 'pw_expire_attribute' => 'sambaPwdMustChange', + // The number of days until samba passwords expire. If this + // is commented out, passwords will never expire. + 'pw_expire_time' => 180, + ) +); + +$backends['sql'] = array ( + 'name' => 'Exampe SQL Server', + 'preferred' => '', + 'password policy' => array( + 'minLength' => 3, + 'maxLength' => 8, + 'maxSpace' => 0, + 'minUpper' => 1, + 'minLower' => 1, + 'minNumeric' => 1, + 'minSymbol' => 1 + ), + 'driver' => 'sql', + 'params' => array( + 'phptype' => 'mysql', + 'hostspec' => 'localhost', + 'username' => 'dbuser', + 'password' => 'dbpasswd', + 'encryption' => 'md5-hex', + 'database' => 'db', + 'table' => 'users', + 'user_col' => 'user_uid', + 'pass_col' => 'user_pass', + 'show_encryption' => false + // The following two settings allow you to specify custom queries for + // lookup and modify functions if special functions need to be + // performed. In places where a username or a password needs to be + // used, refer to this placeholder reference: + // %d -> gets substituted with the domain + // %u -> gets substituted with the user + // %U -> gets substituted with the user without a domain part + // %p -> gets substituted with the plaintext password + // %e -> gets substituted with the encrypted password + // + // 'query_lookup' => 'SELECT user_pass FROM horde_users WHERE user_uid = %u', + // 'query_modify' => 'UPDATE horde_users SET user_pass = %e WHERE user_uid = %u', + ) +); + +$backends['vmailmgr'] = array( + 'name' => 'Example VMailMgr Server', + 'preferred' => '', + 'password policy' => array(), + 'driver' => 'vmailmgr', + 'params' => array( + 'vmailinc' => '/your/path/to/the/vmail.inc' + ) +); + +$backends['vpopmail'] = array ( + 'name' => 'Example Vpopmail Server', + 'preferred' => '', + 'password policy' => array( + 'minLength' => 3, + 'maxLength' => 8, + 'maxSpace' => 0, + 'minUpper' => 0, + 'minLower' => 0, + 'minNumeric' => 0 + ), + 'driver' => 'vpopmail', + 'params' => array( + 'phptype' => 'mysql', + 'hostspec' => 'localhost', + 'username' => '', + 'password' => '', + 'encryption' => 'crypt', + 'database' => 'vpopmail', + 'table' => 'vpopmail', + 'name' => 'pw_name', + 'domain' => 'pw_domain', + 'passwd' => 'pw_passwd', + 'clear_passwd' => 'pw_clear_passwd', + 'use_clear_passwd' => true, + 'show_encryption' => true + ) +); + +$backends['pine'] = array( + 'name' => 'Example Pine Password File', + 'preferred' => '', + 'password policy' => array(), + 'driver' => 'pine', + 'no_reset' => true, + 'params' => array( + // FTP server information. + 'host' => 'localhost', + 'port' => '21', + 'path' => '', + 'file' => '.pinepw', + // Connect using the just-passed-in password? + 'use_new_passwd' => false, + // Host string to look for in the encrypted file. + 'imaphost' => 'localhost' + ) +); + +$backends['kolab'] = array( + 'name' => 'Local Kolab Server', + 'preferred' => '', + 'password policy' => array( + 'minLength' => 3, + 'maxLength' => 8 + ), + 'driver' => 'kolab', + 'params' => array() +); + +$backends['myscript'] = array( + 'name' => 'example.com', + 'preferred' => 'localhost', + 'password policy' => array(), + 'driver' => 'procopen', + 'params' => array( + 'program' => '/path/to/my/script + myargs' + ) +); + +// This is an example configuration for the http driver. This allows +// connecting to an arbitrary URL that contains a password change form. +// The params 'username','oldPasswd','passwd1', and 'passwd2' params should be +// set to the name of the respective form input elements on the html form. If +// there are additional form fields that the form requires, define them in the +// 'fields' array in the form 'formFieldName' => 'formFieldValue'. The driver +// attempts to determine the success or failure based on searching the returned +// html page for the values listed in the 'eval_results' array. +$backends['http'] = array ( + 'name' => 'Email password on IMAP server', + 'preferred' => '', + 'password policy' => array( + 'minLength' => 3, + 'maxLength' => 8, + 'maxSpace' => 0, + 'minUpper' => 0, + 'minLower' => 1, + 'minNumeric' => 1, + 'minSymbol' => 0 + ), + 'driver' => 'http', + 'params' => array( + 'url' => 'http://www.example.com/psoft/servlet/psoft.hsphere.CP', + 'username' => 'mbox', + 'oldPasswd' => 'old_password', + 'passwd1' => 'password', + 'passwd2' => 'password2', + 'fields' => array( + 'action' => 'change_mbox_password', + 'ftemplate' => 'design/mail_passw.html' + ), + 'eval_results' => array( + 'success' => 'Password successfully changed', + 'badPass' => 'Bad old password', + 'badUser' => 'Mailbox not found' + ) + ) +); + +$backends['soap'] = array( + 'name' => 'Example SOAP Server', + 'preferred' => '', + 'password policy' => array(), + 'driver' => 'soap', + 'params' => array( + // If this service doesn't have a WSDL, the 'location' and 'uri' + // parameters below must be specified instead. + 'wsdl' => 'http://www.example.com/service.wsdl', + 'method' => 'changePassword', + // This is the order of the arguments to the method specified above. + 'arguments' => array('username', 'oldpassword', 'newpassword'), + // These parameters are directly passed to the SoapClient object, see + // http://ww.php.net/manual/en/soapclient.soapclient.php for a + // complete list of possible parameters. + 'soap_params' => array( + 'location' => '', + 'uri' => '', + ), + ) +); + +// This is an example configuration for Postfix.admin 2.3. +// Set the 'password_policy' section as you wish. +// In most installations you probably only need to change the +// hostspec and /or password fields. +$backends['postfixadmin'] = array ( + 'name' => 'Postfix Admin server', + 'preferred' => 'true', + 'password policy' => array( + 'minLength' => 6, + 'maxLength' => 20, + 'maxSpace' => 0, + 'minUpper' => 1, + 'minLower' => 1, + 'minNumeric' => 1, + 'minSymbol' => 0 + ), + 'driver' => 'sql', + 'params' => array( + 'phptype' => 'mysql', + 'hostspec' => 'localhost', + 'username' => 'postfix', + 'password' => 'PASSWORD', + 'encryption' => 'crypt-md5', + 'database' => 'postfix', + 'table' => 'mailbox', + 'user_col' => 'username', + 'pass_col' => 'password', + 'show_encryption' => false, + // The following two settings allow you to specify custom queries for + // lookup and modify functions if special functions need to be + // performed. In places where a username or a password needs to be + // used, refer to this placeholder reference: + // %d -> gets substituted with the domain + // %u -> gets substituted with the user + // %U -> gets substituted with the user without a domain part + // %p -> gets substituted with the plaintext password + // %e -> gets substituted with the encrypted password + // + 'query_lookup' => 'SELECT password FROM mailbox WHERE username = %u and active = 1', + 'query_modify' => 'UPDATE mailbox SET password = %e WHERE username = %u' + ) +); + +// This is an example configuration for chaining multiple drivers to allow for +// syncing of passwords across many backends using the composite driver as a +// wrapper. +// +// Each of the subdrivers may contain an optional parameter called 'required' +// that, when set to true, will cause the rest of the drivers be skipped if a +// particular one fails. +$backends['composite'] = array( + 'name' => 'Example All Services', + 'preferred' => '', + 'password policy' => array( + 'minLength' => 3, + 'maxLength' => 8, + 'minClasses' => 2, + ), + 'driver' => 'composite', + 'params' => array('drivers' => array( + 'sql' => array( + 'name' => 'Horde Authentication', + 'driver' => 'sql', + 'required' => true, + 'params' => array( + 'phptype' => 'mysql', + 'hostspec' => 'localhost', + 'username' => 'horde', + 'password' => '', + 'encryption' => 'md5-hex', + 'database' => 'horde', + 'table' => 'horde_users', + 'user_col' => 'user_uid', + 'pass_col' => 'user_pass', + 'show_encryption' => false + // 'query_lookup' => '', + // 'query_modify' => '', + ), + ), + 'smbpasswd' => array( + 'name' => 'Samba Server', + 'driver' => 'smbpasswd', + 'params' => array( + 'program' => '/usr/bin/smbpasswd', + 'host' => 'localhost', + ), + ), + )), +); diff --git a/passwd/config/conf.xml b/passwd/config/conf.xml new file mode 100644 index 000000000..26e46d578 --- /dev/null +++ b/passwd/config/conf.xml @@ -0,0 +1,57 @@ + + + + + Menu settings + + + + + + + + + Backend Settings + hidden + + shown + hidden + + + + + + true + + root,bin,daemon,adm,lp,shutdown,halt,uucp,ftp,anonymous,nobody,httpd,operator,guest,diginext,bind,cyrus,courier,games,kmem,mailnull,man,mysql,news,postfix,sshd,tty,www + + + + + false + + + + true + false + false + false + + diff --git a/passwd/config/hooks.php.dist b/passwd/config/hooks.php.dist new file mode 100644 index 000000000..fe759e458 --- /dev/null +++ b/passwd/config/hooks.php.dist @@ -0,0 +1,79 @@ +_params['drivers'] as $backend => $config) { +// if ($backend == 'http') { +// $driver->_params['drivers']['http']['params']['be_username'] = $userid . '@example.com'; +// break; +// } +// } + // Return the userid unmodified by default. +// return $userid; +// } else { +// return $userid; +// } +// } +//} + +// Here is an example _passwd_hook_default_username function to set the +// username the passwd module sees when resetting passwords based on userid +// and realm. The default is to take a username of user@domain.tld and change +// it to user. If we want to leave it untouched, enable the hook and use this +// function. + +// if (!function_exists('_passwd_hook_default_username')) { +// function _passwd_hook_default_username($userid) +// { +// return $userid; +// } +// } + +// Here is an example _passwd_hook_userdn function that you can use to provide +// your ldap server with a userdn so that you do not have to perform anonymous +// binds. The function takes Auth::getAuth() as a parameter + +// if (!function_exists('_passwd_hook_userdn')) { +// function _passwd_hook_userdn($auth) +// { +// return 'uid=' . $auth . ',o=example.com'; +// } +// } + +// if (!function_exists('_passwd_password_changed')) { +// function _passwd_password_changed($user, $oldpassword, $newpassword) +// { +// Horde::logMessage(sprintf('User %s has changed his password.', $user), __FILE__, __LINE__, PEAR_LOG_NOTICE); +// } +// } diff --git a/passwd/docs/CHANGES b/passwd/docs/CHANGES new file mode 100644 index 000000000..eb33b8834 --- /dev/null +++ b/passwd/docs/CHANGES @@ -0,0 +1,264 @@ +---------- +v3.1.4-cvs +---------- + + + + +------ +v3.1.3 +------ + +[jan] Fix examples using 'minSymbols' instead of 'minSymbol' (Bug #8854). +[jan] Fix PHP error when changing passwords with LDAP driver (Bug #8686). + + +------ +v3.1.2 +------ + +[jan] Enable output compression (horde@albasoft.com, Bug #8649). +[jan] Add Croatian translation (Valentin Vidic ). +[jan] Add option to configure referrals for LDAP driver (manilal@ejyothi.com, + Request #8582). +[jan] Simplify and fix smbldap driver (Bug #8192). + + +------ +v3.1.1 +------ + +[cjh] SECURITY: Fix XSS when specifying an invalid backend key + (security@davidwharton.us, #8398). +[jan] Add hook that's called after successfully changing the password. +[jan] Add SOAP driver. +[jan] Add example configuration for Postfix Admin (Michael Brennen + ). +[jan] Fix binding to LDAP server if using the userdn hook. + + +---- +v3.1 +---- + +[cjh] Fix bad mode for STDERR in the procopen driver + (info@opensolutions.net, Bug #8022). + + +-------- +v3.1-RC2 +-------- + +[cjh] Don't include the old password in LDAP driver error messages + (Joffrey van Wageningen ). + + +-------- +v3.1-RC1 +-------- + +[cjh] Add missing enforcement of minSymbol password policy + (yann@pleiades.fr.eu.org, Bug #7260). +[cjh] Only update smbldap attributes for Samba users, and update all attributes + at once instead of one at a time (marco@csita.unige.it, Request #5977). +[cjh] Don't set smbldap attributes that are commented out in the config + (fabio.pedretti@ing.unibs.it, Request #5937). +[jan] Move all Passwd-specific hook examples from Horde's config/ directory. +[cjh] Add support for switching between encryption schemes + (ulrich-horde@topfen.net, Request #2865). +[jan] Add support for sudo to the example expect script (Dennis Voetelink + , Request #5300). +[mas] Conform to WCAG 1.0 Priority 2/Section 508 accessibility guidelines. + (Request #4080) +[jan] Add new expect driver that uses the expect PECL PHP extension (Duck + ). +[mjr] Add new http driver for changing passwords via an existing web form. +[mjr] Pass reference to driver to the _passwd_hook_username call. +[jan] Add Turkish translation (METU ). + + +------ +v3.0.1 +------ + +[jan] Add placeholders for domain and username parts to SQL driver queries + (Vilius Sumskas , Request #4985). +[jan] Improve error checking in poppassd driver (Bug #4505, + horde@koornneef.net). +[jan] Add Slovenian translation (Duck ). +[jan] Compare hashing algorithms in passwords case insensitively + (andreas@altroot.de, Bug #2708). +[jan] Allow password changing for special Kolab users (mzizka@hotmail.com, + Request #4128). +[jan] Add Japanese translation (Hiromi Kimura ). +[ben] Better support for MS-SQL +[cjh] Add support in the expect driver for rssh, scponly, and other programs + that can execute certain commands over ssh without providing a prompt + (Request #2887). + + +---- +v3.0 +---- + +[jan] Add configuration option to switch between using user names with and + without realms. +[jan] Add Slovak translation (Ivan Noris ). + + +-------- +v3.0-RC1 +-------- + +[jan] Extend the expect script to allow setups with passwd as the login shell + (Request #2550, Lionel Elie Mamane ). +[jan] Don't bind to LDAP anonymously if binding with userdn fails (Bug #2502). +[cjh] Fix updating shadowlastchange attribute in ldap driver (Roel Gloudemans + ). +[jan] Extend the smbldap driver from the ldap driver to support all parameters + of the ldap driver (Request #2499). + + +--------- +v3.0-BETA +--------- + +[cjh] Use Crypt_CHAP to generate smbldap passwords (Bug #1223). +[stb] Add Kolab driver. +[cjh] Allow using admin credentials in the LDAP driver (Bug #1409). +[cjh] Use bind variables in SQL drivers (selsky@columbia.edu, Bug #1718). +[cjh] Allow the list of refused usernames to be empty (Bug #1544). +[cjh] If $conf['user']['change'] is false, don't trust form input for + the userid. +[jan] Add Catalan translation (Joan Jorba Calsina ). +[cjh] Add ADSI and PSPASSWD windows password drivers (LRM + ). +[jan] Add Persian (Western) translation (Vahid Ghafarpour + ). +[jan] Add shadowLastChange and shadowMin configuration items to LDAP driver + (Roel Gloudemans ). +[cjh] Add proc_open() driver (Samuel Nicolary ). +[cjh] Add an SMB LDAP driver (Shane Boulter ). +[cjh] Add SSL support to the LDAP driver (LRM ). +[max] Add minSymbols and minClasses password policies. See comments in + config/backends.php.dist for more information. +[max] Add optional 'required' parameters to composite driver's subdriver + configurations. +[max] Add optional parameter 'no_reset' to the backend configs which prevents + reseting the authenticated user's credentials on password changes. +[max] Properly reset authenticated user's credentials. +[cjh] Use password encryption that's now implemented in the Auth:: package. +[max] Add pine driver which changes a pine-encoded file using FTP. +[max] Fix password checking when encryption requires a random salt. +[max] Add composite driver which will replace all the groups stuff. +[max] Add support for backend groups to allow syncing of multiple backends. +[max] Add query_lookup and query_modify parameters to the sql driver. +[ejr] Add tls support for ldap driver. +[ejr] Fix error reporting in poppassd driver. +[ejr] Add binddn hook submitted by Amith Varghese . +[ejr] make sure oldpassword == horde_login_password before changing horde + cached password. +[ejr] Fix md5 (md5-hex and md5-base64) encryption for ldap/sql (Amith Varghese + ). +[mc] Move templates to horde style directory layout, getName() -> getParam(). +[ejr] Implemented new CVS HEAD themes. +[ejr] Updated to new CVS HEAD notification system. + + +------ +v2.2.2 +------ + +[jan] Close XSS when setting the parent frame's page title by javascript (cjh). +[ejr] Configuration item for showing/changing username in the form. +[jan] Allow to set the protocol version in the LDAP driver (ben@alkaloid.net). +[cjh] Add crypt-blowfish and crypt-md5 encryption types. Also add crypt-des + for completion which is just an alias for crypt (max). +[jan] Add Estonian translation (Toomas Aas ). + + +------ +v2.2.1 +------ +[jan] Bug #40: Fix smbpasswd driver with non-bash shells (Christopher Huyler + ). +[jan] Add Simplified Chinese translation (Zhang Bo ). + + +---------- +v2.2.1-RC1 +---------- +[jan] Add Indonesian language (Slamin ). +[jan] Add Galician translation (Rafael Varela Pet , Guillermo + Mendez ). +[jan] Add Danish translation (Anders Bruun Olsen ). +[jan] Add Arabic (Syria) translation (Platinum Development Team + ). +[jan] Add Hungarian translation (Szabo Gyula ). +[jan] Add Romanian translation (Eugen Hoanca , + Marius Dragulescu ). +[jan] Add Lithuanian translation (Vilius Sumskas ). +[ejr] Fix BC breaks in vpopmail and sql drivers. + + +---- +v2.2 +---- + +[ejr] Allow bc for php versions that don't have the ctype extension. + + +-------- +v2.2-RC2 +-------- + +[ejr] Fix missing path for expect binary. +[ejr] Add missing scripts/ directory and missing expect script. +[ejr] Fix error reporting in expect driver (j.huinink@wanadoo.nl). +[ejr] Remove old realm code that was previously missed. +[ejr] Port servuftp driver to new backends.conf format. +[ejr] Return actual error message text in poppassd driver (submitted by + Leena Heino ). +[ejr] Change is_a(*, 'PEAR_Error') calls to PEAR::isError() calls for php bc. + + +-------- +v2.2-RC1 +-------- + +[ejr] Add vpopmail driver (Anton Nekhoroshikh ). +[mac] Add vmailmgr driver (Marco Kaiser ). +[ejr] Add expect script (Gaudenz Steinlin). +[ejr] Change ldap code to do self-password changes, add phpdoc. +[ejr] Update ldap driver (Tjeerd van der Zee). +[ejr] Reset Horde/IMP cached credentials when changing password. +[ejr] Add username hooks (mac). +[ejr] Rewrite driver system, add backends.conf system (mc). +[jan] Add Italian translation (Fichera Gianrico ). +[cjh] Close several small XSS vulnerabilities (Mitja Kolsek + ). + + +---- +v2.1 +---- + +[ejr] Add servuftp interface. +[ejr] Add exim sql backend. +[ejr] Add smbpasswd support. +[ejr] Move to driver driven system. +[mc] Add javascript checks for form input. +[jan] Add Bulgarian translation (Miroslav Pendev ). + + +---- +v2.0 +---- + +[ejr] Added optional checks for password lengths and strength testing. +[ejr] Remove check for prefs.php in notconfigured.inc. +[ejr] Change from short-tag +- Jan Schneider + + +Driver Developers +================= + +- Anton Nekhoroshikh +- Gaudenz Steinlin +- Ilya Krel +- Lucas +- Luiz R Malheiros (malheiros@gmail.com) +- Marco Kaiser +- Mattias Webjörn Eriksson +- Max Kalika +- Mike Cochrane +- René Lund Jensen +- Samuel Nicolary +- Shane Boulter +- Stuart Bingë +- Tjeerd van der Zee + + +Localization +============ + +===================== ====================================================== +Arabian (Syria) Platinum Development Team +Brazilian Portuguese Darci Antonio Tartari + Fabio de Almeida +Bulgarian Miroslav Pendev +Catalan Joan Jorba Calsina +Chinese (Simplified) Wenzhuo Zhang + Anna Chen +Chinese (Traditional) David Chang +Croatian Valentin Vidic +Czech Pavel Chytil +Dutch Tjeerd van der Zee + Resan Sa-Ardnuam +Danish Anders Bruun Olsen + Brian Truelsen +Estonian Toomas Aas + Alar Sing +Finnish Tero Matinlassi + Leena Heino +French Daniel Huhardeaux + Benoit St-André + Pierre Lachance +Galician Rafael Varela + Guillermo Mendez +German Jens A Tkotz + Jan Schneider +Hungarian Robert Szokovacs + Gyula Szabo +Indonesian Slamin +Italian Gianrico Fichera + Marko Djukic + Fabio Pedretti +Japanese Hiromi Kimura +Lithuanian Darius Matuliauskas + Vilius Å umskas +Norwegian Nynorsk Per-Stian Vatne +Persian MetaNET Amirkabir +Polish Mariusz Zynel + Piotr Adamcio + Tadeusz Lesiecki + Piotr Tarnowski +Portuguese João César Marigonda + Manuel Menezes de Sequeira +Romanian Marius Dragulescu + Eugen Hoanca +Russian Illya Belov +Slovak Ivan Noris +Slovenian Duck +Spanish Manuel Perez Ayala +Swedish Mattias Webjörn Eriksson +Turkish Middle East Technical University +===================== ====================================================== + + +Inactive Developers +=================== + +- Mathieu Clabaut + + +Special thanks +============== + +- Thanks to John Wedoff, who helped debug a session/login problem. +- Thanks to Oliver Schultze (OSL) for help with bug fixes, documentation + suggestions, and moral support. +- Thanks to Tero Tapani Matinlassi for pointing out my bad coding when I + rushed the realm changes into effect, and even providing the solution! +- Thanks to Tjeerd van der Zee and Mattias Webjörn Eriksson for all their help + and patience coding the LDAP support. +- Thanks to Amith Varghese for various contributions + including the LDAP binddn hook. diff --git a/passwd/docs/INSTALL b/passwd/docs/INSTALL new file mode 100644 index 000000000..bbaaf7430 --- /dev/null +++ b/passwd/docs/INSTALL @@ -0,0 +1,353 @@ +======================= + Installing Passwd 3.0 +======================= + +:Last update: $Date: 2009/06/10 08:20:39 $ +:Revision: $Revision: 1.31.2.4 $ +:Contact: sork@lists.horde.org + +.. contents:: Contents +.. section-numbering:: + +This document contains instructions for installing the Passwd Password +Changing Application. + +For information on the capabilities and features of Passwd, see the file +README_ in the top-level directory of the Passwd distribution. + + +Obtaining Passwd +================== + +Passwd can be obtained from the Horde website and FTP server, at + + http://www.horde.org/passwd/ + + ftp://ftp.horde.org/pub/passwd/ + +Or use the mirror closest to you: + + http://www.horde.org/mirrors.php + +Bleeding-edge development versions of Passwd are available via CVS; see the +file `horde/docs/HACKING`_ in the Horde distribution, or the website +http://www.horde.org/source/, for information on accessing the Horde CVS +repository. + + +Prerequisites +============= + +To function properly, Passwd **requires** the following: + +1. A working Horde installation. + + Passwd runs within the `Horde Application Framework`_, a set of common + tools for Web applications written in PHP. You must install Horde before + installing Passwd. + + .. Important:: Passwd 3.0 requires version 3.0+ of the Horde Framework - + earlier versions of Horde will **not** work. + + .. _`Horde Application Framework`: http://www.horde.org/horde/ + + The Horde Framework can be obtained from the Horde website and FTP server, + at + + http://www.horde.org/horde/ + + ftp://ftp.horde.org/pub/horde/ + + Many of Passwd's prerequisites are also Horde prerequisites. + + .. Important:: Be sure to have completed all of the steps in the + `horde/docs/INSTALL`_ file for the Horde Framework before + installing Passwd. + +2. The following PHP capabilities, depending on the drivers you want use: + + a. Character Type support ``--enable-ctype`` + + b. LDAP support ``--with-ldap`` [OPTIONAL] + + LDAP support is required for the kolab, ldap, and smbldap drivers. + + c. Mhash support ``--with-mhash`` [OPTIONAL] + + Mhash support is necessary for the smbldap driver. See `LDAP + Implementation Information`_ for details. + + d. Mcrypt support ``--with-mcrypt`` [OPTIONAL] + + Mcrypt support is necessary for the smbldap driver. See `LDAP + Implementation Information`_ for details. + + e. SOAP support ``--enable-soap`` [OPTIONAL] + + SOAP support is necessary for the soap driver. + +3. The following PEAR modules: + (See `horde/docs/INSTALL`_ for instructions on installing PEAR modules) + + a. Crypt_CHAP [OPTIONAL] + + Passwd uses this package to encrypt passwords for the smbldap driver. + +The following items might be required, depending on the drivers you want to +use: + +1. A poppassd server installed, running, and working on a system. + +2. An LDAP server installed, running, and working for authentication. + +3. A working smbpasswd program on the web server. It can access a remote + server, but the smbpasswd binary must be installed on the local system. + +4. A working SQL authentication system. + +5. Expect installed, and access to telnet or ssh to the machine where + passwords are stored/set (could be localhost). + +6. A SOAP service endpoint. + + +Installing Passwd +=================== + +Passwd is written in PHP, and must be installed in a web-accessible directory. +The precise location of this directory will differ from system to system. +Conventionally, Passwd is installed directly underneath Horde in the web +server's document tree. + +Since Passwd is written in PHP, there is no compilation necessary; simply +expand the distribution where you want it to reside and rename the root +directory of the distribution to whatever you wish to appear in the URL. For +example, with the Apache web server's default document root of +``/usr/local/apache/htdocs``, you would type:: + + cd /usr/local/apache/htdocs/horde + tar zxvf /path/to/passwd-x.y.z.tar.gz + mv passwd-x.y.z passwd + +and would then find Passwd at the URL:: + + http://your-server/horde/passwd/ + + +Configuring Passwd +==================== + +1. Configuring Horde for Passwd + + a. Register the application + + In ``horde/config/registry.php``, find the ``applications['passwd']`` + stanza. The default settings here should be okay, but you can change + them if desired. If you have changed the location of Passwd relative to + Horde, either in the URL, in the filesystem or both, you must update the + ``fileroot`` and ``webroot`` settings to their correct values. + +2. Configuring Passwd + + To configure Passwd, change to the ``config/`` directory of the installed + distribution, and make copies of all of the configuration ``dist`` files + without the ``dist`` suffix:: + + cd config/ + for foo in *.dist; do cp $foo `basename $foo .dist`; done + + Or on Windows:: + + copy *.dist *. + + Documentation on the format and purpose of those files can be found in each + file. You may edit these files if you wish to customize Passwd's + appearance and behavior. With one exception (``backends.php``) the + defaults will be correct for most sites. + + You must login to Horde as a Horde Administrator to finish the + configuration of Passwd. Use the Horde ``Administration`` menu item to get + to the administration page, and then click on the ``Configuration`` icon to + get the configuration page. Select ``Password`` from the selection list of + applications. Fill in or change any configuration values as needed. When + done click on ``Generate Password Configuration`` to generate the + ``conf.php`` file. If your web server doesn't have write permissions to + the Passwd configuration directory or file, it will not be able to write + the file. In this case, go back to ``Configuration`` and choose one of the + other methods to create the configuration file ``passwd/config/conf.php``. + + Note for international users: Passwd uses GNU gettext to provide local + translations of text displayed by applications; the translations are found + in the ``po/`` directory. If a translation is not yet available for your + locale (and you wish to create one), see the ``horde/po/README`` file, or + if you're having trouble using a provided translation, please see the + `horde/docs/TRANSLATIONS`_ file for instructions. + +3. Testing Passwd + + Once you have configured Passwd, bring up the included test page in your + Web browser to ensure that all necessary prerequisites have been met. See + the `horde/docs/INSTALL`_ document for further details on Horde test + scripts. If you installed Passwd as described above, the URL to the test + page would be:: + + http://your-server/horde/passwd/test.php + + +Security considerations +======================= + +.. Note:: Some drivers like poppassd always use clear text password + transmissions. Others may also use clear text passwords (e.g. the + expect script driver using telnet rather than ssh). This can be + considered somewhat safe if the server is on the same machine, there + are no user interactive logins allowed on that machine, and the + connection uses the loopback (localhost) interface. It is up to the + administrator to evaluate the security implications of using this + module, and to understand the security implications of how their + server and this module is configured. + +.. Note:: Setting this module to allow guest access might also be considered a + security risk. By default, guest access is disabled. It is up to + the administrator to evaluate the security implications of using + this module with guest access if they choose to do so. + + +Poppassd Server Software +======================== + +The following are some links that users have submitted. No warrenty is made +for the following links or any software obtained from then. These are all +user submitted links, and have not been tested or verified. Use these links +are your own risk! + +* http://www.ceti.com.pl/~kravietz/prog.html +* http://www.samera.net/rpm/ +* http://netwinsite.com/poppassd/ +* http://echelon.pl/pubs/poppassd-1.8.1.tar.gz + + +LDAP Implementation Information +=============================== + +The module includes support for changing LDAP stored passwords. + +LDAP can store passwords in multiple ways, namely: + +a. Plain text passwords +b. SHA encrypted passwords +c. SSHA encrypted passwords +d. Crypt encrypted passwords +e. MD5 encrypted passwords (using base64 encoding - md5-base64) +f. SMD5 encrypted passwords + +The smbldap extension require the php mhash and mcrypt extensions. To +enable these you must compile php with the ``--with-mhash[=DIR]`` and +``--with-mcrypt[=DIR]`` parameters. ``DIR`` is the mhash or mcrypt +install directory. + +Crypt cannot handle passwords longer than eight characters (it accepts them +but truncates them at 8 characters). If you want to use longer passwords, you +may use SHA. + +The user must be able to authenticate to the LDAP directory and change his own +password now (we no longer do root access to LDAP). This assumes that the +LDAP administrator has allowed everyone to write their own password, something +like:: + + access to attribute=userPassword + by self write + by anonymous auth + by * none + + +SQL Implementation Information +============================== + +The module includes support for changing passwords stored in a SQL database. +The SQL driver is similar to the LDAP driver except that it also supports MD5 +encryption using hex encoding (md5-hex). If you created your passwords using +the PHP md5() function then use md5-hex. + + +Smbpasswd Implementation Information +==================================== + +To use this module, you must pass the address of your Samba domain controller +on which to change the password. This can be "localhost" for the server that +Horde runs on. The remote system name may be passed as the NETBIOS name, the +DNS name, or the IP address of the SMB/CIFS server to connect to. The +username is run through ``escapeshellcmd()``, so any usernames with strange +characters ($, &, etc) may not work. I'm not sure if any of these are even +valid for SMB/CIFS authentication, but if so, they may not work in this +module. + +.. Note:: If changing a Windows NT Domain password the remote machine + specified must be the Primary Domain Controller for the domain + (Backup Domain Controllers only have a read-only copy of the user + account database and will not allow the password change). + +Since smbpasswd works in client-server mode communicating with a local smbd +for a non-root user, the smbd daemon must be running for this to work. A +common problem is to add a restriction to the hosts that may access the smbd +running on the local machine by specifying a allow hosts or deny hosts entry +in the ``smb.conf`` file and neglecting to allow "localhost" access to the +smbd. + +In addition, the smbpasswd command is only useful if Samba has been set up to +use encrypted passwords. + + +Expect Script Information +========================= + +This code allows users to change their passwords via an expect script. The +module requires the expect program and a telnet or ssh program. You may need +to change some of the expect patterns in ``scripts/passwd_expect`` such as the +``badpassword_string`` or ``success_string`` (as only two examples). + +For security reasons, it is generally suggested to use ssh rather than telnet +for the transport, if possible. + + +Expect PECL Information +======================= + +This code allows users to change their passwords via the PHP PECL expect +extension. It requires the PECL expect extension and a ssh program. + +You can find the extension on http://pecl.php.net/package/expect + + +Obtaining Support +================= + +If you encounter problems with Passwd, help is available! + +The Horde Frequently Asked Questions List (FAQ), available on the Web at + + http://www.horde.org/faq/ + +The Horde Project runs a number of mailing lists, for individual applications +and for issues relating to the project as a whole. Information, archives, and +subscription information can be found at + + http://www.horde.org/mail/ + +Lastly, Horde developers, contributors and users may also be found on IRC, +on the channel #horde on the Freenode Network (irc.freenode.net). + +Please keep in mind that Passwd is free software written by volunteers. For +information on reasonable support expectations, please read + + http://www.horde.org/support.php + +Thanks for using Passwd! + +The Passwd team + + +.. _README: ?f=README.html +.. _`horde/docs/HACKING`: ../../horde/docs/?f=HACKING.html +.. _`horde/docs/INSTALL`: ../../horde/docs/?f=INSTALL.html +.. _`horde/docs/TRANSLATIONS`: ../../horde/docs/?f=TRANSLATIONS.html diff --git a/passwd/docs/RELEASE_NOTES b/passwd/docs/RELEASE_NOTES new file mode 100644 index 000000000..7f8e870c8 --- /dev/null +++ b/passwd/docs/RELEASE_NOTES @@ -0,0 +1,37 @@ +notes['fm']['focus'] = array(Horde_Release::FOCUS_MINORBUG); + +/* Mailing list release notes. */ +$this->notes['ml']['changes'] = <<notes['fm']['changes'] = <<notes['name'] = 'Passwd'; +$this->notes['list'] = 'sork'; +$this->notes['fm']['project'] = 'passwd'; +$this->notes['fm']['branch'] = 'Horde 3'; diff --git a/passwd/docs/TODO b/passwd/docs/TODO new file mode 100644 index 000000000..984b4aa27 --- /dev/null +++ b/passwd/docs/TODO @@ -0,0 +1,27 @@ +============================== + Passwd Development TODO List +============================== + +:Last update: $Date: 2008/10/09 17:12:04 $ +:Revision: $Revision: 1.13.2.1 $ +:Contact: sork@lists.horde.org + +- There should be more error checking for various things, in particular sanity + checking of the form variables passed in. + +- Document issues of poppassd/ldap/smbpasswd perhaps allowing weak passwords + or otherwise getting around passwd restrictions. Document the password + length and strength tests, and how bad they really are. We could + investigate using cracklib calls, since it is supported by php, but that + would require cracklib to be installed on the system and in PHP. + +- Need to clean up the ldapd code still. A work in progress. Maybe pass the + password field "userPassword" as a config item so people can name it + differently if needed? + +- The exim sql driver defines a lot of stuff in conf.php that should already + be defined in Horde/IMP's configs. We should probably reuse the Horde/IMP + stuff rather than duplicating the configuration??? + +- Display the password policy to the user that he knows how the password + should look like (Request #2304). diff --git a/passwd/docs/UPGRADING b/passwd/docs/UPGRADING new file mode 100644 index 000000000..f17e0aeeb --- /dev/null +++ b/passwd/docs/UPGRADING @@ -0,0 +1,33 @@ +========================= + Upgrading to Passwd 3.x +========================= + +:Last update: $Date: 2008/10/09 17:12:04 $ +:Revision: $Revision: 1.3.2.1 $ +:Contact: sork@lists.horde.org + + +Upgrading to Passwd 3.1 +======================= + +Application Hooks +----------------- + +All hooks that are specific to Passwd have been moved from the +``horde/config/hooks.php`` file. Move your existing Passwd Hooks from there to +``passwd/config/hooks.php``. + +New Hook Parameters +------------------- + +The _passwd_hook_username hook is now passed an additional parameter, the +Passwd_Driver_* object for the backend that you are changing the password on. +This allows Passwd to support users being able to change passwords on multiple +backends even when the different backends may have different requirements for +the username (such as one requiring'user@example.com' while another only +requiring 'user'). Additionally, the Passwd_Driver_composite driver also +supports this functionality by checking for the existence of a parameter +called 'be_username' in each of it's sub-drivers. If this parameter is +present, its value overrides any other value of $userid it is passed. If you +wish to use this functionality of the composite driver, look at the example +_passwd_hook_username function in horde/config/hooks.php.dist. diff --git a/passwd/index.php b/passwd/index.php new file mode 100644 index 000000000..20ed95951 --- /dev/null +++ b/passwd/index.php @@ -0,0 +1,23 @@ + + * @package Passwd + */ + +@define('PASSWD_BASE', dirname(__FILE__)); +$passwd_configured = (is_readable(PASSWD_BASE . '/config/conf.php') && + is_readable(PASSWD_BASE . '/config/backends.php')); + +if (!$passwd_configured) { + require PASSWD_BASE . '/../lib/Test.php'; + Horde_Test::configFilesMissing('Passwd', PASSWD_BASE, array('conf.php', 'backends.php')); +} + +require PASSWD_BASE . '/main.php'; diff --git a/passwd/lib/.htaccess b/passwd/lib/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/passwd/lib/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/passwd/lib/Driver.php b/passwd/lib/Driver.php new file mode 100644 index 000000000..9c5aba9ca --- /dev/null +++ b/passwd/lib/Driver.php @@ -0,0 +1,132 @@ + + * @author Eric Rostetter + * @since Passwd 2.1 + * @package Passwd + */ +class Passwd_Driver { + + /** + * Hash containing configuration parameters. + * + * @var array + */ + var $_params = array(); + + /** + * Error string that will be returned to the user if an error occurs. + * + * @var string + */ + var $_errorstr; + + /** + * Constructs a new expect Passwd_Driver object. + * + * @param $params A hash containing connection parameters. + */ + function Passwd_Driver($params = array()) + { + $this->_params = $params; + } + + /** + * Compare a plaintext password with an encrypted password. + * + * @return mixed True if they match, PEAR_Error if they differe. + */ + function comparePasswords($encrypted, $plaintext) + { + if (preg_match('/^{[^}]+}/', $encrypted, $match)) { + $encrypted = substr($encrypted, strlen($match[0])); + $encryption = strtolower(substr($match[0], 1, strlen($match[0]) - 2)); + if ($this->_params['driver'] == 'ldap' && $encryption == 'md5') { + $encryption = 'md5-base64'; + } + } else { + $encryption = $this->_params['encryption']; + } + + $hashed = Auth::getCryptedPassword($plaintext, + $encrypted, + $encryption, + false); + + if ($this->_params['show_encryption']) { + /* Convert the hashing algorithm in both strings to uppercase. */ + $encrypted = preg_replace('/^({.*?})/e', "String::upper('\\1')", $encrypted); + $hashed = preg_replace('/^({.*?})/e', "String::upper('\\1')", $hashed); + } + + return ($encrypted == $hashed) ? true : PEAR::raiseError(_("Incorrect old password.")); + } + + /** + * Format a password using the current encryption. + * + * @param string $plaintext The plaintext password to encrypt. + * + * @return string The crypted password. + */ + function encryptPassword($plaintext) + { + return Auth::getCryptedPassword($plaintext, + '', + $this->_params['encryption'], + $this->_params['show_encryption']); + } + + /** + * Change the user's password. + * + * @param string $username The user for which to change the password. + * @param string $oldpassword The old (current) user password. + * @param string $new_password The new user password to set. + * + * @return boolean True or false based on success of the change. + */ + function changePassword($username, $oldpassword, $new_password) + { + return PEAR::raiseError(_("Backend not correctly implemented.")); + } + + /** + * Attempts to return a concrete Passwd_Driver instance based on + * $driver. + * + * @param string $driver The type of concrete passwd_Driver subclass + * to return. The is based on the passwd + * driver ($driver). The code is dynamically + * included. + * + * @param array $params (optional) A hash containing any additional + * configuration or connection parameters a + * subclass might need. + * + * @return mixed The newly created concrete Passwd_Driver + * instance, or false on an error. + */ + function factory($driver, $params = array()) + { + $driver = basename($driver); + require_once dirname(__FILE__) . '/Driver/' . $driver . '.php'; + $class = 'Passwd_Driver_' . $driver; + if (class_exists($class)) { + return new $class($params); + } else { + Horde::fatal(PEAR::raiseError(sprintf(_("No such backend \"%s\" found."), $driver)), __FILE__, __LINE__); + } + } + +} diff --git a/passwd/lib/Driver/adsi.php b/passwd/lib/Driver/adsi.php new file mode 100644 index 000000000..f09e12264 --- /dev/null +++ b/passwd/lib/Driver/adsi.php @@ -0,0 +1,68 @@ + + * $backends['adsi'] = array( + * 'name' => 'Sample ADSI backend', + * 'preferred' => 'localhost', + * 'password policy' => array( + * 'minLength' => 8, + * 'maxLength' => 14 + * ), + * 'driver' => 'adsi', + * 'params' => array( + * 'target' => 'YOUR_MACHINE/DOMAIN_NAME_HERE' + * ) + * ) + * + * + * Backend parameters: + * target = Target Windows machine/domain name (Required) + * + * $Horde: passwd/lib/Driver/adsi.php,v 1.4.2.5 2009/01/06 15:25:23 jan Exp $ + * + * Copyright 2004-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.horde.org/licenses/gpl.php. + * + * @author Luiz R Malheiros + * @since Passwd 3.0 + * @package Passwd + */ +class Passwd_Driver_adsi extends Passwd_Driver { + + function changePassword($user_name, $old_password, $new_password) + { + $target = isset($this->_params['target']) ? $this->_params['target'] : ''; + + if (empty($target)) { + return PEAR::raiseError(_("Password module is missing target parameter.")); + } + + $root = &new COM('WinNT:'); + + if ($adsi = $root->OpenDSObject('WinNT://' . $target . '/' . $user_name . ',user', $target . '\\' . $user_name, $old_password, 1)) { + $result = $adsi->ChangePassword($old_password, $new_password); + if ($result == 0) { + return true; + } else { + return PEAR::raiseError(sprintf(_("ADSI error %s."), $result)); + } + } else { + return PEAR::raiseError(_("Access Denied.")); + } + } + +} diff --git a/passwd/lib/Driver/composite.php b/passwd/lib/Driver/composite.php new file mode 100644 index 000000000..c4d7e2fd7 --- /dev/null +++ b/passwd/lib/Driver/composite.php @@ -0,0 +1,116 @@ + + * @since Passwd 3.0 + * @package Passwd + */ +class Passwd_Driver_composite extends Passwd_Driver { + + /** + * Hash of instantiated drivers. + * + * @var array + */ + var $_drivers = array(); + + /** + * State of the loaded drivers. + * + * @var boolean + */ + var $_loaded = false; + + /** + * Constructs a new Passwd_Driver_composite object. + * + * @param array $params A hash containing chained drivers and their parameters. + */ + function Passwd_Driver_composite($params = array()) + { + if (!isset($params['drivers']) || !is_array($params['drivers'])) { + return PEAR::raiseError(_("Required 'drivers' is misconfigured in Composite configuration.")); + } + + parent::Passwd_Driver($params); + } + + /** + * Instantiate configured drivers. + * + * @return boolean True on success or PEAR_Error on failure. + */ + function _loadDrivers() + { + if ($this->_loaded) { + return true; + } + + foreach ($this->_params['drivers'] as $key => $settings) { + if (!array_key_exists($key, $this->_drivers)) { + $res = Passwd_Driver::factory($settings['driver'], + $settings['params']); + if (is_a($res, 'PEAR_Error')) { + return PEAR::raiseError(sprintf(_("%s: unable to load driver: %s"), + $key, $res->getMessage())); + } + + $this->_drivers[$key] = $res; + } + } + + $this->_loaded = true; + return true; + } + + /** + * Change the user's password. + * + * @param string $username The user for which to change the password. + * @param string $old_password The old (current) user password. + * @param string $new_password The new user password to set. + * + * @return boolean True or false based on success of the change. + */ + function changePassword($username, $old_password, $new_password) + { + global $notification; + + $res = $this->_loadDrivers(); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + foreach ($this->_drivers as $key => $driver) { + if (isset($driver->_params['be_username'])) { + $user = $driver->_params['be_username']; + } else { + $user = $username; + } + $res = $driver->changePassword($user, $old_password, + $new_password); + if (is_a($res, 'PEAR_Error')) { + $res = PEAR::raiseError(sprintf(_("Failure in changing password for %s: %s"), + $this->_params['drivers'][$key]['name'], + $res->getMessage()), 'horde.error'); + if (!empty($this->_params['drivers'][$key]['required'])) { + return $res; + } else { + $notification->push($res, 'horde.warning'); + } + } + } + + return true; + } + +} diff --git a/passwd/lib/Driver/expect.php b/passwd/lib/Driver/expect.php new file mode 100644 index 000000000..aa92d2d86 --- /dev/null +++ b/passwd/lib/Driver/expect.php @@ -0,0 +1,60 @@ + + * @since Passwd 2.2 + * @package Passwd + */ +class Passwd_Driver_expect extends Passwd_Driver { + + /** + * Change the users password by executing an expect script. + * + * @param string $user User ID. + * @param string $old_password Old password. + * @param string $new_password New password. + * + * @return boolean True on success, false or error message on error. + */ + function changePassword($user, $old_password, $new_password) + { + global $conf; + + // Sanity checks. + if (!@is_executable($this->_params['program'])) { + return PEAR::raiseError(sprintf(_("%s does not exist or is not executable."), $this->_params['program'])); + } + + // Temporary logfile for error messages. + $log = tempnam(ini_get('upload_tmp_dir') ? + ini_get('upload_tmp_dir') : + '/tmp', + 'passwd'); + + // Open expect script for writing. + $prog = $this->_params['program'] . ' -f ' . $this->_params['script'] . + ' -- ' . $this->_params['params'] . ' -log ' . $log; + + $exp = @popen($prog, 'w'); + @fwrite($exp, "$user\n"); + @fwrite($exp, "$old_password\n"); + @fwrite($exp, "$new_password\n"); + if (@pclose($exp)) { + $errormsg = implode(' ', @file($log)); + @unlink($log); + return $errormsg ? PEAR::raiseError($errormsg) : false; + } + + return true; + } + +} diff --git a/passwd/lib/Driver/expectpecl.php b/passwd/lib/Driver/expectpecl.php new file mode 100644 index 000000000..4cdfaa1bc --- /dev/null +++ b/passwd/lib/Driver/expectpecl.php @@ -0,0 +1,145 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.horde.org/licenses/gpl.php. + * + * @author Duck + * @since Passwd 3.1 + * @package Passwd + */ +class Passwd_Driver_expectpecl extends Passwd_Driver { + + /** + * Expect connection handle. + * + * @var resource + */ + var $_stream; + + /** + * Handles expect communication. + * + * @param string $expect String to expect + * @param string $error Error message + * + * @return boolean|PEAR_Error True on success, PEAR_Error on failure + */ + function ctl($expect, $error) + { + $cases = array(array(0 => $expect, + 1 => 'ok', + 2 => EXP_REGEXP)); + + $result = expect_expectl($this->_stream, $cases); + + switch ($result) { + case EXP_EOF: + return PEAR::raiseError(_("End of file.")); + break; + case EXP_TIMEOUT: + return PEAR::raiseError(_("Time out.")); + break; + case EXP_FULLBUFFER: + return PEAR::raiseError(_("Full buffer.")); + break; + case 'ok': + return true; + break; + default: + return PEAR::raiseError($error); + break; + } + } + + /** + * Changes the users password by executing an expect script. + * + * @param string $user User ID. + * @param string $old_password Old password. + * @param string $new_password New password. + * + * @return boolean True on success, false or error message on error. + */ + function changePassword($user, $old_password, $new_password) + { + if (!Util::loadExtension('expect')) { + return PEAR::raiseError(sprintf(_("%s extension cannot be loaded!"), 'expect')); + } + + // Set up parameters + if (isset($this->_params['timeout'])) { + ini_set('expect.timeout', $this->_params['timeout']); + } + if (isset($this->_params['loguser'])) { + ini_set('expect.loguser', $this->_params['loguser']); + } + if (isset($this->_params['logfile'])) { + ini_set('expect.logfile', $this->_params['logfile']); + } + + // Open connection + $call = sprintf('ssh %s@%s %s', + $user, + $this->_params['host'], + $this->_params['program']); + if (!($this->_stream = expect_popen($call))) { + return PEAR::raiseError(_("Unable to open expect stream!")); + } + + // Log in + $result = $this->ctl('(P|p)assword.*', + _("Could not login to system (no password prompt)")); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + // Send login password + fwrite($this->_stream, "$old_password\n"); + + // Expect old password prompt + $result = $this->ctl('((O|o)ld|login|current).* (P|p)assword.*', + _("Could not start passwd program (no old password prompt)")); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + // Send old password + fwrite($this->_stream, "$old_password\n"); + + // Expect new password prompt + $result = $this->ctl('(N|n)ew.* (P|p)assword.*', + _("Could not change password (bad old password?)")); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + // Send new password + fwrite($this->_stream, "$new_password\n"); + + // Expect reenter password prompt + $result = $this->ctl("((R|r)e-*enter.*(P|p)assword|Retype new( UNIX)? password|(V|v)erification|(V|v)erify|(A|a)gain).*", + _("New password not valid (too short, bad password, too similar, ...)")); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + // Send new password + fwrite($this->_stream, "$new_password\n"); + + // Expect successfully message + $result = $this->ctl('((P|p)assword.* changed|successfully)', + _("Could not change password.")); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + return $result; + } +} diff --git a/passwd/lib/Driver/http.php b/passwd/lib/Driver/http.php new file mode 100644 index 000000000..140c6438e --- /dev/null +++ b/passwd/lib/Driver/http.php @@ -0,0 +1,80 @@ + + * @package Passwd + * @since Passwd 3.1 + */ +class Passwd_Driver_http extends Passwd_Driver { + + /** + * Constructs a new Passwd_Driver_http object. + * + * @param array $params A hash containing connection parameters. + */ + function Passwd_Driver_http($params = array()) + { + $this->_params = $params; + } + + /** + * Change the user's password. + * + * @param string $username The user for which to change the password. + * @param string $old_password The old (current) user password. + * @param string $new_password The new user password to set. + * + * @return mixed True on success or PEAR_Error on failure. + */ + function changePassword($username, $old_password, $new_password) + { + require_once 'HTTP/Request.php'; + + $req = new HTTP_Request($this->_params['url']); + $req->setMethod(HTTP_REQUEST_METHOD_POST); + + // Add the required fields that most web-based forms would use. + $req->addPostData($this->_params['username'], $username); + $req->addPostData($this->_params['oldPasswd'], $old_password); + $req->addPostData($this->_params['passwd1'], $new_password); + $req->addPostData($this->_params['passwd2'], $new_password); + + // Now add any fields that were passed in _params['fields']. + foreach ($this->_params['fields'] as $fieldName => $fieldValue) { + $req->addPostData($fieldName, $fieldValue); + } + + // Send the request + $result = $req->sendRequest(); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + // Make sure we have a good response code + $responseCode = $req->getResponseCode(); + if ($responseCode != 200) { + return PEAR::raiseError(_("The requested website for changing user passwords could not be reached.")); + } + + // We got *some* response from the server, so get the content and + // let's see if we can't figure out if it was a success or not. + $responseBody = $req->getResponseBody(); + if (strpos($responseBody, $this->_params['eval_results']['badPass'])) { + return PEAR::raiseError(_("Incorrect old password.")); + } elseif (strpos($responseBody, $this->_params['eval_results']['badUser'])) { + return PEAR::raiseError(_("The username could not be found.")); + } elseif (!strpos($responseBody, $this->_params['eval_results']['success'])) { + return PEAR::raiseError(_("Your password could not be changed.")); + } + return true; + } +} diff --git a/passwd/lib/Driver/kolab.php b/passwd/lib/Driver/kolab.php new file mode 100644 index 000000000..af118ba54 --- /dev/null +++ b/passwd/lib/Driver/kolab.php @@ -0,0 +1,100 @@ + + * @since Passwd 3.0 + * @package Passwd + */ +class Passwd_Driver_kolab extends Passwd_Driver { + + /** + * Constructs a new Passwd_Driver_kolab object. + * + * @param array $params A hash containing connection parameters. + */ + function Passwd_Driver_kolab($params = array()) + { + // We don't need any backends.php-configurable parameters + } + + /** + * Changes the user's password. + * + * @param string $username The user for which to change the password. + * @param string $old_password The old (current) user password. + * @param string $new_password The new user password to set. + * + * @return boolean True or false based on success of the change. + */ + function changePassword($username, $old_password, $new_password) + { + // Connect to the LDAP server. + $ds = ldap_connect( + $GLOBALS['conf']['kolab']['ldap']['server'], + $GLOBALS['conf']['kolab']['ldap']['port'] + ); + if (!$ds) { + return PEAR::raiseError(_("Could not connect to LDAP server")); + } + + ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); + + // Bind anonymously, or use the phpdn user if available. + if (!empty($GLOBALS['conf']['kolab']['ldap']['phpdn'])) { + $phpdn = $GLOBALS['conf']['kolab']['ldap']['phpdn']; + $phppw = $GLOBALS['conf']['kolab']['ldap']['phppw']; + $result = @ldap_bind($ds, $phpdn, $phppw); + } else { + $result = @ldap_bind($ds); + } + if (!$result) { + return PEAR::raiseError(_("Could not bind to LDAP server")); + } + + // Make sure we're using the full user@domain format. + if (strstr($username, '@') === false) { + $username .= '@' . $GLOBALS['conf']['kolab']['imap']['maildomain']; + } + + // Find the user's DN. + $result = ldap_search( + $ds, + $GLOBALS['conf']['kolab']['ldap']['basedn'], + "mail=$username" + ); + $entry = ldap_first_entry($ds, $result); + if ($entry === false) { + return PEAR::raiseError(_("User not found.")); + } + + $userdn = ldap_get_dn($ds, $entry); + + // Connect as the user. + $result = @ldap_bind($ds, $userdn, $old_password); + if (!$result) { + return PEAR::raiseError(_("Incorrect old password.")); + } + + // And finally change the password. + $new_details['userPassword'] = '{sha}' . + base64_encode(pack('H*', sha1($new_password))); + + if (!ldap_mod_replace($ds, $userdn, $new_details)) { + return PEAR::raiseError(ldap_error($ds)); + } + + ldap_unbind($ds); + + return true; + } + +} diff --git a/passwd/lib/Driver/ldap.php b/passwd/lib/Driver/ldap.php new file mode 100644 index 000000000..de00b005e --- /dev/null +++ b/passwd/lib/Driver/ldap.php @@ -0,0 +1,275 @@ + + * @author Tjeerd van der Zee + * @author Mattias Webjörn Eriksson + * @author Eric Jon Rostetter + * @package Passwd + */ +class Passwd_Driver_ldap extends Passwd_Driver { + + /** + * LDAP connection handle. + * + * @var resource + */ + var $_ds = false; + + /** + * The user's DN. + * + * @var string + */ + var $_userdn; + + /** + * Constructs a new Passwd_Driver_ldap object. + * + * @param array $params A hash containing connection parameters. + */ + function Passwd_Driver_ldap($params = array()) + { + $this->_params = array_merge( + array('host' => 'localhost', + 'port' => 389, + 'encryption' => 'crypt', + 'show_encryption' => 'true', + 'uid' => 'uid', + 'basedn' => '', + 'admindn' => '', + 'adminpw' => '', + 'realm' => '', + 'filter' => '', + 'tls' => false, + 'attribute' => 'userPassword', + 'shadowlastchange' => 'shadowLastChange', + 'shadowmin' => 'shadowMin'), + $params); + + if (!empty($this->_params['tls']) && + empty($this->_params['sslhost'])) { + $this->_params['sslhost'] = $this->_params['host']; + } + } + + /** + * Does an LDAP connect and binds as the guest user or as the optional + * userdn. + * + * @param string $userdn The dn to use when binding non-anonymously. + * @param string $oldpassword The password for $userdn. + * + * @return boolean True or False based on success of connect and bind. + */ + function _connect() + { + // See if we already have an open connection + if ($this->_ds) { + return true; + } + + if (!empty($this->_params['sslhost']) && empty($this->_params['tls'])) { + $this->_ds = ldap_connect('ldaps://' . $this->_params['sslhost']); + } else { + $this->_ds = ldap_connect($this->_params['host'], $this->_params['port']); + } + if (!$this->_ds) { + return PEAR::raiseError(_("Could not connect to LDAP server")); + } + + if (ldap_set_option($this->_ds, LDAP_OPT_PROTOCOL_VERSION, 3) && + $this->_params['tls']) { + if (!ldap_start_tls($this->_ds)) { + return PEAR::raiseError(_("Could not start TLS connection to LDAP server")); + } + } + + if (!empty($this->_params['referrals'])) { + if (!ldap_set_option($this->_ds, LDAP_OPT_REFERRALS, $this->_params['referrals'])) { + return PEAR::raiseError(_("Unable to disable directory referrals")); + } + } + + return true; + } + + + /** + * Bind (or re-bind) to an LDAP server with the given credentials. + * + * @param string $userdn Bind DN + * @param string $password Bind password + * + * @return mixed True on success; PEAR_Error on error + */ + function _bind($userdn = '', $password = '') + { + $result = false; + // Try to bind as the current userdn with password. + if (!empty($userdn)) { + $result = @ldap_bind($this->_ds, $userdn, $password); + } else { + $result = @ldap_bind($this->_ds); + } + + // If none of the bind attempts succeed, return error. + if (!$result) { + return PEAR::raiseError(_("Could not bind to LDAP server")); + } + } + + /** + * Changes the user's password. + * + * @param string $username The user for which to change the password. + * @param string $old_password The old (current) user password. + * @param string $new_password The new user password to set. + * + * @return boolean True or PEAR_Error based on success of the change. + */ + function changePassword($username, $old_password, $new_password) + { + // See if the old password matches before allowing the change + if ($old_password !== Auth::getCredential('password')) { + return PEAR::raiseError(_("Incorrect old password.")); + } + + // Bind as current user. _connect will try as guest if no user realm + // is found or auth error. + $result = $this->_connect(); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + // Append realm as username@realm if 'realm' parameter is set. + if (!empty($this->_params['realm'])) { + $username .= '@' . $this->_params['realm']; + } + + // Get the user's dn. + if ($GLOBALS['conf']['hooks']['userdn']) { + $this->_userdn = Horde::callHook('_passwd_hook_userdn', + array($username), + 'passwd'); + } else { + $this->_userdn = $this->_lookupdn($username, $old_password); + if (is_a($this->_userdn, 'PEAR_Error')) { + return $this->_userdn; + } + } + + // Connect as the admin DN if configured; otherwise as the user + if (!empty($this->_params['admindn'])) { + $result = $this->_bind($this->_params['admindn'], + $this->_params['adminpw']); + } else { + $result = $this->_bind($this->_userdn, $old_password); + } + + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + // Get existing user information + $result = ldap_read($this->_ds, $this->_userdn, 'objectClass=*'); + $entry = ldap_first_entry($this->_ds, $result); + if ($entry === false) { + return PEAR::raiseError(_("User not found.")); + } + + // Init the shadow policy array + $lookupshadow = array('shadowlastchange' => false, + 'shadowmin' => false); + + $information = @ldap_get_values($this->_ds, $entry, + $this->_params['shadowlastchange']); + if ($information) { + $lookupshadow['shadowlastchange'] = $information[0]; + } + + $information = @ldap_get_values($this->_ds, $entry, + $this->_params['shadowmin']); + if ($information) { + $lookupshadow['shadowmin'] = $information[0]; + } + + // Check if we may change the password + if ($lookupshadow['shadowlastchange'] && + $lookupshadow['shadowmin'] && + ($lookupshadow['shadowlastchange'] + $lookupshadow['shadowmin'] > (time() / 86400))) { + return PEAR::raiseError(_("Minimum password age has not yet expired")); + } + + // Change the user's password and update lastchange + $new_details[$this->_params['attribute']] = $this->encryptPassword($new_password); + + if (!empty($this->_params['shadowlastchange']) && + $lookupshadow['shadowlastchange']) { + $new_details[$this->_params['shadowlastchange']] = floor(time() / 86400); + } + + if (!@ldap_mod_replace($this->_ds, $this->_userdn, $new_details)) { + return PEAR::raiseError(ldap_error($this->_ds)); + } + + // Update the stored credential within the session + Auth::setCredential('password', $new_password); + + return true; + } + + /** + * Looks up and returns the user's dn. + * + * @param string $user The username of the user. + * @param string $passw The password of the user. + * + * @return string The ldap dn for the user. + */ + function _lookupdn($user, $passw) + { + // Search as an admin if so configured + if (!empty($this->_params['admindn'])) { + $this->_bind($this->_params['admindn'], $this->_params['adminpw']); + } else { + $this->_bind(); + } + + // Construct search. + $search = '(' . $this->_params['uid'] . '=' . $user . ')'; + + if (!empty($this->_params['filter'])) { + $search = '(&' . $search . '(' . $this->_params['filter'] . '))'; + } + + // Get userdn. + $result = ldap_search($this->_ds, $this->_params['basedn'], $search); + $entry = ldap_first_entry($this->_ds, $result); + if ($entry === false) { + return PEAR::raiseError(_("User not found.")); + } + + // If we used admin bindings, we have to check the password here. + if (!empty($this->_params['admindn'])) { + $ldappasswd = ldap_get_values($this->_ds, $entry, + $this->_params['attribute']); + $result = $this->comparePasswords($ldappasswd[0], $passw); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + } + + return ldap_get_dn($this->_ds, $entry); + } + +} diff --git a/passwd/lib/Driver/pine.php b/passwd/lib/Driver/pine.php new file mode 100644 index 000000000..a320c1c5f --- /dev/null +++ b/passwd/lib/Driver/pine.php @@ -0,0 +1,287 @@ + + * @since Passwd 3.0 + * @package Passwd + */ +class Passwd_Driver_pine extends Passwd_Driver { + + /** + * FTP connection handle. + * + * @var VFS + */ + var $_ftp; + + /** + * Boolean which contains state of the ftp connection. + * + * @var boolean + */ + var $_connected = false; + + /** + * Contents array of the pine password file. + * + * @var array + */ + var $_contents = array(); + + /** + * Constructs a new pine Passwd_Driver object. + * + * @param array $params A hash containing connection parameters. + */ + function Passwd_Driver_pine($params = array()) + { + /* We self-encrypt here, so plaintext is needed. */ + $this->_params['encryption'] = 'plain'; + $this->_params['show_encryption'] = false; + + /* Sensible FTP server parameters. */ + $this->_params['host'] = isset($params['host']) ? $params['host'] : 'localhost'; + $this->_params['port'] = isset($params['port']) ? $params['port'] : '21'; + $this->_params['path'] = isset($params['path']) ? $params['path'] : ''; + $this->_params['file'] = isset($params['file']) ? $params['file'] : '.pinepw'; + + /* Connect to FTP server using just-passed-in credentials? + * Only useful if using the composite driver and changing + * system (FTP) password prior to this one. */ + $this->_params['use_new_passwd'] = isset($params['use_new_passwd']) ? $params['use_new_passwd'] : false; + + /* What host to look for on each line? */ + $this->_params['imaphost'] = isset($params['imaphost']) ? $params['imaphost'] : 'localhost'; + } + + /** + * Connect to the FTP server. + * + * @return mixed True on success or a PEAR_Error object on failure. + */ + function _connect($user, $password) + { + if (!$this->_connected) { + /* Connect to the FTP server using the supplied + * parameters. */ + require_once 'VFS.php'; + + $params = array( + 'username' => $user, + 'password' => $password, + 'hostspec' => $this->_params['host'], + 'port' => $this->_params['port'], + ); + + $res = $this->_ftp = &VFS::singleton('ftp', $params); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + $res = $this->_ftp->checkCredentials(); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + } + + return true; + } + + /** + * Disconnect from the FTP server and clean up the connection. + * + * @return mixed True on success or a PEAR_Error object on failure. + */ + function _disconnect() + { + if ($this->_connected) { + $this->_connected = false; + return $this->_ftp->disconnect(); + } + + return true; + } + + /** + * Decodes a Pine-encoded password string. The algorithm is + * borrowed from read_passfile() and xlate_out() functions in + * pine/imap.c file distributed in the Pine source archive. + * + * @param string $string The contents of a pine-encoded password file. + * + * @return array List of lines of decoded elements. + */ + function _decode($string) + { + $list = array(); + + $lines = explode("\n", $string); + for ($n = 0; $n < sizeof($lines); $n++) { + $key = $n; + $tmp = $lines[$n]; + for ($i = 0; $i < strlen($tmp); $i++) { + if ((ord($tmp[$i]) >= FIRSTCH) && (ord($tmp[$i]) <= LASTCH)) { + $xch = ord($tmp[$i]) - ($dti = $key); + $xch += ($xch < FIRSTCH - TABSZ) ? 2 * TABSZ : ($xch < FIRSTCH) ? TABSZ : 0; + $dti = ($xch - FIRSTCH) + $dti; + $dti -= ($dti >= 2 * TABSZ) ? 2 * TABSZ : ($dti >= TABSZ) ? TABSZ : 0; + $key = $dti; + $tmp[$i] = chr($xch); + } + } + + if ($i && $tmp[$i - 1] == "\n") { + $tmp = substr($tmp, 0, -1); + } + + $parts = explode("\t", $tmp); + if (count($parts) >= 4) { + $list[] = $parts; + } + } + + return $list; + } + + /** + * Encodes an array of elements into a Pine-readable password string. + * The algorith is borrowed from write_passfile() and xlate_in() functions + * in pine/imap.c file distributed in the Pine source archive. + * + * @param array $lines List of lines of decoded elements + * + * @return array Contents of a pine-readable password file. + */ + function _encode($lines) + { + $string = ''; + for ($n = 0; $n < sizeof($lines); $n++) { + if (isset($lines[$n][4])) { + $lines[$n][4] = "\t" . $lines[$n][4]; + } else { + $lines[$n][4] = ''; + } + + $key = $n; + $tmp = vsprintf("%.100s\t%.100s\t%.100s\t%d%s\n", $lines[$n]); + for ($i = 0; $i < strlen($tmp); $i++) { + $eti = $key; + if ((ord($tmp[$i]) >= FIRSTCH) && (ord($tmp[$i]) <= LASTCH)) { + $eti += ord($tmp[$i]) - FIRSTCH; + $eti -= ($eti >= 2 * TABSZ) ? 2 * TABSZ : ($eti >= TABSZ) ? TABSZ : 0; + $key = $eti; + $tmp[$i] = chr($eti + FIRSTCH); + } + } + + $string .= $tmp; + } + + return $string; + } + + /** + * Find out if a username and password is valid. + * + * @param string $user The userID to check. + * @param string $oldPassword An old password to check. + * + * @return mixed True on success or a PEAR_Error object on failure. + */ + function _lookup($user, $oldPassword) + { + $contents = $this->_ftp->read($this->_params['path'], $this->_params['file']); + if (is_a($contents, 'PEAR_Error')) { + return $contents; + } + + $this->_contents = $this->_decode($contents); + foreach ($this->_contents as $line) { + if ($line[1] == $user && + (($line[2] == $this->_params['imaphost']) || + (!empty($line[4]) && $line[4] == $this->_params['imaphost']))) { + return $this->comparePasswords($line[0], $oldPassword); + } + } + + return PEAR::raiseError(_("User not found.")); + } + + /** + * Modify (update) a pine password record for a user. + * + * @param string $user The user whose record we will udpate. + * @param string $newPassword The new password value to set. + * + * @return mixed True on success or a PEAR_Error object on failure. + */ + function _modify($user, $newPassword) + { + for ($i = 0; $i < sizeof($this->_contents); $i++) { + if ($this->_contents[$i][1] == $user && + (($this->_contents[$i][2] == $this->_params['imaphost']) || + (!empty($this->_contents[$i][4]) && + $this->_contents[$i][4] == $this->_params['imaphost']))) { + $this->_contents[$i][0] = $newPassword; + } + } + + $string = $this->_encode($this->_contents); + return $this->_ftp->writeData($this->_params['path'], $this->_params['file'], $string); + } + + /** + * Change the user's password. + * + * @param string $username The user for which to change the password. + * @param string $oldPassword The old (current) user password. + * @param string $newPassword The new user password to set. + * + * @return mixed True on success or a PEAR_Error object on failure. + */ + function changePassword($username, $oldPassword, $newPassword) + { + /* Connect to the ftp server. */ + $res = $this->_connect($username, $this->_params['use_new_passwd'] ? $newPassword : $oldPassword); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + /* Check the current password. */ + $res = $this->_lookup($username, $oldPassword); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + $res = $this->_modify($username, $newPassword); + + $this->_disconnect(); + + return $res; + } + +} diff --git a/passwd/lib/Driver/poppassd.php b/passwd/lib/Driver/poppassd.php new file mode 100644 index 000000000..b8fba7121 --- /dev/null +++ b/passwd/lib/Driver/poppassd.php @@ -0,0 +1,132 @@ + + * @package Passwd + */ +class Passwd_Driver_poppassd extends Passwd_Driver { + + /** + * Socket connection. + * + * @var resource + */ + var $_fp; + + /** + * Constructs a new poppassd Passwd_Driver object. + * + * @param array $params A hash containing connection parameters. + */ + function Passwd_Driver_poppassd($params = array()) + { + $this->_params['host'] = array_key_exists('host', $params) ? $params['host'] : 'localhost'; + $this->_params['port'] = array_key_exists('port', $params) ? $params['port'] : 106; + } + + /** + * Connect to the server + */ + function _connect() + { + $this->_fp = fsockopen($this->_params['host'], $this->_params['port'], $errno, $errstr, 30); + if (!$this->_fp) { + return PEAR::raiseError($errstr); + } else { + return $this->_getPrompt(); + } + } + + /** + * Disconnect from the server + */ + function _disconnect() + { + if (isset($this->_fp)) { + fputs($this->_fp, "quit\n"); + fclose($this->_fp); + } + } + + /** + * Parse a response from the server to see what it was + */ + function _getPrompt() + { + $prompt = fgets($this->_fp, 4096); + if (!$prompt) { + return PEAR::raiseError(_("No prompt returned from server.")); + } + if (preg_match('/^[1-5][0-9][0-9]/', $prompt)) { + $rc = substr($prompt, 0, 3); + /* This should probably be a regex match for 2?0 or 3?0, no? */ + if ($rc == '200' || $rc == '220' || $rc == '250' || $rc == '300' ) { + return true; + } else { + return PEAR::raiseError($prompt); + } + } else { + return true; + } + } + + /** + * Send a command to the server. + */ + function _sendCommand($cmd, $arg) + { + $line = $cmd . ' ' . $arg . "\n"; + $res_fputs = fputs($this->_fp, $line); + if (!$res_fputs) { + return PEAR::raiseError(_("Cannot send command to server.")); + } + return $this->_getPrompt(); + } + + /** + * Change the user's password. + * + * @param string $username The user for which to change the password. + * @param string $old_password The old (current) user password. + * @param string $new_password The new user password to set. + * + * @return boolean True or false based on success of the change. + */ + function changePassword($username, $old_password, $new_password) + { + $res = $this->_connect(); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + $res = $this->_sendCommand('user', $username); + if (is_a($res, 'PEAR_Error')) { + $this->_disconnect(); + return PEAR::raiseError(_("User not found")); + } + + $res = $this->_sendCommand('pass', $old_password); + if (is_a($res, 'PEAR_Error')) { + $this->_disconnect(); + return PEAR::raiseError(_("Incorrect old password.")); + } + + $res = $this->_sendCommand('newpass', $new_password); + $this->_disconnect(); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + return true; + } + +} diff --git a/passwd/lib/Driver/procopen.php b/passwd/lib/Driver/procopen.php new file mode 100644 index 000000000..f70146471 --- /dev/null +++ b/passwd/lib/Driver/procopen.php @@ -0,0 +1,74 @@ + + * @since Passwd 3.0 + * @package Passwd + */ +class Passwd_Driver_procopen extends Passwd_Driver { + + /** + * Change the user's password by executing a user supplied command. + * + * @param string $user User ID. + * @param string $oldpass Old password. + * @param string $newpass New password. + * + * @return boolean True on success, false or error message on error. + */ + function changePassword($user, $oldpass, $newpass) + { + global $conf; + + $descriptorspec = array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w')); + + $output = ''; + + $process = @proc_open($this->_params['program'], $descriptorspec, $pipes); + if (is_resource($process)) { + fwrite($pipes[0], "$user\n"); + fwrite($pipes[0], "$oldpass\n"); + fwrite($pipes[0], "$newpass\n"); + fclose($pipes[0]); + while (!feof($pipes[1])) { + $output .= fgets($pipes[1], 1024); + } + fclose($pipes[1]); + while (!feof($pipes[2])) { + $output .= fgets($pipes[2], 1024); + } + fclose($pipes[2]); + $return_value = proc_close($process); + } else { + $output = ''; + $return_value = -1; + } + + $output .= " (Exit Status: $return_value)"; + + if ($return_value != 0) { + return $output ? PEAR::raiseError($output) : false; + } else { + return true; + } + } + +} diff --git a/passwd/lib/Driver/pspasswd.php b/passwd/lib/Driver/pspasswd.php new file mode 100644 index 000000000..520ae1580 --- /dev/null +++ b/passwd/lib/Driver/pspasswd.php @@ -0,0 +1,111 @@ + + * $backends['pspasswd'] = array( + * 'name' => 'Sample pspasswd backend', + * 'preferred' => 'localhost', + * 'password policy' => array( + * 'minLength' => 8, + * 'maxLength' => 14 + * ), + * 'driver' => 'pspasswd', + * 'params' => array( + * 'server' => 'YOUR_SERVER_NAME', + * 'bin' => 'DRIVE:\\DIR\\pspasswd.exe', // Notice: "\\" + * 'admusr' => 'Administrator', + * 'admpwd' => 'Password', + * 'domain' => 'YOUR_DOMAIN_NAME' + * ) + * ); + * + * + * Backend parameters:
+ * server	= Machine where you want to change the password (Required)
+ * bin		= Full pathname of the pspasswd.exe program (Required)
+ * admusr	= User with administrative privileges (Required)
+ * admpwd	= Password of the administrative user (Required)
+ * domain	= Windows domain name (Optional)
+ * 
+ * + * For example: Passing a NT4 PDC server name to the server parameter + * means you can change the user's password on that NT4 Domain. + * + * Special thanks to Mark Russinovich (mark@sysinternals.com) for the + * tool and helping me solve some questions about it. + * + * $Horde: passwd/lib/Driver/pspasswd.php,v 1.2.2.5 2009/01/06 15:25:23 jan Exp $ + * + * Copyright 2004-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.horde.org/licenses/gpl.php. + * + * @author Luiz R Malheiros (malheiros@gmail.com) + * @since Passwd 3.0 + * @package Passwd + */ +class Passwd_Driver_pspasswd extends Passwd_Driver { + + function changePassword($user_name, $old_password, $new_password) + { + $server = isset($this->_params['server']) ? $this->_params['server'] : ''; + $bin = isset($this->_params['bin']) ? $this->_params['bin'] : ''; + $admusr = isset($this->_params['admusr']) ? $this->_params['admusr'] : ''; + $admpwd = isset($this->_params['admpwd']) ? $this->_params['admpwd'] : ''; + $domain = isset($this->_params['domain']) ? $this->_params['domain'] : ''; + + if ($server == '' || $bin == '' || $admusr == '' || $admpwd == '') { + return PEAR::raiseError(_("Password module is missing required parameters.")); + } elseif (file_exists($bin) == false) { + return PEAR::raiseError(_("Password module can't find the supplied bin.")); + } + + if ($domain != '') { + $chpwd_adm = $domain . "\\" . $admusr; + $chpwd_usr = $domain . "\\" . $user_name; + } else { + $chpwd_adm = $admusr; + $chpwd_usr = $user_name; + } + + exec('NET USE \\\\' . $server . '\\IPC$ /D >NUL 2>NUL'); + + $cmdline = 'NET USE \\\\' . $server . '\\IPC$ "' . $old_password . '" /USER:' . $chpwd_usr; + + exec($cmdline, $cmdreply, $retval); + + if (strpos(implode(' ', $cmdreply), 'The command completed successfully.') === false) { + return PEAR::raiseError(_("Failed to verify old password.")); + } + + exec('NET USE \\\\' . $server . '\\IPC$ /D >NUL 2>NUL'); + + $cmdline = $bin . ' \\\\' . $server . ' -u ' . $chpwd_adm . ' -p ' . $admpwd . ' ' . $user_name . ' ' . $new_password; + + exec($cmdline, $cmdreply, $retval); + + exec('NET USE \\\\' . $server . '\\IPC$ /D >NUL 2>NUL'); + + if (strpos(implode(' ', $cmdreply), 'Password for ' . $server . '\\' . $user_name . ' successfully changed.') === false) { + return PEAR::raiseError(_("Access Denied.")); + } + + return true; + } + +} diff --git a/passwd/lib/Driver/servuftp.php b/passwd/lib/Driver/servuftp.php new file mode 100644 index 000000000..d94e8962f --- /dev/null +++ b/passwd/lib/Driver/servuftp.php @@ -0,0 +1,120 @@ +fp = fsockopen($server, $port, $errno, $errstr, $timeout); + + if (!$this->fp) { + $this->_errorstr = $errstr; + return false; + } else { + return $this->getPrompt(); + } + } + + function _disconnect() + { + if ($this->fp) { + fputs($this->fp, "quit\n"); + fclose($this->fp); + } + } + + function getPrompt() + { + $prompt = fgets($this->fp, 4096); + $return = ''; + + if (preg_match('/^[1-5][0-9][0-9]/', $prompt, $res)) { + $return = $res[1]; + } + + return $return; + } + + function sendCommand($cmd, $arg) + { + $line = $cmd . ' ' . $arg . "\r\n"; + fputs($this->fp, $line); + return $this->getPrompt(); + } + + function changePassword($user_name, $old_password, $new_password) + { + $server = isset($this->_params['host']) ? $this->_params['host'] : ''; + $port = isset($this->_params['port']) ? $this->_params['port'] : ''; + $timeout = isset($this->_params['timeout']) ? $this->_params['timeout'] : ''; + + if ($server == '' || $port == '') { + $this->_errorstr = _("Password module is not properly configured"); + return false; + } + + $return_value = false; + if ($this->connect($server, $port, $timeout) == $this->ftpd_connected) { + if ($this->sendCommand('user', $user_name) == $this->ftpd_usernameok) { + if ($this->sendCommand('pass', $old_password) == $this->ftpd_passwordok) { + if ($this->sendCommand('site pswd', '"'.$old_password.'" "'.$new_password.'"') == $this->ftpd_passwordok) { + $return_value = true; + } + } + } + + $this->_disconnect(); + } + + return $return_value; + } + + function checkPassword($user_name, $user_password) + { + $server = isset($this->_params['host']) ? $this->_params['host'] : ''; + $port = isset($this->_params['port']) ? $this->_params['port'] : ''; + $timeout = isset($this->_params['timeout']) ? $this->_params['timeout'] : ''; + + if ($server == '' || $port == '') { + $this->_errorstr = _("Password module is not properly configured."); + return false; + } + + $return_value = false; + + if ($this->connect($server, $port, $timeout) == $this->ftpd_connected) { + if ($this->sendCommand('user', $user_name) == $this->ftpd_usernameok) { + if ($this->sendCommand('pass', $user_password) == $this->ftpd_passwordok) { + $return_value = true; + } + } + + $this->_disconnect(); + } else { + // Cannot connect. + $return_value = -1; + } + + return $return_value; + } + +} diff --git a/passwd/lib/Driver/smbldap.php b/passwd/lib/Driver/smbldap.php new file mode 100644 index 000000000..e60b90241 --- /dev/null +++ b/passwd/lib/Driver/smbldap.php @@ -0,0 +1,109 @@ + + * @author Mike Cochrane + * @author Tjeerd van der Zee + * @author Mattias Webjörn Eriksson + * @author Eric Jon Rostetter + * @since Passwd 3.0 + * @package Passwd + */ +class Passwd_Driver_smbldap extends Passwd_Driver_ldap { + + /** + * Constructs a new Passwd_Driver_smbldap object. + * + * @param array $params A hash containing connection parameters. + */ + function Passwd_Driver_smbldap($params = array()) + { + $params = array_merge(array('lm_attribute' => null, + 'nt_attribute' => null, + 'pw_set_attribute' => null, + 'pw_expire_attribute' => null, + 'pw_expire_time' => null, + 'smb_objectclass' => 'sambaSamAccount'), + $params); + parent::Passwd_Driver_ldap($params); + } + + /** + * Change the user's password. + * + * @param string $username The user for which to change the password. + * @param string $old_password The old (current) user password. + * @param string $new_password The new user password to set. + * + * @return boolean True or false based on success of the change. + */ + function changePassword($username, $old_password, $new_password) + { + $result = parent::changePassword($username, $old_password, $new_password); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + // Return success if the user is not a Samba user + if (!@ldap_compare($this->_ds, $this->_userdn, 'objectClass', $this->_params['smb_objectclass'])) { + return true; + } + + require_once 'Crypt/CHAP.php'; + $hash = new Crypt_CHAP_MSv2(); + $hash->password = $new_password; + $lmpasswd = strtoupper(bin2hex($hash->lmPasswordHash())); + $ntpasswd = strtoupper(bin2hex($hash->ntPasswordHash())); + $settime = time(); + + if (!is_null($this->_params['pw_expire_time'])) { + // 24 hours/day * 60 min/hour * 60 secs/min = 86400 seconds/day + $expiretime = $settime + ($this->_params['pw_expire_time'] * 86400); + } else { + // This is NT's version of infinity time: + // http://lists.samba.org/archive/samba/2004-January/078175.html + $expiretime = 2147483647; + } + + // All changes must succeed or fail together. Attributes with + // null name are not updated. + $changes = array(); + if (!is_null($this->_params['lm_attribute'])) { + $changes[$this->_params['lm_attribute']] = $lmpasswd; + } + if (!is_null($this->_params['nt_attribute'])) { + $changes[$this->_params['nt_attribute']] = $ntpasswd; + } + if (!is_null($this->_params['pw_set_attribute'])) { + $changes[$this->_params['pw_set_attribute']] = $settime; + } + if (!is_null($this->_params['pw_expire_attribute'])) { + $changes[$this->_params['pw_expire_attribute']] = $expiretime; + } + + if (count($changes) > 0) { + if (!ldap_mod_replace($this->_ds, $this->_userdn, $changes)) { + return PEAR::raiseError(ldap_error($this->_ds)); + } + } + + return true; + } + +} diff --git a/passwd/lib/Driver/smbpasswd.php b/passwd/lib/Driver/smbpasswd.php new file mode 100644 index 000000000..40113306e --- /dev/null +++ b/passwd/lib/Driver/smbpasswd.php @@ -0,0 +1,136 @@ + + * @package Passwd + */ +class Passwd_Driver_smbpasswd extends Passwd_Driver { + + /** + * Socket connection resource. + * + * @var resource + */ + var $_fp; + + /** + * Constructs a new smbpasswd Passwd_Driver object. + * + * @param array $params A hash containing connection parameters. + */ + function Passwd_Driver_smbpasswd($params = array()) + { + $this->_params = array_merge(array('host' => 'localhost', + 'program' => '/usr/bin/smbpasswd'), + $params); + } + + /** + * Connects a pipe to the sambaserver using the smbpasswd program. + * + * @param string $user The user to change the password for + * @param string $tmpfile The name of a temporary file in which to write + * output. + * @return mixed True on success, PEAR_Error on failure + */ + function _connect($user, $tmpfile) + { + if (!is_executable($this->_params['program'])) { + return PEAR::raiseError(_("Passwd is not properly configured.")); + } + + $cmd = sprintf('%s -r %s -s -U "%s" > %s 2>&1', + $this->_params['program'], + $this->_params['host'], + $user, + $tmpfile); + $this->_fp = @popen($cmd, 'w'); + if (!$this->_fp) { + return PEAR::raiseError(_("Could not open pipe to smbpasswd.")); + } + + return true; + } + + /** + * Disconnects the pipe to the sambaserver. + */ + function _disconnect() + { + @pclose($this->_fp); + } + + /** + * Sends a string to the waiting sambaserver. + * + * @param string $cmd The string to send to the server. + */ + function _sendCommand($cmd) + { + if (fputs($this->_fp, $cmd . "\n") == -1) { + return PEAR::raiseError(_("Error sending data to smbpasswd.")); + } + sleep(1); + return true; + } + + /** + * Changes the user's password. + * + * @param string $username The user for which to change the password. + * @param string $old_password The old (current) user password. + * @param string $new_password The new user password to set. + * + * @return mixed True or PEAR_Error based on success of the change. + */ + function changePassword($username, $old_password, $new_password) + { + $res = true; + + // Clean up user name in case evil characters are in it. + $user = escapeshellcmd($username); + + $tmpfile = Horde::getTempFile('smbpasswd'); + if (is_a($tmpfile, 'PEAR_Error')) { + return $tmpfile; + } + + $res = $this->_connect($user, $tmpfile); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + $res = $this->_sendCommand($old_password); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + $res = $this->_sendCommand($new_password); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + $res = $this->_sendCommand($new_password); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + $this->_disconnect(); + + $res = file($tmpfile); + if (strstr($res[count($res) - 1], 'Password changed for user') === false) { + return PEAR::raiseError(strrchr(trim($res[count($res) - 2]), ':')); + } + + return true; + } + +} diff --git a/passwd/lib/Driver/soap.php b/passwd/lib/Driver/soap.php new file mode 100644 index 000000000..6861892a7 --- /dev/null +++ b/passwd/lib/Driver/soap.php @@ -0,0 +1,81 @@ + + * @package Passwd + */ +class Passwd_Driver_soap extends Passwd_Driver { + + /** + * Constructor. + * + * @param array $params A hash containing connection parameters. + */ + function Passwd_Driver_soap($params = array()) + { + if (isset($params['wsdl'])) { + unset($params['soap_params']['location']); + unset($params['soap_params']['uri']); + } + if (!empty($GLOBALS['conf']['http']['proxy']['proxy_host'])) { + $params['soap_params']['proxy_host'] = $GLOBALS['conf']['http']['proxy']['proxy_host']; + $params['soap_params']['proxy_port'] = $GLOBALS['conf']['http']['proxy']['proxy_port']; + $params['soap_params']['proxy_login'] = $GLOBALS['conf']['http']['proxy']['proxy_user']; + $params['soap_params']['proxy_password'] = $GLOBALS['conf']['http']['proxy']['proxy_pass']; + } + $params['soap_params']['encoding'] = NLS::getCharset(); + $params['soap_params']['exceptions'] = false; + parent::Passwd_Driver($params); + } + + /** + * Changes the user's password. + * + * @param string $username The user for which to change the password. + * @param string $old_password The old (current) user password. + * @param string $new_password The new user password to set. + * + * @return mixed True on success, PEAR_Error on failure. + */ + function changePassword($username, $old_password, $new_password) + { + if (!class_exists('SoapClient')) { + return PEAR::raiseError('You need the soap PHP extension to use this driver.'); + } + if (empty($this->_params['wsdl']) && + (empty($this->_params['soap_params']['location']) || + empty($this->_params['soap_params']['uri']))) { + return PEAR::raiseError('Either the "wsdl" or the "location" and "uri" parameter must be provided.'); + } + + $args = array(); + if (($pos = array_search('username', $this->_params['arguments'])) !== false) { + $args[$pos] = $username; + } + if (($pos = array_search('oldpassword', $this->_params['arguments'])) !== false) { + $args[$pos] = $old_password; + } + if (($pos = array_search('newpassword', $this->_params['arguments'])) !== false) { + $args[$pos] = $new_password; + } + + $client = new SoapClient($this->_params['wsdl'], + $this->_params['soap_params']); + $result = $client->__soapCall($this->_params['method'], $args); + if (is_a($result, 'SoapFault')) { + return PEAR::raiseError($result->getMessage(), $result->getCode()); + } + + return true; + } + +} diff --git a/passwd/lib/Driver/sql.php b/passwd/lib/Driver/sql.php new file mode 100644 index 000000000..c9861862c --- /dev/null +++ b/passwd/lib/Driver/sql.php @@ -0,0 +1,258 @@ + + * @author Ilya Krel + * @author Tjeerd van der Zee + * @author Mattias Webjörn Eriksson + * @author Eric Jon Rostetter + * @package Passwd + */ +class Passwd_Driver_sql extends Passwd_Driver { + + /** + * SQL connection object. + * + * @var DB + */ + var $_db; + + /** + * State of SQL connection. + * + * @var boolean + */ + var $_connected = false; + + /** + * Constructs a new Passwd_Driver_sql object. + * + * @param array $params A hash containing connection parameters. + */ + function Passwd_Driver_sql($params = array()) + { + if (isset($params['phptype'])) { + $this->_params['phptype'] = $params['phptype']; + } else { + return PEAR::raiseError(_("Required 'phptype' not specified in Passwd SQL configuration.")); + } + + /* Use defaults from Horde, but allow overriding in backends.php. */ + $this->_params = array_merge(Horde::getDriverConfig('', 'sql'), $params); + + /* These default to matching the Auth_sql defaults. */ + $this->_params['table'] = isset($params['table']) ? $params['table'] : 'horde_users'; + $this->_params['encryption'] = isset($params['encryption']) ? $params['encryption'] : 'md5'; + $this->_params['user_col'] = isset($params['user_col']) ? $params['user_col'] : 'user_uid'; + $this->_params['pass_col'] = isset($params['pass_col']) ? $params['pass_col'] : 'user_pass'; + $this->_params['show_encryption'] = isset($params['show_encryption']) ? $params['show_encryption'] : false; + $this->_params['query_lookup'] = isset($params['query_lookup']) ? $params['query_lookup'] : false; + $this->_params['query_modify'] = isset($params['query_modify']) ? $params['query_modify'] : false; + } + + /** + * Connect to the database. + * + * @return boolean True on success or PEAR_Error on failure. + */ + function _connect() + { + if (!$this->_connected) { + /* Connect to the SQL server using the supplied parameters. */ + include_once 'DB.php'; + $this->_db = &DB::connect($this->_params, + array('persistent' => !empty($this->_params['persistent']))); + if (is_a($this->_db, 'PEAR_Error')) { + return PEAR::raiseError(_("Unable to connect to SQL server.")); + } + + // Set DB portability options. + switch ($this->_db->phptype) { + case 'mssql': + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); + break; + default: + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); + } + + $this->_connected = true; + } + + return true; + } + + /** + * Find out if a username and password is valid. + * + * @param string $userID The userID to check. + * @param string $old_password An old password to check. + * + * @return boolean True on valid or PEAR_Error on invalid. + */ + function _lookup($user, $old_password) + { + /* Connect to the database */ + $res = $this->_connect(); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + if (!empty($this->_params['query_lookup'])) { + list($sql, $values) = $this->_parseQuery($this->_params['query_lookup'], $user, $old_password); + } else { + /* Build the SQL query. */ + $sql = 'SELECT ' . $this->_params['pass_col'] . ' FROM ' . $this->_params['table'] . + ' WHERE ' . $this->_params['user_col'] . ' = ?'; + $values = array($user); + } + Horde::logMessage('SQL Query by Passwd_Driver_sql::_lookup(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG); + + /* Execute the query. */ + $result = $this->_db->query($sql, $values); + if (!is_a($result, 'PEAR_Error')) { + $row = $result->fetchRow(DB_FETCHMODE_ASSOC); + $result->free(); + if (is_array($row)) { + /* Get the password from the database. */ + if (!isset($row[$this->_params['pass_col']])) { + return PEAR::raiseError(sprintf(_("Password column \"%s\" not found in password table."), $this->_params['pass_col'])); + } + $current_password = $row[$this->_params['pass_col']]; + + /* Check the passwords match. */ + return $this->comparePasswords($current_password, $old_password); + } + } + return PEAR::raiseError(_("User not found")); + } + + /** + * Modify (update) a mysql password record for a user. + * + * @param string $user The user whose record we will udpate. + * @param string $new_password The new password value to set. + * + * @return boolean True or False based on success of the modify. + */ + function _modify($user, $new_password) + { + /* Connect to the database. */ + $res = $this->_connect(); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + if (!empty($this->_params['query_modify'])) { + list($sql, $values) = $this->_parseQuery($this->_params['query_modify'], $user, $new_password); + } else { + /* Encrypt the password. */ + $new_password = $this->encryptPassword($new_password, $this->_params['show_encryption']); + + /* Build the SQL query. */ + $sql = 'UPDATE ' . $this->_params['table'] . + ' SET ' . $this->_params['pass_col'] . ' = ?' . + ' WHERE ' . $this->_params['user_col'] . ' = ?'; + $values = array($new_password, $user); + } + Horde::logMessage('SQL Query by Passwd_Driver_sql::_modify(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG); + + /* Execute the query. */ + $result = $this->_db->query($sql, $values); + + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + return true; + } + + /** + * Parse the string as an SQL query substituting placeholders for + * their values. + * + * @param string $string The string to process as a query. + * @param string $user The user to use for the %u placeholder. + * @param string $password The password to use for the %p and %e placeholders. + * + * @return string The processed SQL query. + */ + function _parseQuery($string, $user, $password) + { + $query = ''; + $values = array(); + $length = strlen($string); + @list($username, $domain) = explode('@', $user); + for ($i = 0; $i < $length; $i++) { + if ($string[$i] == '%' && !empty($string[$i + 1])) { + switch ($string[++$i]) { + case 'd': + $query .= '?'; + $values[] = $domain; + break; + + case 'u': + $query .= '?'; + $values[] = $user; + break; + + case 'U': + $query .= '?'; + $values[] = $username; + break; + + case 'p': + $query .= '?'; + $values[] = $password; + break; + + case 'e': + $query .= '?'; + $values[] = $this->encryptPassword($password, $this->_params['show_encryption']); + break; + + case '%': + $query .= '%'; + break; + + default: + $query .= '%' . $string[$i]; + break; + } + } else { + $query .= $string[$i]; + } + } + + return array($query, $values); + } + + /** + * Change the user's password. + * + * @param string $username The user for which to change the password. + * @param string $old_password The old (current) user password. + * @param string $new_password The new user password to set. + * + * @return boolean True or false based on success of the change. + */ + function changePassword($username, $old_password, $new_password) + { + /* Check the current password. */ + $res = $this->_lookup($username, $old_password); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + return $this->_modify($username, $new_password); + } + +} diff --git a/passwd/lib/Driver/vmailmgr.php b/passwd/lib/Driver/vmailmgr.php new file mode 100644 index 000000000..01cb73bd3 --- /dev/null +++ b/passwd/lib/Driver/vmailmgr.php @@ -0,0 +1,48 @@ + + * @since Passwd 2.2 + * @package Passwd + */ + class Passwd_Driver_vmailmgr extends Passwd_Driver { + + /** + * Change the user's password. + * + * @param string $username The user for which to change the password. + * @param string $old_password The old (current) user password. + * @param string $new_password The new password to set. + * + * @return boolean True or false based on success of the change. + */ + function changePassword($username, $old_password, $new_password) + { + if (is_readable($this->_params['vmailinc']) && isset($this->_params['vmailinc'])) { + @include($this->_params['vmailinc']); + } else { + return PEAR::raiseError('vmail.inc not found ! (' . $this->_params['vmailinc'] . ')'); + } + + $_splitted = explode('@', $username); + $_username = $_splitted[0]; + $_domain = $_splitted[1]; + $_returnChange = vchpass($_domain, $old_password, $_username, $new_password); + + if ($_returnChange[0]) { + return PEAR::raiseError(_("Incorrect old password.")); + } + + return true; + } + +} diff --git a/passwd/lib/Driver/vpopmail.php b/passwd/lib/Driver/vpopmail.php new file mode 100644 index 000000000..f74b2e42b --- /dev/null +++ b/passwd/lib/Driver/vpopmail.php @@ -0,0 +1,225 @@ + + * @author Mike Cochrane + * @author Ilya Krel + * @author Tjeerd van der Zee + * @author Mattias Webjörn Eriksson + * @author Eric Jon Rostetter + * @since Passwd 2.2 + * @package Passwd + */ +class Passwd_Driver_vpopmail extends Passwd_Driver { + + /** + * SQL connection object. + * + * @var DB + */ + var $_db; + + /** + * State of SQL connection. + * + * @var boolean + */ + var $_connected = false; + + /** + * Constructs a new Passwd_Driver_vpopmail object. + * + * @param array $params A hash containing connection parameters. + */ + function Passwd_Driver_vpopmail($params = array()) + { + if (isset($params['phptype'])) { + $this->_params['phptype'] = $params['phptype']; + } else { + return PEAR::raiseError(_("Required 'phptype' not specified in SQL configuration.")); + } + + /* Use defaults from Horde. */ + $defaults = Horde::getDriverConfig('', 'sql'); + $this->_params['hostspec'] = isset($params['hostspec']) ? $params['hostspec'] : $defaults['hostspec']; + $this->_params['protocol'] = isset($params['protocol']) ? $params['protocol'] : $defaults['protocol']; + $this->_params['username'] = isset($params['username']) ? $params['username'] : $defaults['username']; + $this->_params['password'] = isset($params['password']) ? $params['password'] : $defaults['password']; + $this->_params['database'] = isset($params['database']) ? $params['database'] : $defaults['database']; + + /* Defaults to match Auth::sql default. */ + $this->_params['table'] = isset($params['table']) ? $params['table'] : 'horde_users'; + $this->_params['encryption'] = isset($params['encryption']) ? $params['encryption'] : 'crypt'; + $this->_params['name'] = isset($params['name']) ? $params['name'] : 'pw_name'; + $this->_params['domain'] = isset($params['domain']) ? $params['domain'] : 'pw_domain'; + $this->_params['passwd'] = isset($params['passwd']) ? $params['passwd'] : 'pw_passwd'; + $this->_params['clear_passwd'] = isset($params['clear_passwd']) ? $params['clear_passwd'] : 'pw_clear_passwd'; + $this->_params['use_clear_passwd'] = isset($params['use_clear_passwd']) ? $params['use_clear_passwd'] : false; + $this->_params['show_encryption'] = isset($params['show_encryption']) ? $params['show_encryption'] : false; + } + + /** + * Connect to the database. + * + * @return boolean True on success or PEAR_Error on failure. + */ + function _connect() + { + if (!$this->_connected) { + /* Connect to the SQL server using the supplied parameters. */ + include_once 'DB.php'; + $this->_db = &DB::connect($this->_params, true); + if (is_a($this->_db, 'PEAR_Error')) { + return PEAR::raiseError(_("Unable to connect to SQL server.")); + } + + // Set DB portability options. + switch ($this->_db->phptype) { + case 'mssql': + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); + break; + default: + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); + } + + $this->_connected = true; + } + + return true; + } + + /** + * Find out if a username and password is valid. + * + * @param string $username The username to check. + * @param string $old_password An old password to check. + * + * @return boolean True on valid or PEAR_Error on invalid. + */ + function _lookup($username, $old_password) + { + /* Connect to the database. */ + $res = $this->_connect(); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + /* Only split up username if domain is set in backend + * configuration. */ + if (!empty($this->_params['domain'])) { + list($name, $domain) = explode('@', $username); + } else { + $name = $username; + } + + /* Build the SQL query. */ + $sql = 'SELECT ' . $this->_params['passwd'] . + ' FROM ' . $this->_params['table'] . + ' WHERE ' . $this->_params['name'] . ' = ?'; + $values = array($name); + if ($this->_params['domain']) { + $sql .= ' AND ' . $this->_params['domain'] . ' = ?'; + $values[] = $domain; + } + Horde::logMessage('SQL Query by Passwd_Driver_vpopmail::_lookup(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG); + + /* Execute the query. */ + $result = $this->_db->query($sql, $values); + if (!is_a($result, 'PEAR_Error')) { + $row = $result->fetchRow(DB_FETCHMODE_ASSOC); + $result->free(); + if (is_array($row)) { + /* Get the password from the database. */ + $current_password = $row[$this->_params['passwd']]; + + /* See if the passwords match. */ + return $this->comparePasswords($current_password, $old_password); + } + } + + return PEAR::raiseError(_("User not found")); + } + + /** + * Modify (update) a mysql password record for a user. + * + * @param string $username The user whose record we will udpate. + * @param string $new_password The new password value to set. + * + * @return boolean True or False based on success of the modify. + */ + function _modify($username, $new_password) + { + /* Connect to the database. */ + $res = $this->_connect(); + if (is_a($res, 'PEAR_Error')) { + return $res; + } + + /* Only split up username if domain is set in backend. */ + if ($this->_params['domain']) { + list($name, $domain) = explode('@', $username); + } else { + $name = $username; + } + + /* Encrypt the password. */ + $clear_password = $new_password; + $new_password = $this->encryptPassword($new_password, $this->_params['show_encryption']); + + /* Build the SQL query. */ + $sql = 'UPDATE ' . $this->_params['table'] . + ' SET ' . $this->_params['passwd'] . ' = ?'; + $values = array($new_password); + if ($this->_params['use_clear_passwd']) { + $sql .= ', ' . $this->_params['clear_passwd'] . ' = ?'; + $values[] = $clear_password; + } + $sql .= ' WHERE ' . $this->_params['name'] . ' = ?'; + $values[] = $name; + if ($this->_params['domain']) { + $sql .= ' AND ' . $this->_params['domain'] . ' = ?'; + $values[] = $domain; + } + Horde::logMessage('SQL Query by Passwd_Driver_vpopmail::_modify(): ' . $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG); + + /* Execute the query. */ + $result = $this->_db->query($sql, $values); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + return true; + } + + /** + * Change the user's password. + * + * @param string $username The user for which to change the password. + * @param string $old_password The old (current) user password. + * @param string $new_password The new user password to set. + * + * @return boolean True or false based on success of the change. + */ + function changePassword($username, $old_password, $new_password) + { + /* Check the current password. */ + $result = $this->_lookup($username, $old_password); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + return $this->_modify($username, $new_password); + } + +} diff --git a/passwd/lib/Passwd.php b/passwd/lib/Passwd.php new file mode 100644 index 000000000..2d947c23d --- /dev/null +++ b/passwd/lib/Passwd.php @@ -0,0 +1,73 @@ + + * @package Passwd + */ +class Passwd { + + /** + * Determines if the given backend is the "preferred" backend for + * this web server. This decision is based on the global + * 'SERVER_NAME' and 'HTTP_HOST' server variables and the contents + * of the 'preferred' field in the backend's definition. The + * 'preferred' field may take a single value or an array of + * multiple values. + * + * @param array $backend A complete backend entry from the $backends + * hash. + * + * @return boolean True if this entry is "preferred". + */ + function isPreferredBackend($backend) + { + if (!empty($backend['preferred'])) { + if (is_array($backend['preferred'])) { + foreach ($backend['preferred'] as $backend) { + if ($backend == $_SERVER['SERVER_NAME'] || + $backend == $_SERVER['HTTP_HOST']) { + return true; + } + } + } elseif ($backend['preferred'] == $_SERVER['SERVER_NAME'] || + $backend['preferred'] == $_SERVER['HTTP_HOST']) { + return true; + } + } + + return false; + } + + /** + * Change the Horde/IMP/MIMP cached credentials. Should be called + * only after a successful change of the password in the actual + * backend storage. This routine is the same for all backends and + * should not be implemented in the backend classes. + * + * @param string $username The username we're changing. + * @param string $oldpassword The old user password. + * @param string $new_password The new user password to set. + */ + function resetCredentials($old_password, $new_password) + { + if (Auth::getCredential('password') == $old_password) { + Auth::setCredential('password', $new_password); + if (Auth::getProvider() == 'imp') { + $_SESSION['imp']['pass'] = Secret::write(Secret::getKey('imp'), + $new_password); + } elseif (Auth::getProvider() == 'mimp') { + $_SESSION['mimp']['pass'] = Secret::write(Secret::getKey('mimp'), + $new_password); + } + } + } + +} diff --git a/passwd/lib/base.php b/passwd/lib/base.php new file mode 100644 index 000000000..658b27046 --- /dev/null +++ b/passwd/lib/base.php @@ -0,0 +1,56 @@ + + * @package Passwd + */ + +// Check for a prior definition of HORDE_BASE (perhaps by an +// auto_prepend_file definition for site customization). +if (!defined('HORDE_BASE')) { + @define('HORDE_BASE', dirname(__FILE__) . '/../..'); +} + +// Load the Horde Framework core, and set up inclusion paths. +require_once HORDE_BASE . '/lib/core.php'; + +// Registry. +$registry = &Registry::singleton(); +if (is_a(($pushed = $registry->pushApp('passwd', !defined('AUTH_HANDLER'))), 'PEAR_Error')) { + if ($pushed->getCode() == 'permission_denied') { + Horde::authenticationFailureRedirect(); + } + Horde::fatal($pushed, __FILE__, __LINE__, false); +} +$conf = &$GLOBALS['conf']; +@define('PASSWD_TEMPLATES', $registry->get('templates')); + +// Notification system. +$notification = &Notification::singleton(); +$notification->attach('status'); + +// Find the base file path of Passwd +@define('PASSWD_BASE', dirname(__FILE__) . '/..'); + +// Passwd base library. +require_once PASSWD_BASE . '/lib/Passwd.php'; + +// Start compression. +if (!Util::nonInputVar('no_compress')) { + Horde::compressOutput(); +} + +// Horde libraries. +require_once 'Horde/Help.php'; +require_once 'Horde/Secret.php'; diff --git a/passwd/lib/version.php b/passwd/lib/version.php new file mode 100644 index 000000000..bedea1c54 --- /dev/null +++ b/passwd/lib/version.php @@ -0,0 +1 @@ + diff --git a/passwd/locale/ar_SY/LC_MESSAGES/passwd.mo b/passwd/locale/ar_SY/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..a0ab56baf Binary files /dev/null and b/passwd/locale/ar_SY/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/bg_BG/LC_MESSAGES/passwd.mo b/passwd/locale/bg_BG/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..dc19c34ad Binary files /dev/null and b/passwd/locale/bg_BG/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/ca_ES/LC_MESSAGES/passwd.mo b/passwd/locale/ca_ES/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..11b6696dc Binary files /dev/null and b/passwd/locale/ca_ES/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/cs_CZ/LC_MESSAGES/passwd.mo b/passwd/locale/cs_CZ/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..ff3cef1d0 Binary files /dev/null and b/passwd/locale/cs_CZ/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/da_DK/LC_MESSAGES/passwd.mo b/passwd/locale/da_DK/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..49cab5014 Binary files /dev/null and b/passwd/locale/da_DK/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/de_DE/LC_MESSAGES/passwd.mo b/passwd/locale/de_DE/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..f3a299d78 Binary files /dev/null and b/passwd/locale/de_DE/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/de_DE/help.xml b/passwd/locale/de_DE/help.xml new file mode 100644 index 000000000..f18dc2100 --- /dev/null +++ b/passwd/locale/de_DE/help.xml @@ -0,0 +1,47 @@ + + + + + Benutzername + Geben Sie hier Ihren Benutzernamen an, falls er nicht bereits + eingetragen ist. Dieses Feld steht nicht auf allen Systemen zur + Verfügung. + + + Altes Passwort + Geben Sie hier Ihr aktuelles, funktionierendes Passwort an. Sie + müssen Ihr Passwort korrekt angeben, um es ändern zu können. + + + Neues Passwort + Geben Sie hier Ihr neues Passwort an, also den Wert, in den Sie Ihr + Passwort ändern möchten. Beachten Sie, dass Ihr Passwort minimalen + Voraussetzungen entsprechen muss, z.B. genügend lang sein muss, aus + Kombinationen von Buchstaben und anderen Zeichen bestehen muss, nicht + Ihrem Benutzernamen entsprechen darf usw. Anderenfalls erhalten Sie eine + Fehlermeldung. + + + Neues Passwort betätigen + Geben Sie hier Ihr neue Passwort ein zweites Mal an. Damit wird + verhindert, dass Sie sich vertippen und dadurch selbst von Ihrem Konto + ausschließen. + + + Passwort ändern auf + Wählen Sie den Server oder die Anwendung aus, für die Sie Ihr + Passwort ändern möchten. Dieses Feld steht nicht auf allen Systemen zur + Verfügung. + + + Passwort ändern + Drücken Sie den "Passwort ändern"-Knopf am Ende des Formulars, um + das Passwort wie angegeben zu ändern. + + + Zurücksetzen + Drücken Sie den "Zurücksetzen"-Knopf am Ende des Formulars, um alle + Formulardaten, die Sie bisher eingegeben haben zu löschen und von vorne zu + beginnen. + + diff --git a/passwd/locale/en_US/help.xml b/passwd/locale/en_US/help.xml new file mode 100644 index 000000000..e3e773c9b --- /dev/null +++ b/passwd/locale/en_US/help.xml @@ -0,0 +1,44 @@ + + + + + Username + Enter your username here (if it is not already prefilled). Not all + installations support this. + + + Old password + Enter your current, working login password here. You must correctly + enter your current login password, or you cannot change your + password. + + + New password + Enter the new password, that is what you want to change your + password to. Note that the password entered must meet some minimum + standards (sufficient length, not match your username, have a combination + of letters and other characters, etc) or you will get an error. + + + Confirm new password + Enter the new password a second time in the Confirm New Password + area. This is to make sure you don't make a typo or other mistake and + lock you out of your account. + + + Change password on + Select the server or application in which you want to change the + password. Not all installations support this. + + + Change password + Press the "Change your password" button at the bottom of the form + to submit your password request change to the system. This button tells + the system to actually change your password. + + + Reset + Use the Reset button at the bottom of the form to clear any form + data that you have entered and start over. + + diff --git a/passwd/locale/es_ES/LC_MESSAGES/passwd.mo b/passwd/locale/es_ES/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..ecb550bc0 Binary files /dev/null and b/passwd/locale/es_ES/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/es_ES/help.xml b/passwd/locale/es_ES/help.xml new file mode 100644 index 000000000..6261dfa50 --- /dev/null +++ b/passwd/locale/es_ES/help.xml @@ -0,0 +1,32 @@ + + + + + Nombre de usuario + Introduzca aquí su nombre de usuario (si no estaba ya relleno). No todas las instalaciones permiten ésto. + + + Antigua contraseña + Introduzca aquí su contraseña de inicio funcional actual. Tiene que introducir correctamente la contraseña de inicio actual o no podrá cambiarla. + + + Nueva contraseña + Introduzca la nueva contraseña a la que quiera cambiar. Observe que la contraseña introducida tiene que cumplir unos estándares mínimos (longitud suficiente, distinta del nombre de usuario, disponer de una combinación de letrar y otros caracteres, etc.) o se producirá un error. + + + Confirmar la nueva contraseña + Introduzca la nueva contraseña una segunda vez en el campo Confirmar la nueva contraseña. Ésto evitará la introdución de un error de escritura o de cualquier otro tipo con el consiguiente bloqueo de su cuenta. + + + Cambiar contraseña en + Seleccione el servidor o aplicación en el que quiera cambiar la contraseña. No todas las instalaciones permiten ésto. + + + Cambiar la contraseña + Pulse el botón "Cambiar la contraseña" de la parte inferior del formulario para enviar la solicitud de cambio de contraseña al sistema. Este botón indica al sistema que cambie la contraseña. + + + Limpiar + Utilice el botón Limpiar de la parte inferior del formulario para eliminar cualquier dato introducido y empezar de nuevo. + + diff --git a/passwd/locale/et_EE/LC_MESSAGES/passwd.mo b/passwd/locale/et_EE/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..9af98f1ea Binary files /dev/null and b/passwd/locale/et_EE/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/fa_IR/LC_MESSAGES/passwd.mo b/passwd/locale/fa_IR/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..d8c02a026 Binary files /dev/null and b/passwd/locale/fa_IR/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/fa_IR/help.xml b/passwd/locale/fa_IR/help.xml new file mode 100644 index 000000000..ee1c5f23e --- /dev/null +++ b/passwd/locale/fa_IR/help.xml @@ -0,0 +1,55 @@ + + + + + + نام کاربر + نام کاربری شما + + نام کاربری خود را در اين قسمت وارد نماييد. + + + + + + اسم رمز قبلی + اسم رمز قبلی + + اسم رمز فعلی خود را در اين قسمت وارد نماييد. اگر اسم رمز فعلی خود را به درستی وارد ننمائيد تغيير اسم رمز عبور ممکن نخواهد بود. + + + + + اسم رمز جديد + اسم رمز جديد + + اسم رمز جديد خود را وارد نمائيد. توجه داشته باشيد که اسم رمز جديد شما بايد برخی از استانداردهای ساده چون + حداقل طول مجاز، عدم يکسان بودن با نام کاربری شما، وجود ترکيبی از حروف الفبايي و عددی در آن و ... را رعايت نمايد. + + + + + تأييد اسم رمز جديد + تأييد اسم رمز جديد + + اسم رمز جديد خود را دوباره وارد نماييد. اين عمل برای اطمينان از صحت ورود اسم رمز جديد شما صورت می‌پذيرد. + + + + + تغيير اسم رمز شما + تغيير اسم رمز شما + + کليد تغيير اسم رمز را به منظور تغيير اسم رمز خود بفشاريد + + + + + باز نشانی + باز نشانی + + اين کليد را برای آغاز مجدد و پاک کردن کليه اطلاعات وارده قبلی بفشاريد. + + + + diff --git a/passwd/locale/fi_FI/LC_MESSAGES/passwd.mo b/passwd/locale/fi_FI/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..4ea392ece Binary files /dev/null and b/passwd/locale/fi_FI/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/fi_FI/help.xml b/passwd/locale/fi_FI/help.xml new file mode 100644 index 000000000..bbaeedee5 --- /dev/null +++ b/passwd/locale/fi_FI/help.xml @@ -0,0 +1,53 @@ + + + + + Käyttäjätunnus + Käyttäjätunnuksesi + + Laita tähän tarvittaessa käyttäjätunnuksesi (kenttä saattaa myös olla jo täytetty valmiiksi). + + + + Vanha salasana + Vanha salasana + + Laita tähän nykyinen, toimiva salasanasi. Sinun pitää kirjoittaa salasanasi oikein tai et pysty vaihtamaan salasanaasi. + + + + Uusi salasana + Uusi salasana + + Kirjoita tähän uusi salasanasi, tämä tulee olemaan uusi salasanasi. Huomaathan että salasanasi pitää täytää tietyt minimivaatimukset (tarpeeksi pitkä, ei saa olla käyttäjätunnuksesi tai nimesi, salasanan pitää sisältää kirjaimia, numeroita ja muita merkkejä jne.) tai muuten tulee virheilmoitus ja salasanaa ei saa vaihdettua. + + + + Vahvista uusi salasana + Vahvista uusi salasana + + Kirjoita tähän uusi salasanasi toiseen kertaan. Tämä varmistus tehdään siksi että et tule vahingossa tehneeksi kirjoitusvirhettä uuteen salasanaasi ja siten tulisit lukinneeksi tunnuksesi. + + + + Vaihda salasana järjestelmässä + Vaihda salasana järjestelmässä + + Voit valita sen järjestelmän, johon salasana vaihdetaan. Kaikki järjestelmät eivät tue tätä toimintoa. + + + + Vaihda salasanasi + Vaihda salasanasi + + Napsauta "Vaihda salasanasi" -painiketta sivun alalaidassa lähettääksesi salasananvaihtopyynnön. Vasta tämän painikkeen painamisen jälkeen salasananvaihtopyyntö lähtee palvelimelle. + + + + Tyhjennä + Tyhjennä + + Käytä sivun alalaidassa olevaa "Tyhjennä" -painiketta tyhjentääksesi lomakkeen tiedot ja aloittaaksesi alusta. + + + diff --git a/passwd/locale/fr_FR/LC_MESSAGES/passwd.mo b/passwd/locale/fr_FR/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..0ffa20769 Binary files /dev/null and b/passwd/locale/fr_FR/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/gl_ES/LC_MESSAGES/passwd.mo b/passwd/locale/gl_ES/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..b0c722bd2 Binary files /dev/null and b/passwd/locale/gl_ES/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/hr_HR/LC_MESSAGES/passwd.mo b/passwd/locale/hr_HR/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..fde1659d1 Binary files /dev/null and b/passwd/locale/hr_HR/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/hr_HR/help.xml b/passwd/locale/hr_HR/help.xml new file mode 100644 index 000000000..3ceba5228 --- /dev/null +++ b/passwd/locale/hr_HR/help.xml @@ -0,0 +1,42 @@ + + + + Korisničko ime + Unesite svoje korisničko ime (ako već nije automatski uneseno). + Neke instalacije ne podržavaju promjenu korisničkog imena. + + + Stara lozinka + Ovdje unesite svoju trenutnu radeću lozinku. Promjena lozinke neće + uspjeti ako ne unesete ispravnu trenutnu lozinku. + + + Nova lozinka + Unesite novu lozinku koju želite koristiti nakon promjene. + Ako nova lozinka ne zadovoljava određene kriterije sigurnosti + (minimalna dužina, različita od korisničkog imena, određeni broj + slova, brojeva itd.) sustav će prijaviti grešku i promjena lozinke + neće uspjeti. + + + Potvrda nove lozinke + Još jednom unesite novu lozinku u polje Potvrda nove lozinke. + Ponavljanje lozinke služi da greška u unosu nove lozinke ne promijeni + lozinku na pogrešnu vrijednost i time onemogući ponovnu prijavu. + + + Promjena lozinke za + Odaberite poslužitelj ili aplikaciju za koju želite promijeniti + lozinku. Neke instalacije ne podržavaju promjenu ovog polja. + + + Promijeni lozinku + Klik na gumb "Promijeni lozinku" pokreće proceduru promjene lozinke + na odabranom poslužitelju. + + + Resetiraj + Gumb Resetiraj briše sadržaj svih izmijenjenih polja i omogućava + ponovan unos lozinki. + + diff --git a/passwd/locale/hu_HU/LC_MESSAGES/passwd.mo b/passwd/locale/hu_HU/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..529e5a7c3 Binary files /dev/null and b/passwd/locale/hu_HU/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/hu_HU/help.xml b/passwd/locale/hu_HU/help.xml new file mode 100644 index 000000000..dc39bb9e5 --- /dev/null +++ b/passwd/locale/hu_HU/help.xml @@ -0,0 +1,60 @@ + + + + + Felhasználói név + Az ön felhasználói neve + + Írja be ide a felhasználói nevét (ha még nincs kitöltve) + + + + Régi jelszó + Régi jelszó + + Írja be ide a mostani, működő belépési jelszavát. Helyesen kell megadnia ezt a jelszót, máskülönben nem változtatjatja meg. + + + + Új jelszó + Új jelszó + + Írja be ide az új jelszót, amire változtatni akar. Az új jelszónak meg kell felelni bizony minimális elvárásoknak (megfelelő hossz, nem egyezhet meg a felhasználói névvel, kell benne lenni betűnek és más karakternek is, satöbbi), különben hiba üzenetet fog kapni! + + + + Erősítse meg az új jelszót + Erősítse meg az új jelszót + + Ide az új jelszót kell mégegyszer beirnia. Ezzel biztosítjuk, hogy egy gépelési hiba vagy más tévedés miatt nem zárja ki magát a rendszerből. + + + + Change password on + Change password on + + Select the server or application in which you want to change the password. Not all installations support this. + + + + A jelszó megváltoztatása + A jelszó megváltoztatása + + A jelszóváltoztatási kérelem elküldéséhez nyomja meg a "A jelszó megváltoztatása" gombot alul. Ennek a gombnak a megnyomása kérvényezi a konkrét változtatást. + + + + Reset + Reset + + A "Reset" gomb megnyomása kitörli az eddig beírt adatokat és újrakezdi azok bekérését. + + + diff --git a/passwd/locale/id_ID/LC_MESSAGES/passwd.mo b/passwd/locale/id_ID/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..32ce5d985 Binary files /dev/null and b/passwd/locale/id_ID/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/it_IT/LC_MESSAGES/passwd.mo b/passwd/locale/it_IT/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..c5491bf81 Binary files /dev/null and b/passwd/locale/it_IT/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/ja_JP/LC_MESSAGES/passwd.mo b/passwd/locale/ja_JP/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..b93786c18 Binary files /dev/null and b/passwd/locale/ja_JP/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/lt_LT/LC_MESSAGES/passwd.mo b/passwd/locale/lt_LT/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..d57cf07dd Binary files /dev/null and b/passwd/locale/lt_LT/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/nl_NL/LC_MESSAGES/passwd.mo b/passwd/locale/nl_NL/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..b014ab9ab Binary files /dev/null and b/passwd/locale/nl_NL/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/nl_NL/help.xml b/passwd/locale/nl_NL/help.xml new file mode 100644 index 000000000..377a6fedd --- /dev/null +++ b/passwd/locale/nl_NL/help.xml @@ -0,0 +1,88 @@ + + + + + Username + Your Username + + Enter your username here (if it is not already prefilled). Not all installations support this. + + + + Oude wachtwoord + Oude wachtwoord + + Enter your current, working login password here. You must correctly enter your current login password, or you can not change your password. + + + + Nieuw wachtwoord + Nieuw wachtwoord + + Geef uw nieuwe wachtwoord op, dit is het wachtwoord waarin u het oude wilt veranderen. Onthoud dat het wachtwoord aan verschillende eisen moet voldoen (is het lang genoeg, is het niet gelijk aan uw gebruikersnaam, een combinatie van letters en cijfers, etc) anders krijgt u een foutmelding. + + + + Nogmaals nieuw wachtwoord + Nogmaals nieuw wachtwoord + + Geef opnieuw uw nieuwe wachtwoord op in het Verifieer wacthwoord veld. Dit is om te verkomen dat u per ongeluk een type fout heeft gemaakt en niet meer in uw account kunt komen. + + + + Change password on + Change password on + + Select the server or application in which you want to change the password. Not all installations support this. + + + + Verander wachtwoord + Verander wachtwoord + + Druk op de "Change your password" button onder de velden om het wachtwoord te wijzigen in het systeem. + + + + Reset + Reset + + Druk op deze knop om alle velden te legen zodat u opnieuw uw wachtwoorden in kunt vullen. + + + diff --git a/passwd/locale/nn_NO/LC_MESSAGES/passwd.mo b/passwd/locale/nn_NO/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..1f7bd8c83 Binary files /dev/null and b/passwd/locale/nn_NO/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/pl_PL/LC_MESSAGES/passwd.mo b/passwd/locale/pl_PL/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..57acb9e3b Binary files /dev/null and b/passwd/locale/pl_PL/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/pt_BR/LC_MESSAGES/passwd.mo b/passwd/locale/pt_BR/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..d74c22f9c Binary files /dev/null and b/passwd/locale/pt_BR/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/pt_PT/LC_MESSAGES/passwd.mo b/passwd/locale/pt_PT/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..898d904f0 Binary files /dev/null and b/passwd/locale/pt_PT/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/ro_RO/LC_MESSAGES/passwd.mo b/passwd/locale/ro_RO/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..4954c349b Binary files /dev/null and b/passwd/locale/ro_RO/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/ru_RU/LC_MESSAGES/passwd.mo b/passwd/locale/ru_RU/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..2619a7f3f Binary files /dev/null and b/passwd/locale/ru_RU/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/ru_RU/help.xml b/passwd/locale/ru_RU/help.xml new file mode 100644 index 000000000..a32a2d510 --- /dev/null +++ b/passwd/locale/ru_RU/help.xml @@ -0,0 +1,62 @@ + + + + + + Èìÿ ïîëüçîâàòåëÿ + Âàøå èìÿ ïîëüçîâàòåëÿ + + Ââåäèòå ñþäà èìÿ ïîëüçîâàòåëÿ (åñëè îíî åùå íå çàïîëíåíî). Íå âñå óñòàíîâêè ïîääåðæèâàþò ýòî. + + + + + + Ñòàðûé ïàðîëü + Ñòàðûé ïàðîëü + + Ââåäèòå ñþäà Âàø òåêóùèé ðàáî÷èé ïàðîëü. Âû äîëæíû You must ïðàâèëüíî ââåñòè òåêóùèé ïàðîëü, èíà÷å âû íå ñìîæåòå óñòàíîâèòü íîâûé ïàðîëü. + + + + + Íîâûé ïàðîëü + Íîâûé ïàðîëü + + Ââåäèòå íîâûé ïàðîëü, êîòîðûé Âû õîòèòå óñòàíîâèòü. Ââåäåííûé ïàðîëü äîëæåí ñîîòâåòñâîâàòü íåêîòîðûì ìèíèìàëüíûì ñòàíäàðòàì (äîñòàòî÷íàÿ äëèííà, íå ñîâïàäàòü ñ èìåíåì ïîëüçîâàòåëÿ, ñîñòîÿòü èç êîìáèíàöèè áóêâ è äðóãèõ ñèìâîëîâ, è ò.ä.), èíà÷å Âû ïîëó÷èòå îøèáêó. + + + + + Ïîäòâåðæäåíèå íîâîãî ïàðîëÿ + Ïîäòâåðæäåíèå íîâîãî ïàðîëÿ + + Ââåäèò íîâûé ïàðîëü âòîðîé ðàç â ïîëå "Ïîäòâåðäèòå íîâûé ïàðîëü". Ýòî äàåò óâåðåííîñòü, ÷òî âû íå äîïóñòèëè îøèáêè. + + + + + Ñìåíèòü ïàðîëü íà + Ñìåíèòü ïàðîëü íà + + Âûáåðåòå ñåðâåð èëè ïðèëîæåíèå, ñ ïîìîùüþ êîòîðîãî ñìåíèòü ïàðîëü. Íå âñå óñòàíîâêè ïîääåðæèâàþò ýòî. + + + + + Ñìåíèòü ïàðîëü + Ñìåíèòü ïàðîëü + + Íàæìèòå êíîïêó "Ñìåíèòü ïàðîëü" äëÿ ïîäà÷è çàïðîñà íà ñìåíó ïàðîëÿ. + + + + + Ñáðîñèòü + Ñáðîñèòü + + Èñïîëüçóéòå êíîïêó Ñáðîñèòü âíèçó ôîðìû äëÿ î÷èñòêè ðàíåå ââåäåííûõ äàííûõ. + + + + diff --git a/passwd/locale/sk_SK/LC_MESSAGES/passwd.mo b/passwd/locale/sk_SK/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..8748441c8 Binary files /dev/null and b/passwd/locale/sk_SK/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/sl_SI/LC_MESSAGES/passwd.mo b/passwd/locale/sl_SI/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..5824cd01d Binary files /dev/null and b/passwd/locale/sl_SI/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/sv_SE/LC_MESSAGES/passwd.mo b/passwd/locale/sv_SE/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..25462375a Binary files /dev/null and b/passwd/locale/sv_SE/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/tr_TR/LC_MESSAGES/passwd.mo b/passwd/locale/tr_TR/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..48574f809 Binary files /dev/null and b/passwd/locale/tr_TR/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/zh_CN/LC_MESSAGES/passwd.mo b/passwd/locale/zh_CN/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..00d266cdb Binary files /dev/null and b/passwd/locale/zh_CN/LC_MESSAGES/passwd.mo differ diff --git a/passwd/locale/zh_TW/LC_MESSAGES/passwd.mo b/passwd/locale/zh_TW/LC_MESSAGES/passwd.mo new file mode 100644 index 000000000..e2fc7fc08 Binary files /dev/null and b/passwd/locale/zh_TW/LC_MESSAGES/passwd.mo differ diff --git a/passwd/main.php b/passwd/main.php new file mode 100644 index 000000000..d58ee30be --- /dev/null +++ b/passwd/main.php @@ -0,0 +1,284 @@ + + * @package Passwd + */ + +@define('PASSWD_BASE', dirname(__FILE__)); +require_once PASSWD_BASE . '/lib/base.php'; +require PASSWD_BASE . '/config/backends.php'; + +// Get the backend details. +$backend_key = Util::getFormData('backend', false); +if (!isset($backends[$backend_key])) { + $backend_key = null; +} + +// Use a do-while to allow easy breaking if an error is found. +do { + if (!$backend_key) { + break; + } + + // Has the user submitted the form yet? + $submit = Util::getFormData('submit', false); + if (!$submit) { + // No so we don't need to do anything in this loop. + break; + } + + $driver = $backends[$backend_key]['driver']; + $params = $backends[$backend_key]['params']; + $password_policy = isset($backends[$backend_key]['password policy']) + ? $backends[$backend_key]['password policy'] + : array(); + + // Get the username. + if ($conf['user']['change'] === true) { + $userid = Util::getFormData('userid'); + } else { + if ($conf['hooks']['default_username']) { + $userid = Horde::callHook('_passwd_hook_default_username', + array(Auth::getAuth()), + 'passwd'); + } elseif ($conf['hooks']['full_name']) { + $userid = Auth::getAuth(); + } else { + $userid = Auth::getBareAuth(); + } + } + + // Check for users that cannot change their passwords. + if (in_array($userid, $conf['user']['refused'])) { + $notification->push(sprintf(_("You can't change password for user %s"), + $userid), 'horde.error'); + break; + } + + // We must be passed the old (current) password, or its an error. + $old_password = Util::getFormData('oldpassword', false); + if (!$old_password) { + $notification->push(_("You must give your current password"), + 'horde.warning'); + break; + } + + // See if they entered the new password and verified it. + $new_password0 = Util::getFormData('newpassword0', false); + $new_password1 = Util::getFormData('newpassword1', false); + if (!$new_password0) { + $notification->push(_("You must give your new password"), 'horde.warning'); + break; + } + if (!$new_password1) { + $notification->push(_("You must verify your new password"), 'horde.warning'); + break; + } + if ($new_password0 != $new_password1) { + $notification->push(_("Your new passwords didn't match"), 'horde.warning'); + break; + } + if ($new_password0 == $old_password) { + $notification->push(_("Your new password must be different from your current password"), 'horde.warning'); + break; + } + + // Check max/min lengths if specified in the backend config. + if (isset($password_policy['minLength']) && + strlen($new_password0) < $password_policy['minLength']) { + $notification->push(sprintf(_("Your new password must be at least %d characters long!"), $password_policy['minLength']), 'horde.warning'); + break; + } + if (isset($password_policy['maxLength']) && + strlen($new_password0) > $password_policy['maxLength']) { + $notification->push(sprintf(_("Your new password is too long; passwords may not be more than %d characters long!"), $password_policy['maxLength']), 'horde.warning'); + break; + } + + // Disect the password in a localised way. + $classes = array(); + $alpha = $alnum = $num = $upper = $lower = $space = $symbol = 0; + for ($i = 0; $i < strlen($new_password0); $i++) { + $char = substr($new_password0, $i, 1); + if (ctype_lower($char)) { + $lower++; $alpha++; $alnum++; $classes['lower'] = 1; + } elseif (ctype_upper($char)) { + $upper++; $alpha++; $alnum++; $classes['upper'] = 1; + } elseif (ctype_digit($char)) { + $num++; $alnum++; $classes['number'] = 1; + } elseif (ctype_punct($char)) { + $symbol++; $classes['symbol'] = 1; + } elseif (ctype_space($char)) { + $space++; $classes['symbol'] = 1; + } + } + + // Check reamaining password policy options. + if (isset($password_policy['minUpper']) && + $password_policy['minUpper'] > $upper) { + $notification->push(sprintf(ngettext("Your new password must contain at least %d uppercase character.", "Your new password must contain at least %d uppercase characters.", $password_policy['minUpper']), $password_policy['minUpper']), 'horde.warning'); + break; + } + if (isset($password_policy['minLower']) && + $password_policy['minLower'] > $lower) { + $notification->push(sprintf(ngettext("Your new password must contain at least %d lowercase character.", "Your new password must contain at least %d lowercase characters.", $password_policy['minLower']), $password_policy['minLower']), 'horde.warning'); + break; + } + if (isset($password_policy['minNumeric']) && + $password_policy['minNumeric'] > $num) { + $notification->push(sprintf(ngettext("Your new password must contain at least %d numeric character.", "Your new password must contain at least %d numeric characters.", $password_policy['minNumeric']), $password_policy['minNumeric']), 'horde.warning'); + break; + } + if (isset($password_policy['minAlpha']) && + $password_policy['minAlpha'] > $alpha) { + $notification->push(sprintf(ngettext("Your new password must contain at least %d alphabetic character.", "Your new password must contain at least %d alphabetic characters.", $password_policy['minAlpha']), $password_policy['minAlpha']), 'horde.warning'); + break; + } + if (isset($password_policy['minAlphaNum']) && + $password_policy['minAlphaNum'] > $alnum) { + $notification->push(sprintf(ngettext("Your new password must contain at least %d alphanumeric character.", "Your new password must contain at least %d alphanumeric characters.", $password_policy['minAlphaNum']), $password_policy['minAlphaNum']), 'horde.warning'); + break; + } + if (isset($password_policy['minClasses']) && + $password_policy['minClasses'] > array_sum($classes)) { + $notification->push(sprintf(_("Your new password must contain at least %d different types of characters. The types are: lower, upper, numeric, and symbols."), $password_policy['minClasses']), 'horde.warning'); + break; + } + if (isset($password_policy['maxSpace']) && + $password_policy['maxSpace'] < $space) { + if ($password_policy['maxSpace'] > 0) { + $notification->push(sprintf(_("Your new password must contain less than %d whitespace characters."), $password_policy['maxSpace'] + 1), 'horde.warning'); + } else { + $notification->push(_("Your new password must not contain whitespace characters."), 'horde.warning'); + } + break; + } + if (isset($password_policy['minSymbol']) && + $password_policy['minSymbol'] > $symbol) { + $notification->push(sprintf(ngettext("Your new password must contain at least %d symbol character.", "Your new password must contain at least %d symbol characters.", $password_policy['minSymbol']), $password_policy['minSymbol']), 'horde.warning'); + break; + } + + // Do some simple strength tests, if enabled in the config file. + if ($conf['password']['strengthtests']) { + // Check for new==old, pass==user, simple reverse strings, etc. + if ((strcasecmp($new_password0, $userid) == 0) || + (strcasecmp($new_password0, strrev($userid)) == 0) || + (strcasecmp($new_password0, $old_password) == 0) || + (strcasecmp($new_password0, strrev($old_password)) == 0) ) { + $notification->push(_("Your new password is too simple to guess. Not changed!"), + 'horde.warning'); + break; + } + // Check for percentages similarity also. This will catch very simple + // Things like "password" -> "password2" or "xpasssword"... + @similar_text($new_password0, $old_password, $percent1); + @similar_text($new_password0, $userid, $percent2); + if (($percent1 > 80) || ($percent2 > 80)) { + $notification->push(_("Your new password is too simple to guess! Not changed!"), + 'horde.warning'); + break; + } + } + + // Create a Password_Driver instance. + require_once PASSWD_BASE . '/lib/Driver.php'; + $daemon = Passwd_Driver::factory($driver, $params); + + if (is_a($daemon, 'PEAR_Error')) { + $notification->push(_("Password module is not properly configured"), + 'horde.error'); + break; + } + + $backend_userid = $userid; + + if ($conf['hooks']['username']) { + $backend_userid = Horde::callHook('_passwd_hook_username', + array($userid, &$daemon), + 'passwd'); + if (is_a($backend_userid, 'PEAR_Error')) { + $notification->push($backend_userid, 'horde.error'); + break; + } + } + + $res = $daemon->changePassword($backend_userid, $old_password, + $new_password0); + + if (!is_a($res, 'PEAR_Error')) { + if (!isset($backends[$backend_key]['no_reset']) || + !$backends[$backend_key]['no_reset']) { + Passwd::resetCredentials($old_password, $new_password0); + } + + $notification->push(sprintf(_("Password changed on %s."), + $backends[$backend_key]['name']), 'horde.success'); + + Horde::callHook('_passwd_password_changed', + array($backend_userid, $old_password, $new_password0), + 'passwd'); + + $return_to = Util::getFormData('return_to'); + if (!empty($return_to)) { + header('Location: ' . $return_to); + exit; + } + } else { + $notification->push(sprintf(_("Failure in changing password for %s: %s"), + $backends[$backend_key]['name'], + $res->getMessage()), 'horde.error'); + } +} while (false); + +// Choose the prefered backend from config/backends.php. +foreach ($backends as $key => $current_backend) { + if (!isset($backend_key) && substr($key, 0, 1) != '_') { + $backend_key = $key; + } + if (Passwd::isPreferredBackend($current_backend)) { + $backend_key = $key; + break; + } +} + +// Build the + + + + + +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ " onclick="return submit_form();" /> + " /> +
+ diff --git a/passwd/test.php b/passwd/test.php new file mode 100644 index 000000000..09e43a9d1 --- /dev/null +++ b/passwd/test.php @@ -0,0 +1,89 @@ + + */ + +/* 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 '

The Horde_Util package was not found. If PHP\'s error_reporting setting is high enough and display_errors is on, 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 framework module from Horde CVS, and install the packages in it with the install-packages.php script.

'; + 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( + 'ctype' => 'Ctype Support', + 'ldap' => array( + 'descrip' => 'LDAP Support', + 'error' => 'If you will be using the any of the LDAP drivers for password changes, PHP must have ldap support. Compile PHP --with-ldap before continuing.' + ), + 'mcrypt' => array( + 'descrip' => 'Mcrypt Support', + 'error' => 'If you will be using the smbldap driver for password changes, PHP must have mcrypt support. Compile PHP --with-mcrypt before continuing.' + ), + 'mhash' => array( + 'descrip' => 'Mhash Support', + 'error' => 'If you will be using the smbldap driver for password changes, PHP must have mhash support. Compile PHP --with-mhash before continuing.' + ), + 'soap' => array( + 'descrip' => 'SOAP Support', + 'error' => 'If you will be using the SOAP driver for password changes, PHP must have soap support. Compile PHP with --enable-soap 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.' + ), + 'HTTP_Request' => array( + 'path' => 'HTTP/Request.php', + 'error' => 'If you will be using the http driver for password changes, then you must install the PEAR HTTP_Request module.' + ) +); + +/* Get the status output now. */ +$module_output = $horde_test->phpModuleCheck($module_list); + +?> +

PHP Module Capabilities

+
    + +
+ +

PEAR Modules

+
    + PEARModuleCheck($pear_list) ?> +
+ +