2 /** External authentication via LDAP for FusionForge
3 * Copyright 2003 Roland Mas <lolando@debian.org>
4 * Copyright 2004 Roland Mas <roland@gnurandal.com>
5 * The Gforge Group, LLC <http://gforgegroup.com/>
6 * Copyright 2004 Christian Bayle <bayle@debian.org>
7 * Copyright 2009 Alain Peyrat, Alcatel-Lucent
9 * This file is part of FusionForge
11 * This plugin, like FusionForge, is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2 of
14 * the License, or (at your option) any later version.
16 * FusionForge is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with FusionForge; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 US
26 require_once $gfcommon.'include/User.class.php';
27 require_once $gfconfig.'plugins/ldapextauth/mapping.php' ;
29 class LdapextauthPlugin extends Plugin {
30 function LdapextauthPlugin () {
33 $this->name = "ldapextauth";
34 $this->hooks[] = "session_before_login";
36 $this->ldap_conn = false ;
38 $this->ldap_server = $sys_ldap_server ;
39 $this->ldap_port = $sys_ldap_port ;
40 $this->ldap_altserver = '';
41 $this->ldap_altport = '';
42 $this->ldap_start_tls = false;
43 $this->ldap_bind_dn = '';
44 $this->ldap_bind_pwd = '';
45 $this->ldap_skip_users = '';
46 require_once $gfconfig.'plugins/ldapextauth/config.php' ;
47 if (isset($base_dn)) {
48 $this->base_dn = $base_dn ;
50 if (isset($ldap_server)) {
51 $this->ldap_server = $ldap_server ;
53 if (isset($ldap_port)) {
54 $this->ldap_port = $ldap_port ;
56 if (isset($ldap_altserver)) {
57 $this->ldap_altserver = $ldap_altserver ;
59 if (isset($ldap_altport)) {
60 $this->ldap_altport = $ldap_altport ;
62 if (isset($ldap_start_tls)) {
63 $this->ldap_start_tls = $ldap_start_tls ;
65 if (isset($ldap_kind)) {
66 $this->ldap_kind = $ldap_kind ;
68 if (isset($ldap_bind_dn)) {
69 $this->ldap_bind_dn = $ldap_bind_dn;
71 if (isset($ldap_bind_pwd)) {
72 $this->ldap_bind_pwd = $ldap_bind_pwd;
74 if (isset($ldap_skip_users)) {
75 // Array of login not managed by LDAP (local account).
76 $this->ldap_skip_users = $ldap_skip_users ;
80 function CallHook ($hookname, $params) {
83 $loginname = $params['loginname'] ;
84 $passwd = $params['passwd'] ;
86 debuglog("\nLDAP: CallHook ($hookname) - ". date("F j, Y, g:i a") );
88 // Skip users from LDAP check (local account).
89 if (in_array($loginname, $this->ldap_skip_users))
93 case "session_before_login":
94 // Authenticate against LDAP
95 return $this->AuthUser ($loginname, $passwd) ;
105 function AuthUser ($loginname, $passwd) {
108 debuglog("LDAP: AuthUser ($loginname)");
110 if (!function_exists ( "ldap_connect" )) {
111 debuglog("LDAP: No ldap_connect function, ldap is missing in your php.");
115 if (!$this->ldap_conn) {
116 $r = $this->ConnectLdap($this->ldap_server, $this->ldap_port);
117 if (!$r && $this->ldap_altserver) {
118 ldap_close($this->ldap_conn);
119 $r = $this->ConnectLdap($this->ldap_altserver, $this->ldap_altport);
122 // Unable to connect to LDAP server(s), use internal login.
123 $GLOBALS['ldap_auth_failed']=true;
128 $dn = plugin_ldapextauth_getdn ($this, $loginname) ;
130 @ldap_unbind($this->ldap_conn);
131 $GLOBALS['ldap_auth_failed']=true;
134 debuglog("LDAP: Using dn: $dn (searching)");
137 if ($this->ldap_kind=="AD"){
138 $res = ldap_search ($this->ldap_conn, $this->base_dn, "sAMAccountName=".$loginname) ;
140 $res = ldap_search ($this->ldap_conn, $this->base_dn, $dn) ;
141 debuglog("LDAP: ldap_search ($this->ldap_conn, $this->base_dn, $dn)");
142 debuglog("LDAP: Search handle is: $res");
146 // User not found in LDAP => Account invalid
147 @ldap_unbind($this->ldap_conn);
148 debuglog("LDAP: Wrong password according to LDAP ($loginname)");
149 $feedback=_('Invalid Password Or User Name');
150 $GLOBALS['ldap_auth_failed']=true;
154 $nb_result = ldap_count_entries($this->ldap_conn, $res);
155 if ($nb_result!==1) {
156 @ldap_unbind($this->ldap_conn);
157 debuglog("LDAP: ldap_count_entries() returned $nb_result values");
158 $GLOBALS['ldap_auth_failed']=true;
162 $info = ldap_get_entries ($this->ldap_conn,$res);
163 $dn = $info[0]['dn'];
165 debuglog("LDAP: dn=$dn");
167 // Prevent problem with php quoting.
168 $raw_passwd = get_magic_quotes_gpc() ? stripslashes($passwd) : $passwd;
170 $u = user_get_object_by_name ($loginname) ;
173 debuglog("LDAP: User is present in GForge database");
176 if (@ldap_bind($this->ldap_conn, $dn, $raw_passwd)) {
177 debuglog("LDAP: ldap_bind() ok (user bind)");
178 // Password from form is valid in LDAP
179 if (session_login_valid_dbonly ($loginname, $passwd, false)) {
180 // Also according to DB
181 $GLOBALS['ldap_auth_failed']=false;
184 // Passwords mismatch, update DB's
185 $u->setPasswd ($raw_passwd) ;
186 $GLOBALS['ldap_auth_failed']=false;
190 // If LDAP server is down, do not refuse the login
191 // for this reason (let the DB check occur).
192 // 0x51 is errno for LDAP_SERVER_DOWN
193 if (ldap_errno($this->ldap_conn) == 0x51) {
194 syslog(LOG_ERR, "FusionForge:LDAP server down, using DB login/passwd instead.");
195 $GLOBALS['ldap_auth_failed']=true;
198 // Wrong password according to LDAP
199 debuglog("LDAP: Wrong password according to LDAP ($loginname)");
200 $feedback=_('Invalid Password Or User Name');
201 $GLOBALS['ldap_auth_failed']=true;
205 debuglog("LDAP: User is not present in database\n");
207 // User doesn't exist in DB yet
208 if (@ldap_bind($this->ldap_conn, $dn, $raw_passwd))
211 $ldapentry = $info[0] ;
213 debuglog("=> \$info => ". var_export($info, true));
214 debuglog("=> \$info[cn] => ". $ldapentry['dn']);
216 $mappedinfo = plugin_ldapextauth_mapping ($ldapentry) ;
221 $unix_name = $loginname ;
224 $password1 = $passwd ;
225 $password2 = $passwd ;
231 $jabber_address = '' ;
243 if ($mappedinfo['unix_name']) {
244 $unix_name = $mappedinfo['unix_name'] ;
246 if ($mappedinfo['firstname']) {
247 $firstname = $mappedinfo['firstname'] ;
249 if ($mappedinfo['lastname']) {
250 $lastname = $mappedinfo['lastname'] ;
252 if ($mappedinfo['email']) {
253 $email = $mappedinfo['email'] ;
255 if ($mappedinfo['language_id']) {
256 $language_id = $mappedinfo['language_id'] ;
258 if ($mappedinfo['timezone']) {
259 $timezone = $mappedinfo['timezone'] ;
261 if ($mappedinfo['jabber_address']) {
262 $jabber_address = $mappedinfo['jabber_address'] ;
264 if ($mappedinfo['address']) {
265 $address = $mappedinfo['address'] ;
267 if ($mappedinfo['address2']) {
268 $address2 = $mappedinfo['address2'] ;
270 if ($mappedinfo['phone']) {
271 $phone = $mappedinfo['phone'] ;
273 if ($mappedinfo['fax']) {
274 $fax = $mappedinfo['fax'] ;
276 if ($mappedinfo['title']) {
277 $title = $mappedinfo['title'] ;
279 if ($mappedinfo['ccode']) {
280 $ccode = $mappedinfo['ccode'] ;
282 if ($mappedinfo['themeid']) {
283 $theme_id = $mappedinfo['themeid'] ;
286 debuglog("creating account for $unix_name");
287 if (!$u->create ($unix_name,$firstname,$lastname,$password1,$password2,$email,
288 $mail_site,$mail_va,$language_id,$timezone,$jabber_address,$jabber_only,$theme_id,
289 $unix_box, $address, $address2, $phone, $fax, $title, $ccode, $send_mail)) {
290 $GLOBALS['ldap_auth_failed']=true;
291 $feedback = "<br>Error Creating User: ".$u->getErrorMessage();
295 debuglog("activating account for $unix_name");
296 if (!$u->setStatus ('A')) {
297 $GLOBALS['ldap_auth_failed']=true;
298 debuglog("u->setStatus('A') failed: ".$u->getErrorMessage());
299 $feedback = "<br>Error Activating User: ".$u->getErrorMessage();
302 $GLOBALS['ldap_auth_failed']=false;
303 $GLOBALS['ldap_first_login']=true;
306 $GLOBALS['ldap_auth_failed']=true;
307 $feedback=_('Invalid Password Or User Name');
308 return false ; // Probably ignored, but just in case
313 function ConnectLDAP($server, $port) {
315 debuglog("LDAP: ldap_connect($server,$port)");
317 $this->ldap_conn = ldap_connect ($server, $port);
319 $this->ldap_conn = ldap_connect ($server);
321 debuglog("LDAP: Ldap handle: ".$this->ldap_conn);
323 if ($GLOBALS['sys_ldap_version']) {
324 debuglog("LDAP: ldap_set_option ($this->ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $GLOBALS[sys_ldap_version]);");
325 if (!ldap_set_option ($this->ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $GLOBALS['sys_ldap_version'])) {
326 debuglog("LDAP: ldap_set_option() failed: ".ldap_error($this->ldap_conn));
331 if ($this->ldap_start_tls) {
332 debuglog("LDAP: ldap_start_tls($this->ldap_conn)");
333 if (!ldap_start_tls($this->ldap_conn)) {
334 syslog(LOG_ERR, "GForge: LDAP start_tls failed: ".ldap_error($this->ldap_conn));
335 debuglog("LDAP: ldap_start_tls() failed: ".ldap_error($this->ldap_conn));
340 // If the ldap server does not allow anonymous bind,
341 // then authentificate with the server.
342 if ($this->ldap_bind_dn) {
343 debuglog("LDAP: ldap_bind() (application bind)");
344 if (!@ldap_bind($this->ldap_conn, $this->ldap_bind_dn, $this->ldap_bind_pwd)) {
345 debuglog("LDAP: ldap_bind() failed (application bind): ". ldap_error($this->ldap_conn));
346 syslog(LOG_ERR, "GForge:LDAP application bind failed, using DB login/passwd instead.");
355 function debuglog($msg) {
356 $fp = fopen("/tmp/ldap.log", "a+");
357 fwrite ($fp, $msg."\n");
363 // c-file-style: "bsd"