Subversion Repositories Applications.papyrus

Rev

Rev 320 | Rev 1713 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 320 Rev 1173
Line 1... Line 1...
1
<?php
1
<?php
2
//
-
 
3
// +----------------------------------------------------------------------+
-
 
4
// | PHP Version 4                                                        |
-
 
5
// +----------------------------------------------------------------------+
-
 
6
// | Copyright (c) 1997-2003 The PHP Group                                |
-
 
7
// +----------------------------------------------------------------------+
-
 
8
// | This source file is subject to version 2.02 of the PHP license,      |
-
 
9
// | that is bundled with this package in the file LICENSE, and is        |
-
 
10
// | available at through the world-wide-web at                           |
-
 
11
// | http://www.php.net/license/2_02.txt.                                 |
-
 
12
// | If you did not receive a copy of the PHP license and are unable to   |
-
 
13
// | obtain it through the world-wide-web, please send a note to          |
-
 
14
// | license@php.net so we can mail you a copy immediately.               |
2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
15
// +----------------------------------------------------------------------+
-
 
16
// | Authors: Jan Wagner <wagner@netsols.de>                              |
-
 
17
// +----------------------------------------------------------------------+
-
 
18
//
-
 
19
// $Id: LDAP.php,v 1.1 2005-03-30 08:50:33 jpm Exp $
-
 
20
//
-
 
Line -... Line 3...
-
 
3
 
-
 
4
/**
-
 
5
 * Storage driver for use against an LDAP server
-
 
6
 *
-
 
7
 * PHP versions 4 and 5
-
 
8
 *
-
 
9
 * LICENSE: This source file is subject to version 3.01 of the PHP license
-
 
10
 * that is available through the world-wide-web at the following URI:
-
 
11
 * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
-
 
12
 * the PHP License and are unable to obtain it through the web, please
-
 
13
 * send a note to license@php.net so we can mail you a copy immediately.
-
 
14
 *
-
 
15
 * @category   Authentication
-
 
16
 * @package    Auth
-
 
17
 * @author     Jan Wagner <wagner@netsols.de> 
-
 
18
 * @author     Adam Ashley <aashley@php.net>
-
 
19
 * @author     Hugues Peeters <hugues.peeters@claroline.net>
-
 
20
 * @copyright  2001-2006 The PHP Group
-
 
21
 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
-
 
22
 * @version    CVS: $Id: LDAP.php,v 1.2 2006-12-14 15:04:28 jp_milcent Exp $
-
 
23
 * @link       http://pear.php.net/package/Auth
-
 
24
 */
-
 
25
 
-
 
26
/**
-
 
27
 * Include Auth_Container base class
21
 
28
 */
-
 
29
require_once "Auth/Container.php";
-
 
30
/**
-
 
31
 * Include PEAR package for error handling
22
require_once "Auth/Container.php";
32
 */
Line 23... Line 33...
23
require_once "PEAR.php";
33
require_once "PEAR.php";
24
 
34
 
