1<?php
2
3/**
4 * Authentication classes
5 * @package framework
6 * @subpackage auth
7 */
8
9/**
10 * Base class for authentication
11 * Creating a new authentication method requires extending this class
12 * and overriding the check_credentials method
13 * @abstract
14 */
15abstract class Hm_Auth {
16
17    /* site configuration object */
18    protected $site_config;
19
20    /* bool flag defining if users are internal */
21    static public $internal_users = false;
22
23    /**
24     * Assign site config
25     * @param object $config site config
26     */
27    public function __construct($config) {
28        $this->site_config = $config;
29    }
30
31    /**
32     * This is the method new auth mechs need to override.
33     * @param string $user username
34     * @param string $pass password
35     * @return bool true if the user is authenticated, false otherwise
36     */
37    abstract public function check_credentials($user, $pass);
38
39    /**
40     * Optional method for auth mech to save login details
41     * @param object $session session object
42     * @return void
43     */
44    public function save_auth_detail($session) {}
45}
46
47/**
48 * Stub for dynamic authentication
49 */
50class Hm_Auth_Dynamic extends Hm_Auth {
51    public function check_credentials($user, $pass) {
52        return false;
53    }
54}
55/**
56 * Authenticate against an included DB
57 */
58class Hm_Auth_DB extends Hm_Auth {
59
60    /* bool flag indicating this is an internal user setup */
61    static public $internal_users = true;
62
63    /* database conneciton handle */
64    public $dbh;
65
66    /**
67     * Send the username and password to the configured DB for authentication
68     * @param string $user username
69     * @param string $pass password
70     * @return bool true if authentication worked
71     */
72    public function check_credentials($user, $pass) {
73        $this->connect();
74        $row = Hm_DB::execute($this->dbh, 'select hash from hm_user where username = ?', array($user));
75        if ($row && array_key_exists('hash', $row) && $row['hash'] && Hm_Crypt::check_password($pass, $row['hash'])) {
76            return true;
77        }
78        sleep(2);
79        Hm_Debug::add(sprintf('DB AUTH failed for %s', $user));
80        return false;
81    }
82
83    /**
84     * Delete a user account from the db
85     * @param string $user username
86     * @return bool true if successful
87     */
88    public function delete($user) {
89        $this->connect();
90        if (Hm_DB::execute($this->dbh, 'delete from hm_user where username = ?', array($user))) {
91            return true;
92        }
93        return false;
94    }
95
96    /**
97     * Create a new or re-use an existing DB connection
98     * @return bool true if the connection is available
99     */
100    protected function connect() {
101        $this->dbh = Hm_DB::connect($this->site_config);
102        if ($this->dbh) {
103            return true;
104        }
105        Hm_Debug::add(sprintf('Unable to connect to the DB auth server %s', $this->site_config->get('db_host')));
106        return false;
107    }
108
109    /**
110     * Change the password for a user in the DB
111     * @param string $user username
112     * @param string $pass password
113     * @return bool true on success
114     */
115    public function change_pass($user, $pass) {
116        $this->connect();
117        $hash = Hm_Crypt::hash_password($pass);
118        if (Hm_DB::execute($this->dbh, 'update hm_user set hash=? where username=?', array($hash, $user))) {
119            return true;
120        }
121        return false;
122    }
123
124    /**
125     * Create a new user in the DB
126     * @param string $user username
127     * @param string $pass password
128     * @return integer
129     */
130    public function create($user, $pass) {
131        $this->connect();
132        $result = 0;
133        $res = Hm_DB::execute($this->dbh, 'select username from hm_user where username = ?', array($user));
134        if (!empty($res)) {
135            $result = 1;
136        }
137        else {
138            $hash = Hm_Crypt::hash_password($pass);
139            if (Hm_DB::execute($this->dbh, 'insert into hm_user values(?,?)', array($user, $hash))) {
140                $result = 2;
141            }
142        }
143        return $result;
144    }
145}
146
147/**
148 * Authenticate against an IMAP server
149 */
150class Hm_Auth_IMAP extends Hm_Auth {
151
152    /**
153     * Assign site config, get required libs
154     * @param object $config site config
155     */
156    public function __construct($config) {
157        $this->site_config = $config;
158        require_once APP_PATH.'modules/imap/hm-imap.php';
159    }
160
161    /* IMAP authentication server settings */
162    private $imap_settings = array();
163
164    /**
165     * @param object $imap imap connection object
166     * @return boolean
167     */
168    private function check_connection($imap) {
169        $imap->connect($this->imap_settings);
170        if ($imap->get_state() == 'authenticated') {
171            return true;
172        }
173        elseif ($imap->get_state() != 'connected') {
174            Hm_Debug::add($imap->show_debug(true));
175            Hm_Debug::add(sprintf('Unable to connect to the IMAP auth server %s', $this->imap_settings['server']));
176            return false;
177        }
178        else {
179            Hm_Debug::add($imap->show_debug(true));
180            Hm_Debug::add(sprintf('IMAP AUTH failed for %s', $this->imap_settings['username']));
181            return false;
182        }
183    }
184
185    /**
186     * Send the username and password to the configured IMAP server for authentication
187     * @param string $user username
188     * @param string $pass password
189     * @return bool true if authentication worked
190     */
191    public function check_credentials($user, $pass) {
192        $imap = new Hm_IMAP();
193        list($server, $port, $tls) = get_auth_config($this->site_config, 'imap');
194        if (!$user || !$pass || !$server || !$port) {
195            Hm_Debug::add($imap->show_debug(true));
196            Hm_Debug::add('Invalid IMAP auth configuration settings');
197            return false;
198        }
199        $this->imap_settings = array('server' => $server, 'port' => $port,
200            'tls' => $tls, 'username' => $user, 'password' => $pass,
201            'no_caps' => false, 'blacklisted_extensions' => array('enable')
202        );
203        return $this->check_connection($imap);
204    }
205
206    /**
207     * Save IMAP server details
208     * @param object $session session object
209     * @return void
210     */
211    public function save_auth_detail($session) {
212        $session->set('imap_auth_server_settings', $this->imap_settings);
213    }
214}
215
216/**
217 * Authenticate against a POP3 server
218 */
219class Hm_Auth_POP3 extends Hm_Auth {
220
221    /* POP3 authentication server settings */
222    private $pop3_settings = array();
223
224    /**
225     * Assign site config, get required libs
226     * @param object $config site config
227     */
228    public function __construct($config) {
229        $this->site_config = $config;
230        require_once APP_PATH.'modules/pop3/hm-pop3.php';
231    }
232
233    /**
234     * @param object $pop3 pop3 connection object
235     * @param string $user username to login with
236     * @param string $pass password to login with
237     * @param string $server server to login to
238     * @return boolean
239     */
240    private function check_connection($pop3, $user, $pass, $server) {
241        if (!$pop3->connect()) {
242            Hm_Debug::add($pop3->puke());
243            Hm_Debug::add(sprintf('Unable to connect to the POP3 auth server %s', $server));
244            return false;
245        }
246        if (!$pop3->auth($user, $pass)) {
247            Hm_Debug::add($pop3->puke());
248            Hm_Debug::add(sprintf('POP3 AUTH failed for %s', $user));
249            return false;
250        }
251        return true;
252    }
253
254    /**
255     * Send the username and password to the configured POP3 server for authentication
256     * @param string $user username
257     * @param string $pass password
258     * @return bool true if authentication worked
259     */
260    public function check_credentials($user, $pass) {
261        $pop3 = new Hm_POP3();
262        list($server, $port, $tls) = get_auth_config($this->site_config, 'pop3');
263        if ($user && $pass && $server && $port) {
264            $this->pop3_settings = array(
265                'server' => $server,
266                'port' => $port,
267                'tls' => $tls,
268                'username' => $user,
269                'password' => $pass,
270                'no_caps' => true
271            );
272            $pop3->server = $server;
273            $pop3->port = $port;
274            $pop3->tls = $tls;
275            return $this->check_connection($pop3, $user, $pass, $server);
276        }
277        Hm_Debug::add($pop3->puke());
278        Hm_Debug::add('Invalid POP3 auth configuration settings');
279        return false;
280    }
281
282    /**
283     * Save POP3 server details
284     * @param object $session session object
285     * @return void
286     */
287    public function save_auth_detail($session) {
288        $session->set('pop3_auth_server_settings', $this->pop3_settings);
289    }
290}
291
292/**
293 * Authenticate against an LDAP server
294 */
295class Hm_Auth_LDAP extends Hm_Auth {
296
297    protected $config = array();
298    protected $fh;
299    protected $source = 'ldap';
300
301    /**
302     * Build connect uri
303     * @return string
304     */
305    private function connect_details() {
306        $prefix = 'ldaps://';
307        $server = $this->apply_config_value('server', 'localhost');
308        $port = $this->apply_config_value('port', 389);
309        if (array_key_exists('enable_tls', $this->config) && !$this->config['enable_tls']) {
310            $prefix = 'ldap://';
311        }
312        return $prefix.$server.':'.$port;
313    }
314
315    /**
316     * @param string $name
317     * @param mixed $default
318     * @return mixed
319     */
320    private function apply_config_value($name, $default) {
321        if (array_key_exists($name, $this->config) && trim($this->config[$name])) {
322            return $this->config[$name];
323        }
324        return $default;
325    }
326
327    /**
328     * Check a username and password
329     * @param string $user username
330     * @param string $pass password
331     * @return boolean
332     */
333    public function check_credentials($user, $pass) {
334        list($server, $port, $tls) = get_auth_config($this->site_config, 'ldap');
335        $base_dn = $this->site_config->get('ldap_auth_base_dn', false);
336        if ($server && $port && $base_dn) {
337            $user = sprintf('cn=%s,%s', $user, $base_dn);
338            $this->config = array(
339                'server' => $server,
340                'port' => $port,
341                'enable_tls' => $tls,
342                'base_dn' => $base_dn,
343                'user' => $user,
344                'pass' => $pass
345            );
346            return $this->connect();
347        }
348        Hm_Debug::add('Invalid LDAP auth configuration settings');
349        return false;
350    }
351
352    /**
353     * Connect and auth to the LDAP server
354     * @return boolean
355     */
356    public function connect() {
357        if (!Hm_Functions::function_exists('ldap_connect')) {
358            return false;
359        }
360        $uri = $this->connect_details();
361        $this->fh = @ldap_connect($uri);
362        if ($this->fh) {
363            ldap_set_option($this->fh, LDAP_OPT_PROTOCOL_VERSION, 3);
364            return $this->auth();
365        }
366        Hm_Debug::add(sprintf('Unable to connect to the LDAP auth server %s', $this->config['server']));
367        return false;
368    }
369
370    /**
371     * Authenticate to the LDAP server
372     * @return boolean
373     */
374    protected function auth() {
375        $result = @ldap_bind($this->fh, $this->config['user'], $this->config['pass']);
376        ldap_unbind($this->fh);
377        if (!$result) {
378            Hm_Debug::add(sprintf('LDAP AUTH failed for %s', $this->config['user']));
379        }
380        return $result;
381    }
382}
383
384/*
385 * @param object $config site config object
386 * @param string $prefix settings prefix
387 * @return array
388 */
389function get_auth_config($config, $prefix) {
390    $server = $config->get($prefix.'_auth_server', false);
391    $port = $config->get($prefix.'_auth_port', false);
392    $tls = $config->get($prefix.'_auth_tls', false);
393    return array($server, $port, $tls);
394}
395