1<?php
2
3/**
4 * OpenLDAP + Kerberos authentication backend
5 *
6 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
7 * @author     Sebastián Santisi <s@ntisi.com.ar>
8 */
9class auth_plugin_authopenldapkerberos extends DokuWiki_Auth_Plugin
10{
11    /** @var array filter pattern */
12    protected $pattern = array();
13
14    public function __construct()
15    {
16        parent::__construct();
17
18        if(!function_exists('ldap_connect') || !class_exists('KRB5CCache')) {
19            // This condition isn't checking for a valid Kerberos ticket or for
20            // a system GSSAPI library, the only way to check that is trying to
21            // bind to LDAP.
22            $this->success = false;
23            return;
24        }
25
26        $this->cando['getUsers']     = true;
27        $this->cando['getUserCount'] = true;
28        $this->cando['getGroups']    = true;
29    }
30
31    public function checkPass($user, $pass)
32    {
33        try {
34            $p = new KRB5CCache();
35            $p->initPassword($user, $pass);
36            return true;
37        }
38        catch(Exception $e) {
39            return false;
40        }
41    }
42
43    public function getUserData($user, $requireGroups = true)
44    {
45        $c = $this->connect();
46        $r = ldap_search(
47            $c,
48            "ou={$this->getConf('user_ou')},{$this->getConf('base_dn')}",
49            "({$this->getConf('userkey')}=$user)",
50            Array($this->getConf('username'), $this->getConf('usergid'), $this->getConf('usermail'))
51        );
52
53        $e = ldap_get_entries($c, $r);
54
55        $userdata = Array(
56            'name' => $e[0][$this->getConf('username')][0],
57            'mail' => $e[0][$this->getConf('usermail')][0],
58            'grps' => Array()
59	    );
60
61        if($requireGroups) {
62            $gid = $e[0][$this->getConf('usergid')][0];
63
64            $r = ldap_search(
65                $c,
66                "ou={$this->getConf('group_ou')},{$this->getConf('base_dn')}",
67                "({$this->getConf('groupkey')}=*)",
68                Array($this->getConf('groupkey'), $this->getConf('groupgid'), $this->getConf('groupuids'))
69            );
70            $e = ldap_get_entries($c, $r);
71
72            for($i = 0; $i < $e['count']; $i++) {
73                $cn = $e[$i][$this->getConf('groupkey')][0];
74                $gn = $e[$i][$this->getConf('groupgid')]['0'];
75
76                if($gn == $gid)
77                    $userdata['grps'][] = $cn;
78                else if(array_key_exists($this->getConf('groupuids'), $e[$i])) {
79                    for($j = 0; $j < $e[$i][$this->getConf('groupuids')]['count']; $j++) {
80                        if($e[$i][$this->getConf('groupuids')][$j] == $user)
81                             $userdata['grps'][] = $cn;
82                    }
83                }
84            }
85        }
86
87        ldap_unbind($c);
88
89        return $userdata;
90    }
91
92    protected function connect() {
93        $c = ldap_connect($this->getConf('server'), $this->getConf('port'));
94        ldap_set_option($c, LDAP_OPT_PROTOCOL_VERSION, 3);
95        ldap_sasl_bind($c, NULL, NULL, 'GSSAPI');
96        return $c;
97    }
98
99    public function getUserCount($filter = array())
100    {
101        $c = $this->connect();
102        $r = ldap_search(
103            $c,
104            "ou={$this->getConf('user_ou')},{$this->getConf('base_dn')}",
105            "({$this->getConf('userkey')}=*)",
106            Array($this->getConf('usergid'))
107        );
108        $e = ldap_get_entries($c, $r);
109
110        ldap_unbind($c);
111
112        return $e['count'];
113    }
114
115    public function retrieveUsers($start = 0, $limit = 0, $filter = array())
116    {
117        $this->constructPattern($filter);
118
119        $c = $this->connect();
120        $r = ldap_search(
121            $c,
122            "ou={$this->getConf('user_ou')},{$this->getConf('base_dn')}",
123            "({$this->getConf('userkey')}=*)",
124            Array($this->getConf('userkey'), $this->getConf('username'), $this->getConf('usergid'), $this->getConf('usermail'))
125        );
126        $e = ldap_get_entries($c, $r);
127
128        $users = Array();
129
130        for($i = 0; $i < $e['count']; $i++)
131            $users[$e[$i][$this->getConf('userkey')][0]] = Array(
132                'name' => $e[$i][$this->getConf('username')][0],
133                'mail' => $e[$i][$this->getConf('usermail')][0],
134                'grps' => Array(),
135                'gid' => $e[$i][$this->getConf('usergid')][0]
136            );
137
138        $r = ldap_search(
139            $c,
140            "ou={$this->getConf('group_ou')},{$this->getConf('base_dn')}",
141            "({$this->getConf('groupkey')}=*)",
142            Array($this->getConf('groupkey'), $this->getConf('groupgid'), $this->getConf('groupuids'))
143        );
144	    $e = ldap_get_entries($c, $r);
145
146        ldap_unbind($c);
147
148        $groups = Array();
149
150        for($i = 0; $i < $e['count']; $i++) {
151            $cn = $e[$i][$this->getConf('groupkey')][0];
152            $gn = $e[$i][$this->getConf('groupgid')]['0'];
153
154            $groups[$gn] = $cn;
155
156            if(array_key_exists($this->getConf('groupuids'), $e[$i]))
157                for($j = 0; $j < $e[$i][$this->getConf('groupuids')]['count']; $j++)
158                    $users[$e[$i][$this->getConf('groupuids')][$j]]['grps'][] = $cn;
159        }
160
161        $out = Array();
162
163        $uids = array_keys($users);
164        sort($uids);
165
166        foreach($uids as $uid) {
167            $users[$uid]['grps'][] = $groups[$users[$uid]['gid']];
168
169            if($this->filter($uid, $users[$uid])) {
170                if($start > 0)
171                    $start--;
172                else {
173                    $out[$uid] = $users[$uid];
174                    if(count($out) == $limit)
175                        break;
176                }
177            }
178        }
179
180        return $out;
181    }
182
183    public function retrieveGroups($start = 0, $limit = 0)
184    {
185        $c = $this->connect();
186        $r = ldap_search(
187            $c,
188            "ou={$this->getConf('group_ou')},{$this->getConf('base_dn')}",
189            "({$this->getConf('groupkey')}=*)",
190            Array($this->getConf('groupkey'))
191        );
192        $e = ldap_get_entries($c, $r);
193
194        ldap_unbind($c);
195
196        if($limit == 0 || $limit > $e['count']) $limit = $e['count'];
197
198        $groups = Array();
199
200        for($i = $start; $i < $limit; $i++) {
201                $groups[] = $e[$i][$this->getConf('groupkey')][0];
202        }
203
204        return $groups;
205    }
206
207
208    /**
209     * return true if $user + $info match $filter criteria, false otherwise
210     *
211     * @author   Chris Smith <chris@jalakai.co.uk>
212     *
213     * @param string $user User login
214     * @param array  $info User's userinfo array
215     * @return bool
216     */
217    protected function filter($user, $info)
218    {
219        foreach ($this->pattern as $item => $pattern) {
220            if ($item == 'user') {
221                if (!preg_match($pattern, $user)) return false;
222            } elseif ($item == 'grps') {
223                if (!count(preg_grep($pattern, $info['grps']))) return false;
224            } else {
225                if (!preg_match($pattern, $info[$item])) return false;
226            }
227        }
228        return true;
229    }
230
231    /**
232     * construct a filter pattern
233     *
234     * @param array $filter
235     */
236    protected function constructPattern($filter)
237    {
238        $this->pattern = array();
239        foreach ($filter as $item => $pattern) {
240            $this->pattern[$item] = '/'.str_replace('/', '\/', $pattern).'/i'; // allow regex characters
241        }
242    }
243}
244