25
/**
35
/**
Line 39... Line 49...
39
 * host:        localhost (default), ldap.netsols.de or 127.0.0.1
49
 * host:        localhost (default), ldap.netsols.de or 127.0.0.1
40
 * port:        389 (default) or 636 or whereever your server runs
50
 * port:        389 (default) or 636 or whereever your server runs
41
 * url:         ldap://localhost:389/
51
 * url:         ldap://localhost:389/
42
 *              useful for ldaps://, works only with openldap2 ?
52
 *              useful for ldaps://, works only with openldap2 ?
43
 *              it will be preferred over host and port
53
 *              it will be preferred over host and port
-
 
54
 * version:     LDAP version to use, ususally 2 (default) or 3,
-
 
55
 *              must be an integer!
-
 
56
 * referrals:   If set, determines whether the LDAP library automatically
-
 
57
 *              follows referrals returned by LDAP servers or not. Possible
-
 
58
 *              values are true (default) or false.
44
 * binddn:      If set, searching for user will be done after binding
59
 * binddn:      If set, searching for user will be done after binding
45
 *              as this user, if not set the bind will be anonymous.
60
 *              as this user, if not set the bind will be anonymous.
46
 *              This is reported to make the container work with MS
61
 *              This is reported to make the container work with MS
47
 *              Active Directory, but should work with any server that
62
 *              Active Directory, but should work with any server that
48
 *              is configured this way.
63
 *              is configured this way.
49
 *              This has to be a complete dn for now (basedn and
64
 *              This has to be a complete dn for now (basedn and
50
 *              userdn will not be appended).
65
 *              userdn will not be appended).
51
 * bindpw:      The password to use for binding with binddn
66
 * bindpw:      The password to use for binding with binddn
52
 * scope:       one, sub (default), or base
-
 
53
 * basedn:      the base dn of your server
67
 * basedn:      the base dn of your server
54
 * userdn:      gets prepended to basedn when searching for user
68
 * userdn:      gets prepended to basedn when searching for user
-
 
69
 * userscope:   Scope for user searching: one, sub (default), or base
55
 * userattr:    the user attribute to search for (default: uid)
70
 * userattr:    the user attribute to search for (default: uid)
56
 * useroc:      objectclass of user (for the search filter)
71
 * userfilter:  filter that will be added to the search filter
-
 
72
 *              this way: (&(userattr=username)(userfilter))
57
 *              (default: posixAccount)
73
 *              default: (objectClass=posixAccount)
-
 
74
 * attributes:  array of additional attributes to fetch from entry.
-
 
75
 *              these will added to auth data and can be retrieved via
-
 
76
 *              Auth::getAuthData(). An empty array will fetch all attributes,
-
 
77
 *              array('') will fetch no attributes at all (default)
-
 
78
 *              If you add 'dn' as a value to this array, the users DN that was
-
 
79
 *              used for binding will be added to auth data as well.
-
 
80
 * attrformat:  The returned format of the additional data defined in the
-
 
81
 *              'attributes' option. Two formats are available.
-
 
82
 *              LDAP returns data formatted in a
-
 
83
 *              multidimensional array where each array starts with a
-
 
84
 *              'count' element providing the number of attributes in the
-
 
85
 *              entry, or the number of values for attributes. When set
-
 
86
 *              to this format, the only way to retrieve data from the
-
 
87
 *              Auth object is by calling getAuthData('attributes').
-
 
88
 *              AUTH returns data formatted in a
-
 
89
 *              structure more compliant with other Auth Containers,
-
 
90
 *              where each attribute element can be directly called by
-
 
91
 *              getAuthData() method from Auth.
-
 
92
 *              For compatibily with previous LDAP container versions,
-
 
93
 *              the default format is LDAP.
58
 * groupdn:     gets prepended to basedn when searching for group
94
 * groupdn:     gets prepended to basedn when searching for group
59
 * groupattr  : the group attribute to search for (default: cn)
95
 * groupattr:   the group attribute to search for (default: cn)
60
 * groupoc    : objectclass of group (for the search filter)
96
 * groupfilter: filter that will be added to the search filter when
-
 
97
 *              searching for a group:
-
 
98
 *              (&(groupattr=group)(memberattr=username)(groupfilter))
61
 *              (default: groupOfUniqueNames)
99
 *              default: (objectClass=groupOfUniqueNames)
62
 * memberattr : the attribute of the group object where the user dn
100
 * memberattr : the attribute of the group object where the user dn
63
 *              may be found (default: uniqueMember)
101
 *              may be found (default: uniqueMember)
64
 * memberisdn:  whether the memberattr is the dn of the user (default)
102
 * memberisdn:  whether the memberattr is the dn of the user (default)
65
 *              or the value of userattr (usually uid)
103
 *              or the value of userattr (usually uid)
66
 * group:       the name of group to search for
104
 * group:       the name of group to search for
-
 
105
 * groupscope:  Scope for group searching: one, sub (default), or base
-
 
106
 * start_tls:   enable/disable the use of START_TLS encrypted connection 
-
 
107
 *              (default: false)
67
 * debug:       Enable/Disable debugging output (default: false)
108
 * debug:       Enable/Disable debugging output (default: false)
-
 
109
 * try_all:     Whether to try all user accounts returned from the search
-
 
110
 *              or just the first one. (default: false)
68
 *
111
 *
69
 * To use this storage container, you have to use the following syntax:
112
 * To use this storage container, you have to use the following syntax:
70
 *
113
 *
71
 * <?php
114
 * <?php
72
 * ...
115
 * ...
73
 *
116
 *
74
 * $a = new Auth("LDAP", array(
117
 * $a1 = new Auth("LDAP", array(
75
 *       'host' => 'localhost',
118
 *       'host' => 'localhost',
76
 *       'port' => '389',
119
 *       'port' => '389',
-
 
120
 *       'version' => 3,
77
 *       'basedn' => 'o=netsols,c=de',
121
 *       'basedn' => 'o=netsols,c=de',
78
 *       'userattr' => 'uid'
122
 *       'userattr' => 'uid'
79
 *       'binddn' => 'cn=admin,o=netsols,c=de',
123
 *       'binddn' => 'cn=admin,o=netsols,c=de',
80
 *       'bindpw' => 'password'));
124
 *       'bindpw' => 'password'));
81
 *
125
 *
82
 * $a2 = new Auth('LDAP', array(
126
 * $a2 = new Auth('LDAP', array(
83
 *       'url' => 'ldaps://ldap.netsols.de',
127
 *       'url' => 'ldaps://ldap.netsols.de',
84
 *       'basedn' => 'o=netsols,c=de',
128
 *       'basedn' => 'o=netsols,c=de',
85
 *       'scope' => 'one',
129
 *       'userscope' => 'one',
86
 *       'userdn' => 'ou=People',
130
 *       'userdn' => 'ou=People',
87
 *       'groupdn' => 'ou=Groups',
131
 *       'groupdn' => 'ou=Groups',
88
 *       'groupoc' => 'posixGroup',
132
 *       'groupfilter' => '(objectClass=posixGroup)',
89
 *       'memberattr' => 'memberUid',
133
 *       'memberattr' => 'memberUid',
90
 *       'memberisdn' => false,
134
 *       'memberisdn' => false,
91
 *       'group' => 'admin'
135
 *       'group' => 'admin'
92
 *       ));
136
 *       ));
93
 *
137
 *
94
 * $a3 = new Auth('LDAP', array(
138
 * $a3 = new Auth('LDAP', array(
95
 *         'host' => 'ad.netsols.de',
139
 *       'host' => 'ldap.netsols.de',
-
 
140
 *       'port' => 389,
-
 
141
 *       'version' => 3,
-
 
142
 *       'referrals' => false,
96
 *         'basedn' => 'dc=netsols,dc=de',
143
 *       'basedn' => 'dc=netsols,dc=de',
97
 *         'userdn' => 'ou=Users',
-
 
98
 *         'binddn' => 'cn=Jan Wagner,ou=Users,dc=netsols,dc=de',
144
 *       'binddn' => 'cn=Jan Wagner,cn=Users,dc=netsols,dc=de',
99
 *         'bindpw' => '*******',
145
 *       'bindpw' => 'password',
100
 *         'userattr' => 'samAccountName',
146
 *       'userattr' => 'samAccountName',
-
 
147
 *       'userfilter' => '(objectClass=user)',
-
 
148
 *       'attributes' => array(''),
-
 
149
 *       'group' => 'testing',
-
 
150
 *       'groupattr' => 'samAccountName',
-
 
151
 *       'groupfilter' => '(objectClass=group)',
101
 *         'useroc' => 'user',
152
 *       'memberattr' => 'member',
102
 *          'debug' => true
153
 *       'memberisdn' => true,
-
 
154
 *       'groupdn' => 'cn=Users',
-
 
155
 *       'groupscope' => 'one',
103
 *         ));             
156
 *       'debug' => true);
104
 *
157
 *
105
 * The parameter values have to correspond
158
 * The parameter values have to correspond
106
 * to the ones for your LDAP server of course.
159
 * to the ones for your LDAP server of course.
107
 *
160
 *
108
 * When talking to a Microsoft ActiveDirectory server you have to
161
 * When talking to a Microsoft ActiveDirectory server you have to
Line 121... Line 174...
121
 * would become
174
 * would become
122
 *   "OU=Accounts, OU=Custom, DC=win2000, DC=example, DC=org'
175
 *   "OU=Accounts, OU=Custom, DC=win2000, DC=example, DC=org'
123
 *
176
 *
124
 * It seems that binding anonymously to an Active Directory
177
 * It seems that binding anonymously to an Active Directory
125
 * is not allowed, so you have to set binddn and bindpw for
178
 * is not allowed, so you have to set binddn and bindpw for
126
 * user searching,
179
 * user searching.
127
 *
180
 * 
128
 * Example a3 shows a tested example for connenction to Windows 2000
181
 * LDAP Referrals need to be set to false for AD to work sometimes.
129
 * Active Directory
-
 
130
 *
182
 *
-
 
183
 * Example a3 shows a full blown and tested example for connection to 
-
 
184
 * Windows 2000 Active Directory with group mebership checking
-
 
185
 *
-
 
186
 * Note also that if you want an encrypted connection to an MS LDAP 
131
 * @author   Jan Wagner <wagner@netsols.de>
187
 * server, then, on your webserver, you must specify 
-
 
188
 *        TLS_REQCERT never
-
 
189
 * in /etc/ldap/ldap.conf or in the webserver user's ~/.ldaprc (which
-
 
190
 * may or may not be read depending on your configuration).
-
 
191
 *
-
 
192
 *
-
 
193
 * @category   Authentication
132
 * @package  Auth
194
 * @package    Auth
-
 
195
 * @author     Jan Wagner <wagner@netsols.de>
-
 
196
 * @author     Adam Ashley <aashley@php.net>
-
 
197
 * @author     Hugues Peeters <hugues.peeters@claroline.net>
-
 
198
 * @copyright  2001-2006 The PHP Group
-
 
199
 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
133
 * @version  $Revision: 1.1 $
200
 * @version    Release: 1.4.3  File: $Revision: 1.2 $
-
 
201
 * @link       http://pear.php.net/package/Auth
134
 */
