Project

General

Profile

Download (47.5 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="/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']) . ").  If not needed, this check can be disabled 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
	$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
275

    
276
	if (isset($userindex[$name])) {
277
		return $config['system']['user'][$userindex[$name]];
278
	} elseif ($authcfg['type'] != "Local Database") {
279
		$user = array();
280
		$user['name'] = $name;
281
		return $user;
282
	}
283
}
284

    
285
function & getUserEntryByUID($uid) {
286
	global $debug, $config;
287

    
288
	if (is_array($config['system']['user'])) {
289
		foreach ($config['system']['user'] as & $user) {
290
			if ($user['uid'] == $uid) {
291
				return $user;
292
			}
293
		}
294
	}
295

    
296
	return false;
297
}
298

    
299
function & getGroupEntry($name) {
300
	global $debug, $config, $groupindex;
301
	if (isset($groupindex[$name])) {
302
		return $config['system']['group'][$groupindex[$name]];
303
	}
304
}
305

    
306
function & getGroupEntryByGID($gid) {
307
	global $debug, $config;
308

    
309
	if (is_array($config['system']['group'])) {
310
		foreach ($config['system']['group'] as & $group) {
311
			if ($group['gid'] == $gid) {
312
				return $group;
313
			}
314
		}
315
	}
316

    
317
	return false;
318
}
319

    
320
function get_user_privileges(& $user) {
321
	global $config;
322

    
323
	$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
324
	$names = array();
325

    
326
	$privs = $user['priv'];
327
	if (!is_array($privs)) {
328
		$privs = array();
329
	}
330

    
331
	if ($authcfg['type'] == "ldap") {
332
		$names = @ldap_get_groups($user['name'], $authcfg);
333
	} elseif ($authcfg['type'] == "radius") {
334
		$names = @radius_get_groups($_SESSION['user_radius_attributes']);
335
	}
336

    
337
	if (empty($names)) {
338
		$names = local_user_get_groups($user, true);
339
	}
340

    
341
	foreach ($names as $name) {
342
		$group = getGroupEntry($name);
343
		if (is_array($group['priv'])) {
344
			$privs = array_merge($privs, $group['priv']);
345
		}
346
	}
347

    
348
	return $privs;
349
}
350

    
351
function userHasPrivilege($userent, $privid = false) {
352

    
353
	if (!$privid || !is_array($userent)) {
354
		return false;
355
	}
356

    
357
	$privs = get_user_privileges($userent);
358

    
359
	if (!is_array($privs)) {
360
		return false;
361
	}
362

    
363
	if (!in_array($privid, $privs)) {
364
		return false;
365
	}
366

    
367
	return true;
368
}
369

    
370
function local_backed($username, $passwd) {
371

    
372
	$user = getUserEntry($username);
373
	if (!$user) {
374
		return false;
375
	}
376

    
377
	if (is_account_disabled($username) || is_account_expired($username)) {
378
		return false;
379
	}
380

    
381
	if ($user['bcrypt-hash']) {
382
		if (password_verify($passwd, $user['bcrypt-hash'])) {
383
			return true;
384
		}
385
	}
386

    
387
	//for backwards compatibility
388
	if ($user['password']) {
389
		if (crypt($passwd, $user['password']) == $user['password']) {
390
			return true;
391
		}
392
	}
393

    
394
	if ($user['md5-hash']) {
395
		if (md5($passwd) == $user['md5-hash']) {
396
			return true;
397
		}
398
	}
399

    
400
	return false;
401
}
402

    
403
function local_sync_accounts() {
404
	global $debug, $config;
405
	conf_mount_rw();
406

    
407
	/* remove local users to avoid uid conflicts */
408
	$fd = popen("/usr/sbin/pw usershow -a", "r");
409
	if ($fd) {
410
		while (!feof($fd)) {
411
			$line = explode(":", fgets($fd));
412
			if (((!strncmp($line[0], "_", 1)) || ($line[2] < 2000) || ($line[2] > 65000)) && ($line[0] != "admin")) {
413
				continue;
414
			}
415
			/*
416
			 * If a crontab was created to user, pw userdel will be interactive and
417
			 * can cause issues. Just remove crontab before run it when necessary
418
			 */
419
			unlink_if_exists("/var/cron/tabs/{$line[0]}");
420
			$cmd = "/usr/sbin/pw userdel -n '{$line[0]}'";
421
			if ($debug) {
422
				log_error(sprintf(gettext("Running: %s"), $cmd));
423
			}
424
			mwexec($cmd);
425
		}
426
		pclose($fd);
427
	}
428

    
429
	/* remove local groups to avoid gid conflicts */
430
	$gids = array();
431
	$fd = popen("/usr/sbin/pw groupshow -a", "r");
432
	if ($fd) {
433
		while (!feof($fd)) {
434
			$line = explode(":", fgets($fd));
435
			if (!strncmp($line[0], "_", 1)) {
436
				continue;
437
			}
438
			if ($line[2] < 2000) {
439
				continue;
440
			}
441
			if ($line[2] > 65000) {
442
				continue;
443
			}
444
			$cmd = "/usr/sbin/pw groupdel -g {$line[2]}";
445
			if ($debug) {
446
				log_error(sprintf(gettext("Running: %s"), $cmd));
447
			}
448
			mwexec($cmd);
449
		}
450
		pclose($fd);
451
	}
452

    
453
	/* make sure the all group exists */
454
	$allgrp = getGroupEntryByGID(1998);
455
	local_group_set($allgrp, true);
456

    
457
	/* sync all local users */
458
	if (is_array($config['system']['user'])) {
459
		foreach ($config['system']['user'] as $user) {
460
			local_user_set($user);
461
		}
462
	}
463

    
464
	/* sync all local groups */
465
	if (is_array($config['system']['group'])) {
466
		foreach ($config['system']['group'] as $group) {
467
			local_group_set($group);
468
		}
469
	}
470

    
471
	conf_mount_ro();
472

    
473
}
474

    
475
function local_user_set(& $user) {
476
	global $g, $debug;
477

    
478
	if (empty($user['password']) && empty($user['bcrypt-hash'])) {
479
		log_error("There is something wrong in the config because user {$user['name']} password is missing!");
480
		return;
481
	}
482

    
483
	conf_mount_rw();
484

    
485
	$home_base = "/home/";
486
	$user_uid = $user['uid'];
487
	$user_name = $user['name'];
488
	$user_home = "{$home_base}{$user_name}";
489
	$user_shell = "/etc/rc.initial";
490
	$user_group = "nobody";
491

    
492
	// Ensure $home_base exists and is writable
493
	if (!is_dir($home_base)) {
494
		mkdir($home_base, 0755);
495
	}
496

    
497
	$lock_account = false;
498
	/* configure shell type */
499
	/* Cases here should be ordered by most privileged to least privileged. */
500
	if (userHasPrivilege($user, "user-shell-access") || userHasPrivilege($user, "page-all")) {
501
		$user_shell = "/bin/tcsh";
502
	} elseif (userHasPrivilege($user, "user-copy-files")) {
503
		$user_shell = "/usr/local/bin/scponly";
504
	} elseif (userHasPrivilege($user, "user-ssh-tunnel")) {
505
		$user_shell = "/usr/local/sbin/ssh_tunnel_shell";
506
	} elseif (userHasPrivilege($user, "user-ipsec-xauth-dialin")) {
507
		$user_shell = "/sbin/nologin";
508
	} else {
509
		$user_shell = "/sbin/nologin";
510
		$lock_account = true;
511
	}
512

    
513
	/* Lock out disabled or expired users, unless it's root/admin. */
514
	if ((is_account_disabled($user_name) || is_account_expired($user_name)) && ($user_uid != 0)) {
515
		$user_shell = "/sbin/nologin";
516
		$lock_account = true;
517
	}
518

    
519
	/* root user special handling */
520
	if ($user_uid == 0) {
521
		$cmd = "/usr/sbin/pw usermod -q -n root -s /bin/sh -H 0";
522
		if ($debug) {
523
			log_error(sprintf(gettext("Running: %s"), $cmd));
524
		}
525
		$fd = popen($cmd, "w");
526
		if (empty($user['bcrypt-hash'])) {
527
			fwrite($fd, $user['password']);
528
		} else {
529
			fwrite($fd, $user['bcrypt-hash']);
530
		}
531
		pclose($fd);
532
		$user_group = "wheel";
533
		$user_home = "/root";
534
		$user_shell = "/etc/rc.initial";
535
	}
536

    
537
	/* read from pw db */
538
	$fd = popen("/usr/sbin/pw usershow -n {$user_name} 2>&1", "r");
539
	$pwread = fgets($fd);
540
	pclose($fd);
541
	$userattrs = explode(":", trim($pwread));
542

    
543
	/* determine add or mod */
544
	if (($userattrs[0] != $user['name']) || (!strncmp($pwread, "pw:", 3))) {
545
		$user_op = "useradd -m -k /etc/skel -o";
546
	} else {
547
		$user_op = "usermod";
548
	}
549

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

    
556
	if ($debug) {
557
		log_error(sprintf(gettext("Running: %s"), $cmd));
558
	}
559
	$fd = popen($cmd, "w");
560
	if (empty($user['bcrypt-hash'])) {
561
		fwrite($fd, $user['password']);
562
	} else {
563
		fwrite($fd, $user['bcrypt-hash']);
564
	}
565
	pclose($fd);
566

    
567
	/* create user directory if required */
568
	if (!is_dir($user_home)) {
569
		mkdir($user_home, 0700);
570
	}
571
	@chown($user_home, $user_name);
572
	@chgrp($user_home, $user_group);
573

    
574
	/* write out ssh authorized key file */
575
	if ($user['authorizedkeys']) {
576
		if (!is_dir("{$user_home}/.ssh")) {
577
			@mkdir("{$user_home}/.ssh", 0700);
578
			@chown("{$user_home}/.ssh", $user_name);
579
		}
580
		$keys = base64_decode($user['authorizedkeys']);
581
		@file_put_contents("{$user_home}/.ssh/authorized_keys", $keys);
582
		@chown("{$user_home}/.ssh/authorized_keys", $user_name);
583
	} else {
584
		unlink_if_exists("{$user_home}/.ssh/authorized_keys");
585
	}
586

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

    
590
	conf_mount_ro();
591
}
592

    
593
function local_user_del($user) {
594
	global $debug;
595

    
596
	/* remove all memberships */
597
	local_user_set_groups($user);
598

    
599
	/* Don't remove /root */
600
	if ($user['uid'] != 0) {
601
		$rmhome = "-r";
602
	}
603

    
604
	/* read from pw db */
605
	$fd = popen("/usr/sbin/pw usershow -n {$user['name']} 2>&1", "r");
606
	$pwread = fgets($fd);
607
	pclose($fd);
608
	$userattrs = explode(":", trim($pwread));
609

    
610
	if ($userattrs[0] != $user['name']) {
611
		log_error("Tried to remove user {$user['name']} but got user {$userattrs[0]} instead. Bailing.");
612
		return;
613
	}
614

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

    
618
	if ($debug) {
619
		log_error(sprintf(gettext("Running: %s"), $cmd));
620
	}
621
	mwexec($cmd);
622

    
623
	/* Delete user from groups needs a call to write_config() */
624
	local_group_del_user($user);
625
}
626

    
627
function local_user_set_password(&$user, $password) {
628

    
629
	unset($user['password']);
630
	unset($user['md5-hash']);
631
	$user['bcrypt-hash'] = password_hash($password, PASSWORD_BCRYPT);
632

    
633
	/* Maintain compatibility with FreeBSD - change $2y$ prefix to $2b$
634
	 * https://reviews.freebsd.org/D2742
635
	 * XXX: Can be removed as soon as r284483 is MFC'd.
636
	 */
637
	if ($user['bcrypt-hash'][2] == "y") {
638
		$user['bcrypt-hash'][2] = "b";
639
	}
640

    
641
	// Converts ascii to unicode.
642
	$astr = (string) $password;
643
	$ustr = '';
644
	for ($i = 0; $i < strlen($astr); $i++) {
645
		$a = ord($astr{$i}) << 8;
646
		$ustr .= sprintf("%X", $a);
647
	}
648

    
649
}
650

    
651
function local_user_get_groups($user, $all = false) {
652
	global $debug, $config;
653

    
654
	$groups = array();
655
	if (!is_array($config['system']['group'])) {
656
		return $groups;
657
	}
658

    
659
	foreach ($config['system']['group'] as $group) {
660
		if ($all || (!$all && ($group['name'] != "all"))) {
661
			if (is_array($group['member'])) {
662
				if (in_array($user['uid'], $group['member'])) {
663
					$groups[] = $group['name'];
664
				}
665
			}
666
		}
667
	}
668

    
669
	if ($all) {
670
		$groups[] = "all";
671
	}
672

    
673
	sort($groups);
674

    
675
	return $groups;
676

    
677
}
678

    
679
function local_user_set_groups($user, $new_groups = NULL) {
680
	global $debug, $config, $groupindex;
681

    
682
	if (!is_array($config['system']['group'])) {
683
		return;
684
	}
685

    
686
	$cur_groups = local_user_get_groups($user, true);
687
	$mod_groups = array();
688

    
689
	if (!is_array($new_groups)) {
690
		$new_groups = array();
691
	}
692

    
693
	if (!is_array($cur_groups)) {
694
		$cur_groups = array();
695
	}
696

    
697
	/* determine which memberships to add */
698
	foreach ($new_groups as $groupname) {
699
		if ($groupname == '' || in_array($groupname, $cur_groups)) {
700
			continue;
701
		}
702
		$group = & $config['system']['group'][$groupindex[$groupname]];
703
		$group['member'][] = $user['uid'];
704
		$mod_groups[] = $group;
705
	}
706
	unset($group);
707

    
708
	/* determine which memberships to remove */
709
	foreach ($cur_groups as $groupname) {
710
		if (in_array($groupname, $new_groups)) {
711
			continue;
712
		}
713
		if (!isset($config['system']['group'][$groupindex[$groupname]])) {
714
			continue;
715
		}
716
		$group = & $config['system']['group'][$groupindex[$groupname]];
717
		if (is_array($group['member'])) {
718
			$index = array_search($user['uid'], $group['member']);
719
			array_splice($group['member'], $index, 1);
720
			$mod_groups[] = $group;
721
		}
722
	}
723
	unset($group);
724

    
725
	/* sync all modified groups */
726
	foreach ($mod_groups as $group) {
727
		local_group_set($group);
728
	}
729
}
730

    
731
function local_group_del_user($user) {
732
	global $config;
733

    
734
	if (!is_array($config['system']['group'])) {
735
		return;
736
	}
737

    
738
	foreach ($config['system']['group'] as $group) {
739
		if (is_array($group['member'])) {
740
			foreach ($group['member'] as $idx => $uid) {
741
				if ($user['uid'] == $uid) {
742
					unset($config['system']['group']['member'][$idx]);
743
				}
744
			}
745
		}
746
	}
747
}
748

    
749
function local_group_set($group, $reset = false) {
750
	global $debug;
751

    
752
	$group_name = $group['name'];
753
	$group_gid = $group['gid'];
754
	$group_members = '';
755
	if (!$reset && !empty($group['member']) && count($group['member']) > 0) {
756
		$group_members = implode(",", $group['member']);
757
	}
758

    
759
	if (empty($group_name) || $group['scope'] == "remote") {
760
		return;
761
	}
762

    
763
	/* determine add or mod */
764
	if (mwexec("/usr/sbin/pw groupshow -g {$group_gid} 2>&1", true) == 0) {
765
		$group_op = "groupmod -l";
766
	} else {
767
		$group_op = "groupadd -n";
768
	}
769

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

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

    
778
}
779

    
780
function local_group_del($group) {
781
	global $debug;
782

    
783
	/* delete from group db */
784
	$cmd = "/usr/sbin/pw groupdel {$group['name']}";
785

    
786
	if ($debug) {
787
		log_error(sprintf(gettext("Running: %s"), $cmd));
788
	}
789
	mwexec($cmd);
790
}
791

    
792
function ldap_test_connection($authcfg) {
793
	global $debug, $config, $g;
794

    
795
	if ($authcfg) {
796
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
797
			$ldapproto = "ldap";
798
		} else {
799
			$ldapproto = "ldaps";
800
		}
801
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
802
		$ldapport = $authcfg['ldap_port'];
803
		if (!empty($ldapport)) {
804
			$ldapserver .= ":{$ldapport}";
805
		}
806
		$ldapbasedn = $authcfg['ldap_basedn'];
807
		$ldapbindun = $authcfg['ldap_binddn'];
808
		$ldapbindpw = $authcfg['ldap_bindpw'];
809
	} else {
810
		return false;
811
	}
