2 rcs_id('$Id: LDAP.php,v 1.9 2007-06-13 12:48:14 rurban Exp $');
3 /* Copyright (C) 2004,2007 $ThePhpWikiProgrammingTeam
4 * This file is part of PhpWiki. Terms and Conditions see LICENSE. (GPL2)
10 * Define the vars LDAP_AUTH_HOST and LDAP_BASE_DN in config/config.ini
12 * Preferences are handled in _PassUser
16 if ($this->_ldap = ldap_connect(LDAP_AUTH_HOST)) { // must be a valid LDAP server!
17 global $LDAP_SET_OPTION;
18 if (!empty($LDAP_SET_OPTION)) {
19 foreach ($LDAP_SET_OPTION as $key => $value) {
20 //if (is_string($key) and defined($key))
21 // $key = constant($key);
22 ldap_set_option($this->_ldap, $key, $value);
26 if (LDAP_AUTH_PASSWORD)
27 // Windows Active Directory Server is strict
28 $r = ldap_bind($this->_ldap, LDAP_AUTH_USER, LDAP_AUTH_PASSWORD);
30 $r = ldap_bind($this->_ldap, LDAP_AUTH_USER);
32 $r = true; // anonymous bind allowed
35 trigger_error(sprintf(_("Unable to bind LDAP server %s using %s %s"),
36 LDAP_AUTH_HOST, LDAP_AUTH_USER, LDAP_AUTH_PASSWORD),
47 if (isset($this->_sr) and is_resource($this->_sr)) ldap_free_result($this->_sr);
48 if (isset($this->_ldap) and is_resource($this->_ldap)) ldap_close($this->_ldap);
53 function checkPass($submitted_password) {
55 $this->_authmethod = 'LDAP';
56 $userid = $this->_userid;
57 if (!$this->isValidName()) {
58 trigger_error(_("Invalid username."), E_USER_WARNING);
60 return $this->_tryNextPass($submitted_password);
62 if (!$this->_checkPassLength($submitted_password)) {
64 return WIKIAUTH_FORBIDDEN;
66 // A LDAP speciality: empty passwords are valid with ldap_bind!!!
67 if (strlen($submitted_password) == 0) {
68 trigger_error(_("Empty password not allowed for LDAP"), E_USER_WARNING);
70 return WIKIAUTH_FORBIDDEN;
72 if (strstr($userid,'*')) {
73 trigger_error(fmt("Invalid username '%s' for LDAP Auth", $userid),
75 return WIKIAUTH_FORBIDDEN;
78 if ($ldap = $this->_init()) {
79 // Need to set the right root search information. See config/config.ini
80 $st_search = LDAP_SEARCH_FIELD
81 ? LDAP_SEARCH_FIELD."=$userid"
83 if (!$this->_sr = ldap_search($ldap, LDAP_BASE_DN, $st_search)) {
84 trigger_error(_("Could not search in LDAP"), E_USER_WARNING);
86 return $this->_tryNextPass($submitted_password);
88 $info = ldap_get_entries($ldap, $this->_sr);
89 if (empty($info["count"])) {
91 trigger_error(_("User not found in LDAP"), E_USER_WARNING);
93 return $this->_tryNextPass($submitted_password);
95 // There may be more hits with this userid.
96 // Of course it would be better to narrow down the BASE_DN
97 for ($i = 0; $i < $info["count"]; $i++) {
98 $dn = $info[$i]["dn"];
99 // The password is still plain text.
100 // LDAP allows all chars but *, (, ), \, NUL
101 // Quoting is done by \xx (two-digit hexcode). * <=> \2a
102 // Handling '?' is unspecified
103 $password = strtr($submitted_password,
110 // On wrong password the ldap server will return:
111 // "Unable to bind to server: Server is unwilling to perform"
112 // The @ catches this error message.
113 if ($r = @ldap_bind($ldap, $dn, $password)) {
114 // ldap_bind will return TRUE if everything matches
115 // Get the mail from ldap
116 if (!empty($info[$i]["mail"][0])) {
117 $this->_prefs->_prefs['email']->default_value = $info[$i]["mail"][0];
120 $this->_level = WIKIAUTH_USER;
121 return $this->_level;
125 trigger_error(_("Wrong password: ") .
126 str_repeat("*",strlen($submitted_password)),
131 trigger_error(_("Could not connect to LDAP"), E_USER_WARNING);
134 return $this->_tryNextPass($submitted_password);
138 function isValidName ($userid = false) {
139 if (!$userid) $userid = $this->_userid;
140 // LDAP allows all chars but *, (, ), \, NUL
141 // Quoting is done by \xx (two-digit hexcode). * <=> \2a
142 // We are more restrictive here, but must allow explitly utf-8
143 return preg_match("/^[\-\w_\.@ ]+$/u", $userid) and strlen($userid) < 64;
146 function userExists() {
147 $userid = $this->_userid;
148 if (strstr($userid, '*')) {
149 trigger_error(fmt("Invalid username '%s' for LDAP Auth", $userid),
153 if ($ldap = $this->_init()) {
154 // Need to set the right root search information. see ../index.php
155 $st_search = LDAP_SEARCH_FIELD
156 ? LDAP_SEARCH_FIELD."=$userid"
158 if (!$this->_sr = ldap_search($ldap, LDAP_BASE_DN, $st_search)) {
160 return $this->_tryNextUser();
162 $info = ldap_get_entries($ldap, $this->_sr);
164 if ($info["count"] > 0) {
166 UpgradeUser($GLOBALS['ForbiddenUser'], $this);
171 return $this->_tryNextUser();
174 function mayChangePass() {
180 // $Log: not supported by cvs2svn $
181 // Revision 1.8 2007/06/07 16:31:33 rurban
182 // Important! Fixes bug #1732882 ldap_bind with empty password
183 // Adds diagnostics on other ldap failures
184 // Fix password quoting
186 // Revision 1.7 2007/05/30 21:56:17 rurban
187 // Back to default uid for LDAP
189 // Revision 1.6 2007/05/29 16:56:15 rurban
190 // Allow more password und userid chars. uid => cn: default for certain testusers
192 // Revision 1.5 2005/10/10 19:43:49 rurban
193 // add DBAUTH_PREF_INSERT: self-creating users. by John Stevens
195 // Revision 1.4 2004/12/26 17:11:17 rurban
198 // Revision 1.3 2004/12/20 16:05:01 rurban
199 // gettext msg unification
201 // Revision 1.2 2004/12/19 00:58:02 rurban
202 // Enforce PASSWORD_LENGTH_MINIMUM in almost all PassUser checks,
203 // Provide an errormessage if so. Just PersonalPage and BogoLogin not.
204 // Simplify httpauth logout handling and set sessions for all methods.
205 // fix main.php unknown index "x" getLevelDescription() warning.
207 // Revision 1.1 2004/11/01 10:43:58 rurban
208 // seperate PassUser methods into seperate dir (memory usage)
209 // fix WikiUser (old) overlarge data session
210 // remove wikidb arg from various page class methods, use global ->_dbi instead
218 // c-hanging-comment-ender-p: nil
219 // indent-tabs-mode: nil