202
 */
135
class Auth_Container_LDAP extends Auth_Container
203
class Auth_Container_LDAP extends Auth_Container
136
{
204
{
-
 
205
 
-
 
206
    // {{{ properties
-
 
207
 
137
    /**
208
    /**
138
     * Options for the class
209
     * Options for the class
139
     * @var array
210
     * @var array
140
     */
211
     */
141
    var $options = array();
212
    var $options = array();
Line 144... Line 215...
144
     * Connection ID of LDAP Link
215
     * Connection ID of LDAP Link
145
     * @var string
216
     * @var string
146
     */
217
     */
147
    var $conn_id = false;
218
    var $conn_id = false;
Line 148... Line 219...
148
 
219
 
149
    /**
-
 
150
     * LDAP search function to use
-
 
151
     * @var string
220
    // }}}
152
     */
221
 
Line 153... Line 222...
153
    var $ldap_search_func;
222
    // {{{ Auth_Container_LDAP() [constructor]
154
 
223
 
155
    /**
224
    /**
156
     * Constructor of the container class
225
     * Constructor of the container class
157
     *
226
     *
158
     * @param  $params, associative hash with host,port,basedn and userattr key
227
     * @param  $params, associative hash with host,port,basedn and userattr key
159
     * @return object Returns an error object if something went wrong
228
     * @return object Returns an error object if something went wrong
160
     */
229
     */
-
 
230
    function Auth_Container_LDAP($params)
-
 
231
    {
-
 
232
        if (false === extension_loaded('ldap')) {
-
 
233
            return PEAR::raiseError('Auth_Container_LDAP: LDAP Extension not loaded',
-
 
234
                    41, PEAR_ERROR_DIE);
161
    function Auth_Container_LDAP($params)
235
        }
Line 162... Line 236...
162
    {
236
 
163
        $this->_setDefaults();
237
        $this->_setDefaults();
164
 
238
 
165
        if (is_array($params)) {
239
        if (is_array($params)) {
Line 166... Line 240...
166
            $this->_parseOptions($params);
240
            $this->_parseOptions($params);
-
 
241
        }
-
 
242
    }
-
 
243
 
-
 
244
    // }}}
-
 
245
    // {{{ _prepare()
-
 
246
 
-
 
247
    /**
-
 
248
     * Prepare LDAP connection
-
 
249
     *
-
 
250
     * This function checks if we have already opened a connection to
-
 
251
     * the LDAP server. If that's not the case, a new connection is opened.
-
 
252
     *
-
 
253
     * @access private
-
 
254
     * @return mixed True or a PEAR error object.
-
 
255
     */
-
 
256
    function _prepare()
-
 
257
    {
-
 
258
        if (!$this->_isValidLink()) {
-
 
259
            $res = $this->_connect();
-
 
260
            if (PEAR::isError($res)) {
-
 
261
                return $res;
-
 
262
            }
-
 
263
        }
167
        }
264
        return true;
Line 168... Line 265...
168
    }
265
    }
169
 
266
 
170
    // }}}
267
    // }}}
Line 186... Line 283...
186
            $this->_debug('Connecting with host:port', __LINE__);
283
            $this->_debug('Connecting with host:port', __LINE__);
187
            $conn_params = array($this->options['host'], $this->options['port']);
284
            $conn_params = array($this->options['host'], $this->options['port']);
188
        }
285
        }