812

    
813
	/* first check if there is even an LDAP server populated */
814
	if (!$ldapserver) {
815
		return false;
816
	}
817

    
818
	/* Setup CA environment if needed. */
819
	ldap_setup_caenv($authcfg);
820

    
821
	/* connect and see if server is up */
822
	$error = false;
823
	if (!($ldap = ldap_connect($ldapserver))) {
824
		$error = true;
825
	}
826

    
827
	if ($error == true) {
828
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
829
		return false;
830
	}
831

    
832
	return true;
833
}
834

    
835
function ldap_setup_caenv($authcfg) {
836
	global $g;
837
	require_once("certs.inc");
838

    
839
	unset($caref);
840
	if (empty($authcfg['ldap_caref']) || !strstr($authcfg['ldap_urltype'], "SSL")) {
841
		putenv('LDAPTLS_REQCERT=never');
842
		return;
843
	} else {
844
		$caref = lookup_ca($authcfg['ldap_caref']);
845
		if (!$caref) {
846
			log_error(sprintf(gettext("LDAP: Could not lookup CA by reference for host %s."), $authcfg['ldap_caref']));
847
			/* XXX: Prevent for credential leaking since we cannot setup the CA env. Better way? */
848
			putenv('LDAPTLS_REQCERT=hard');
849
			return;
850
		}
851
		if (!is_dir("{$g['varrun_path']}/certs")) {
852
			@mkdir("{$g['varrun_path']}/certs");
853
		}
854
		if (file_exists("{$g['varrun_path']}/certs/{$caref['refid']}.ca")) {
855
			@unlink("{$g['varrun_path']}/certs/{$caref['refid']}.ca");
856
		}
857
		file_put_contents("{$g['varrun_path']}/certs/{$caref['refid']}.ca", base64_decode($caref['crt']));
858
		@chmod("{$g['varrun_path']}/certs/{$caref['refid']}.ca", 0600);
859
		putenv('LDAPTLS_REQCERT=hard');
860
		/* XXX: Probably even the hashed link should be created for this? */
861
		putenv("LDAPTLS_CACERTDIR={$g['varrun_path']}/certs");
862
		putenv("LDAPTLS_CACERT={$g['varrun_path']}/certs/{$caref['refid']}.ca");
863
	}
864
}
865

    
866
function ldap_test_bind($authcfg) {
867
	global $debug, $config, $g;
868

    
869
	if ($authcfg) {
870
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
871
			$ldapproto = "ldap";
872
		} else {
873
			$ldapproto = "ldaps";
874
		}
875
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
876
		$ldapport = $authcfg['ldap_port'];
877
		if (!empty($ldapport)) {
878
			$ldapserver .= ":{$ldapport}";
879
		}
880
		$ldapbasedn = $authcfg['ldap_basedn'];
881
		$ldapbindun = $authcfg['ldap_binddn'];
882
		$ldapbindpw = $authcfg['ldap_bindpw'];
883
		$ldapver = $authcfg['ldap_protver'];
884
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 25;
885
		if (empty($ldapbndun) || empty($ldapbindpw)) {
886
			$ldapanon = true;
887
		} else {
888
			$ldapanon = false;
889
		}
890
	} else {
891
		return false;
892
	}
