Project

General

Profile

Download (46.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	auth.inc
4
*/
5
/* ====================================================================
6
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
7
 *	Copyright (c)  2005-2006 Bill Marquette <bill.marquette@gmail.com>
8
 *	Copyright (c)  2006 Paul Taylor <paultaylor@winn-dixie.com>
9
 *	Copyright (c)  2003-2006 Manuel Kasper <mk@neon1.net>
10
 *
11
 *	Redistribution and use in source and binary forms, with or without modification,
12
 *	are permitted provided that the following conditions are met:
13
 *
14
 *	1. Redistributions of source code must retain the above copyright notice,
15
 *		this list of conditions and the following disclaimer.
16
 *
17
 *	2. Redistributions in binary form must reproduce the above copyright
18
 *		notice, this list of conditions and the following disclaimer in
19
 *		the documentation and/or other materials provided with the
20
 *		distribution.
21
 *
22
 *	3. All advertising materials mentioning features or use of this software
23
 *		must display the following acknowledgment:
24
 *		"This product includes software developed by the pfSense Project
25
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
26
 *
27
 *	4. The names "pfSense" and "pfSense Project" must not be used to
28
 *		 endorse or promote products derived from this software without
29
 *		 prior written permission. For written permission, please contact
30
 *		 coreteam@pfsense.org.
31
 *
32
 *	5. Products derived from this software may not be called "pfSense"
33
 *		nor may "pfSense" appear in their names without prior written
34
 *		permission of the Electric Sheep Fencing, LLC.
35
 *
36
 *	6. Redistributions of any form whatsoever must retain the following
37
 *		acknowledgment:
38
 *
39
 *	"This product includes software developed by the pfSense Project
40
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
41
 *
42
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
43
 *	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
45
 *	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
46
 *	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
49
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
51
 *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
52
 *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
53
 *	OF THE POSSIBILITY OF SUCH DAMAGE.
54
 *
55
 *	====================================================================
56
 *
57
 */
58
/*
59
 * NOTE : Portions of the mschapv2 support was based on the BSD licensed CHAP.php
60
 * file courtesy of Michael Retterklieber.
61
 */
62
if (!$do_not_include_config_gui_inc) {
63
	require_once("config.gui.inc");
64
}
65

    
66
// Will be changed to false if security checks fail
67
$security_passed = true;
68

    
69
/* If this function doesn't exist, we're being called from Captive Portal or
70
   another internal subsystem which does not include authgui.inc */
71
if (function_exists("display_error_form") && !isset($config['system']['webgui']['nodnsrebindcheck'])) {
72
	/* DNS ReBinding attack prevention.  https://redmine.pfsense.org/issues/708 */
73
	$found_host = false;
74

    
75
	/* Either a IPv6 address with or without a alternate port */
76
	if (strstr($_SERVER['HTTP_HOST'], "]")) {
77
		$http_host_port = explode("]", $_SERVER['HTTP_HOST']);
78
		/* v6 address has more parts, drop the last part */
79
		if (count($http_host_port) > 1) {
80
			array_pop($http_host_port);
81
			$http_host = str_replace(array("[", "]"), "", implode(":", $http_host_port));
82
		} else {
83
			$http_host = str_replace(array("[", "]"), "", implode(":", $http_host_port));
84
		}
85
	} else {
86
		$http_host = explode(":", $_SERVER['HTTP_HOST']);
87
		$http_host = $http_host[0];
88
	}
89
	if (is_ipaddr($http_host) or $_SERVER['SERVER_ADDR'] == "127.0.0.1" or
90
		strcasecmp($http_host, "localhost") == 0 or $_SERVER['SERVER_ADDR'] == "::1") {
91
		$found_host = true;
92
	}
93
	if (strcasecmp($http_host, $config['system']['hostname'] . "." . $config['system']['domain']) == 0 or
94
		strcasecmp($http_host, $config['system']['hostname']) == 0) {
95
		$found_host = true;
96
	}
97

    
98
	if (is_array($config['dyndnses']['dyndns']) && !$found_host) {
99
		foreach ($config['dyndnses']['dyndns'] as $dyndns) {
100
			if (strcasecmp($dyndns['host'], $http_host) == 0) {
101
				$found_host = true;
102
				break;
103
			}
104
		}
105
	}
106

    
107
	if (is_array($config['dnsupdates']['dnsupdate']) && !$found_host) {
108
		foreach ($config['dnsupdates']['dnsupdate'] as $rfc2136) {
109
			if (strcasecmp($rfc2136['host'], $http_host) == 0) {
110
				$found_host = true;
111
				break;
112
			}
113
		}
114
	}
115

    
116
	if (!empty($config['system']['webgui']['althostnames']) && !$found_host) {
117
		$althosts = explode(" ", $config['system']['webgui']['althostnames']);
118
		foreach ($althosts as $ah) {
119
			if (strcasecmp($ah, $http_host) == 0 or strcasecmp($ah, $_SERVER['SERVER_ADDR']) == 0) {
120
				$found_host = true;
121
				break;
122
			}
123
		}
124
	}
125

    
126
	if ($found_host == false) {
127
		if (!security_checks_disabled()) {
128
			display_error_form("501", gettext("Potential DNS Rebind attack detected, see http://en.wikipedia.org/wiki/DNS_rebinding<br />Try accessing the router by IP address instead of by hostname."));
129
			exit;
130
		}
131
		$security_passed = false;
132
	}
133
}
134

    
135
// If the HTTP_REFERER is something other than ourselves then disallow.
136
if (function_exists("display_error_form") && !isset($config['system']['webgui']['nohttpreferercheck'])) {
137
	if ($_SERVER['HTTP_REFERER']) {
138
		if (file_exists("{$g['tmp_path']}/setupwizard_lastreferrer")) {
139
			if ($_SERVER['HTTP_REFERER'] == file_get_contents("{$g['tmp_path']}/setupwizard_lastreferrer")) {
140
				unlink("{$g['tmp_path']}/setupwizard_lastreferrer");
141
				header("Refresh: 1; url=index.php");
142
?>
143
<!DOCTYPE html>
144
<html lang="en">
145
<head>
146
	<link rel="stylesheet" href="/bootstrap/css/pfSense.css" />
147
	<title><?=gettext("Redirecting..."); ?></title>
148
</head>
149
<body id="error" class="no-menu">
150
	<div id="jumbotron">
151
		<div class="container">
152
			<div class="col-sm-offset-3 col-sm-6 col-xs-12">
153
				<p><?=gettext("Redirecting to the dashboard...")?></p>
154
			</div>
155
		</div>
156
	</div>
157
</body>
158
</html>
159
<?php
160
				exit;
161
			}
162
		}
163
		$found_host = false;
164
		$referrer_host = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);
165
		$referrer_host = str_replace(array("[", "]"), "", $referrer_host);
166
		if ($referrer_host) {
167
			if (strcasecmp($referrer_host, $config['system']['hostname'] . "." . $config['system']['domain']) == 0 ||
168
			    strcasecmp($referrer_host, $config['system']['hostname']) == 0) {
169
				$found_host = true;
170
			}
171

    
172
			if (!empty($config['system']['webgui']['althostnames']) && !$found_host) {
173
				$althosts = explode(" ", $config['system']['webgui']['althostnames']);
174
				foreach ($althosts as $ah) {
175
					if (strcasecmp($referrer_host, $ah) == 0) {
176
						$found_host = true;
177
						break;
178
					}
179
				}
180
			}
181

    
182
			if (is_array($config['dyndnses']['dyndns']) && !$found_host) {
183
				foreach ($config['dyndnses']['dyndns'] as $dyndns) {
184
					if (strcasecmp($dyndns['host'], $referrer_host) == 0) {
185
						$found_host = true;
186
						break;
187
					}
188
				}
189
			}
190

    
191
			if (is_array($config['dnsupdates']['dnsupdate']) && !$found_host) {
192
				foreach ($config['dnsupdates']['dnsupdate'] as $rfc2136) {
193
					if (strcasecmp($rfc2136['host'], $referrer_host) == 0) {
194
						$found_host = true;
195
						break;
196
					}
197
				}
198
			}
199

    
200
			if (!$found_host) {
201
				$interface_list_ips = get_configured_ip_addresses();
202
				foreach ($interface_list_ips as $ilips) {
203
					if (strcasecmp($referrer_host, $ilips) == 0) {
204
						$found_host = true;
205
						break;
206
					}
207
				}
208
				$interface_list_ipv6s = get_configured_ipv6_addresses();
209
				foreach ($interface_list_ipv6s as $ilipv6s) {
210
					if (strcasecmp($referrer_host, $ilipv6s) == 0) {
211
						$found_host = true;
212
						break;
213
					}
214
				}
215
				if ($referrer_host == "127.0.0.1" || $referrer_host == "localhost") {
216
					// allow SSH port forwarded connections and links from localhost
217
					$found_host = true;
218
				}
219
			}
220
		}
221
		if ($found_host == false) {
222
			if (!security_checks_disabled()) {
223
				display_error_form("501", "An HTTP_REFERER was detected other than what is defined in System -> Advanced (" . htmlspecialchars($_SERVER['HTTP_REFERER']) . ").  You can disable this check if needed in System -> Advanced -> Admin.");
224
				exit;
225
			}
226
			$security_passed = false;
227
		}
228
	} else {
229
		$security_passed = false;
230
	}
231
}
232

    
233
if (function_exists("display_error_form") && $security_passed) {
234
	/* Security checks passed, so it should be OK to turn them back on */
235
	restore_security_checks();
236
}
237
unset($security_passed);
238

    
239
$groupindex = index_groups();
240
$userindex = index_users();
241

    
242
function index_groups() {
243
	global $g, $debug, $config, $groupindex;
244

    
245
	$groupindex = array();
246

    
247
	if (is_array($config['system']['group'])) {
248
		$i = 0;
249
		foreach ($config['system']['group'] as $groupent) {
250
			$groupindex[$groupent['name']] = $i;
251
			$i++;
252
		}
253
	}
254

    
255
	return ($groupindex);
256
}
257

    
258
function index_users() {
259
	global $g, $debug, $config;
260

    
261
	if (is_array($config['system']['user'])) {
262
		$i = 0;
263
		foreach ($config['system']['user'] as $userent) {
264
			$userindex[$userent['name']] = $i;
265
			$i++;
266
		}
267
	}
268

    
269
	return ($userindex);
270
}
271

    
272
function & getUserEntry($name) {
273
	global $debug, $config, $userindex;
274
	if (isset($userindex[$name])) {
275
		return $config['system']['user'][$userindex[$name]];
276
	}
277
}
278

    
279
function & getUserEntryByUID($uid) {
280
	global $debug, $config;
281

    
282
	if (is_array($config['system']['user'])) {
283
		foreach ($config['system']['user'] as & $user) {
284
			if ($user['uid'] == $uid) {
285
				return $user;
286
			}
287
		}
288
	}
289

    
290
	return false;
291
}
292

    
293
function & getGroupEntry($name) {
294
	global $debug, $config, $groupindex;
295
	if (isset($groupindex[$name])) {
296
		return $config['system']['group'][$groupindex[$name]];
297
	}
298
}
299

    
300
function & getGroupEntryByGID($gid) {
301
	global $debug, $config;
302

    
303
	if (is_array($config['system']['group'])) {
304
		foreach ($config['system']['group'] as & $group) {
305
			if ($group['gid'] == $gid) {
306
				return $group;
307
			}
308
		}
309
	}
310

    
311
	return false;
312
}
313

    
314
function get_user_privileges(& $user) {
315

    
316
	$privs = $user['priv'];
317
	if (!is_array($privs)) {
318
		$privs = array();
319
	}
320

    
321
	$names = local_user_get_groups($user, true);
322

    
323
	foreach ($names as $name) {
324
		$group = getGroupEntry($name);
325
		if (is_array($group['priv'])) {
326
			$privs = array_merge($privs, $group['priv']);
327
		}
328
	}
329

    
330
	return $privs;
331
}
332

    
333
function userHasPrivilege($userent, $privid = false) {
334

    
335
	if (!$privid || !is_array($userent)) {
336
		return false;
337
	}
338

    
339
	$privs = get_user_privileges($userent);
340

    
341
	if (!is_array($privs)) {
342
		return false;
343
	}
344

    
345
	if (!in_array($privid, $privs)) {
346
		return false;
347
	}
348

    
349
	return true;
350
}
351

    
352
function local_backed($username, $passwd) {
353

    
354
	$user = getUserEntry($username);
355
	if (!$user) {
356
		return false;
357
	}
358

    
359
	if (is_account_disabled($username) || is_account_expired($username)) {
360
		return false;
361
	}
362

    
363
	if ($user['bcrypt-hash']) {
364
		if (password_verify($passwd, $user['bcrypt-hash'])) {
365
			return true;
366
		}
367
	}
368

    
369
	//for backwards compatibility
370
	if ($user['password']) {
371
		if (crypt($passwd, $user['password']) == $user['password']) {
372
			return true;
373
		}
374
	}
375

    
376
	if ($user['md5-hash']) {
377
		if (md5($passwd) == $user['md5-hash']) {
378
			return true;
379
		}
380
	}
381

    
382
	return false;
383
}
384

    
385
function local_sync_accounts() {
386
	global $debug, $config;
387
	conf_mount_rw();
388

    
389
	/* remove local users to avoid uid conflicts */
390
	$fd = popen("/usr/sbin/pw usershow -a", "r");
391
	if ($fd) {
392
		while (!feof($fd)) {
393
			$line = explode(":", fgets($fd));
394
			if (((!strncmp($line[0], "_", 1)) || ($line[2] < 2000) || ($line[2] > 65000)) && ($line[0] != "admin")) {
395
				continue;
396
			}
397
			/*
398
			 * If a crontab was created to user, pw userdel will be interactive and
399
			 * can cause issues. Just remove crontab before run it when necessary
400
			 */
401
			unlink_if_exists("/var/cron/tabs/{$line[0]}");
402
			$cmd = "/usr/sbin/pw userdel -n '{$line[0]}'";
403
			if ($debug) {
404
				log_error(sprintf(gettext("Running: %s"), $cmd));
405
			}
406
			mwexec($cmd);
407
		}
408
		pclose($fd);
409
	}
410

    
411
	/* remove local groups to avoid gid conflicts */
412
	$gids = array();
413
	$fd = popen("/usr/sbin/pw groupshow -a", "r");
414
	if ($fd) {
415
		while (!feof($fd)) {
416
			$line = explode(":", fgets($fd));
417
			if (!strncmp($line[0], "_", 1)) {
418
				continue;
419
			}
420
			if ($line[2] < 2000) {
421
				continue;
422
			}
423
			if ($line[2] > 65000) {
424
				continue;
425
			}
426
			$cmd = "/usr/sbin/pw groupdel {$line[2]}";
427
			if ($debug) {
428
				log_error(sprintf(gettext("Running: %s"), $cmd));
429
			}
430
			mwexec($cmd);
431
		}
432
		pclose($fd);
433
	}
434

    
435
	/* make sure the all group exists */
436
	$allgrp = getGroupEntryByGID(1998);
437
	local_group_set($allgrp, true);
438

    
439
	/* sync all local users */
440
	if (is_array($config['system']['user'])) {
441
		foreach ($config['system']['user'] as $user) {
442
			local_user_set($user);
443
		}
444
	}
445

    
446
	/* sync all local groups */
447
	if (is_array($config['system']['group'])) {
448
		foreach ($config['system']['group'] as $group) {
449
			local_group_set($group);
450
		}
451
	}
452

    
453
	conf_mount_ro();
454

    
455
}
456

    
457
function local_user_set(& $user) {
458
	global $g, $debug;
459

    
460
	if (empty($user['password']) && empty($user['bcrypt-hash'])) {
461
		log_error("There is something wrong in your config because user {$user['name']} password is missing!");
462
		return;
463
	}
464

    
465
	conf_mount_rw();
466

    
467
	$home_base = "/home/";
468
	$user_uid = $user['uid'];
469
	$user_name = $user['name'];
470
	$user_home = "{$home_base}{$user_name}";
471
	$user_shell = "/etc/rc.initial";
472
	$user_group = "nobody";
473

    
474
	// Ensure $home_base exists and is writable
475
	if (!is_dir($home_base)) {
476
		mkdir($home_base, 0755);
477
	}
478

    
479
	$lock_account = false;
480
	/* configure shell type */
481
	/* Cases here should be ordered by most privileged to least privileged. */
482
	if (userHasPrivilege($user, "user-shell-access") || userHasPrivilege($user, "page-all")) {
483
		$user_shell = "/bin/tcsh";
484
	} elseif (userHasPrivilege($user, "user-copy-files")) {
485
		$user_shell = "/usr/local/bin/scponly";
486
	} elseif (userHasPrivilege($user, "user-ssh-tunnel")) {
487
		$user_shell = "/usr/local/sbin/ssh_tunnel_shell";
488
	} elseif (userHasPrivilege($user, "user-ipsec-xauth-dialin")) {
489
		$user_shell = "/sbin/nologin";
490
	} else {
491
		$user_shell = "/sbin/nologin";
492
		$lock_account = true;
493
	}
494

    
495
	/* Lock out disabled or expired users, unless it's root/admin. */
496
	if ((is_account_disabled($user_name) || is_account_expired($user_name)) && ($user_uid != 0)) {
497
		$user_shell = "/sbin/nologin";
498
		$lock_account = true;
499
	}
500

    
501
	/* root user special handling */
502
	if ($user_uid == 0) {
503
		$cmd = "/usr/sbin/pw usermod -q -n root -s /bin/sh -H 0";
504
		if ($debug) {
505
			log_error(sprintf(gettext("Running: %s"), $cmd));
506
		}
507
		$fd = popen($cmd, "w");
508
		if (empty($user['bcrypt-hash'])) {
509
			fwrite($fd, $user['password']);
510
		} else {
511
			fwrite($fd, $user['bcrypt-hash']);
512
		}
513
		pclose($fd);
514
		$user_group = "wheel";
515
		$user_home = "/root";
516
		$user_shell = "/etc/rc.initial";
517
	}
518

    
519
	/* read from pw db */
520
	$fd = popen("/usr/sbin/pw usershow -n {$user_name} 2>&1", "r");
521
	$pwread = fgets($fd);
522
	pclose($fd);
523
	$userattrs = explode(":", trim($pwread));
524

    
525
	/* determine add or mod */
526
	if (($userattrs[0] != $user['name']) || (!strncmp($pwread, "pw:", 3))) {
527
		$user_op = "useradd -m -k /etc/skel -o";
528
	} else {
529
		$user_op = "usermod";
530
	}
531

    
532
	$comment = str_replace(array(":", "!", "@"), " ", $user['descr']);
533
	/* add or mod pw db */
534
	$cmd = "/usr/sbin/pw {$user_op} -q -u {$user_uid} -n {$user_name}".
535
			" -g {$user_group} -s {$user_shell} -d {$user_home}".
536
			" -c ".escapeshellarg($comment)." -H 0 2>&1";
537

    
538
	if ($debug) {
539
		log_error(sprintf(gettext("Running: %s"), $cmd));
540
	}
541
	$fd = popen($cmd, "w");
542
	if (empty($user['bcrypt-hash'])) {
543
		fwrite($fd, $user['password']);
544
	} else {
545
		fwrite($fd, $user['bcrypt-hash']);
546
	}
547
	pclose($fd);
548

    
549
	/* create user directory if required */
550
	if (!is_dir($user_home)) {
551
		mkdir($user_home, 0700);
552
	}
553
	@chown($user_home, $user_name);
554
	@chgrp($user_home, $user_group);
555

    
556
	/* write out ssh authorized key file */
557
	if ($user['authorizedkeys']) {
558
		if (!is_dir("{$user_home}/.ssh")) {
559
			@mkdir("{$user_home}/.ssh", 0700);
560
			@chown("{$user_home}/.ssh", $user_name);
561
		}
562
		$keys = base64_decode($user['authorizedkeys']);
563
		@file_put_contents("{$user_home}/.ssh/authorized_keys", $keys);
564
		@chown("{$user_home}/.ssh/authorized_keys", $user_name);
565
	} else {
566
		unlink_if_exists("{$user_home}/.ssh/authorized_keys");
567
	}
568

    
569
	$un = $lock_account ? "" : "un";
570
	exec("/usr/sbin/pw {$un}lock {$user_name} -q 2>/dev/null");
571

    
572
	conf_mount_ro();
573
}
574

    
575
function local_user_del($user) {
576
	global $debug;
577

    
578
	/* remove all memberships */
579
	local_user_set_groups($user);
580

    
581
	/* Don't remove /root */
582
	if ($user['uid'] != 0) {
583
		$rmhome = "-r";
584
	}
585

    
586
	/* read from pw db */
587
	$fd = popen("/usr/sbin/pw usershow -n {$user['name']} 2>&1", "r");
588
	$pwread = fgets($fd);
589
	pclose($fd);
590
	$userattrs = explode(":", trim($pwread));
591

    
592
	if ($userattrs[0] != $user['name']) {
593
		log_error("Tried to remove user {$user['name']} but got user {$userattrs[0]} instead. Bailing.");
594
		return;
595
	}
596

    
597
	/* delete from pw db */
598
	$cmd = "/usr/sbin/pw userdel -n {$user['name']} {$rmhome}";
599

    
600
	if ($debug) {
601
		log_error(sprintf(gettext("Running: %s"), $cmd));
602
	}
603
	mwexec($cmd);
604

    
605
	/* Delete user from groups needs a call to write_config() */
606
	local_group_del_user($user);
607
}
608

    
609
function local_user_set_password(&$user, $password) {
610

    
611
	unset($user['password']);
612
	unset($user['md5-hash']);
613
	$user['bcrypt-hash'] = password_hash($password, PASSWORD_BCRYPT);
614

    
615
	/* Maintain compatibility with FreeBSD - change $2y$ prefix to $2b$
616
	 * https://reviews.freebsd.org/D2742
617
	 * XXX: Can be removed as soon as r284483 is MFC'd.
618
	 */
619
	if ($user['bcrypt-hash'][2] == "y") {
620
		$user['bcrypt-hash'][2] = "b";
621
	}
622

    
623
	// Converts ascii to unicode.
624
	$astr = (string) $password;
625
	$ustr = '';
626
	for ($i = 0; $i < strlen($astr); $i++) {
627
		$a = ord($astr{$i}) << 8;
628
		$ustr .= sprintf("%X", $a);
629
	}
630

    
631
}
632

    
633
function local_user_get_groups($user, $all = false) {
634
	global $debug, $config;
635

    
636
	$groups = array();
637
	if (!is_array($config['system']['group'])) {
638
		return $groups;
639
	}
640

    
641
	foreach ($config['system']['group'] as $group) {
642
		if ($all || (!$all && ($group['name'] != "all"))) {
643
			if (is_array($group['member'])) {
644
				if (in_array($user['uid'], $group['member'])) {
645
					$groups[] = $group['name'];
646
				}
647
			}
648
		}
649
	}
650

    
651
	if ($all) {
652
		$groups[] = "all";
653
	}
654

    
655
	sort($groups);
656

    
657
	return $groups;
658

    
659
}
660

    
661
function local_user_set_groups($user, $new_groups = NULL) {
662
	global $debug, $config, $groupindex;
663

    
664
	if (!is_array($config['system']['group'])) {
665
		return;
666
	}
667

    
668
	$cur_groups = local_user_get_groups($user, true);
669
	$mod_groups = array();
670

    
671
	if (!is_array($new_groups)) {
672
		$new_groups = array();
673
	}
674

    
675
	if (!is_array($cur_groups)) {
676
		$cur_groups = array();
677
	}
678

    
679
	/* determine which memberships to add */
680
	foreach ($new_groups as $groupname) {
681
		if ($groupname == '' || in_array($groupname, $cur_groups)) {
682
			continue;
683
		}
684
		$group = & $config['system']['group'][$groupindex[$groupname]];
685
		$group['member'][] = $user['uid'];
686
		$mod_groups[] = $group;
687
	}
688
	unset($group);
689

    
690
	/* determine which memberships to remove */
691
	foreach ($cur_groups as $groupname) {
692
		if (in_array($groupname, $new_groups)) {
693
			continue;
694
		}
695
		if (!isset($config['system']['group'][$groupindex[$groupname]])) {
696
			continue;
697
		}
698
		$group = & $config['system']['group'][$groupindex[$groupname]];
699
		if (is_array($group['member'])) {
700
			$index = array_search($user['uid'], $group['member']);
701
			array_splice($group['member'], $index, 1);
702
			$mod_groups[] = $group;
703
		}
704
	}
705
	unset($group);
706

    
707
	/* sync all modified groups */
708
	foreach ($mod_groups as $group) {
709
		local_group_set($group);
710
	}
711
}
712

    
713
function local_group_del_user($user) {
714
	global $config;
715

    
716
	if (!is_array($config['system']['group'])) {
717
		return;
718
	}
719

    
720
	foreach ($config['system']['group'] as $group) {
721
		if (is_array($group['member'])) {
722
			foreach ($group['member'] as $idx => $uid) {
723
				if ($user['uid'] == $uid) {
724
					unset($config['system']['group']['member'][$idx]);
725
				}
726
			}
727
		}
728
	}
729
}
730

    
731
function local_group_set($group, $reset = false) {
732
	global $debug;
733

    
734
	$group_name = $group['name'];
735
	$group_gid = $group['gid'];
736
	$group_members = '';
737
	if (!$reset && !empty($group['member']) && count($group['member']) > 0) {
738
		$group_members = implode(",", $group['member']);
739
	}
740

    
741
	if (empty($group_name)) {
742
		return;
743
	}
744

    
745
	/* read from group db */
746
	$fd = popen("/usr/sbin/pw groupshow {$group_name} 2>&1", "r");
747
	$pwread = fgets($fd);
748
	pclose($fd);
749

    
750
	/* determine add or mod */
751
	if (!strncmp($pwread, "pw:", 3)) {
752
		$group_op = "groupadd";
753
	} else {
754
		$group_op = "groupmod";
755
	}
756

    
757
	/* add or mod group db */
758
	$cmd = "/usr/sbin/pw {$group_op} {$group_name} -g {$group_gid} -M '{$group_members}' 2>&1";
759

    
760
	if ($debug) {
761
		log_error(sprintf(gettext("Running: %s"), $cmd));
762
	}
763
	mwexec($cmd);
764

    
765
}
766

    
767
function local_group_del($group) {
768
	global $debug;
769

    
770
	/* delete from group db */
771
	$cmd = "/usr/sbin/pw groupdel {$group['name']}";
772

    
773
	if ($debug) {
774
		log_error(sprintf(gettext("Running: %s"), $cmd));
775
	}
776
	mwexec($cmd);
777
}
778

    
779
function ldap_test_connection($authcfg) {
780
	global $debug, $config, $g;
781

    
782
	if ($authcfg) {
783
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
784
			$ldapproto = "ldap";
785
		} else {
786
			$ldapproto = "ldaps";
787
		}
788
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
789
		$ldapport = $authcfg['ldap_port'];
790
		if (!empty($ldapport)) {
791
			$ldapserver .= ":{$ldapport}";
792
		}
793
		$ldapbasedn = $authcfg['ldap_basedn'];
794
		$ldapbindun = $authcfg['ldap_binddn'];
795
		$ldapbindpw = $authcfg['ldap_bindpw'];
796
	} else {
797
		return false;
798
	}
