xref: /dokuwiki/lib/plugins/authldap/auth.php (revision f4476bd9b5badd36cd0617d76538e47d9649986b)
1*f4476bd9SJan Schumann<?php
2*f4476bd9SJan Schumann/**
3*f4476bd9SJan Schumann * Plugin auth provider
4*f4476bd9SJan Schumann *
5*f4476bd9SJan Schumann * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6*f4476bd9SJan Schumann * @author     Jan Schumann <js@schumann-it.com>
7*f4476bd9SJan Schumann */
8*f4476bd9SJan Schumann// must be run within Dokuwiki
9*f4476bd9SJan Schumannif(!defined('DOKU_INC')) die();
10*f4476bd9SJan Schumann
11*f4476bd9SJan Schumann/**
12*f4476bd9SJan Schumann * LDAP authentication backend
13*f4476bd9SJan Schumann *
14*f4476bd9SJan Schumann * @license   GPL 2 (http://www.gnu.org/licenses/gpl.html)
15*f4476bd9SJan Schumann * @author    Andreas Gohr <andi@splitbrain.org>
16*f4476bd9SJan Schumann * @author    Chris Smith <chris@jalakaic.co.uk>
17*f4476bd9SJan Schumann * @author    Jan Schumann <js@schumann-it.com>
18*f4476bd9SJan Schumann */
19*f4476bd9SJan Schumannclass auth_plugin_authldap extends DokuWiki_Auth_Plugin
20*f4476bd9SJan Schumann{
21*f4476bd9SJan Schumann    var $cnf = null;
22*f4476bd9SJan Schumann    var $con = null;
23*f4476bd9SJan Schumann    var $bound = 0; // 0: anonymous, 1: user, 2: superuser
24*f4476bd9SJan Schumann
25*f4476bd9SJan Schumann    /**
26*f4476bd9SJan Schumann     * Constructor
27*f4476bd9SJan Schumann     */
28*f4476bd9SJan Schumann    function auth_plugin_authldap(){
29*f4476bd9SJan Schumann        global $conf;
30*f4476bd9SJan Schumann        $this->cnf = $conf['auth']['ldap'];
31*f4476bd9SJan Schumann
32*f4476bd9SJan Schumann        // ldap extension is needed
33*f4476bd9SJan Schumann        if(!function_exists('ldap_connect')) {
34*f4476bd9SJan Schumann            if ($this->cnf['debug'])
35*f4476bd9SJan Schumann                msg("LDAP err: PHP LDAP extension not found.",-1,__LINE__,__FILE__);
36*f4476bd9SJan Schumann            $this->success = false;
37*f4476bd9SJan Schumann            return;
38*f4476bd9SJan Schumann        }
39*f4476bd9SJan Schumann
40*f4476bd9SJan Schumann        if(empty($this->cnf['groupkey']))   $this->cnf['groupkey']   = 'cn';
41*f4476bd9SJan Schumann        if(empty($this->cnf['userscope']))  $this->cnf['userscope']  = 'sub';
42*f4476bd9SJan Schumann        if(empty($this->cnf['groupscope'])) $this->cnf['groupscope'] = 'sub';
43*f4476bd9SJan Schumann
44*f4476bd9SJan Schumann        // auth_ldap currently just handles authentication, so no
45*f4476bd9SJan Schumann        // capabilities are set
46*f4476bd9SJan Schumann    }
47*f4476bd9SJan Schumann
48*f4476bd9SJan Schumann    /**
49*f4476bd9SJan Schumann     * Check user+password
50*f4476bd9SJan Schumann     *
51*f4476bd9SJan Schumann     * Checks if the given user exists and the given
52*f4476bd9SJan Schumann     * plaintext password is correct by trying to bind
53*f4476bd9SJan Schumann     * to the LDAP server
54*f4476bd9SJan Schumann     *
55*f4476bd9SJan Schumann     * @author  Andreas Gohr <andi@splitbrain.org>
56*f4476bd9SJan Schumann     * @return  bool
57*f4476bd9SJan Schumann     */
58*f4476bd9SJan Schumann    function checkPass($user,$pass){
59*f4476bd9SJan Schumann        // reject empty password
60*f4476bd9SJan Schumann        if(empty($pass)) return false;
61*f4476bd9SJan Schumann        if(!$this->_openLDAP()) return false;
62*f4476bd9SJan Schumann
63*f4476bd9SJan Schumann        // indirect user bind
64*f4476bd9SJan Schumann        if($this->cnf['binddn'] && $this->cnf['bindpw']){
65*f4476bd9SJan Schumann            // use superuser credentials
66*f4476bd9SJan Schumann            if(!@ldap_bind($this->con,$this->cnf['binddn'],$this->cnf['bindpw'])){
67*f4476bd9SJan Schumann                if($this->cnf['debug'])
68*f4476bd9SJan Schumann                    msg('LDAP bind as superuser: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
69*f4476bd9SJan Schumann                return false;
70*f4476bd9SJan Schumann            }
71*f4476bd9SJan Schumann            $this->bound = 2;
72*f4476bd9SJan Schumann        }else if($this->cnf['binddn'] &&
73*f4476bd9SJan Schumann                 $this->cnf['usertree'] &&
74*f4476bd9SJan Schumann                 $this->cnf['userfilter']) {
75*f4476bd9SJan Schumann            // special bind string
76*f4476bd9SJan Schumann            $dn = $this->_makeFilter($this->cnf['binddn'],
77*f4476bd9SJan Schumann                                     array('user'=>$user,'server'=>$this->cnf['server']));
78*f4476bd9SJan Schumann
79*f4476bd9SJan Schumann        }else if(strpos($this->cnf['usertree'], '%{user}')) {
80*f4476bd9SJan Schumann            // direct user bind
81*f4476bd9SJan Schumann            $dn = $this->_makeFilter($this->cnf['usertree'],
82*f4476bd9SJan Schumann                                     array('user'=>$user,'server'=>$this->cnf['server']));
83*f4476bd9SJan Schumann
84*f4476bd9SJan Schumann        }else{
85*f4476bd9SJan Schumann            // Anonymous bind
86*f4476bd9SJan Schumann            if(!@ldap_bind($this->con)){
87*f4476bd9SJan Schumann                msg("LDAP: can not bind anonymously",-1);
88*f4476bd9SJan Schumann                if($this->cnf['debug'])
89*f4476bd9SJan Schumann                    msg('LDAP anonymous bind: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
90*f4476bd9SJan Schumann                return false;
91*f4476bd9SJan Schumann            }
92*f4476bd9SJan Schumann        }
93*f4476bd9SJan Schumann
94*f4476bd9SJan Schumann        // Try to bind to with the dn if we have one.
95*f4476bd9SJan Schumann        if(!empty($dn)) {
96*f4476bd9SJan Schumann            // User/Password bind
97*f4476bd9SJan Schumann            if(!@ldap_bind($this->con,$dn,$pass)){
98*f4476bd9SJan Schumann                if($this->cnf['debug']){
99*f4476bd9SJan Schumann                    msg("LDAP: bind with $dn failed", -1,__LINE__,__FILE__);
100*f4476bd9SJan Schumann                    msg('LDAP user dn bind: '.htmlspecialchars(ldap_error($this->con)),0);
101*f4476bd9SJan Schumann                }
102*f4476bd9SJan Schumann                return false;
103*f4476bd9SJan Schumann            }
104*f4476bd9SJan Schumann            $this->bound = 1;
105*f4476bd9SJan Schumann            return true;
106*f4476bd9SJan Schumann        }else{
107*f4476bd9SJan Schumann            // See if we can find the user
108*f4476bd9SJan Schumann            $info = $this->getUserData($user,true);
109*f4476bd9SJan Schumann            if(empty($info['dn'])) {
110*f4476bd9SJan Schumann                return false;
111*f4476bd9SJan Schumann            } else {
112*f4476bd9SJan Schumann                $dn = $info['dn'];
113*f4476bd9SJan Schumann            }
114*f4476bd9SJan Schumann
115*f4476bd9SJan Schumann            // Try to bind with the dn provided
116*f4476bd9SJan Schumann            if(!@ldap_bind($this->con,$dn,$pass)){
117*f4476bd9SJan Schumann                if($this->cnf['debug']){
118*f4476bd9SJan Schumann                    msg("LDAP: bind with $dn failed", -1,__LINE__,__FILE__);
119*f4476bd9SJan Schumann                    msg('LDAP user bind: '.htmlspecialchars(ldap_error($this->con)),0);
120*f4476bd9SJan Schumann                }
121*f4476bd9SJan Schumann                return false;
122*f4476bd9SJan Schumann            }
123*f4476bd9SJan Schumann            $this->bound = 1;
124*f4476bd9SJan Schumann            return true;
125*f4476bd9SJan Schumann        }
126*f4476bd9SJan Schumann
127*f4476bd9SJan Schumann        return false;
128*f4476bd9SJan Schumann    }
129*f4476bd9SJan Schumann
130*f4476bd9SJan Schumann    /**
131*f4476bd9SJan Schumann     * Return user info
132*f4476bd9SJan Schumann     *
133*f4476bd9SJan Schumann     * Returns info about the given user needs to contain
134*f4476bd9SJan Schumann     * at least these fields:
135*f4476bd9SJan Schumann     *
136*f4476bd9SJan Schumann     * name string  full name of the user
137*f4476bd9SJan Schumann     * mail string  email addres of the user
138*f4476bd9SJan Schumann     * grps array   list of groups the user is in
139*f4476bd9SJan Schumann     *
140*f4476bd9SJan Schumann     * This LDAP specific function returns the following
141*f4476bd9SJan Schumann     * addional fields:
142*f4476bd9SJan Schumann     *
143*f4476bd9SJan Schumann     * dn     string  distinguished name (DN)
144*f4476bd9SJan Schumann     * uid    string  Posix User ID
145*f4476bd9SJan Schumann     * inbind bool    for internal use - avoid loop in binding
146*f4476bd9SJan Schumann     *
147*f4476bd9SJan Schumann     * @author  Andreas Gohr <andi@splitbrain.org>
148*f4476bd9SJan Schumann     * @author  Trouble
149*f4476bd9SJan Schumann     * @author  Dan Allen <dan.j.allen@gmail.com>
150*f4476bd9SJan Schumann     * @author  <evaldas.auryla@pheur.org>
151*f4476bd9SJan Schumann     * @author  Stephane Chazelas <stephane.chazelas@emerson.com>
152*f4476bd9SJan Schumann     * @return  array containing user data or false
153*f4476bd9SJan Schumann     */
154*f4476bd9SJan Schumann    function getUserData($user,$inbind=false) {
155*f4476bd9SJan Schumann        global $conf;
156*f4476bd9SJan Schumann        if(!$this->_openLDAP()) return false;
157*f4476bd9SJan Schumann
158*f4476bd9SJan Schumann        // force superuser bind if wanted and not bound as superuser yet
159*f4476bd9SJan Schumann        if($this->cnf['binddn'] && $this->cnf['bindpw'] && $this->bound < 2){
160*f4476bd9SJan Schumann            // use superuser credentials
161*f4476bd9SJan Schumann            if(!@ldap_bind($this->con,$this->cnf['binddn'],$this->cnf['bindpw'])){
162*f4476bd9SJan Schumann                if($this->cnf['debug'])
163*f4476bd9SJan Schumann                    msg('LDAP bind as superuser: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
164*f4476bd9SJan Schumann                return false;
165*f4476bd9SJan Schumann            }
166*f4476bd9SJan Schumann            $this->bound = 2;
167*f4476bd9SJan Schumann        }elseif($this->bound == 0 && !$inbind) {
168*f4476bd9SJan Schumann            // in some cases getUserData is called outside the authentication workflow
169*f4476bd9SJan Schumann            // eg. for sending email notification on subscribed pages. This data might not
170*f4476bd9SJan Schumann            // be accessible anonymously, so we try to rebind the current user here
171*f4476bd9SJan Schumann            list($loginuser,$loginsticky,$loginpass) = auth_getCookie();
172*f4476bd9SJan Schumann            if($loginuser && $loginpass){
173*f4476bd9SJan Schumann                $loginpass = PMA_blowfish_decrypt($loginpass, auth_cookiesalt(!$loginsticky));
174*f4476bd9SJan Schumann                $this->checkPass($loginuser, $loginpass);
175*f4476bd9SJan Schumann            }
176*f4476bd9SJan Schumann        }
177*f4476bd9SJan Schumann
178*f4476bd9SJan Schumann        $info['user']   = $user;
179*f4476bd9SJan Schumann        $info['server'] = $this->cnf['server'];
180*f4476bd9SJan Schumann
181*f4476bd9SJan Schumann        //get info for given user
182*f4476bd9SJan Schumann        $base = $this->_makeFilter($this->cnf['usertree'], $info);
183*f4476bd9SJan Schumann        if(!empty($this->cnf['userfilter'])) {
184*f4476bd9SJan Schumann            $filter = $this->_makeFilter($this->cnf['userfilter'], $info);
185*f4476bd9SJan Schumann        } else {
186*f4476bd9SJan Schumann            $filter = "(ObjectClass=*)";
187*f4476bd9SJan Schumann        }
188*f4476bd9SJan Schumann
189*f4476bd9SJan Schumann        $sr     = $this->_ldapsearch($this->con, $base, $filter, $this->cnf['userscope']);
190*f4476bd9SJan Schumann        $result = @ldap_get_entries($this->con, $sr);
191*f4476bd9SJan Schumann        if($this->cnf['debug']){
192*f4476bd9SJan Schumann            msg('LDAP user search: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
193*f4476bd9SJan Schumann            msg('LDAP search at: '.htmlspecialchars($base.' '.$filter),0,__LINE__,__FILE__);
194*f4476bd9SJan Schumann        }
195*f4476bd9SJan Schumann
196*f4476bd9SJan Schumann        // Don't accept more or less than one response
197*f4476bd9SJan Schumann        if(!is_array($result) || $result['count'] != 1){
198*f4476bd9SJan Schumann            return false; //user not found
199*f4476bd9SJan Schumann        }
200*f4476bd9SJan Schumann
201*f4476bd9SJan Schumann        $user_result = $result[0];
202*f4476bd9SJan Schumann        ldap_free_result($sr);
203*f4476bd9SJan Schumann
204*f4476bd9SJan Schumann        // general user info
205*f4476bd9SJan Schumann        $info['dn']   = $user_result['dn'];
206*f4476bd9SJan Schumann        $info['gid']  = $user_result['gidnumber'][0];
207*f4476bd9SJan Schumann        $info['mail'] = $user_result['mail'][0];
208*f4476bd9SJan Schumann        $info['name'] = $user_result['cn'][0];
209*f4476bd9SJan Schumann        $info['grps'] = array();
210*f4476bd9SJan Schumann
211*f4476bd9SJan Schumann        // overwrite if other attribs are specified.
212*f4476bd9SJan Schumann        if(is_array($this->cnf['mapping'])){
213*f4476bd9SJan Schumann            foreach($this->cnf['mapping'] as $localkey => $key) {
214*f4476bd9SJan Schumann                if(is_array($key)) {
215*f4476bd9SJan Schumann                    // use regexp to clean up user_result
216*f4476bd9SJan Schumann                    list($key, $regexp) = each($key);
217*f4476bd9SJan Schumann                    if($user_result[$key]) foreach($user_result[$key] as $grp){
218*f4476bd9SJan Schumann                        if (preg_match($regexp,$grp,$match)) {
219*f4476bd9SJan Schumann                            if($localkey == 'grps') {
220*f4476bd9SJan Schumann                                $info[$localkey][] = $match[1];
221*f4476bd9SJan Schumann                            } else {
222*f4476bd9SJan Schumann                                $info[$localkey] = $match[1];
223*f4476bd9SJan Schumann                            }
224*f4476bd9SJan Schumann                        }
225*f4476bd9SJan Schumann                    }
226*f4476bd9SJan Schumann                } else {
227*f4476bd9SJan Schumann                    $info[$localkey] = $user_result[$key][0];
228*f4476bd9SJan Schumann                }
229*f4476bd9SJan Schumann            }
230*f4476bd9SJan Schumann        }
231*f4476bd9SJan Schumann        $user_result = array_merge($info,$user_result);
232*f4476bd9SJan Schumann
233*f4476bd9SJan Schumann        //get groups for given user if grouptree is given
234*f4476bd9SJan Schumann        if ($this->cnf['grouptree'] || $this->cnf['groupfilter']) {
235*f4476bd9SJan Schumann            $base   = $this->_makeFilter($this->cnf['grouptree'], $user_result);
236*f4476bd9SJan Schumann            $filter = $this->_makeFilter($this->cnf['groupfilter'], $user_result);
237*f4476bd9SJan Schumann            $sr = $this->_ldapsearch($this->con, $base, $filter, $this->cnf['groupscope'], array($this->cnf['groupkey']));
238*f4476bd9SJan Schumann            if($this->cnf['debug']){
239*f4476bd9SJan Schumann                msg('LDAP group search: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
240*f4476bd9SJan Schumann                msg('LDAP search at: '.htmlspecialchars($base.' '.$filter),0,__LINE__,__FILE__);
241*f4476bd9SJan Schumann            }
242*f4476bd9SJan Schumann            if(!$sr){
243*f4476bd9SJan Schumann                msg("LDAP: Reading group memberships failed",-1);
244*f4476bd9SJan Schumann                return false;
245*f4476bd9SJan Schumann            }
246*f4476bd9SJan Schumann            $result = ldap_get_entries($this->con, $sr);
247*f4476bd9SJan Schumann            ldap_free_result($sr);
248*f4476bd9SJan Schumann
249*f4476bd9SJan Schumann            if(is_array($result)) foreach($result as $grp){
250*f4476bd9SJan Schumann                if(!empty($grp[$this->cnf['groupkey']][0])){
251*f4476bd9SJan Schumann                    if($this->cnf['debug'])
252*f4476bd9SJan Schumann                        msg('LDAP usergroup: '.htmlspecialchars($grp[$this->cnf['groupkey']][0]),0,__LINE__,__FILE__);
253*f4476bd9SJan Schumann                    $info['grps'][] = $grp[$this->cnf['groupkey']][0];
254*f4476bd9SJan Schumann                }
255*f4476bd9SJan Schumann            }
256*f4476bd9SJan Schumann        }
257*f4476bd9SJan Schumann
258*f4476bd9SJan Schumann        // always add the default group to the list of groups
259*f4476bd9SJan Schumann        if(!in_array($conf['defaultgroup'],$info['grps'])){
260*f4476bd9SJan Schumann            $info['grps'][] = $conf['defaultgroup'];
261*f4476bd9SJan Schumann        }
262*f4476bd9SJan Schumann        return $info;
263*f4476bd9SJan Schumann    }
264*f4476bd9SJan Schumann
265*f4476bd9SJan Schumann    /**
266*f4476bd9SJan Schumann     * Most values in LDAP are case-insensitive
267*f4476bd9SJan Schumann     */
268*f4476bd9SJan Schumann    function isCaseSensitive(){
269*f4476bd9SJan Schumann        return false;
270*f4476bd9SJan Schumann    }
271*f4476bd9SJan Schumann
272*f4476bd9SJan Schumann    /**
273*f4476bd9SJan Schumann     * Bulk retrieval of user data
274*f4476bd9SJan Schumann     *
275*f4476bd9SJan Schumann     * @author  Dominik Eckelmann <dokuwiki@cosmocode.de>
276*f4476bd9SJan Schumann     * @param   start     index of first user to be returned
277*f4476bd9SJan Schumann     * @param   limit     max number of users to be returned
278*f4476bd9SJan Schumann     * @param   filter    array of field/pattern pairs, null for no filter
279*f4476bd9SJan Schumann     * @return  array of userinfo (refer getUserData for internal userinfo details)
280*f4476bd9SJan Schumann     */
281*f4476bd9SJan Schumann    function retrieveUsers($start=0,$limit=-1,$filter=array()) {
282*f4476bd9SJan Schumann        if(!$this->_openLDAP()) return false;
283*f4476bd9SJan Schumann
284*f4476bd9SJan Schumann        if (!isset($this->users)) {
285*f4476bd9SJan Schumann            // Perform the search and grab all their details
286*f4476bd9SJan Schumann            if(!empty($this->cnf['userfilter'])) {
287*f4476bd9SJan Schumann                $all_filter = str_replace('%{user}', '*', $this->cnf['userfilter']);
288*f4476bd9SJan Schumann            } else {
289*f4476bd9SJan Schumann                $all_filter = "(ObjectClass=*)";
290*f4476bd9SJan Schumann            }
291*f4476bd9SJan Schumann            $sr=ldap_search($this->con,$this->cnf['usertree'],$all_filter);
292*f4476bd9SJan Schumann            $entries = ldap_get_entries($this->con, $sr);
293*f4476bd9SJan Schumann            $users_array = array();
294*f4476bd9SJan Schumann            for ($i=0; $i<$entries["count"]; $i++){
295*f4476bd9SJan Schumann                array_push($users_array, $entries[$i]["uid"][0]);
296*f4476bd9SJan Schumann            }
297*f4476bd9SJan Schumann            asort($users_array);
298*f4476bd9SJan Schumann            $result = $users_array;
299*f4476bd9SJan Schumann            if (!$result) return array();
300*f4476bd9SJan Schumann            $this->users = array_fill_keys($result, false);
301*f4476bd9SJan Schumann        }
302*f4476bd9SJan Schumann        $i = 0;
303*f4476bd9SJan Schumann        $count = 0;
304*f4476bd9SJan Schumann        $this->_constructPattern($filter);
305*f4476bd9SJan Schumann        $result = array();
306*f4476bd9SJan Schumann
307*f4476bd9SJan Schumann        foreach ($this->users as $user => &$info) {
308*f4476bd9SJan Schumann            if ($i++ < $start) {
309*f4476bd9SJan Schumann                continue;
310*f4476bd9SJan Schumann            }
311*f4476bd9SJan Schumann            if ($info === false) {
312*f4476bd9SJan Schumann                $info = $this->getUserData($user);
313*f4476bd9SJan Schumann            }
314*f4476bd9SJan Schumann            if ($this->_filter($user, $info)) {
315*f4476bd9SJan Schumann                $result[$user] = $info;
316*f4476bd9SJan Schumann                if (($limit >= 0) && (++$count >= $limit)) break;
317*f4476bd9SJan Schumann            }
318*f4476bd9SJan Schumann        }
319*f4476bd9SJan Schumann        return $result;
320*f4476bd9SJan Schumann
321*f4476bd9SJan Schumann
322*f4476bd9SJan Schumann    }
323*f4476bd9SJan Schumann
324*f4476bd9SJan Schumann    /**
325*f4476bd9SJan Schumann     * Make LDAP filter strings.
326*f4476bd9SJan Schumann     *
327*f4476bd9SJan Schumann     * Used by auth_getUserData to make the filter
328*f4476bd9SJan Schumann     * strings for grouptree and groupfilter
329*f4476bd9SJan Schumann     *
330*f4476bd9SJan Schumann     * filter      string  ldap search filter with placeholders
331*f4476bd9SJan Schumann     * placeholders array   array with the placeholders
332*f4476bd9SJan Schumann     *
333*f4476bd9SJan Schumann     * @author  Troels Liebe Bentsen <tlb@rapanden.dk>
334*f4476bd9SJan Schumann     * @return  string
335*f4476bd9SJan Schumann     */
336*f4476bd9SJan Schumann    function _makeFilter($filter, $placeholders) {
337*f4476bd9SJan Schumann        preg_match_all("/%{([^}]+)/", $filter, $matches, PREG_PATTERN_ORDER);
338*f4476bd9SJan Schumann        //replace each match
339*f4476bd9SJan Schumann        foreach ($matches[1] as $match) {
340*f4476bd9SJan Schumann            //take first element if array
341*f4476bd9SJan Schumann            if(is_array($placeholders[$match])) {
342*f4476bd9SJan Schumann                $value = $placeholders[$match][0];
343*f4476bd9SJan Schumann            } else {
344*f4476bd9SJan Schumann                $value = $placeholders[$match];
345*f4476bd9SJan Schumann            }
346*f4476bd9SJan Schumann            $value = $this->_filterEscape($value);
347*f4476bd9SJan Schumann            $filter = str_replace('%{'.$match.'}', $value, $filter);
348*f4476bd9SJan Schumann        }
349*f4476bd9SJan Schumann        return $filter;
350*f4476bd9SJan Schumann    }
351*f4476bd9SJan Schumann
352*f4476bd9SJan Schumann    /**
353*f4476bd9SJan Schumann     * return 1 if $user + $info match $filter criteria, 0 otherwise
354*f4476bd9SJan Schumann     *
355*f4476bd9SJan Schumann     * @author   Chris Smith <chris@jalakai.co.uk>
356*f4476bd9SJan Schumann     */
357*f4476bd9SJan Schumann    function _filter($user, $info) {
358*f4476bd9SJan Schumann        foreach ($this->_pattern as $item => $pattern) {
359*f4476bd9SJan Schumann            if ($item == 'user') {
360*f4476bd9SJan Schumann                if (!preg_match($pattern, $user)) return 0;
361*f4476bd9SJan Schumann            } else if ($item == 'grps') {
362*f4476bd9SJan Schumann                if (!count(preg_grep($pattern, $info['grps']))) return 0;
363*f4476bd9SJan Schumann            } else {
364*f4476bd9SJan Schumann                if (!preg_match($pattern, $info[$item])) return 0;
365*f4476bd9SJan Schumann            }
366*f4476bd9SJan Schumann        }
367*f4476bd9SJan Schumann        return 1;
368*f4476bd9SJan Schumann    }
369*f4476bd9SJan Schumann
370*f4476bd9SJan Schumann    function _constructPattern($filter) {
371*f4476bd9SJan Schumann        $this->_pattern = array();
372*f4476bd9SJan Schumann        foreach ($filter as $item => $pattern) {
373*f4476bd9SJan Schumann//          $this->_pattern[$item] = '/'.preg_quote($pattern,"/").'/i';          // don't allow regex characters
374*f4476bd9SJan Schumann            $this->_pattern[$item] = '/'.str_replace('/','\/',$pattern).'/i';    // allow regex characters
375*f4476bd9SJan Schumann        }
376*f4476bd9SJan Schumann    }
377*f4476bd9SJan Schumann
378*f4476bd9SJan Schumann    /**
379*f4476bd9SJan Schumann     * Escape a string to be used in a LDAP filter
380*f4476bd9SJan Schumann     *
381*f4476bd9SJan Schumann     * Ported from Perl's Net::LDAP::Util escape_filter_value
382*f4476bd9SJan Schumann     *
383*f4476bd9SJan Schumann     * @author Andreas Gohr
384*f4476bd9SJan Schumann     */
385*f4476bd9SJan Schumann    function _filterEscape($string){
386*f4476bd9SJan Schumann        return preg_replace('/([\x00-\x1F\*\(\)\\\\])/e',
387*f4476bd9SJan Schumann                            '"\\\\\".join("",unpack("H2","$1"))',
388*f4476bd9SJan Schumann                            $string);
389*f4476bd9SJan Schumann    }
390*f4476bd9SJan Schumann
391*f4476bd9SJan Schumann    /**
392*f4476bd9SJan Schumann     * Opens a connection to the configured LDAP server and sets the wanted
393*f4476bd9SJan Schumann     * option on the connection
394*f4476bd9SJan Schumann     *
395*f4476bd9SJan Schumann     * @author  Andreas Gohr <andi@splitbrain.org>
396*f4476bd9SJan Schumann     */
397*f4476bd9SJan Schumann    function _openLDAP(){
398*f4476bd9SJan Schumann        if($this->con) return true; // connection already established
399*f4476bd9SJan Schumann
400*f4476bd9SJan Schumann        $this->bound = 0;
401*f4476bd9SJan Schumann
402*f4476bd9SJan Schumann        $port = ($this->cnf['port']) ? $this->cnf['port'] : 389;
403*f4476bd9SJan Schumann        $this->con = @ldap_connect($this->cnf['server'],$port);
404*f4476bd9SJan Schumann        if(!$this->con){
405*f4476bd9SJan Schumann            msg("LDAP: couldn't connect to LDAP server",-1);
406*f4476bd9SJan Schumann            return false;
407*f4476bd9SJan Schumann        }
408*f4476bd9SJan Schumann
409*f4476bd9SJan Schumann        //set protocol version and dependend options
410*f4476bd9SJan Schumann        if($this->cnf['version']){
411*f4476bd9SJan Schumann            if(!@ldap_set_option($this->con, LDAP_OPT_PROTOCOL_VERSION,
412*f4476bd9SJan Schumann                                 $this->cnf['version'])){
413*f4476bd9SJan Schumann                msg('Setting LDAP Protocol version '.$this->cnf['version'].' failed',-1);
414*f4476bd9SJan Schumann                if($this->cnf['debug'])
415*f4476bd9SJan Schumann                    msg('LDAP version set: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
416*f4476bd9SJan Schumann            }else{
417*f4476bd9SJan Schumann                //use TLS (needs version 3)
418*f4476bd9SJan Schumann                if($this->cnf['starttls']) {
419*f4476bd9SJan Schumann                    if (!@ldap_start_tls($this->con)){
420*f4476bd9SJan Schumann                        msg('Starting TLS failed',-1);
421*f4476bd9SJan Schumann                        if($this->cnf['debug'])
422*f4476bd9SJan Schumann                            msg('LDAP TLS set: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
423*f4476bd9SJan Schumann                    }
424*f4476bd9SJan Schumann                }
425*f4476bd9SJan Schumann                // needs version 3
426*f4476bd9SJan Schumann                if(isset($this->cnf['referrals'])) {
427*f4476bd9SJan Schumann                    if(!@ldap_set_option($this->con, LDAP_OPT_REFERRALS,
428*f4476bd9SJan Schumann                       $this->cnf['referrals'])){
429*f4476bd9SJan Schumann                        msg('Setting LDAP referrals to off failed',-1);
430*f4476bd9SJan Schumann                        if($this->cnf['debug'])
431*f4476bd9SJan Schumann                            msg('LDAP referal set: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
432*f4476bd9SJan Schumann                    }
433*f4476bd9SJan Schumann                }
434*f4476bd9SJan Schumann            }
435*f4476bd9SJan Schumann        }
436*f4476bd9SJan Schumann
437*f4476bd9SJan Schumann        //set deref mode
438*f4476bd9SJan Schumann        if($this->cnf['deref']){
439*f4476bd9SJan Schumann            if(!@ldap_set_option($this->con, LDAP_OPT_DEREF, $this->cnf['deref'])){
440*f4476bd9SJan Schumann                msg('Setting LDAP Deref mode '.$this->cnf['deref'].' failed',-1);
441*f4476bd9SJan Schumann                if($this->cnf['debug'])
442*f4476bd9SJan Schumann                    msg('LDAP deref set: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
443*f4476bd9SJan Schumann            }
444*f4476bd9SJan Schumann        }
445*f4476bd9SJan Schumann
446*f4476bd9SJan Schumann        $this->canDo['getUsers'] = true;
447*f4476bd9SJan Schumann        return true;
448*f4476bd9SJan Schumann    }
449*f4476bd9SJan Schumann
450*f4476bd9SJan Schumann    /**
451*f4476bd9SJan Schumann     * Wraps around ldap_search, ldap_list or ldap_read depending on $scope
452*f4476bd9SJan Schumann     *
453*f4476bd9SJan Schumann     * @param  $scope string - can be 'base', 'one' or 'sub'
454*f4476bd9SJan Schumann     * @author Andreas Gohr <andi@splitbrain.org>
455*f4476bd9SJan Schumann     */
456*f4476bd9SJan Schumann    function _ldapsearch($link_identifier, $base_dn, $filter, $scope='sub', $attributes=null,
457*f4476bd9SJan Schumann                         $attrsonly=0, $sizelimit=0, $timelimit=0, $deref=LDAP_DEREF_NEVER){
458*f4476bd9SJan Schumann        if(is_null($attributes)) $attributes = array();
459*f4476bd9SJan Schumann
460*f4476bd9SJan Schumann        if($scope == 'base'){
461*f4476bd9SJan Schumann            return @ldap_read($link_identifier, $base_dn, $filter, $attributes,
462*f4476bd9SJan Schumann                             $attrsonly, $sizelimit, $timelimit, $deref);
463*f4476bd9SJan Schumann        }elseif($scope == 'one'){
464*f4476bd9SJan Schumann            return @ldap_list($link_identifier, $base_dn, $filter, $attributes,
465*f4476bd9SJan Schumann                             $attrsonly, $sizelimit, $timelimit, $deref);
466*f4476bd9SJan Schumann        }else{
467*f4476bd9SJan Schumann            return @ldap_search($link_identifier, $base_dn, $filter, $attributes,
468*f4476bd9SJan Schumann                                $attrsonly, $sizelimit, $timelimit, $deref);
469*f4476bd9SJan Schumann        }
470*f4476bd9SJan Schumann    }
471*f4476bd9SJan Schumann}