893

    
894
	/* first check if there is even an LDAP server populated */
895
	if (!$ldapserver) {
896
		return false;
897
	}
898

    
899
	/* Setup CA environment if needed. */
900
	ldap_setup_caenv($authcfg);
901

    
902
	/* connect and see if server is up */
903
	$error = false;
904
	if (!($ldap = ldap_connect($ldapserver))) {
905
		$error = true;
906
	}
907

    
908
	if ($error == true) {
909
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
910
		return false;
911
	}
912

    
913
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
914
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
915
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
916
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
917
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
918

    
919
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
920
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
921
	if ($ldapanon == true) {
922
		if (!($res = @ldap_bind($ldap))) {
923
			@ldap_close($ldap);
924
			return false;
925
		}
926
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
927
		@ldap_close($ldap);
928
		return false;
929
	}
930

    
931
	@ldap_unbind($ldap);
932

    
933
	return true;
934
}
935

    
936
function ldap_get_user_ous($show_complete_ou=true, $authcfg) {
937
	global $debug, $config, $g;
938

    
939
	if (!function_exists("ldap_connect")) {
940
		return;
941
	}
942

    
943
	$ous = array();
944

    
945
	if ($authcfg) {
946
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
947
			$ldapproto = "ldap";
948
		} else {
949
			$ldapproto = "ldaps";
950
		}
951
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
952
		$ldapport = $authcfg['ldap_port'];
953
		if (!empty($ldapport)) {
954
			$ldapserver .= ":{$ldapport}";
955
		}
956
		$ldapbasedn = $authcfg['ldap_basedn'];
957
		$ldapbindun = $authcfg['ldap_binddn'];
958
		$ldapbindpw = $authcfg['ldap_bindpw'];
959
		$ldapver = $authcfg['ldap_protver'];
960
		if (empty($ldapbindun) || empty($ldapbindpw)) {
961
			$ldapanon = true;
962
		} else {
963
			$ldapanon = false;
964
		}
965
		$ldapname = $authcfg['name'];
966
		$ldapfallback = false;
967
		$ldapscope = $authcfg['ldap_scope'];
968
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 25;
969
	} else {
970
		return false;
971
	}