799

    
800
	/* first check if there is even an LDAP server populated */
801
	if (!$ldapserver) {
802
		return false;
803
	}
804

    
805
	/* Setup CA environment if needed. */
806
	ldap_setup_caenv($authcfg);
807

    
808
	/* connect and see if server is up */
809
	$error = false;
810
	if (!($ldap = ldap_connect($ldapserver))) {
811
		$error = true;
812
	}
813

    
814
	if ($error == true) {
815
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
816
		return false;
817
	}
818

    
819
	return true;
820
}
821

    
822
function ldap_setup_caenv($authcfg) {
823
	global $g;
824
	require_once("certs.inc");
825

    
826
	unset($caref);
827
	if (empty($authcfg['ldap_caref']) || !strstr($authcfg['ldap_urltype'], "SSL")) {
828
		putenv('LDAPTLS_REQCERT=never');
829
		return;
830
	} else {
831
		$caref = lookup_ca($authcfg['ldap_caref']);
832
		if (!$caref) {
833
			log_error(sprintf(gettext("LDAP: Could not lookup CA by reference for host %s."), $authcfg['ldap_caref']));
834
			/* XXX: Prevent for credential leaking since we cannot setup the CA env. Better way? */
835
			putenv('LDAPTLS_REQCERT=hard');
836
			return;
837
		}
838
		if (!is_dir("{$g['varrun_path']}/certs")) {
839
			@mkdir("{$g['varrun_path']}/certs");
840
		}
841
		if (file_exists("{$g['varrun_path']}/certs/{$caref['refid']}.ca")) {
842
			@unlink("{$g['varrun_path']}/certs/{$caref['refid']}.ca");
843
		}
844
		file_put_contents("{$g['varrun_path']}/certs/{$caref['refid']}.ca", base64_decode($caref['crt']));
845
		@chmod("{$g['varrun_path']}/certs/{$caref['refid']}.ca", 0600);
846
		putenv('LDAPTLS_REQCERT=hard');
847
		/* XXX: Probably even the hashed link should be created for this? */
848
		putenv("LDAPTLS_CACERTDIR={$g['varrun_path']}/certs");
849
		putenv("LDAPTLS_CACERT={$g['varrun_path']}/certs/{$caref['refid']}.ca");
850
	}
851
}
852

    
853
function ldap_test_bind($authcfg) {
854
	global $debug, $config, $g;
855

    
856
	if ($authcfg) {
857
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
858
			$ldapproto = "ldap";
859
		} else {
860
			$ldapproto = "ldaps";
861
		}
862
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
863
		$ldapport = $authcfg['ldap_port'];
864
		if (!empty($ldapport)) {
865
			$ldapserver .= ":{$ldapport}";
866
		}
867
		$ldapbasedn = $authcfg['ldap_basedn'];
868
		$ldapbindun = $authcfg['ldap_binddn'];
869
		$ldapbindpw = $authcfg['ldap_bindpw'];
870
		$ldapver = $authcfg['ldap_protver'];
871
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 25;
872
		if (empty($ldapbndun) || empty($ldapbindpw)) {
873
			$ldapanon = true;
874
		} else {
875
			$ldapanon = false;
876
		}
877
	} else {
878
		return false;
879
	}