Line 189... Line 286...
189
 
286
 
190
        if(($this->conn_id = @call_user_func_array('ldap_connect', $conn_params)) === false) {
287
        if (($this->conn_id = @call_user_func_array('ldap_connect', $conn_params)) === false) {
191
            return PEAR::raiseError('Auth_Container_LDAP: Could not connect to server.', 41, PEAR_ERROR_DIE);
288
            return PEAR::raiseError('Auth_Container_LDAP: Could not connect to server.', 41);
192
        }
289
        }
Line 193... Line 290...
193
        $this->_debug('Successfully connected to server', __LINE__);
290
        $this->_debug('Successfully connected to server', __LINE__);
194
 
291
 
-
 
292
        // switch LDAP version
195
        // try switchig to LDAPv3
293
        if (is_numeric($this->options['version']) && $this->options['version'] > 2) {
-
 
294
            $this->_debug("Switching to LDAP version {$this->options['version']}", __LINE__);
-
 
295
            @ldap_set_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $this->options['version']);
-
 
296
        
196
        $ver = 0;
297
            // start TLS if available
-
 
298
            if (isset($this->options['start_tls']) && $this->options['start_tls']) {           
-
 
299
                $this->_debug("Starting TLS session", __LINE__);
-
 
300
                if (@ldap_start_tls($this->conn_id) === false) {
-
 
301
                    return PEAR::raiseError('Auth_Container_LDAP: Could not start tls.', 41);
-
 
302
                }
-
 
303
            }
-
 
304
        }
-
 
305
 
-
 
306
        // switch LDAP referrals
197
        if(@ldap_get_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $ver) && $ver >= 2) {
307
        if (is_bool($this->options['referrals'])) {
198
            $this->_debug('Switching to LDAPv3', __LINE__);
308
          $this->_debug("Switching LDAP referrals to " . (($this->options['referrals']) ? 'true' : 'false'), __LINE__);
Line 199... Line 309...
199
            @ldap_set_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, 3);
309
          @ldap_set_option($this->conn_id, LDAP_OPT_REFERRALS, $this->options['referrals']);
200
        }
310
        }
201
 
311
 
202
        // bind with credentials or anonymously
312
        // bind with credentials or anonymously
203
        if($this->options['binddn'] && $this->options['bindpw']) {
313
        if (strlen($this->options['binddn']) && strlen($this->options['bindpw'])) {
204
            $this->_debug('Binding with credentials', __LINE__);
314
            $this->_debug('Binding with credentials', __LINE__);
205
            $bind_params = array($this->conn_id, $this->options['binddn'], $this->options['bindpw']);
315
            $bind_params = array($this->conn_id, $this->options['binddn'], $this->options['bindpw']);
206
        } else {
316
        } else {
Line 207... Line 317...
207
            $this->_debug('Binding anonymously', __LINE__);
317
            $this->_debug('Binding anonymously', __LINE__);
208
            $bind_params = array($this->conn_id);
318
            $bind_params = array($this->conn_id);
209
        }
319
        }
210
        
320
 
211
        // bind for searching
321
        // bind for searching
212
        if ((@call_user_func_array('ldap_bind', $bind_params)) == false) {
322
        if ((@call_user_func_array('ldap_bind', $bind_params)) === false) {
213
            $this->_debug();
323
            $this->_debug();
-
 
324
            $this->_disconnect();
-
 
325
            return PEAR::raiseError("Auth_Container_LDAP: Could not bind to LDAP server.", 41);
214
            $this->_disconnect();
326
        }
Line -... Line 327...
-
 
327
        $this->_debug('Binding was successful', __LINE__);
-
 
328
 
-
 
329
        return true;
215
            return PEAR::raiseError("Auth_Container_LDAP: Could not bind to LDAP server.", 41, PEAR_ERROR_DIE);
330
    }
216
        }
331
 
217
        $this->_debug('Binding was successful', __LINE__);
332
    // }}}
218
    }
333
    // {{{ _disconnect()
219
 
334
 
Line 228... Line 343...
228
            $this->_debug('disconnecting from server');
343
            $this->_debug('disconnecting from server');
229
            @ldap_unbind($this->conn_id);
344
            @ldap_unbind($this->conn_id);
230
        }
345
        }
231
    }
346
    }
Line -... Line 347...
-
 
347
 
-
 
348
    // }}}
-
 
349
    // {{{ _getBaseDN()
232
 
350
 
233
    /**
351
    /**
234
     * Tries to find Basedn via namingContext Attribute
352
     * Tries to find Basedn via namingContext Attribute
235
     *
353
     *
236
     * @access private
354
     * @access private
237
     */
355
     */
238
    function _getBaseDN()
356
    function _getBaseDN()
-
 