972

    
973
	/* first check if there is even an LDAP server populated */
974
	if (!$ldapserver) {
975
		log_error(gettext("ERROR!  ldap_get_user_ous() backed selected with no LDAP authentication server defined."));
976
		return $ous;
977
	}
978

    
979
	/* Setup CA environment if needed. */
980
	ldap_setup_caenv($authcfg);
981

    
982
	/* connect and see if server is up */
983
	$error = false;
984
	if (!($ldap = ldap_connect($ldapserver))) {
985
		$error = true;
986
	}
987

    
988
	if ($error == true) {
989
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
990
		return $ous;
991
	}
992

    
993
	$ldapfilter = "(|(ou=*)(cn=Users))";
994

    
995
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
996
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
997
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
998
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
999
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1000

    
1001
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1002
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1003
	if ($ldapanon == true) {
1004
		if (!($res = @ldap_bind($ldap))) {
1005
			log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind anonymously to server %s."), $ldapname));
1006
			@ldap_close($ldap);
1007
			return $ous;
1008
		}
1009
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1010
		log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind to server %s."), $ldapname));
1011
		@ldap_close($ldap);
1012
		return $ous;
1013
	}
1014

    
1015
	if ($ldapscope == "one") {
1016
		$ldapfunc = "ldap_list";
1017
	} else {
1018
		$ldapfunc = "ldap_search";
1019
	}
1020

    
1021
	$search = @$ldapfunc($ldap, $ldapbasedn, $ldapfilter);
1022
	$info = @ldap_get_entries($ldap, $search);