880

    
881
	/* first check if there is even an LDAP server populated */
882
	if (!$ldapserver) {
883
		return false;
884
	}
885

    
886
	/* Setup CA environment if needed. */
887
	ldap_setup_caenv($authcfg);
888

    
889
	/* connect and see if server is up */
890
	$error = false;
891
	if (!($ldap = ldap_connect($ldapserver))) {
892
		$error = true;
893
	}
894

    
895
	if ($error == true) {
896
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
897
		return false;
898
	}
899

    
900
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
901
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
902
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
903
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
904
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
905

    
906
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
907
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
908
	if ($ldapanon == true) {
909
		if (!($res = @ldap_bind($ldap))) {
910
			@ldap_close($ldap);
911
			return false;
912
		}
913
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
914
		@ldap_close($ldap);
915
		return false;
916
	}
917

    
918
	@ldap_unbind($ldap);
919

    
920
	return true;
921
}
922

    
923
function ldap_get_user_ous($show_complete_ou=true, $authcfg) {
924
	global $debug, $config, $g;
925

    
926
	if (!function_exists("ldap_connect")) {
927
		return;
928
	}
929

    
930
	$ous = array();
931

    
932
	if ($authcfg) {
933
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
934
			$ldapproto = "ldap";
935
		} else {
936
			$ldapproto = "ldaps";
937
		}
938
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
939
		$ldapport = $authcfg['ldap_port'];
940
		if (!empty($ldapport)) {
941
			$ldapserver .= ":{$ldapport}";
942
		}
943
		$ldapbasedn = $authcfg['ldap_basedn'];
944
		$ldapbindun = $authcfg['ldap_binddn'];
945
		$ldapbindpw = $authcfg['ldap_bindpw'];
946
		$ldapver = $authcfg['ldap_protver'];
947
		if (empty($ldapbindun) || empty($ldapbindpw)) {
948
			$ldapanon = true;
949
		} else {
950
			$ldapanon = false;
951
		}
952
		$ldapname = $authcfg['name'];
953
		$ldapfallback = false;
954
		$ldapscope = $authcfg['ldap_scope'];
955
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 25;
956
	} else {
957
		return false;
958
	}