357
    {
-
 
358
        $err = $this->_prepare();
-
 
359
        if ($err !== true) {
-
 
360
            return PEAR::raiseError($err->getMessage(), $err->getCode());
-
 
361
        }
239
    {
362
 
240
        if ($this->options['basedn'] == "" && $this->_isValidLink()) {           
363
        if ($this->options['basedn'] == "" && $this->_isValidLink()) {
Line 241... Line 364...
241
            $this->_debug("basedn not set, searching via namingContexts.", __LINE__);
364
            $this->_debug("basedn not set, searching via namingContexts.", __LINE__);
Line 242... Line 365...
242
 
365
 
Line 243... Line 366...
243
            $result_id = @ldap_read($this->conn_id, "", "(objectclass=*)", array("namingContexts"));
366
            $result_id = @ldap_read($this->conn_id, "", "(objectclass=*)", array("namingContexts"));
Line 244... Line 367...
244
            
367
 
245
            if (ldap_count_entries($this->conn_id, $result_id) == 1) {
368
            if (@ldap_count_entries($this->conn_id, $result_id) == 1) {
246
                
369
 
Line 247... Line 370...
247
                $this->_debug("got result for namingContexts", __LINE__);
370
                $this->_debug("got result for namingContexts", __LINE__);
248
                
371
 
249
                $entry_id = ldap_first_entry($this->conn_id, $result_id);
372
                $entry_id = @ldap_first_entry($this->conn_id, $result_id);
250
                $attrs = ldap_get_attributes($this->conn_id, $entry_id);
373
                $attrs = @ldap_get_attributes($this->conn_id, $entry_id);
251
                $basedn = $attrs['namingContexts'][0];
374
                $basedn = $attrs['namingContexts'][0];
252
 
375
 
253
                if ($basedn != "") {
376
                if ($basedn != "") {
Line 254... Line 377...
254
                    $this->_debug("result for namingContexts was $basedn", __LINE__);
377
                    $this->_debug("result for namingContexts was $basedn", __LINE__);
255
                    $this->options['basedn'] = $basedn;
378
                    $this->options['basedn'] = $basedn;
256
                }
379
                }
257
            }
380
            }
258
            ldap_free_result($result_id);
381
            @ldap_free_result($result_id);
259
        }
382
        }
Line -... Line 383...
-
 
383
 
-
 
384
        // if base ist still not set, raise error
-
 
385
        if ($this->options['basedn'] == "") {
260
 
386
            return PEAR::raiseError("Auth_Container_LDAP: LDAP search base not specified!", 41);
261
        // if base ist still not set, raise error
387
        }
262
        if ($this->options['basedn'] == "") {
388
        return true;
263
            return PEAR::raiseError("Auth_Container_LDAP: LDAP search base not specified!", 41, PEAR_ERROR_DIE);
389
    }
264
        }        
390
 
Line 279... Line 405...
279
            }
405
            }
280
        }
406
        }
281
        return false;
407
        return false;
282
    }
408
    }
Line -... Line 409...
-
 
409
 
-
 
410
    // }}}
-
 
411
    // {{{ _setDefaults()
283
 
412
 
284
    /**
413
    /**
285
     * Set some default options
414
     * Set some default options
286
     *
415
     *
287
     * @access private
416
     * @access private
288
     */
417
     */
289
    function _setDefaults()
418
    function _setDefaults()
-
 
419
    {
290
    {
420
        $this->options['url']         = '';
291
        $this->options['host']        = 'localhost';
421
        $this->options['host']        = 'localhost';
-
 
422
        $this->options['port']        = '389';
-
 
423
        $this->options['version']     = 2;
292
        $this->options['port']        = '389';
424
        $this->options['referrals']   = true;
293
        $this->options['binddn']      = '';
425
        $this->options['binddn']      = '';
294
        $this->options['bindpw']      = '';
-
 
295
        $this->options['scope']       = 'sub';
426
        $this->options['bindpw']      = '';
296
        $this->options['basedn']      = '';
427
        $this->options['basedn']      = '';
-
 
428
        $this->options['userdn']      = '';
297
        $this->options['userdn']      = '';
429
        $this->options['userscope']   = 'sub';
298
        $this->options['userattr']    = "uid";
430
        $this->options['userattr']    = 'uid';
-
 
431
        $this->options['userfilter']  = '(objectClass=posixAccount)';
-
 
432
        $this->options['attributes']  = array(''); // no attributes
-
 
433
     // $this->options['attrformat']  = 'LDAP'; // returns attribute array as PHP LDAP functions return it
-
 
434
        $this->options['attrformat']  = 'AUTH'; // returns attribute like other Auth containers
299
        $this->options['useroc']      = 'posixAccount';
435
        $this->options['group']       = '';
-
 
436
        $this->options['groupdn']     = '';
300
        $this->options['groupdn']     = '';
437
        $this->options['groupscope']  = 'sub';
301
        $this->options['groupattr']   = 'cn';
438
        $this->options['groupattr']   = 'cn';
302
        $this->options['groupoc']     = 'groupOfUniqueNames';
439
        $this->options['groupfilter'] = '(objectClass=groupOfUniqueNames)';
303
        $this->options['memberattr']  = 'uniqueMember';
440
        $this->options['memberattr']  = 'uniqueMember';
-
 
441
        $this->options['memberisdn']  = true;
304
        $this->options['memberisdn']  = true;
442
        $this->options['start_tls']   = false;
-
 
443
        $this->options['debug']       = false;
305
        $this->options['debug']       = false;
444
        $this->options['try_all']     = false; // Try all user ids returned not just the first one
Line -... Line 445...
-
 
445
    }
-
 
446
 
-
 
447
    // }}}
306
    }
448
    // {{{ _parseOptions()
307
 
449
 
308
    /**
450
    /**
309
     * Parse options passed to the container class
451
     * Parse options passed to the container class
310
     *
452
     *
311
     * @access private
453
     * @access private
312
     * @param  array
454
     * @param  array
313
     */
455
     */
-
 
456
    function _parseOptions($array)
-
 