1023

    
1024
	if (is_array($info)) {
1025
		foreach ($info as $inf) {
1026
			if (!$show_complete_ou) {
1027
				$inf_split = explode(",", $inf['dn']);
1028
				$ou = $inf_split[0];
1029
				$ou = str_replace("OU=", "", $ou);
1030
				$ou = str_replace("CN=", "", $ou);
1031
			} else {
1032
				if ($inf['dn']) {
1033
					$ou = $inf['dn'];
1034
				}
1035
			}
1036
			if ($ou) {
1037
				$ous[] = $ou;
1038
			}
1039
		}
1040
	}
1041

    
1042
	@ldap_unbind($ldap);
1043

    
1044
	return $ous;
1045
}
1046

    
1047
function ldap_get_groups($username, $authcfg) {
1048
	global $debug, $config;
1049

    
1050
	if (!function_exists("ldap_connect")) {
1051
		return;
1052
	}
1053

    
1054
	if (!$username) {
1055
		return false;
1056
	}
1057

    
1058
	if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) {
1059
		$username_split = explode("@", $username);
1060
		$username = $username_split[0];
1061
	}
1062

    
1063
	if (stristr($username, "\\")) {
1064
		$username_split = explode("\\", $username);
1065
		$username = $username_split[0];
1066
	}
1067

    
1068
	//log_error("Getting LDAP groups for {$username}.");
1069
	if ($authcfg) {
1070
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
1071
			$ldapproto = "ldap";
1072
		} else {
1073
			$ldapproto = "ldaps";
1074
		}
1075
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1076
		$ldapport = $authcfg['ldap_port'];
1077
		if (!empty($ldapport)) {
1078
			$ldapserver .= ":{$ldapport}";
1079
		}
1080
		$ldapbasedn = $authcfg['ldap_basedn'];
1081
		$ldapbindun = $authcfg['ldap_binddn'];
1082
		$ldapbindpw = $authcfg['ldap_bindpw'];
1083
		$ldapauthcont = $authcfg['ldap_authcn'];
1084
		$ldapnameattribute = strtolower($authcfg['ldap_attr_user']);
1085
		$ldapgroupattribute = strtolower($authcfg['ldap_attr_member']);
1086
		if (isset($authcfg['ldap_rfc2307'])) {
1087
			$ldapfilter         = "(&(objectClass={$authcfg['ldap_attr_groupobj']})({$ldapgroupattribute}={$username}))";
1088
		} else {
1089
			$ldapfilter         = "({$ldapnameattribute}={$username})";
1090
		}
1091
		$ldaptype = "";
1092
		$ldapver = $authcfg['ldap_protver'];
1093
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1094
			$ldapanon = true;
1095
		} else {
1096
			$ldapanon = false;
1097
		}
1098
		$ldapname = $authcfg['name'];
1099
		$ldapfallback = false;
1100
		$ldapscope = $authcfg['ldap_scope'];
1101
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 25;
1102
	} else {
1103
		return false;
1104
	}
1105

    
1106
	if (isset($authcfg['ldap_rfc2307'])) {
1107
		$ldapdn = $ldapbasedn;
1108
	} else {
1109
		$ldapdn = $_SESSION['ldapdn'];
1110
	}
1111

    
1112
	/*Convert attribute to lowercase.  php ldap arrays put everything in lowercase */
1113
	$ldapgroupattribute = strtolower($ldapgroupattribute);
1114
	$memberof = array();
1115

    
1116
	/* Setup CA environment if needed. */
1117
	ldap_setup_caenv($authcfg);
1118

    
1119
	/* connect and see if server is up */
1120
	$error = false;
1121
	if (!($ldap = ldap_connect($ldapserver))) {
1122
		$error = true;
1123
	}
1124

    
1125
	if ($error == true) {
1126
		log_error(sprintf(gettext("ERROR! ldap_get_groups() Could not connect to server %s."), $ldapname));
1127
		return memberof;
1128
	}
1129

    
1130
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1131
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1132
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1133
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1134
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1135

    
1136
	/* bind as user that has rights to read group attributes */
1137
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1138
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1139
	if ($ldapanon == true) {
1140
		if (!($res = @ldap_bind($ldap))) {
1141
			log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind anonymously to server %s."), $ldapname));
1142
			@ldap_close($ldap);
1143
			return false;
1144
		}
1145
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1146
		log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind to server %s."), $ldapname));
1147
		@ldap_close($ldap);
1148
		return memberof;
1149
	}
1150

    
1151
	/* get groups from DN found */
1152
	/* use ldap_read instead of search so we don't have to do a bunch of extra work */
1153
	/* since we know the DN is in $_SESSION['ldapdn'] */
1154
	//$search    = ldap_read($ldap, $ldapdn, "(objectclass=*)", array($ldapgroupattribute));
1155
	if ($ldapscope == "one") {
1156
		$ldapfunc = "ldap_list";
1157
	} else {
1158
		$ldapfunc = "ldap_search";
1159
	}
1160

    
1161
	$search = @$ldapfunc($ldap, $ldapdn, $ldapfilter, array($ldapgroupattribute));
1162
	$info = @ldap_get_entries($ldap, $search);
1163

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

    
1166
	if (is_array($gresults)) {
1167
		/* Iterate through the groups and throw them into an array */
1168
		foreach ($gresults as $grp) {
1169
			if (((isset($authcfg['ldap_rfc2307'])) && (stristr($grp["dn"], "CN=") !== false)) ||
1170
			    ((!isset($authcfg['ldap_rfc2307'])) && (stristr($grp, "CN=") !== false))) {
1171
				$grpsplit = isset($authcfg['ldap_rfc2307']) ? explode(",", $grp["dn"]) : explode(",", $grp);
1172
				$memberof[] = preg_replace("/CN=/i", "", $grpsplit[0]);
1173
			}
1174
		}
1175
	}
1176

    
1177
	/* Time to close LDAP connection */
1178
	@ldap_unbind($ldap);