959

    
960
	/* first check if there is even an LDAP server populated */
961
	if (!$ldapserver) {
962
		log_error(gettext("ERROR!  ldap_get_user_ous() backed selected with no LDAP authentication server defined."));
963
		return $ous;
964
	}
965

    
966
	/* Setup CA environment if needed. */
967
	ldap_setup_caenv($authcfg);
968

    
969
	/* connect and see if server is up */
970
	$error = false;
971
	if (!($ldap = ldap_connect($ldapserver))) {
972
		$error = true;
973
	}
974

    
975
	if ($error == true) {
976
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
977
		return $ous;
978
	}
979

    
980
	$ldapfilter = "(|(ou=*)(cn=Users))";
981

    
982
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
983
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
984
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
985
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
986
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
987

    
988
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
989
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
990
	if ($ldapanon == true) {
991
		if (!($res = @ldap_bind($ldap))) {
992
			log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind anonymously to server %s."), $ldapname));
993
			@ldap_close($ldap);
994
			return $ous;
995
		}
996
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
997
		log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind to server %s."), $ldapname));
998
		@ldap_close($ldap);
999
		return $ous;
1000
	}
1001

    
1002
	if ($ldapscope == "one") {
1003
		$ldapfunc = "ldap_list";
1004
	} else {
1005
		$ldapfunc = "ldap_search";
1006
	}