457
    {
314
    function _parseOptions($array)
458
        $array = $this->_setV12OptionsToV13($array);
-
 
459
 
-
 
460
        foreach ($array as $key => $value) {
-
 
461
            if (array_key_exists($key, $this->options)) {
-
 
462
                if ($key == 'attributes') {
-
 
463
                    if (is_array($value)) {
-
 
464
                        $this->options[$key] = $value;
-
 
465
                    } else {
-
 
466
                        $this->options[$key] = explode(',', $value);
315
    {
467
                    }
316
        foreach ($array as $key => $value) {
468
                } else {
-
 
469
                    $this->options[$key] = $value;
-
 
470
                }
-
 
471
            }
-
 
472
        }
-
 
473
    }
-
 
474
 
Line -... Line 475...
-
 
475
    // }}}
-
 
476
    // {{{ _setV12OptionsToV13()
-
 
477
 
-
 
478
    /**
-
 
479
     * Adapt deprecated options from Auth 1.2 LDAP to Auth 1.3 LDAP
-
 
480
     * 
-
 
481
     * @author Hugues Peeters <hugues.peeters@claroline.net>
-
 
482
     * @access private
-
 
483
     * @param array
-
 
484
     * @return array
-
 
485
     */
-
 
486
    function _setV12OptionsToV13($array)
-
 
487
    {
-
 
488
        if (isset($array['useroc']))
-
 
489
            $array['userfilter'] = "(objectClass=".$array['useroc'].")";
-
 
490
        if (isset($array['groupoc']))
-
 
491
            $array['groupfilter'] = "(objectClass=".$array['groupoc'].")";
-
 
492
        if (isset($array['scope']))
-
 
493
            $array['userscope'] = $array['scope'];
-
 
494
 
-
 
495
        return $array;
-
 
496
    }
-
 
497
 
-
 
498
    // }}}
317
            $this->options[$key] = $value;
499
    // {{{ _scope2function()
-
 
500
 
-
 
501
    /**
-
 
502
     * Get search function for scope
-
 
503
     *
-
 
504
     * @param  string scope
-
 
505
     * @return string ldap search function
318
        }
506
     */
319
 
507
    function _scope2function($scope)
320
        // get the according search function for selected scope
508
    {
321
        switch($this->options['scope']) {
509
        switch($scope) {
322
        case 'one':
510
        case 'one':
323
            $this->ldap_search_func = 'ldap_list';
511
            $function = 'ldap_list';
324
            break;
512
            break;
325
        case 'base':
513
        case 'base':
326
            $this->ldap_search_func = 'ldap_read';
514
            $function = 'ldap_read';
327
            break;
515
            break;
328
        default:
516
        default:
329
            $this->ldap_search_func = 'ldap_search';
517
            $function = 'ldap_search';
330
            break;
518
            break;
Line -... Line 519...
-
 
519
        }
-
 
520
        return $function;
-
 
521
    }
331
        }
522
 
332
        $this->_debug("LDAP search function will be: {$this->ldap_search_func}", __LINE__);
523
    // }}}
333
    }
524
    // {{{ fetchData()
334
 
525
 
-
 
526
    /**
335
    /**
527
     * Fetch data from LDAP server
336
     * Fetch data from LDAP server
528
     *
337
     *
529
     * Searches the LDAP server for the given username/password
338
     * Searches the LDAP server for the given username/password
530
     * combination.  Escapes all LDAP meta characters in username
339
     * combination.
531
     * before performing the query.
340
     *
532
     *
341
     * @param  string Username
533
     * @param  string Username
342
     * @param  string Password
534
     * @param  string Password
-
 
535
     * @return boolean
-
 
536
     */
-
 
537
    function fetchData($username, $password)
-
 
538
    {
Line 343... Line 539...
343
     * @return boolean
539
        $err = $this->_prepare();
344
     */
540
        if ($err !== true) {
-
 
541
            return PEAR::raiseError($err->getMessage(), $err->getCode());
-
 
542
        }
-
 
543
 
-
 
544
        $err = $this->_getBaseDN();
-
 
545
        if ($err !== true) {
-
 
546
            return PEAR::raiseError($err->getMessage(), $err->getCode());
-
 
547
        }
-
 
548
 
Line 345... Line 549...
345
    function fetchData($username, $password)
549
        // UTF8 Encode username for LDAPv3
-
 
550
        if (@ldap_get_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $ver) && $ver == 3) {
-
 
551
            $this->_debug('UTF8 encoding username for LDAPv3', __LINE__);
346
    {        
552
            $username = utf8_encode($username);
-
 
553
        }
Line 347... Line 554...
347
 
554
 
348
        $this->_connect();
555
        // make search filter
349
        $this->_getBaseDN();
556
        $filter = sprintf('(&(%s=%s)%s)',
350
        
557
                          $this->options['userattr'],
351
        // make search filter
558
                          $this->_quoteFilterString($username),
352
        $filter = sprintf('(&(objectClass=%s)(%s=%s))', $this->options['useroc'], $this->options['userattr'], $username);
559
                          $this->options['userfilter']);
Line -... Line 560...
-
 
560
 
-
 
561
        // make search base dn
-
 
562
        $search_basedn = $this->options['userdn'];
353
 
563
        if ($search_basedn != '' && substr($search_basedn, -1) != ',') {
354
        // make search base dn
564
            $search_basedn .= ',';
Line -... Line 565...
-
 
565
        }
-
 
566
        $search_basedn .= $this->options['basedn'];
-
 
567
 
355
        $search_basedn = $this->options['userdn'];
568
        // attributes
Line 356... Line 569...
356
        if ($search_basedn != '' && substr($search_basedn, -1) != ',') {
569
        $attributes = $this->options['attributes'];
357
            $search_basedn .= ',';
570
 
358
        }
571
        // make functions params array
359
        $search_basedn .= $this->options['basedn'];
572
        $func_params = array($this->conn_id, $search_basedn, $filter, $attributes);
-
 
573
 
-
 
574
        // search function to use
Line -... Line 575...
-
 
575
        $func_name = $this->_scope2function($this->options['userscope']);
360
        
576
 
-
 
577
        $this->_debug("Searching with $func_name and filter $filter in $search_basedn", __LINE__);
-
 
578
 
Line 361... Line 579...
361
        // make functions params array
579
        // search
-
 
580
        if (($result_id = @call_user_func_array($func_name, $func_params)) === false) {
362
        $func_params = array($this->conn_id, $search_basedn, $filter, array($this->options['userattr']));
581
            $this->_debug('User not found', __LINE__);
-
 
582
        } elseif (@ldap_count_entries($this->conn_id, $result_id) >= 1) { // did we get some possible results?
-
 
583
 
-
 
584
            $this->_debug('User(s) found', __LINE__);
-
 
585
 
-
 
586
            $first = true;
-
 
587
            $entry_id = null;
363
 
588
 
-
 
589
            do {
-
 
590
                
-
 
591
                // then get the user dn
-
 
592
                if ($first) {
-
 
593
                    $entry_id = @ldap_first_entry($this->conn_id, $result_id);
-
 
594
                    $first = false;
Line -... Line 595...
-
 
595
                } else {
-
 
596
                    $entry_id = @ldap_next_entry($this->conn_id, $entry_id);
-
 
597
                    if ($entry_id === false)
-
 
598
                        break;
-
 
599
                }
-
 
600
                $user_dn  = @ldap_get_dn($this->conn_id, $entry_id);
-
 
601
 
-
 
602
                // as the dn is not fetched as an attribute, we save it anyway
-
 
603
                if (is_array($attributes) && in_array('dn', $attributes)) {
-
 
604
                    $this->_debug('Saving DN to AuthData', __LINE__);
-
 
605
                    $this->_auth_obj->setAuthData('dn', $user_dn);
-
 
606
                }
-
 
607
            
-
 
608
                // fetch attributes
-
 
609
                if ($attributes = @ldap_get_attributes($this->conn_id, $entry_id)) {
-
 
610
 
-
 
611
                    if (is_array($attributes) && isset($attributes['count']) &&
-
 
612
                         $attributes['count'] > 0) {
-
 
613
 
-
 
614
                        // ldap_get_attributes() returns a specific multi dimensional array
-
 
615
                        // format containing all the attributes and where each array starts
-
 
616
                        // with a 'count' element providing the number of attributes in the
-
 
617
                        // entry, or the number of values for attribute. For compatibility
-
 
618
                        // reasons, it remains the default format returned by LDAP container
-
 
619
                        // setAuthData().
-
 
620
                        // The code below optionally returns attributes in another format,
-
 
621
                        // more compliant with other Auth containers, where each attribute
-
 
622
                        // element are directly set in the 'authData' list. This option is
-
 
623
                        // enabled by setting 'attrformat' to
-
 
624
                        // 'AUTH' in the 'options' array.
-
 
625
                        // eg. $this->options['attrformat'] = 'AUTH'
-
 
626
 
-
 
627
                        if ( strtoupper($this->options['attrformat']) == 'AUTH' ) {
-
 
628
                            $this->_debug('Saving attributes to Auth data in AUTH format', __LINE__);
-
 
629
                            unset ($attributes['count']);
-
 
630
                            foreach ($attributes as $attributeName => $attributeValue ) {
-
 
631
                                if (is_int($attributeName)) continue;
-
 
632
                                if (is_array($attributeValue) && isset($attributeValue['count'])) {
364
        $this->_debug("Searching with $filter in $search_basedn", __LINE__);
633
                                    unset ($attributeValue['count']);
Line 365... Line 634...
365
 
634
                                }
366
        // search
635
                                if (count($attributeValue)<=1) $attributeValue = $attributeValue[0];
367
        if (($result_id = @call_user_func_array($this->ldap_search_func, $func_params)) == false) {
636
                                $this->_auth_obj->setAuthData($attributeName, $attributeValue);
368
            $this->_debug('User not found', __LINE__);
637
                            }
Line 384... Line 653...
384
                // try binding as this user with the supplied password
653
                    // try binding as this user with the supplied password
385
                if (@ldap_bind($this->conn_id, $user_dn, $password)) {
654
                    if (@ldap_bind($this->conn_id, $user_dn, $password)) {
386
                    $this->_debug('Bind successful', __LINE__);
655
                        $this->_debug('Bind successful', __LINE__);
Line 387... Line 656...
387
 
656
 
388
                    // check group if appropiate
657
                        // check group if appropiate
389
                    if(isset($this->options['group'])) {
658
                        if (strlen($this->options['group'])) {
390
                        // decide whether memberattr value is a dn or the username
659
                            // decide whether memberattr value is a dn or the username
391
                        $this->_debug('Checking group membership', __LINE__);
660
                            $this->_debug('Checking group membership', __LINE__);
-
 
661
                            $return = $this->checkGroup(($this->options['memberisdn']) ? $user_dn : $username);
-
 
662
                            $this->_disconnect();
392
                        return $this->checkGroup(($this->options['memberisdn']) ? $user_dn : $username);
663
                            return $return;
393
                    } else {
664
                        } else {
394
                        $this->_debug('Authenticated', __LINE__);
665
                            $this->_debug('Authenticated', __LINE__);
395
                        $this->_disconnect();
666
                            $this->_disconnect();
396
                        return true; // user authenticated
667
                            return true; // user authenticated
397
                    } // checkGroup
668
                        } // checkGroup
398
                } // bind
669
                    } // bind
-
 
670
                } // non-empty password
399
            } // non-empty password
671
            } while ($this->options['try_all'] == true); // interate through entries
400
        } // one entry
672
        } // get results
401
        // default
673
        // default
402
        $this->_debug('NOT authenticated!', __LINE__);
674
        $this->_debug('NOT authenticated!', __LINE__);
403
        $this->_disconnect();
675
        $this->_disconnect();
404
        return false;
676
        return false;
Line -... Line 677...
-
 
677
    }
-
 
678
 
-
 
679
    // }}}
405
    }
680
    // {{{ checkGroup()
406
 
681
 
407
    /**
682
    /**
408
     * Validate group membership
683
     * Validate group membership
-
 
684
     *
409
     *
685
     * Searches the LDAP server for group membership of the
410
     * Searches the LDAP server for group membership of the
686
     * supplied username.  Quotes all LDAP filter meta characters in
411
     * authenticated user
687
     * the user name before querying the LDAP server.
412
     *
688
     *
413
     * @param  string Distinguished Name of the authenticated User
689
     * @param  string Distinguished Name of the authenticated User
414
     * @return boolean
690
     * @return boolean
415
     */
691
     */
-
 
692
    function checkGroup($user)
-
 
693
    {
-
 
694
        $err = $this->_prepare();
-
 
695
        if ($err !== true) {
-
 
696
            return PEAR::raiseError($err->getMessage(), $err->getCode());
416
    function checkGroup($user) 
697
        }
417
    {
698
 
418
        // make filter
699
        // make filter
419
        $filter = sprintf('(&(%s=%s)(objectClass=%s)(%s=%s))',
700
        $filter = sprintf('(&(%s=%s)(%s=%s)%s)',
420
                          $this->options['groupattr'],
-
 
421
                          $this->options['group'],
701
                          $this->options['groupattr'],
422
                          $this->options['groupoc'],
702
                          $this->options['group'],
423
                          $this->options['memberattr'],
703
                          $this->options['memberattr'],
Line 424... Line 704...
424
                          $user
704
                          $this->_quoteFilterString($user),
425
                          );
705
                          $this->options['groupfilter']);
426
 
706
 
427
        // make search base dn
707
        // make search base dn
428
        $search_basedn = $this->options['groupdn'];
708
        $search_basedn = $this->options['groupdn'];
429
        if($search_basedn != '' && substr($search_basedn, -1) != ',') {
709
        if ($search_basedn != '' && substr($search_basedn, -1) != ',') {
Line 430... Line 710...
430
            $search_basedn .= ',';
710
            $search_basedn .= ',';
-
 
711
        }
-
 
712
        $search_basedn .= $this->options['basedn'];
Line 431... Line 713...
431
        }
713
 
Line 432... Line 714...
432
        $search_basedn .= $this->options['basedn'];
714
        $func_params = array($this->conn_id, $search_basedn, $filter,
433
        
715
                             array($this->options['memberattr']));
434
        $func_params = array($this->conn_id, $search_basedn, $filter, array($this->options['memberattr']));
716
        $func_name = $this->_scope2function($this->options['groupscope']);
435
 
717
 
436
        $this->_debug("Searching with $filter in $search_basedn", __LINE__);
718
        $this->_debug("Searching with $func_name and filter $filter in $search_basedn", __LINE__);
437
        
-
 
438
        // search
719
 
439
        if(($result_id = @call_user_func_array($this->ldap_search_func, $func_params)) != false) {
720
        // search
440
            if(ldap_count_entries($this->conn_id, $result_id) == 1) {                
721
        if (($result_id = @call_user_func_array($func_name, $func_params)) != false) {
441
                ldap_free_result($result_id);
-
 
442
                $this->_debug('User is member of group', __LINE__);
722
            if (@ldap_count_entries($this->conn_id, $result_id) == 1) {
443
                $this->_disconnect();
723
                @ldap_free_result($result_id);
444
                return true;
-
 
445
            }
724
                $this->_debug('User is member of group', __LINE__);
446
        }
725
                return true;
Line -... Line 726...
-
 
726
            }
-
 
727
        }
-
 
728
        // default
447
 
729
        $this->_debug('User is NOT member of group', __LINE__);
448
        // default
730
        return false;
449
        $this->_debug('User is NOT member of group', __LINE__);
731
    }
450
        $this->_disconnect();
732
 
451
        return false;
733
    // }}}
452
    }
734
    // {{{ _debug()
453
 
735
 
454
    /**
736
    /**
455
     * Outputs debugging messages
737
     * Outputs debugging messages
456
     *
738
     *
457
     * @access private
739
     * @access private
458
     * @param string Debugging Message
740
     * @param string Debugging Message
459
     * @param integer Line number
741
     * @param integer Line number
460
     */
742
     */
461
    function _debug($msg = '', $line = 0)
743
    function _debug($msg = '', $line = 0)
462
    {
744
    {
-
 
745
        if ($this->options['debug'] == true) {
-
 
746
            if ($msg == '' && $this->_isValidLink()) {
-
 
747
                $msg = 'LDAP_Error: ' . @ldap_err2str(@ldap_errno($this->_conn_id));
-
 
748
            }
-
 
749
            print("$line: $msg <br />");
-
 
750
        }
-
 
751
    }
-
 
752
 
-
 
753
    // }}}
-
 
754
    // {{{ _quoteFilterString()
-
 
755
 
-
 
756
    /**
-
 
757
     * Escapes LDAP filter special characters as defined in RFC 2254.
-
 
758
     *
-
 
759
     * @access private
-
 
760
     * @param string Filter String
-
 
761
     */
-
 
762
    function _quoteFilterString($filter_str)
-
 
763
    {
463
        if($this->options['debug'] === true) {
764
        $metas        = array(  '\\',  '*',  '(',  ')',   "\x00");
Line 464... Line 765...
464
            if($msg == '' && $this->_isValidLink()) {
765
        $quoted_metas = array('\\\\', '\*', '\(', '\)', "\\\x00");