1179

    
1180
	$groups = print_r($memberof, true);
1181

    
1182
	//log_error("Returning groups ".$groups." for user $username");
1183

    
1184
	return $memberof;
1185
}
1186

    
1187
function ldap_format_host($host) {
1188
	return is_ipaddrv6($host) ? "[$host]" : $host ;
1189
}
1190

    
1191
function ldap_backed($username, $passwd, $authcfg) {
1192
	global $debug, $config;
1193

    
1194
	if (!$username) {
1195
		return;
1196
	}
1197

    
1198
	if (!function_exists("ldap_connect")) {
1199
		return;
1200
	}
1201

    
1202
	if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) {
1203
		$username_split = explode("@", $username);
1204
		$username = $username_split[0];
1205
	}
1206
	if (stristr($username, "\\")) {
1207
		$username_split = explode("\\", $username);
1208
		$username = $username_split[0];
1209
	}
1210

    
1211
	if ($authcfg) {
1212
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
1213
			$ldapproto = "ldap";
1214
		} else {
1215
			$ldapproto = "ldaps";
1216
		}
1217
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1218
		$ldapport = $authcfg['ldap_port'];
1219
		if (!empty($ldapport)) {
1220
			$ldapserver .= ":{$ldapport}";
1221
		}
1222
		$ldapbasedn = $authcfg['ldap_basedn'];
1223
		$ldapbindun = $authcfg['ldap_binddn'];
1224
		$ldapbindpw = $authcfg['ldap_bindpw'];
1225
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1226
			$ldapanon = true;
1227
		} else {
1228
			$ldapanon = false;
1229
		}
1230
		$ldapauthcont = $authcfg['ldap_authcn'];
1231
		$ldapnameattribute = strtolower($authcfg['ldap_attr_user']);
1232
		$ldapextendedqueryenabled = $authcfg['ldap_extended_enabled'];
1233
		$ldapextendedquery = $authcfg['ldap_extended_query'];
1234
		$ldapfilter = "";
1235
		if (!$ldapextendedqueryenabled) {
1236
			$ldapfilter = "({$ldapnameattribute}={$username})";
1237
		} else {
1238
			$ldapfilter = "(&({$ldapnameattribute}={$username})({$ldapextendedquery}))";
1239
		}
1240
		$ldaptype = "";
1241
		$ldapver = $authcfg['ldap_protver'];
1242
		$ldapname = $authcfg['name'];
1243
		$ldapscope = $authcfg['ldap_scope'];
1244
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 25;
1245
	} else {
1246
		return false;
1247
	}
1248

    
1249
	/* first check if there is even an LDAP server populated */
1250
	if (!$ldapserver) {
1251
		if ($ldapfallback) {
1252
			log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined.  Defaulting to local user database. Visit System -> User Manager."));
1253
			return local_backed($username, $passwd);
1254
		} else {
1255
			log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined."));
1256
		}
1257

    
1258
		return false;
1259
	}
1260

    
1261
	/* Setup CA environment if needed. */
1262
	ldap_setup_caenv($authcfg);
1263

    
1264
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1265
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1266
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1267
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1268
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1269

    
1270
	/* Make sure we can connect to LDAP */
1271
	$error = false;
1272
	if (!($ldap = ldap_connect($ldapserver))) {
1273
		$error = true;
1274
	}
1275

    
1276
	if ($error == true) {
1277
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
1278
		return false;
1279
	}
1280

    
1281
	/* ok, its up.  now, lets bind as the bind user so we can search it */
1282
	$error = false;
1283
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1284
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1285
	if ($ldapanon == true) {
1286
		if (!($res = @ldap_bind($ldap))) {
1287
			$error = true;
1288
		}
1289
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1290
		$error = true;
1291
	}
1292

    
1293
	if ($error == true) {
1294
		@ldap_close($ldap);
1295
		log_error(sprintf(gettext("ERROR! Could not bind to server %s."), $ldapname));
1296
		return false;
1297
	}
1298

    
1299
	/* Get LDAP Authcontainers and split em up. */
1300
	$ldac_splits = explode(";", $ldapauthcont);
1301

    
1302
	/* setup the usercount so we think we haven't found anyone yet */
1303
	$usercount = 0;
1304

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

    
1350
	if ($usercount != 1) {
1351
		@ldap_unbind($ldap);
1352
		log_error(gettext("ERROR! Either LDAP search failed, or multiple users were found."));
1353
		return false;
1354
	}
1355

    
1356
	/* Now lets bind as the user we found */
1357
	$passwd = isset($authcfg['ldap_utf8']) ? utf8_encode($passwd) : $passwd;
1358
	if (!($res = @ldap_bind($ldap, $userdn, $passwd))) {
1359
		log_error(sprintf(gettext('ERROR! Could not login to server %1$s as user %2$s: %3$s'), $ldapname, $username, ldap_error($ldap)));
1360
		@ldap_unbind($ldap);
1361
		return false;
1362
	}
1363

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

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

    
1372
	return true;