1007

    
1008
	$search = @$ldapfunc($ldap, $ldapbasedn, $ldapfilter);
1009
	$info = @ldap_get_entries($ldap, $search);
1010

    
1011
	if (is_array($info)) {
1012
		foreach ($info as $inf) {
1013
			if (!$show_complete_ou) {
1014
				$inf_split = explode(",", $inf['dn']);
1015
				$ou = $inf_split[0];
1016
				$ou = str_replace("OU=", "", $ou);
1017
				$ou = str_replace("CN=", "", $ou);
1018
			} else {
1019
				if ($inf['dn']) {
1020
					$ou = $inf['dn'];
1021
				}
1022
			}
1023
			if ($ou) {
1024
				$ous[] = $ou;
1025
			}
1026
		}
1027
	}
1028

    
1029
	@ldap_unbind($ldap);
1030

    
1031
	return $ous;
1032
}
1033

    
1034
function ldap_get_groups($username, $authcfg) {
1035
	global $debug, $config;
1036

    
1037
	if (!function_exists("ldap_connect")) {
1038
		return;
1039
	}
1040

    
1041
	if (!$username) {
1042
		return false;
1043
	}
1044

    
1045
	if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) {
1046
		$username_split = explode("@", $username);
1047
		$username = $username_split[0];
1048
	}
1049

    
1050
	if (stristr($username, "\\")) {
1051
		$username_split = explode("\\", $username);
1052
		$username = $username_split[0];
1053
	}
1054

    
1055
	//log_error("Getting LDAP groups for {$username}.");
1056
	if ($authcfg) {
1057
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
1058
			$ldapproto = "ldap";
1059
		} else {
1060
			$ldapproto = "ldaps";
1061
		}
1062
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1063
		$ldapport = $authcfg['ldap_port'];
1064
		if (!empty($ldapport)) {
1065
			$ldapserver .= ":{$ldapport}";
1066
		}
1067
		$ldapbasedn = $authcfg['ldap_basedn'];
1068
		$ldapbindun = $authcfg['ldap_binddn'];
1069
		$ldapbindpw = $authcfg['ldap_bindpw'];
1070
		$ldapauthcont = $authcfg['ldap_authcn'];
1071
		$ldapnameattribute = strtolower($authcfg['ldap_attr_user']);
1072
		$ldapgroupattribute = strtolower($authcfg['ldap_attr_member']);
1073
		if (isset($authcfg['ldap_rfc2307'])) {
1074
			$ldapfilter         = "(&(objectClass={$authcfg['ldap_attr_groupobj']})({$ldapgroupattribute}={$username}))";
1075
		} else {
1076
			$ldapfilter         = "({$ldapnameattribute}={$username})";
1077
		}
1078
		$ldaptype = "";
1079
		$ldapver = $authcfg['ldap_protver'];
1080
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1081
			$ldapanon = true;
1082
		} else {
1083
			$ldapanon = false;
1084
		}
1085
		$ldapname = $authcfg['name'];
1086
		$ldapfallback = false;
1087
		$ldapscope = $authcfg['ldap_scope'];
1088
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 25;
1089
	} else {
1090
		return false;
1091
	}
1092

    
1093
	if (isset($authcfg['ldap_rfc2307'])) {
1094
		$ldapdn = $ldapbasedn;
1095
	} else {
1096
		$ldapdn = $_SESSION['ldapdn'];
1097
	}
1098

    
1099
	/*Convert attribute to lowercase.  php ldap arrays put everything in lowercase */
1100
	$ldapgroupattribute = strtolower($ldapgroupattribute);
1101
	$memberof = array();
1102

    
1103
	/* Setup CA environment if needed. */
1104
	ldap_setup_caenv($authcfg);
1105

    
1106
	/* connect and see if server is up */
1107
	$error = false;
1108
	if (!($ldap = ldap_connect($ldapserver))) {
1109
		$error = true;
1110
	}
1111

    
1112
	if ($error == true) {
1113
		log_error(sprintf(gettext("ERROR! ldap_get_groups() Could not connect to server %s."), $ldapname));
1114
		return memberof;
1115
	}
1116

    
1117
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1118
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1119
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1120
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1121
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1122

    
1123
	/* bind as user that has rights to read group attributes */
1124
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1125
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1126
	if ($ldapanon == true) {
1127
		if (!($res = @ldap_bind($ldap))) {
1128
			log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind anonymously to server %s."), $ldapname));
1129
			@ldap_close($ldap);
1130
			return false;
1131
		}
1132
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1133
		log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind to server %s."), $ldapname));
1134
		@ldap_close($ldap);
1135
		return memberof;
1136
	}
1137

    
1138
	/* get groups from DN found */
1139
	/* use ldap_read instead of search so we don't have to do a bunch of extra work */
1140
	/* since we know the DN is in $_SESSION['ldapdn'] */
1141
	//$search    = ldap_read($ldap, $ldapdn, "(objectclass=*)", array($ldapgroupattribute));
1142
	if ($ldapscope == "one") {
1143
		$ldapfunc = "ldap_list";
1144
	} else {
1145
		$ldapfunc = "ldap_search";
1146
	}
1147

    
1148
	$search = @$ldapfunc($ldap, $ldapdn, $ldapfilter, array($ldapgroupattribute));
1149
	$info = @ldap_get_entries($ldap, $search);
1150

    
1151
	$gresults = isset($authcfg['ldap_rfc2307']) ? $info : $info[0][$ldapgroupattribute];
1152

    
1153
	if (is_array($gresults)) {
1154
		/* Iterate through the groups and throw them into an array */
1155
		foreach ($gresults as $grp) {
1156
			if (((isset($authcfg['ldap_rfc2307'])) && (stristr($grp["dn"], "CN=") !== false)) ||
1157
			    ((!isset($authcfg['ldap_rfc2307'])) && (stristr($grp, "CN=") !== false))) {
1158
				$grpsplit = isset($authcfg['ldap_rfc2307']) ? explode(",", $grp["dn"]) : explode(",", $grp);
1159
				$memberof[] = preg_replace("/CN=/i", "", $grpsplit[0]);
1160
			}
1161
		}
1162
	}
1163

    
1164
	/* Time to close LDAP connection */
1165
	@ldap_unbind($ldap);
1166

    
1167
	$groups = print_r($memberof, true);
1168

    
1169
	//log_error("Returning groups ".$groups." for user $username");
1170

    
1171
	return $memberof;
1172
}
1173

    
1174
function ldap_format_host($host) {
1175
	return is_ipaddrv6($host) ? "[$host]" : $host ;
1176
}
1177

    
1178
function ldap_backed($username, $passwd, $authcfg) {
1179
	global $debug, $config;
1180

    
1181
	if (!$username) {
1182
		return;
1183
	}
1184

    
1185
	if (!function_exists("ldap_connect")) {
1186
		return;
1187
	}
1188

    
1189
	if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) {
1190
		$username_split = explode("@", $username);
1191
		$username = $username_split[0];
1192
	}
1193
	if (stristr($username, "\\")) {
1194
		$username_split = explode("\\", $username);
1195
		$username = $username_split[0];
1196
	}
1197

    
1198
	if ($authcfg) {
1199
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
1200
			$ldapproto = "ldap";
1201
		} else {
1202
			$ldapproto = "ldaps";
1203
		}
1204
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1205
		$ldapport = $authcfg['ldap_port'];
1206
		if (!empty($ldapport)) {
1207
			$ldapserver .= ":{$ldapport}";
1208
		}
1209
		$ldapbasedn = $authcfg['ldap_basedn'];
1210
		$ldapbindun = $authcfg['ldap_binddn'];
1211
		$ldapbindpw = $authcfg['ldap_bindpw'];
1212
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1213
			$ldapanon = true;
1214
		} else {
1215
			$ldapanon = false;
1216
		}
1217
		$ldapauthcont = $authcfg['ldap_authcn'];
1218
		$ldapnameattribute = strtolower($authcfg['ldap_attr_user']);
1219
		$ldapextendedqueryenabled = $authcfg['ldap_extended_enabled'];
1220
		$ldapextendedquery = $authcfg['ldap_extended_query'];
1221
		$ldapfilter = "";
1222
		if (!$ldapextendedqueryenabled) {
1223
			$ldapfilter = "({$ldapnameattribute}={$username})";
1224
		} else {
1225
			$ldapfilter = "(&({$ldapnameattribute}={$username})({$ldapextendedquery}))";
1226
		}
1227
		$ldaptype = "";
1228
		$ldapver = $authcfg['ldap_protver'];
1229
		$ldapname = $authcfg['name'];
1230
		$ldapscope = $authcfg['ldap_scope'];
1231
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 25;
1232
	} else {
1233
		return false;
1234
	}
1235

    
1236
	/* first check if there is even an LDAP server populated */
1237
	if (!$ldapserver) {
1238
		if ($ldapfallback) {
1239
			log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined.  Defaulting to local user database. Visit System -> User Manager."));
1240
			return local_backed($username, $passwd);
1241
		} else {
1242
			log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined."));
1243
		}
1244

    
1245
		return false;
1246
	}
1247

    
1248
	/* Setup CA environment if needed. */
1249
	ldap_setup_caenv($authcfg);
1250

    
1251
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1252
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1253
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1254
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1255
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1256

    
1257
	/* Make sure we can connect to LDAP */
1258
	$error = false;
1259
	if (!($ldap = ldap_connect($ldapserver))) {
1260
		$error = true;
1261
	}
1262

    
1263
	if ($error == true) {
1264
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
1265
		return false;
1266
	}
1267

    
1268
	/* ok, its up.  now, lets bind as the bind user so we can search it */
1269
	$error = false;
1270
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1271
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1272
	if ($ldapanon == true) {
1273
		if (!($res = @ldap_bind($ldap))) {
1274
			$error = true;
1275
		}
1276
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1277
		$error = true;
1278
	}
1279

    
1280
	if ($error == true) {
1281
		@ldap_close($ldap);
1282
		log_error(sprintf(gettext("ERROR! Could not bind to server %s."), $ldapname));
1283
		return false;
1284
	}
1285

    
1286
	/* Get LDAP Authcontainers and split em up. */
1287
	$ldac_splits = explode(";", $ldapauthcont);
1288

    
1289
	/* setup the usercount so we think we haven't found anyone yet */
1290
	$usercount = 0;
1291

    
1292
	/*****************************************************************/
1293
	/*  We first find the user based on username and filter          */
1294
	/*  then, once we find the first occurrence of that person       */
1295
	/*  we set session variables to point to the OU and DN of the    */
1296
	/*  person.  To later be used by ldap_get_groups.                */
1297
	/*  that way we don't have to search twice.                      */
1298
	/*****************************************************************/
1299
	if ($debug) {
1300
		log_auth(sprintf(gettext("Now Searching for %s in directory."), $username));
1301
	}
1302
	/* Iterate through the user containers for search */
1303
	foreach ($ldac_splits as $i => $ldac_split) {
1304
		$ldac_split = isset($authcfg['ldap_utf8']) ? utf8_encode($ldac_split) : $ldac_split;
1305
		$ldapfilter = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapfilter) : $ldapfilter;
1306
		$ldapsearchbasedn = isset($authcfg['ldap_utf8']) ? utf8_encode("{$ldac_split},{$ldapbasedn}") : "{$ldac_split},{$ldapbasedn}";