1373
}
1374

    
1375
function radius_backed($username, $passwd, $authcfg, &$attributes = array()) {
1376
	global $debug, $config;
1377
	$ret = false;
1378

    
1379
	require_once("radius.inc");
1380

    
1381
	$rauth = new Auth_RADIUS_PAP($username, $passwd);
1382
	if ($authcfg) {
1383
		$radiusservers = array();
1384
		$radiusservers[0]['ipaddr'] = $authcfg['host'];
1385
		$radiusservers[0]['port'] = $authcfg['radius_auth_port'];
1386
		$radiusservers[0]['sharedsecret'] = $authcfg['radius_secret'];
1387
		$radiusservers[0]['timeout'] = $authcfg['radius_timeout'];
1388
	} else {
1389
		return false;
1390
	}
1391

    
1392
	/* Add new servers to our instance */
1393
	foreach ($radiusservers as $radsrv) {
1394
		$timeout = (is_numeric($radsrv['timeout'])) ? $radsrv['timeout'] : 5;
1395
		$rauth->addServer($radsrv['ipaddr'], $radsrv['port'], $radsrv['sharedsecret'], $timeout);
1396
	}
1397

    
1398
	if (PEAR::isError($rauth->start())) {
1399
		$retvalue['auth_val'] = 1;
1400
		$retvalue['error'] = $rauth->getError();
1401
		if ($debug) {
1402
			printf(gettext("RADIUS start: %s<br />\n"), $retvalue['error']);
1403
		}
1404
	}
1405

    
1406
	// XXX - billm - somewhere in here we need to handle securid challenge/response
1407

    
1408
	/* Send request */
1409
	$result = $rauth->send();
1410
	if (PEAR::isError($result)) {
1411
		$retvalue['auth_val'] = 1;
1412
		$retvalue['error'] = $result->getMessage();
1413
		if ($debug) {
1414
			printf(gettext("RADIUS send failed: %s<br />\n"), $retvalue['error']);
1415
		}
1416
	} else if ($result === true) {
1417
		if ($rauth->getAttributes()) {
1418
			$attributes = $rauth->listAttributes();
1419
		}
1420
		$retvalue['auth_val'] = 2;
1421
		if ($debug) {
1422
			printf(gettext("RADIUS Auth succeeded")."<br />\n");
1423
		}
1424
		$ret = true;
1425
	} else {
1426
		$retvalue['auth_val'] = 3;
1427
		if ($debug) {
1428
			printf(gettext("RADIUS Auth rejected")."<br />\n");
1429
		}
1430
	}
1431

    
1432
	// close OO RADIUS_AUTHENTICATION
1433
	$rauth->close();
1434

    
1435
	return $ret;
1436
}
1437

    
1438
/*
1439
	$attributes must contain a "class" key containing the groups and local
1440
	groups must exist to match.
1441
*/
1442
function radius_get_groups($attributes) {
1443
	$groups = array();
1444
	if (!empty($attributes) && is_array($attributes) && (!empty($attributes['class']) || !empty($attributes['class_int']))) {
1445
		/* Some RADIUS servers return multiple class attributes, so check them all. */
1446
		$groups = array();
1447
		if (!empty($attributes['class']) && is_array($attributes['class'])) {
1448
			foreach ($attributes['class'] as $class) {
1449
				$groups = array_unique(array_merge($groups, explode(";", $class)));
1450
			}
1451
		}
1452

    
1453
		foreach ($groups as & $grp) {
1454
			$grp = trim($grp);
1455
			if (strtolower(substr($grp, 0, 3)) == "ou=") {
1456
				$grp = substr($grp, 3);
1457
			}
1458
		}
1459
	}
1460
	return $groups;
1461
}
1462

    
1463
function get_user_expiration_date($username) {
1464
	$user = getUserEntry($username);
1465
	if ($user['expires']) {
1466
		return $user['expires'];
1467
	}
1468
}
1469

    
1470
function is_account_expired($username) {
1471
	$expirydate = get_user_expiration_date($username);
1472
	if ($expirydate) {
1473
		if (strtotime("-1 day") > strtotime(date("m/d/Y", strtotime($expirydate)))) {
1474
			return true;
1475
		}
1476
	}
1477

    
1478
	return false;
1479
}
1480

    
1481
function is_account_disabled($username) {
1482
	$user = getUserEntry($username);
1483
	if (isset($user['disabled'])) {
1484
		return true;
1485
	}
1486

    
1487
	return false;
1488
}
1489

    
1490
function auth_get_authserver($name) {
1491
	global $config;
1492

    
1493
	if (is_array($config['system']['authserver'])) {
1494
		foreach ($config['system']['authserver'] as $authcfg) {
1495
			if ($authcfg['name'] == $name) {
1496
				return $authcfg;
1497
			}
1498
		}
1499
	}
1500
	if ($name == "Local Database") {
1501
		return array("name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']);
1502
	}
1503
}
1504

    
1505
function auth_get_authserver_list() {
1506
	global $config;
1507

    
1508
	$list = array();
1509

    
1510
	if (is_array($config['system']['authserver'])) {
1511
		foreach ($config['system']['authserver'] as $authcfg) {
1512
			/* Add support for disabled entries? */
1513
			$list[$authcfg['name']] = $authcfg;
1514
		}
1515
	}
1516

    
1517
	$list["Local Database"] = array("name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']);
1518
	return $list;
1519
}
1520

    
1521
function getUserGroups($username, $authcfg, &$attributes = array()) {
1522
	global $config;
1523

    
1524
	$allowed_groups = array();
1525

    
1526
	switch ($authcfg['type']) {
1527
		case 'ldap':
1528
			$allowed_groups = @ldap_get_groups($username, $authcfg);
1529
			break;
1530
		case 'radius':
1531
			$allowed_groups = @radius_get_groups($attributes);
1532
			break;
1533
		default:
1534
			$user = getUserEntry($username);
1535
			$allowed_groups = @local_user_get_groups($user, true);
1536
			break;
1537
	}
1538

    
1539
	$member_groups = array();
1540
	if (is_array($config['system']['group'])) {
1541
		foreach ($config['system']['group'] as $group) {
1542
			if (in_array($group['name'], $allowed_groups)) {
1543
				$member_groups[] = $group['name'];
1544
			}
1545
		}
1546
	}
1547

    
1548
	return $member_groups;
1549
}
1550

    
1551
function authenticate_user($username, $password, $authcfg = NULL, &$attributes = array()) {
1552

    
1553
	if (is_array($username) || is_array($password)) {
1554
		return false;
1555
	}
1556

    
1557
	if (!$authcfg) {
1558
		return local_backed($username, $password);
1559
	}
1560

    
1561
	$authenticated = false;
1562
	switch ($authcfg['type']) {
1563
		case 'ldap':
1564
			if (ldap_backed($username, $password, $authcfg)) {
1565
				$authenticated = true;
1566
			}
1567
			break;
1568
		case 'radius':
1569
			if (radius_backed($username, $password, $authcfg, $attributes)) {
1570
				$authenticated = true;
1571
			}
1572
			break;
1573
		default:
1574
			/* lookup user object by name */
1575
			if (local_backed($username, $password)) {
1576
				$authenticated = true;
1577
			}
1578
			break;
1579
		}
1580

    
1581
	return $authenticated;
1582
}
1583

    
1584
function session_auth() {
1585
	global $config, $_SESSION, $page;
1586

    
1587
	// Handle HTTPS httponly and secure flags
1588
	$currentCookieParams = session_get_cookie_params();
1589
	session_set_cookie_params(
1590
		$currentCookieParams["lifetime"],
1591
		$currentCookieParams["path"],
1592
		NULL,
1593
		($config['system']['webgui']['protocol'] == "https"),
1594
		true
1595
	);
1596

    
1597
	if (!session_id()) {
1598
		session_start();
1599
	}
1600

    
1601
	// Detect protocol change
1602
	if (!isset($_POST['login']) && !empty($_SESSION['Logged_In']) && $_SESSION['protocol'] != $config['system']['webgui']['protocol']) {
1603
		return false;
1604
	}
1605

    
1606
	/* Validate incoming login request */
1607
	$attributes = array();
1608
	if (isset($_POST['login']) && !empty($_POST['usernamefld']) && !empty($_POST['passwordfld'])) {
1609
		$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
1610
		if (authenticate_user($_POST['usernamefld'], $_POST['passwordfld'], $authcfg, $attributes) ||
1611
		    authenticate_user($_POST['usernamefld'], $_POST['passwordfld'])) {
1612
			// Generate a new id to avoid session fixation
1613
			session_regenerate_id();
1614
			$_SESSION['Logged_In'] = "True";
1615
			$_SESSION['Username'] = $_POST['usernamefld'];
1616
			$_SESSION['user_radius_attributes'] = $attributes;
1617
			$_SESSION['last_access'] = time();
1618
			$_SESSION['protocol'] = $config['system']['webgui']['protocol'];
1619
			if (!isset($config['system']['webgui']['quietlogin'])) {
1620
				log_auth(sprintf(gettext("Successful login for user '%1\$s' from: %2\$s"), $_POST['usernamefld'], $_SERVER['REMOTE_ADDR']));
1621
			}
1622
			if (isset($_POST['postafterlogin'])) {
1623
				return true;
1624
			} else {
1625
				if (empty($page)) {
1626
					$page = "/";
1627
				}
1628
				header("Location: {$page}");
1629
			}
1630
			exit;
1631
		} else {
1632
			/* give the user an error message */
1633
			$_SESSION['Login_Error'] = "Username or Password incorrect";
1634
			log_auth("webConfigurator authentication error for '{$_POST['usernamefld']}' from {$_SERVER['REMOTE_ADDR']}");
1635
			if (isAjax()) {
1636
				echo "showajaxmessage('{$_SESSION['Login_Error']}');";
1637
				return;
1638
			}
1639
		}
1640
	}
1641

    
1642
	/* Show login page if they aren't logged in */
1643
	if (empty($_SESSION['Logged_In'])) {
1644
		return false;
1645
	}
1646

    
1647
	/* If session timeout isn't set, we don't mark sessions stale */
1648
	if (!isset($config['system']['webgui']['session_timeout'])) {
1649
		/* Default to 4 hour timeout if one is not set */
1650
		if ($_SESSION['last_access'] < (time() - 14400)) {
1651
			$_GET['logout'] = true;
1652
			$_SESSION['Logout'] = true;
1653
		} else {
1654
			$_SESSION['last_access'] = time();
1655
		}
1656
	} else if (intval($config['system']['webgui']['session_timeout']) == 0) {
1657
		/* only update if it wasn't ajax */
1658
		if (!isAjax()) {
1659
			$_SESSION['last_access'] = time();
1660
		}
1661
	} else {
1662
		/* Check for stale session */
1663
		if ($_SESSION['last_access'] < (time() - ($config['system']['webgui']['session_timeout'] * 60))) {
1664
			$_GET['logout'] = true;
1665
			$_SESSION['Logout'] = true;
1666
		} else {
1667
			/* only update if it wasn't ajax */
1668
			if (!isAjax()) {
1669
				$_SESSION['last_access'] = time();
1670
			}
1671
		}
1672
	}
1673

    
1674
	/* user hit the logout button */
1675
	if (isset($_GET['logout'])) {
1676

    
1677
		if ($_SESSION['Logout']) {
1678
			log_error(sprintf(gettext("Session timed out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR']));
1679
		} else {
1680
			log_error(sprintf(gettext("User logged out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR']));
1681
		}
1682

    
1683
		/* wipe out $_SESSION */
1684
		$_SESSION = array();
1685

    
1686
		if (isset($_COOKIE[session_name()])) {
1687
			setcookie(session_name(), '', time()-42000, '/');
1688
		}
1689

    
1690
		/* and destroy it */
1691
		session_destroy();
1692

    
1693
		$scriptName = explode("/", $_SERVER["SCRIPT_FILENAME"]);
1694
		$scriptElms = count($scriptName);
1695
		$scriptName = $scriptName[$scriptElms-1];
1696

    
1697
		if (isAjax()) {
1698
			return false;
1699
		}
1700

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

    
1704
		return false;
1705
	}
1706

    
1707
	/*
1708
	 * this is for debugging purpose if you do not want to use Ajax
1709
	 * to submit a HTML form. It basically disables the observation
1710
	 * of the submit event and hence does not trigger Ajax.
1711
	 */
1712
	if ($_GET['disable_ajax']) {
1713
		$_SESSION['NO_AJAX'] = "True";
1714
	}
1715

    
1716
	/*
1717
	 * Same to re-enable Ajax.
1718
	 */
1719
	if ($_GET['enable_ajax']) {
1720
		unset($_SESSION['NO_AJAX']);
1721
	}
1722

    
1723
	return true;
1724
}
1725

    
1726
?>
(4-4/65)