1307
		/* Make sure we just use the first user we find */
1308
		if ($debug) {
1309
			log_auth(sprintf(gettext('Now Searching in server %1$s, container %2$s with filter %3$s.'), $ldapname, utf8_decode($ldac_split), utf8_decode($ldapfilter)));
1310
		}
1311
		if ($ldapscope == "one") {
1312
			$ldapfunc = "ldap_list";
1313
		} else {
1314
			$ldapfunc = "ldap_search";
1315
		}
1316
		/* Support legacy auth container specification. */
1317
		if (stristr($ldac_split, "DC=") || empty($ldapbasedn)) {
1318
			$search = @$ldapfunc($ldap, $ldac_split, $ldapfilter);
1319
		} else {
1320
			$search = @$ldapfunc($ldap, $ldapsearchbasedn, $ldapfilter);
1321
		}
1322
		if (!$search) {
1323
			log_error(sprintf(gettext("Search resulted in error: %s"), ldap_error($ldap)));
1324
			continue;
1325
		}
1326
		$info = ldap_get_entries($ldap, $search);
1327
		$matches = $info['count'];
1328
		if ($matches == 1) {
1329
			$userdn = $_SESSION['ldapdn'] = $info[0]['dn'];
1330
			$_SESSION['ldapou'] = $ldac_split[$i];
1331
			$_SESSION['ldapon'] = "true";
1332
			$usercount = 1;
1333
			break;
1334
		}
1335
	}
1336

    
1337
	if ($usercount != 1) {
1338
		@ldap_unbind($ldap);
1339
		log_error(gettext("ERROR! Either LDAP search failed, or multiple users were found."));
1340
		return false;
1341
	}
1342

    
1343
	/* Now lets bind as the user we found */
1344
	$passwd = isset($authcfg['ldap_utf8']) ? utf8_encode($passwd) : $passwd;
1345
	if (!($res = @ldap_bind($ldap, $userdn, $passwd))) {
1346
		log_error(sprintf(gettext('ERROR! Could not login to server %1$s as user %2$s: %3$s'), $ldapname, $username, ldap_error($ldap)));
1347
		@ldap_unbind($ldap);
1348
		return false;
1349
	}
1350

    
1351
	if ($debug) {
1352
		$userdn = isset($authcfg['ldap_utf8']) ? utf8_decode($userdn) : $userdn;
1353
		log_auth(sprintf(gettext('Logged in successfully as %1$s via LDAP server %2$s with DN = %3$s.'), $username, $ldapname, $userdn));
1354
	}
1355

    
1356
	/* At this point we are bound to LDAP so the user was auth'd okay. Close connection. */
1357
	@ldap_unbind($ldap);
1358

    
1359
	return true;
1360
}
1361

    
1362
function radius_backed($username, $passwd, $authcfg, &$attributes = array()) {
1363
	global $debug, $config;
1364
	$ret = false;
1365

    
1366
	require_once("radius.inc");
1367

    
1368
	$rauth = new Auth_RADIUS_PAP($username, $passwd);
1369
	if ($authcfg) {
1370
		$radiusservers = array();
1371
		$radiusservers[0]['ipaddr'] = $authcfg['host'];
1372
		$radiusservers[0]['port'] = $authcfg['radius_auth_port'];
1373
		$radiusservers[0]['sharedsecret'] = $authcfg['radius_secret'];
1374
		$radiusservers[0]['timeout'] = $authcfg['radius_timeout'];
1375
	} else {
1376
		return false;
1377
	}
1378

    
1379
	/* Add new servers to our instance */
1380
	foreach ($radiusservers as $radsrv) {
1381
		$timeout = (is_numeric($radsrv['timeout'])) ? $radsrv['timeout'] : 5;
1382
		$rauth->addServer($radsrv['ipaddr'], $radsrv['port'], $radsrv['sharedsecret'], $timeout);
1383
	}
1384

    
1385
	if (PEAR::isError($rauth->start())) {
1386
		$retvalue['auth_val'] = 1;
1387
		$retvalue['error'] = $rauth->getError();
1388
		if ($debug) {
1389
			printf(gettext("RADIUS start: %s<br />\n"), $retvalue['error']);
1390
		}
1391
	}
1392

    
1393
	// XXX - billm - somewhere in here we need to handle securid challenge/response
1394

    
1395
	/* Send request */
1396
	$result = $rauth->send();
1397
	if (PEAR::isError($result)) {
1398
		$retvalue['auth_val'] = 1;
1399
		$retvalue['error'] = $result->getMessage();
1400
		if ($debug) {
1401
			printf(gettext("RADIUS send failed: %s<br />\n"), $retvalue['error']);
1402
		}
1403
	} else if ($result === true) {
1404
		if ($rauth->getAttributes()) {
1405
			$attributes = $rauth->listAttributes();
1406
		}
1407
		$retvalue['auth_val'] = 2;
1408
		if ($debug) {
1409
			printf(gettext("RADIUS Auth succeeded")."<br />\n");
1410
		}
1411
		$ret = true;
1412
	} else {
1413
		$retvalue['auth_val'] = 3;
1414
		if ($debug) {
1415
			printf(gettext("RADIUS Auth rejected")."<br />\n");
1416
		}
1417
	}
1418

    
1419
	// close OO RADIUS_AUTHENTICATION
1420
	$rauth->close();
1421

    
1422
	return $ret;
1423
}
1424

    
1425
/*
1426
	$attributes must contain a "class" key containing the groups and local
1427
	groups must exist to match.
1428
*/
1429
function radius_get_groups($attributes) {
1430
	$groups = array();
1431
	if (!empty($attributes) && is_array($attributes) && !empty($attributes['class'])) {
1432
		$groups = explode(";", $attributes['class']);
1433
		foreach ($groups as & $grp) {
1434
			$grp = trim($grp);
1435
			if (strtolower(substr($grp, 0, 3)) == "ou=") {
1436
				$grp = substr($grp, 3);
1437
			}
1438
		}
1439
	}
1440
	return $groups;
1441
}
1442

    
1443
function get_user_expiration_date($username) {
1444
	$user = getUserEntry($username);
1445
	if ($user['expires']) {
1446
		return $user['expires'];
1447
	}
1448
}
1449

    
1450
function is_account_expired($username) {
1451
	$expirydate = get_user_expiration_date($username);
1452
	if ($expirydate) {
1453
		if (strtotime("-1 day") > strtotime(date("m/d/Y", strtotime($expirydate)))) {
1454
			return true;
1455
		}
1456
	}
1457

    
1458
	return false;
1459
}
1460

    
1461
function is_account_disabled($username) {
1462
	$user = getUserEntry($username);
1463
	if (isset($user['disabled'])) {
1464
		return true;
1465
	}
1466

    
1467
	return false;
1468
}
1469

    
1470
function auth_get_authserver($name) {
1471
	global $config;
1472

    
1473
	if (is_array($config['system']['authserver'])) {
1474
		foreach ($config['system']['authserver'] as $authcfg) {
1475
			if ($authcfg['name'] == $name) {
1476
				return $authcfg;
1477
			}
1478
		}
1479
	}
1480
	if ($name == "Local Database") {
1481
		return array("name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']);
1482
	}
1483
}
1484

    
1485
function auth_get_authserver_list() {
1486
	global $config;
1487

    
1488
	$list = array();
1489

    
1490
	if (is_array($config['system']['authserver'])) {
1491
		foreach ($config['system']['authserver'] as $authcfg) {
1492
			/* Add support for disabled entries? */
1493
			$list[$authcfg['name']] = $authcfg;
1494
		}
1495
	}
1496

    
1497
	$list["Local Database"] = array("name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']);
1498
	return $list;
1499
}
1500

    
1501
function getUserGroups($username, $authcfg, &$attributes = array()) {
1502
	global $config;
1503

    
1504
	$allowed_groups = array();
1505

    
1506
	switch ($authcfg['type']) {
1507
		case 'ldap':
1508
			$allowed_groups = @ldap_get_groups($username, $authcfg);
1509
			break;
1510
		case 'radius':
1511
			$allowed_groups = @radius_get_groups($attributes);
1512
			break;
1513
		default:
1514
			$user = getUserEntry($username);
1515
			$allowed_groups = @local_user_get_groups($user, true);
1516
			break;
1517
	}
1518

    
1519
	$member_groups = array();
1520
	if (is_array($config['system']['group'])) {
1521
		foreach ($config['system']['group'] as $group) {
1522
			if (in_array($group['name'], $allowed_groups)) {
1523
				$member_groups[] = $group['name'];
1524
			}
1525
		}
1526
	}
1527

    
1528
	return $member_groups;
1529
}
1530

    
1531
function authenticate_user($username, $password, $authcfg = NULL, &$attributes = array()) {
1532

    
1533
	if (is_array($username) || is_array($password)) {
1534
		return false;
1535
	}
1536

    
1537
	if (!$authcfg) {
1538
		return local_backed($username, $password);
1539
	}
1540

    
1541
	$authenticated = false;
1542
	switch ($authcfg['type']) {
1543
		case 'ldap':
1544
			if (ldap_backed($username, $password, $authcfg)) {
1545
				$authenticated = true;
1546
			}
1547
			break;
1548
		case 'radius':
1549
			if (radius_backed($username, $password, $authcfg, $attributes)) {
1550
				$authenticated = true;
1551
			}
1552
			break;
1553
		default:
1554
			/* lookup user object by name */
1555
			if (local_backed($username, $password)) {
1556
				$authenticated = true;
1557
			}
1558
			break;
1559
		}
1560

    
1561
	return $authenticated;
1562
}
1563

    
1564
function session_auth() {
1565
	global $config, $_SESSION, $page;
1566

    
1567
	// Handle HTTPS httponly and secure flags
1568
	$currentCookieParams = session_get_cookie_params();
1569
	session_set_cookie_params(
1570
		$currentCookieParams["lifetime"],
1571
		$currentCookieParams["path"],
1572
		NULL,
1573
		($config['system']['webgui']['protocol'] == "https"),
1574
		true
1575
	);
1576

    
1577
	if (!session_id()) {
1578
		session_start();
1579
	}
1580

    
1581
	// Detect protocol change
1582
	if (!isset($_POST['login']) && !empty($_SESSION['Logged_In']) && $_SESSION['protocol'] != $config['system']['webgui']['protocol']) {
1583
		return false;
1584
	}
1585

    
1586
	/* Validate incoming login request */
1587
	$attributes = array();
1588
	if (isset($_POST['login']) && !empty($_POST['usernamefld']) && !empty($_POST['passwordfld'])) {
1589
		$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
1590
		if (authenticate_user($_POST['usernamefld'], $_POST['passwordfld'], $authcfg, $attributes) ||
1591
		    authenticate_user($_POST['usernamefld'], $_POST['passwordfld'])) {
1592
			// Generate a new id to avoid session fixation
1593
			session_regenerate_id();
1594
			$_SESSION['Logged_In'] = "True";
1595
			$_SESSION['Username'] = $_POST['usernamefld'];
1596
			$_SESSION['user_radius_attributes'] = $attributes;
1597
			$_SESSION['last_access'] = time();
1598
			$_SESSION['protocol'] = $config['system']['webgui']['protocol'];
1599
			if (!isset($config['system']['webgui']['quietlogin'])) {
1600
				log_auth(sprintf(gettext("Successful login for user '%1\$s' from: %2\$s"), $_POST['usernamefld'], $_SERVER['REMOTE_ADDR']));
1601
			}
1602
			if (isset($_POST['postafterlogin'])) {
1603
				return true;
1604
			} else {
1605
				if (empty($page)) {
1606
					$page = "/";
1607
				}
1608
				header("Location: {$page}");
1609
			}
1610
			exit;
1611
		} else {
1612
			/* give the user an error message */
1613
			$_SESSION['Login_Error'] = "Username or Password incorrect";
1614
			log_auth("webConfigurator authentication error for '{$_POST['usernamefld']}' from {$_SERVER['REMOTE_ADDR']}");
1615
			if (isAjax()) {
1616
				echo "showajaxmessage('{$_SESSION['Login_Error']}');";
1617
				return;
1618
			}
1619
		}
1620
	}
1621

    
1622
	/* Show login page if they aren't logged in */
1623
	if (empty($_SESSION['Logged_In'])) {
1624
		return false;
1625
	}
1626

    
1627
	/* If session timeout isn't set, we don't mark sessions stale */
1628
	if (!isset($config['system']['webgui']['session_timeout'])) {
1629
		/* Default to 4 hour timeout if one is not set */
1630
		if ($_SESSION['last_access'] < (time() - 14400)) {
1631
			$_GET['logout'] = true;
1632
			$_SESSION['Logout'] = true;
1633
		} else {
1634
			$_SESSION['last_access'] = time();
1635
		}
1636
	} else if (intval($config['system']['webgui']['session_timeout']) == 0) {
1637
		/* only update if it wasn't ajax */
1638
		if (!isAjax()) {
1639
			$_SESSION['last_access'] = time();
1640
		}
1641
	} else {
1642
		/* Check for stale session */
1643
		if ($_SESSION['last_access'] < (time() - ($config['system']['webgui']['session_timeout'] * 60))) {
1644
			$_GET['logout'] = true;
1645
			$_SESSION['Logout'] = true;
1646
		} else {
1647
			/* only update if it wasn't ajax */
1648
			if (!isAjax()) {
1649
				$_SESSION['last_access'] = time();
1650
			}
1651
		}
1652
	}
1653

    
1654
	/* user hit the logout button */
1655
	if (isset($_GET['logout'])) {
1656

    
1657
		if ($_SESSION['Logout']) {
1658
			log_error(sprintf(gettext("Session timed out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR']));
1659
		} else {
1660
			log_error(sprintf(gettext("User logged out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR']));
1661
		}
1662

    
1663
		/* wipe out $_SESSION */
1664
		$_SESSION = array();
1665

    
1666
		if (isset($_COOKIE[session_name()])) {
1667
			setcookie(session_name(), '', time()-42000, '/');
1668
		}
1669

    
1670
		/* and destroy it */
1671
		session_destroy();
1672

    
1673
		$scriptName = explode("/", $_SERVER["SCRIPT_FILENAME"]);
1674
		$scriptElms = count($scriptName);
1675
		$scriptName = $scriptName[$scriptElms-1];
1676

    
1677
		if (isAjax()) {
1678
			return false;
1679
		}
1680

    
1681
		/* redirect to page the user is on, it'll prompt them to login again */
1682
		header("Location: {$scriptName}");
1683

    
1684
		return false;
1685
	}
1686

    
1687
	/*
1688
	 * this is for debugging purpose if you do not want to use Ajax
1689
	 * to submit a HTML form. It basically disables the observation
1690
	 * of the submit event and hence does not trigger Ajax.
1691
	 */
1692
	if ($_GET['disable_ajax']) {
1693
		$_SESSION['NO_AJAX'] = "True";
1694
	}
1695

    
1696
	/*
1697
	 * Same to re-enable Ajax.
1698
	 */
1699
	if ($_GET['enable_ajax']) {
1700
		unset($_SESSION['NO_AJAX']);
1701
	}
1702

    
1703
	return true;
1704
}
1705

    
1706
?>
(4-4/65)