Project

General

Profile

Download (48.3 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(true);
209
				foreach ($interface_list_ipv6s as $ilipv6s) {
210
					$ilipv6s = explode('%', $ilipv6s)[0];
211
					if (strcasecmp($referrer_host, $ilipv6s) == 0) {
212
						$found_host = true;
213
						break;
214
					}
215
				}
216
				if ($referrer_host == "127.0.0.1" || $referrer_host == "localhost") {
217
					// allow SSH port forwarded connections and links from localhost
218
					$found_host = true;
219
				}
220
			}
221
		}
222
		if ($found_host == false) {
223
			if (!security_checks_disabled()) {
224
				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.");
225
				exit;
226
			}
227
			$security_passed = false;
228
		}
229
	} else {
230
		$security_passed = false;
231
	}
232
}
233

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

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

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

    
246
	$groupindex = array();
247

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

    
256
	return ($groupindex);
257
}
258

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

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

    
270
	return ($userindex);
271
}
272

    
273
function & getUserEntry($name) {
274
	global $debug, $config, $userindex;
275
	$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
276

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

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

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

    
297
	return false;
298
}
299

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

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

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

    
318
	return false;
319
}
320

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

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

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

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

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

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

    
349
	return $privs;
350
}
351

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

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

    
358
	$privs = get_user_privileges($userent);
359

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

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

    
368
	return true;
369
}
370

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

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

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

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

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

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

    
401
	return false;
402
}
403

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

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

    
438
	/* remove local groups to avoid gid conflicts */
439
	$gids = array();
440
	$fd = popen("/usr/sbin/pw groupshow -a", "r");
441
	if ($fd) {
442
		while (!feof($fd)) {
443
			$line = explode(":", fgets($fd));
444
			if (!strncmp($line[0], "_", 1)) {
445
				continue;
446
			}
447
			if ($line[2] < 2000) {
448
				continue;
449
			}
450
			if ($line[2] > 65000) {
451
				continue;
452
			}
453
			$cmd = "/usr/sbin/pw groupdel -g " . escapeshellarg($line[2]);
454
			if ($debug) {
455
				log_error(sprintf(gettext("Running: %s"), $cmd));
456
			}
457
			mwexec($cmd);
458
		}
459
		pclose($fd);
460
	}
461

    
462
	/* make sure the all group exists */
463
	$allgrp = getGroupEntryByGID(1998);
464
	local_group_set($allgrp, true);
465

    
466
	/* sync all local users */
467
	if (is_array($config['system']['user'])) {
468
		foreach ($config['system']['user'] as $user) {
469
			local_user_set($user);
470
		}
471
	}
472

    
473
	/* sync all local groups */
474
	if (is_array($config['system']['group'])) {
475
		foreach ($config['system']['group'] as $group) {
476
			local_group_set($group);
477
		}
478
	}
479

    
480
	conf_mount_ro();
481

    
482
}
483

    
484
function local_user_set(& $user) {
485
	global $g, $debug;
486

    
487
	if (empty($user['password']) && empty($user['bcrypt-hash'])) {
488
		log_error("There is something wrong in the config because user {$user['name']} password is missing!");
489
		return;
490
	}
491

    
492
	conf_mount_rw();
493

    
494
	$home_base = "/home/";
495
	$user_uid = $user['uid'];
496
	$user_name = $user['name'];
497
	$user_home = "{$home_base}{$user_name}";
498
	$user_shell = "/etc/rc.initial";
499
	$user_group = "nobody";
500

    
501
	// Ensure $home_base exists and is writable
502
	if (!is_dir($home_base)) {
503
		mkdir($home_base, 0755);
504
	}
505

    
506
	$lock_account = false;
507
	/* configure shell type */
508
	/* Cases here should be ordered by most privileged to least privileged. */
509
	if (userHasPrivilege($user, "user-shell-access") || userHasPrivilege($user, "page-all")) {
510
		$user_shell = "/bin/tcsh";
511
	} elseif (userHasPrivilege($user, "user-copy-files")) {
512
		$user_shell = "/usr/local/bin/scponly";
513
	} elseif (userHasPrivilege($user, "user-ssh-tunnel")) {
514
		$user_shell = "/usr/local/sbin/ssh_tunnel_shell";
515
	} elseif (userHasPrivilege($user, "user-ipsec-xauth-dialin")) {
516
		$user_shell = "/sbin/nologin";
517
	} else {
518
		$user_shell = "/sbin/nologin";
519
		$lock_account = true;
520
	}
521

    
522
	/* Lock out disabled or expired users, unless it's root/admin. */
523
	if ((is_account_disabled($user_name) || is_account_expired($user_name)) && ($user_uid != 0)) {
524
		$user_shell = "/sbin/nologin";
525
		$lock_account = true;
526
	}
527

    
528
	/* root user special handling */
529
	if ($user_uid == 0) {
530
		$cmd = "/usr/sbin/pw usermod -q -n root -s /bin/sh -H 0";
531
		if ($debug) {
532
			log_error(sprintf(gettext("Running: %s"), $cmd));
533
		}
534
		$fd = popen($cmd, "w");
535
		if (empty($user['bcrypt-hash'])) {
536
			fwrite($fd, $user['password']);
537
		} else {
538
			fwrite($fd, $user['bcrypt-hash']);
539
		}
540
		pclose($fd);
541
		$user_group = "wheel";
542
		$user_home = "/root";
543
		$user_shell = "/etc/rc.initial";
544
	}
545

    
546
	/* read from pw db */
547
	$fd = popen("/usr/sbin/pw usershow -n {$user_name} 2>&1", "r");
548
	$pwread = fgets($fd);
549
	pclose($fd);
550
	$userattrs = explode(":", trim($pwread));
551

    
552
	$skel_dir = '/etc/skel';
553

    
554
	/* determine add or mod */
555
	if (($userattrs[0] != $user['name']) || (!strncmp($pwread, "pw:", 3))) {
556
		$user_op = "useradd -m -k " . escapeshellarg($skel_dir) . " -o";
557
	} else {
558
		$user_op = "usermod";
559
	}
560

    
561
	$comment = str_replace(array(":", "!", "@"), " ", $user['descr']);
562
	/* add or mod pw db */
563
	$cmd = "/usr/sbin/pw {$user_op} -q " .
564
			" -u " . escapeshellarg($user_uid) .
565
			" -n " . escapeshellarg($user_name) .
566
			" -g " . escapeshellarg($user_group) .
567
			" -s " . escapeshellarg($user_shell) .
568
			" -d " . escapeshellarg($user_home) .
569
			" -c " . escapeshellarg($comment) .
570
			" -H 0 2>&1";
571

    
572
	if ($debug) {
573
		log_error(sprintf(gettext("Running: %s"), $cmd));
574
	}
575
	$fd = popen($cmd, "w");
576
	if (empty($user['bcrypt-hash'])) {
577
		fwrite($fd, $user['password']);
578
	} else {
579
		fwrite($fd, $user['bcrypt-hash']);
580
	}
581
	pclose($fd);
582

    
583
	/* create user directory if required */
584
	if (!is_dir($user_home)) {
585
		mkdir($user_home, 0700);
586
	}
587
	@chown($user_home, $user_name);
588
	@chgrp($user_home, $user_group);
589

    
590
	/* Make sure all users have last version of config files */
591
	foreach (glob("{$skel_dir}/dot.*") as $dot_file) {
592
		$target = $user_home . '/' . substr(basename($dot_file), 3);
593
		@copy($dot_file, $target);
594
		@chown($target, $user_name);
595
		@chgrp($target, $user_group);
596
	}
597

    
598
	/* write out ssh authorized key file */
599
	if ($user['authorizedkeys']) {
600
		if (!is_dir("{$user_home}/.ssh")) {
601
			@mkdir("{$user_home}/.ssh", 0700);
602
			@chown("{$user_home}/.ssh", $user_name);
603
		}
604
		$keys = base64_decode($user['authorizedkeys']);
605
		@file_put_contents("{$user_home}/.ssh/authorized_keys", $keys);
606
		@chown("{$user_home}/.ssh/authorized_keys", $user_name);
607
	} else {
608
		unlink_if_exists("{$user_home}/.ssh/authorized_keys");
609
	}
610

    
611
	$un = $lock_account ? "" : "un";
612
	exec("/usr/sbin/pw {$un}lock " . escapeshellarg($user_name) . " -q 2>/dev/null");
613

    
614
	conf_mount_ro();
615
}
616

    
617
function local_user_del($user) {
618
	global $debug;
619

    
620
	/* remove all memberships */
621
	local_user_set_groups($user);
622

    
623
	/* Don't remove /root */
624
	if ($user['uid'] != 0) {
625
		$rmhome = "-r";
626
	}
627

    
628
	/* read from pw db */
629
	$fd = popen("/usr/sbin/pw usershow -n {$user['name']} 2>&1", "r");
630
	$pwread = fgets($fd);
631
	pclose($fd);
632
	$userattrs = explode(":", trim($pwread));
633

    
634
	if ($userattrs[0] != $user['name']) {
635
		log_error("Tried to remove user {$user['name']} but got user {$userattrs[0]} instead. Bailing.");
636
		return;
637
	}
638

    
639
	/* delete from pw db */
640
	$cmd = "/usr/sbin/pw userdel -n " . escapeshellarg($user['name']) . " " . escapeshellarg($rmhome);
641

    
642
	if ($debug) {
643
		log_error(sprintf(gettext("Running: %s"), $cmd));
644
	}
645
	mwexec($cmd);
646

    
647
	/* Delete user from groups needs a call to write_config() */
648
	local_group_del_user($user);
649
}
650

    
651
function local_user_set_password(&$user, $password) {
652

    
653
	unset($user['password']);
654
	unset($user['md5-hash']);
655
	$user['bcrypt-hash'] = password_hash($password, PASSWORD_BCRYPT);
656

    
657
	/* Maintain compatibility with FreeBSD - change $2y$ prefix to $2b$
658
	 * https://reviews.freebsd.org/D2742
659
	 * XXX: Can be removed as soon as r284483 is MFC'd.
660
	 */
661
	if ($user['bcrypt-hash'][2] == "y") {
662
		$user['bcrypt-hash'][2] = "b";
663
	}
664

    
665
	// Converts ascii to unicode.
666
	$astr = (string) $password;
667
	$ustr = '';
668
	for ($i = 0; $i < strlen($astr); $i++) {
669
		$a = ord($astr{$i}) << 8;
670
		$ustr .= sprintf("%X", $a);
671
	}
672

    
673
}
674

    
675
function local_user_get_groups($user, $all = false) {
676
	global $debug, $config;
677

    
678
	$groups = array();
679
	if (!is_array($config['system']['group'])) {
680
		return $groups;
681
	}
682

    
683
	foreach ($config['system']['group'] as $group) {
684
		if ($all || (!$all && ($group['name'] != "all"))) {
685
			if (is_array($group['member'])) {
686
				if (in_array($user['uid'], $group['member'])) {
687
					$groups[] = $group['name'];
688
				}
689
			}
690
		}
691
	}
692

    
693
	if ($all) {
694
		$groups[] = "all";
695
	}
696

    
697
	sort($groups);
698

    
699
	return $groups;
700

    
701
}
702

    
703
function local_user_set_groups($user, $new_groups = NULL) {
704
	global $debug, $config, $groupindex;
705

    
706
	if (!is_array($config['system']['group'])) {
707
		return;
708
	}
709

    
710
	$cur_groups = local_user_get_groups($user, true);
711
	$mod_groups = array();
712

    
713
	if (!is_array($new_groups)) {
714
		$new_groups = array();
715
	}
716

    
717
	if (!is_array($cur_groups)) {
718
		$cur_groups = array();
719
	}
720

    
721
	/* determine which memberships to add */
722
	foreach ($new_groups as $groupname) {
723
		if ($groupname == '' || in_array($groupname, $cur_groups)) {
724
			continue;
725
		}
726
		$group = & $config['system']['group'][$groupindex[$groupname]];
727
		$group['member'][] = $user['uid'];
728
		$mod_groups[] = $group;
729
	}
730
	unset($group);
731

    
732
	/* determine which memberships to remove */
733
	foreach ($cur_groups as $groupname) {
734
		if (in_array($groupname, $new_groups)) {
735
			continue;
736
		}
737
		if (!isset($config['system']['group'][$groupindex[$groupname]])) {
738
			continue;
739
		}
740
		$group = & $config['system']['group'][$groupindex[$groupname]];
741
		if (is_array($group['member'])) {
742
			$index = array_search($user['uid'], $group['member']);
743
			array_splice($group['member'], $index, 1);
744
			$mod_groups[] = $group;
745
		}
746
	}
747
	unset($group);
748

    
749
	/* sync all modified groups */
750
	foreach ($mod_groups as $group) {
751
		local_group_set($group);
752
	}
753
}
754

    
755
function local_group_del_user($user) {
756
	global $config;
757

    
758
	if (!is_array($config['system']['group'])) {
759
		return;
760
	}
761

    
762
	foreach ($config['system']['group'] as $group) {
763
		if (is_array($group['member'])) {
764
			foreach ($group['member'] as $idx => $uid) {
765
				if ($user['uid'] == $uid) {
766
					unset($config['system']['group']['member'][$idx]);
767
				}
768
			}
769
		}
770
	}
771
}
772

    
773
function local_group_set($group, $reset = false) {
774
	global $debug;
775

    
776
	$group_name = $group['name'];
777
	$group_gid = $group['gid'];
778
	$group_members = '';
779
	if (!$reset && !empty($group['member']) && count($group['member']) > 0) {
780
		$group_members = implode(",", $group['member']);
781
	}
782

    
783
	if (empty($group_name) || $group['scope'] == "remote") {
784
		return;
785
	}
786

    
787
	/* determine add or mod */
788
	if (mwexec("/usr/sbin/pw groupshow -g " . escapeshellarg($group_gid) . " 2>&1", true) == 0) {
789
		$group_op = "groupmod -l";
790
	} else {
791
		$group_op = "groupadd -n";
792
	}
793

    
794
	/* add or mod group db */
795
	$cmd = "/usr/sbin/pw {$group_op} " .
796
		escapeshellarg($group_name) .
797
		" -g " . escapeshellarg($group_gid) .
798
		" -M " . escapeshellarg($group_members) . " 2>&1";
799

    
800
	if ($debug) {
801
		log_error(sprintf(gettext("Running: %s"), $cmd));
802
	}
803
	mwexec($cmd);
804

    
805
}
806

    
807
function local_group_del($group) {
808
	global $debug;
809

    
810
	/* delete from group db */
811
	$cmd = "/usr/sbin/pw groupdel " . escapeshellarg($group['name']);
812

    
813
	if ($debug) {
814
		log_error(sprintf(gettext("Running: %s"), $cmd));
815
	}
816
	mwexec($cmd);
817
}
818

    
819
function ldap_test_connection($authcfg) {
820
	global $debug, $config, $g;
821

    
822
	if ($authcfg) {
823
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
824
			$ldapproto = "ldap";
825
		} else {
826
			$ldapproto = "ldaps";
827
		}
828
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
829
		$ldapport = $authcfg['ldap_port'];
830
		if (!empty($ldapport)) {
831
			$ldapserver .= ":{$ldapport}";
832
		}
833
		$ldapbasedn = $authcfg['ldap_basedn'];
834
		$ldapbindun = $authcfg['ldap_binddn'];
835
		$ldapbindpw = $authcfg['ldap_bindpw'];
836
	} else {
837
		return false;
838
	}
839

    
840
	/* first check if there is even an LDAP server populated */
841
	if (!$ldapserver) {
842
		return false;
843
	}
844

    
845
	/* Setup CA environment if needed. */
846
	ldap_setup_caenv($authcfg);
847

    
848
	/* connect and see if server is up */
849
	$error = false;
850
	if (!($ldap = ldap_connect($ldapserver))) {
851
		$error = true;
852
	}
853

    
854
	if ($error == true) {
855
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
856
		return false;
857
	}
858

    
859
	return true;
860
}
861

    
862
function ldap_setup_caenv($authcfg) {
863
	global $g;
864
	require_once("certs.inc");
865

    
866
	unset($caref);
867
	if (empty($authcfg['ldap_caref']) || !strstr($authcfg['ldap_urltype'], "SSL")) {
868
		putenv('LDAPTLS_REQCERT=never');
869
		return;
870
	} else {
871
		$caref = lookup_ca($authcfg['ldap_caref']);
872
		if (!$caref) {
873
			log_error(sprintf(gettext("LDAP: Could not lookup CA by reference for host %s."), $authcfg['ldap_caref']));
874
			/* XXX: Prevent for credential leaking since we cannot setup the CA env. Better way? */
875
			putenv('LDAPTLS_REQCERT=hard');
876
			return;
877
		}
878
		if (!is_dir("{$g['varrun_path']}/certs")) {
879
			@mkdir("{$g['varrun_path']}/certs");
880
		}
881
		if (file_exists("{$g['varrun_path']}/certs/{$caref['refid']}.ca")) {
882
			@unlink("{$g['varrun_path']}/certs/{$caref['refid']}.ca");
883
		}
884
		file_put_contents("{$g['varrun_path']}/certs/{$caref['refid']}.ca", base64_decode($caref['crt']));
885
		@chmod("{$g['varrun_path']}/certs/{$caref['refid']}.ca", 0600);
886
		putenv('LDAPTLS_REQCERT=hard');
887
		/* XXX: Probably even the hashed link should be created for this? */
888
		putenv("LDAPTLS_CACERTDIR={$g['varrun_path']}/certs");
889
		putenv("LDAPTLS_CACERT={$g['varrun_path']}/certs/{$caref['refid']}.ca");
890
	}
891
}
892

    
893
function ldap_test_bind($authcfg) {
894
	global $debug, $config, $g;
895

    
896
	if ($authcfg) {
897
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
898
			$ldapproto = "ldap";
899
		} else {
900
			$ldapproto = "ldaps";
901
		}
902
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
903
		$ldapport = $authcfg['ldap_port'];
904
		if (!empty($ldapport)) {
905
			$ldapserver .= ":{$ldapport}";
906
		}
907
		$ldapbasedn = $authcfg['ldap_basedn'];
908
		$ldapbindun = $authcfg['ldap_binddn'];
909
		$ldapbindpw = $authcfg['ldap_bindpw'];
910
		$ldapver = $authcfg['ldap_protver'];
911
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
912
		if (empty($ldapbndun) || empty($ldapbindpw)) {
913
			$ldapanon = true;
914
		} else {
915
			$ldapanon = false;
916
		}
917
	} else {
918
		return false;
919
	}
920

    
921
	/* first check if there is even an LDAP server populated */
922
	if (!$ldapserver) {
923
		return false;
924
	}
925

    
926
	/* Setup CA environment if needed. */
927
	ldap_setup_caenv($authcfg);
928

    
929
	/* connect and see if server is up */
930
	$error = false;
931
	if (!($ldap = ldap_connect($ldapserver))) {
932
		$error = true;
933
	}
934

    
935
	if ($error == true) {
936
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
937
		return false;
938
	}
939

    
940
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
941
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
942
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
943
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
944
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
945

    
946
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
947
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
948
	if ($ldapanon == true) {
949
		if (!($res = @ldap_bind($ldap))) {
950
			@ldap_close($ldap);
951
			return false;
952
		}
953
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
954
		@ldap_close($ldap);
955
		return false;
956
	}
957

    
958
	@ldap_unbind($ldap);
959

    
960
	return true;
961
}
962

    
963
function ldap_get_user_ous($show_complete_ou=true, $authcfg) {
964
	global $debug, $config, $g;
965

    
966
	if (!function_exists("ldap_connect")) {
967
		return;
968
	}
969

    
970
	$ous = array();
971

    
972
	if ($authcfg) {
973
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
974
			$ldapproto = "ldap";
975
		} else {
976
			$ldapproto = "ldaps";
977
		}
978
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
979
		$ldapport = $authcfg['ldap_port'];
980
		if (!empty($ldapport)) {
981
			$ldapserver .= ":{$ldapport}";
982
		}
983
		$ldapbasedn = $authcfg['ldap_basedn'];
984
		$ldapbindun = $authcfg['ldap_binddn'];
985
		$ldapbindpw = $authcfg['ldap_bindpw'];
986
		$ldapver = $authcfg['ldap_protver'];
987
		if (empty($ldapbindun) || empty($ldapbindpw)) {
988
			$ldapanon = true;
989
		} else {
990
			$ldapanon = false;
991
		}
992
		$ldapname = $authcfg['name'];
993
		$ldapfallback = false;
994
		$ldapscope = $authcfg['ldap_scope'];
995
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
996
	} else {
997
		return false;
998
	}
999

    
1000
	/* first check if there is even an LDAP server populated */
1001
	if (!$ldapserver) {
1002
		log_error(gettext("ERROR!  ldap_get_user_ous() backed selected with no LDAP authentication server defined."));
1003
		return $ous;
1004
	}
1005

    
1006
	/* Setup CA environment if needed. */
1007
	ldap_setup_caenv($authcfg);
1008

    
1009
	/* connect and see if server is up */
1010
	$error = false;
1011
	if (!($ldap = ldap_connect($ldapserver))) {
1012
		$error = true;
1013
	}
1014

    
1015
	if ($error == true) {
1016
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
1017
		return $ous;
1018
	}
1019

    
1020
	$ldapfilter = "(|(ou=*)(cn=Users))";
1021

    
1022
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1023
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1024
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1025
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1026
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1027

    
1028
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1029
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1030
	if ($ldapanon == true) {
1031
		if (!($res = @ldap_bind($ldap))) {
1032
			log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind anonymously to server %s."), $ldapname));
1033
			@ldap_close($ldap);
1034
			return $ous;
1035
		}
1036
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1037
		log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind to server %s."), $ldapname));
1038
		@ldap_close($ldap);
1039
		return $ous;
1040
	}
1041

    
1042
	if ($ldapscope == "one") {
1043
		$ldapfunc = "ldap_list";
1044
	} else {
1045
		$ldapfunc = "ldap_search";
1046
	}
1047

    
1048
	$search = @$ldapfunc($ldap, $ldapbasedn, $ldapfilter);
1049
	$info = @ldap_get_entries($ldap, $search);
1050

    
1051
	if (is_array($info)) {
1052
		foreach ($info as $inf) {
1053
			if (!$show_complete_ou) {
1054
				$inf_split = explode(",", $inf['dn']);
1055
				$ou = $inf_split[0];
1056
				$ou = str_replace("OU=", "", $ou);
1057
				$ou = str_replace("CN=", "", $ou);
1058
			} else {
1059
				if ($inf['dn']) {
1060
					$ou = $inf['dn'];
1061
				}
1062
			}
1063
			if ($ou) {
1064
				$ous[] = $ou;
1065
			}
1066
		}
1067
	}
1068

    
1069
	@ldap_unbind($ldap);
1070

    
1071
	return $ous;
1072
}
1073

    
1074
function ldap_get_groups($username, $authcfg) {
1075
	global $debug, $config;
1076

    
1077
	if (!function_exists("ldap_connect")) {
1078
		return;
1079
	}
1080

    
1081
	if (!$username) {
1082
		return false;
1083
	}
1084

    
1085
	if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) {
1086
		$username_split = explode("@", $username);
1087
		$username = $username_split[0];
1088
	}
1089

    
1090
	if (stristr($username, "\\")) {
1091
		$username_split = explode("\\", $username);
1092
		$username = $username_split[0];
1093
	}
1094

    
1095
	//log_error("Getting LDAP groups for {$username}.");
1096
	if ($authcfg) {
1097
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
1098
			$ldapproto = "ldap";
1099
		} else {
1100
			$ldapproto = "ldaps";
1101
		}
1102
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1103
		$ldapport = $authcfg['ldap_port'];
1104
		if (!empty($ldapport)) {
1105
			$ldapserver .= ":{$ldapport}";
1106
		}
1107
		$ldapbasedn = $authcfg['ldap_basedn'];
1108
		$ldapbindun = $authcfg['ldap_binddn'];
1109
		$ldapbindpw = $authcfg['ldap_bindpw'];
1110
		$ldapauthcont = $authcfg['ldap_authcn'];
1111
		$ldapnameattribute = strtolower($authcfg['ldap_attr_user']);
1112
		$ldapgroupattribute = strtolower($authcfg['ldap_attr_member']);
1113
		if (isset($authcfg['ldap_rfc2307'])) {
1114
			$ldapfilter         = "(&(objectClass={$authcfg['ldap_attr_groupobj']})({$ldapgroupattribute}={$username}))";
1115
		} else {
1116
			$ldapfilter         = "({$ldapnameattribute}={$username})";
1117
		}
1118
		$ldaptype = "";
1119
		$ldapver = $authcfg['ldap_protver'];
1120
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1121
			$ldapanon = true;
1122
		} else {
1123
			$ldapanon = false;
1124
		}
1125
		$ldapname = $authcfg['name'];
1126
		$ldapfallback = false;
1127
		$ldapscope = $authcfg['ldap_scope'];
1128
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
1129
	} else {
1130
		return false;
1131
	}
1132

    
1133
	if (isset($authcfg['ldap_rfc2307'])) {
1134
		$ldapdn = $ldapbasedn;
1135
	} else {
1136
		$ldapdn = $_SESSION['ldapdn'];
1137
	}
1138

    
1139
	/*Convert attribute to lowercase.  php ldap arrays put everything in lowercase */
1140
	$ldapgroupattribute = strtolower($ldapgroupattribute);
1141
	$memberof = array();
1142

    
1143
	/* Setup CA environment if needed. */
1144
	ldap_setup_caenv($authcfg);
1145

    
1146
	/* connect and see if server is up */
1147
	$error = false;
1148
	if (!($ldap = ldap_connect($ldapserver))) {
1149
		$error = true;
1150
	}
1151

    
1152
	if ($error == true) {
1153
		log_error(sprintf(gettext("ERROR! ldap_get_groups() Could not connect to server %s."), $ldapname));
1154
		return memberof;
1155
	}
1156

    
1157
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1158
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1159
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1160
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1161
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1162

    
1163
	/* bind as user that has rights to read group attributes */
1164
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1165
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1166
	if ($ldapanon == true) {
1167
		if (!($res = @ldap_bind($ldap))) {
1168
			log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind anonymously to server %s."), $ldapname));
1169
			@ldap_close($ldap);
1170
			return false;
1171
		}
1172
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1173
		log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind to server %s."), $ldapname));
1174
		@ldap_close($ldap);
1175
		return memberof;
1176
	}
1177

    
1178
	/* get groups from DN found */
1179
	/* use ldap_read instead of search so we don't have to do a bunch of extra work */
1180
	/* since we know the DN is in $_SESSION['ldapdn'] */
1181
	//$search    = ldap_read($ldap, $ldapdn, "(objectclass=*)", array($ldapgroupattribute));
1182
	if ($ldapscope == "one") {
1183
		$ldapfunc = "ldap_list";
1184
	} else {
1185
		$ldapfunc = "ldap_search";
1186
	}
1187

    
1188
	$search = @$ldapfunc($ldap, $ldapdn, $ldapfilter, array($ldapgroupattribute));
1189
	$info = @ldap_get_entries($ldap, $search);
1190

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

    
1193
	if (is_array($gresults)) {
1194
		/* Iterate through the groups and throw them into an array */
1195
		foreach ($gresults as $grp) {
1196
			if (((isset($authcfg['ldap_rfc2307'])) && (stristr($grp["dn"], "CN=") !== false)) ||
1197
			    ((!isset($authcfg['ldap_rfc2307'])) && (stristr($grp, "CN=") !== false))) {
1198
				$grpsplit = isset($authcfg['ldap_rfc2307']) ? explode(",", $grp["dn"]) : explode(",", $grp);
1199
				$memberof[] = preg_replace("/CN=/i", "", $grpsplit[0]);
1200
			}
1201
		}
1202
	}
1203

    
1204
	/* Time to close LDAP connection */
1205
	@ldap_unbind($ldap);
1206

    
1207
	$groups = print_r($memberof, true);
1208

    
1209
	//log_error("Returning groups ".$groups." for user $username");
1210

    
1211
	return $memberof;
1212
}
1213

    
1214
function ldap_format_host($host) {
1215
	return is_ipaddrv6($host) ? "[$host]" : $host ;
1216
}
1217

    
1218
function ldap_backed($username, $passwd, $authcfg) {
1219
	global $debug, $config;
1220

    
1221
	if (!$username) {
1222
		return;
1223
	}
1224

    
1225
	if (!function_exists("ldap_connect")) {
1226
		return;
1227
	}
1228

    
1229
	if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) {
1230
		$username_split = explode("@", $username);
1231
		$username = $username_split[0];
1232
	}
1233
	if (stristr($username, "\\")) {
1234
		$username_split = explode("\\", $username);
1235
		$username = $username_split[0];
1236
	}
1237

    
1238
	if ($authcfg) {
1239
		if (strstr($authcfg['ldap_urltype'], "Standard")) {
1240
			$ldapproto = "ldap";
1241
		} else {
1242
			$ldapproto = "ldaps";
1243
		}
1244
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1245
		$ldapport = $authcfg['ldap_port'];
1246
		if (!empty($ldapport)) {
1247
			$ldapserver .= ":{$ldapport}";
1248
		}
1249
		$ldapbasedn = $authcfg['ldap_basedn'];
1250
		$ldapbindun = $authcfg['ldap_binddn'];
1251
		$ldapbindpw = $authcfg['ldap_bindpw'];
1252
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1253
			$ldapanon = true;
1254
		} else {
1255
			$ldapanon = false;
1256
		}
1257
		$ldapauthcont = $authcfg['ldap_authcn'];
1258
		$ldapnameattribute = strtolower($authcfg['ldap_attr_user']);
1259
		$ldapextendedqueryenabled = $authcfg['ldap_extended_enabled'];
1260
		$ldapextendedquery = $authcfg['ldap_extended_query'];
1261
		$ldapfilter = "";
1262
		if (!$ldapextendedqueryenabled) {
1263
			$ldapfilter = "({$ldapnameattribute}={$username})";
1264
		} else {
1265
			$ldapfilter = "(&({$ldapnameattribute}={$username})({$ldapextendedquery}))";
1266
		}
1267
		$ldaptype = "";
1268
		$ldapver = $authcfg['ldap_protver'];
1269
		$ldapname = $authcfg['name'];
1270
		$ldapscope = $authcfg['ldap_scope'];
1271
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
1272
	} else {
1273
		return false;
1274
	}
1275

    
1276
	/* first check if there is even an LDAP server populated */
1277
	if (!$ldapserver) {
1278
		if ($ldapfallback) {
1279
			log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined.  Defaulting to local user database. Visit System -> User Manager."));
1280
			return local_backed($username, $passwd);
1281
		} else {
1282
			log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined."));
1283
		}
1284

    
1285
		return false;
1286
	}
1287

    
1288
	/* Setup CA environment if needed. */
1289
	ldap_setup_caenv($authcfg);
1290

    
1291
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1292
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1293
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1294
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1295
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1296

    
1297
	/* Make sure we can connect to LDAP */
1298
	$error = false;
1299
	if (!($ldap = ldap_connect($ldapserver))) {
1300
		$error = true;
1301
	}
1302

    
1303
	if ($error == true) {
1304
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
1305
		return false;
1306
	}
1307

    
1308
	/* ok, its up.  now, lets bind as the bind user so we can search it */
1309
	$error = false;
1310
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1311
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1312
	if ($ldapanon == true) {
1313
		if (!($res = @ldap_bind($ldap))) {
1314
			$error = true;
1315
		}
1316
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1317
		$error = true;
1318
	}
1319

    
1320
	if ($error == true) {
1321
		@ldap_close($ldap);
1322
		log_error(sprintf(gettext("ERROR! Could not bind to server %s."), $ldapname));
1323
		return false;
1324
	}
1325

    
1326
	/* Get LDAP Authcontainers and split em up. */
1327
	$ldac_splits = explode(";", $ldapauthcont);
1328

    
1329
	/* setup the usercount so we think we haven't found anyone yet */
1330
	$usercount = 0;
1331

    
1332
	/*****************************************************************/
1333
	/*  We first find the user based on username and filter          */
1334
	/*  then, once we find the first occurrence of that person       */
1335
	/*  we set session variables to point to the OU and DN of the    */
1336
	/*  person.  To later be used by ldap_get_groups.                */
1337
	/*  that way we don't have to search twice.                      */
1338
	/*****************************************************************/
1339
	if ($debug) {
1340
		log_auth(sprintf(gettext("Now Searching for %s in directory."), $username));
1341
	}
1342
	/* Iterate through the user containers for search */
1343
	foreach ($ldac_splits as $i => $ldac_split) {
1344
		$ldac_split = isset($authcfg['ldap_utf8']) ? utf8_encode($ldac_split) : $ldac_split;
1345
		$ldapfilter = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapfilter) : $ldapfilter;
1346
		$ldapsearchbasedn = isset($authcfg['ldap_utf8']) ? utf8_encode("{$ldac_split},{$ldapbasedn}") : "{$ldac_split},{$ldapbasedn}";
1347
		/* Make sure we just use the first user we find */
1348
		if ($debug) {
1349
			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)));
1350
		}
1351
		if ($ldapscope == "one") {
1352
			$ldapfunc = "ldap_list";
1353
		} else {
1354
			$ldapfunc = "ldap_search";
1355
		}
1356
		/* Support legacy auth container specification. */
1357
		if (stristr($ldac_split, "DC=") || empty($ldapbasedn)) {
1358
			$search = @$ldapfunc($ldap, $ldac_split, $ldapfilter);
1359
		} else {
1360
			$search = @$ldapfunc($ldap, $ldapsearchbasedn, $ldapfilter);
1361
		}
1362
		if (!$search) {
1363
			log_error(sprintf(gettext("Search resulted in error: %s"), ldap_error($ldap)));
1364
			continue;
1365
		}
1366
		$info = ldap_get_entries($ldap, $search);
1367
		$matches = $info['count'];
1368
		if ($matches == 1) {
1369
			$userdn = $_SESSION['ldapdn'] = $info[0]['dn'];
1370
			$_SESSION['ldapou'] = $ldac_split[$i];
1371
			$_SESSION['ldapon'] = "true";
1372
			$usercount = 1;
1373
			break;
1374
		}
1375
	}
1376

    
1377
	if ($usercount != 1) {
1378
		@ldap_unbind($ldap);
1379
		log_error(gettext("ERROR! Either LDAP search failed, or multiple users were found."));
1380
		return false;
1381
	}
1382

    
1383
	/* Now lets bind as the user we found */
1384
	$passwd = isset($authcfg['ldap_utf8']) ? utf8_encode($passwd) : $passwd;
1385
	if (!($res = @ldap_bind($ldap, $userdn, $passwd))) {
1386
		log_error(sprintf(gettext('ERROR! Could not login to server %1$s as user %2$s: %3$s'), $ldapname, $username, ldap_error($ldap)));
1387
		@ldap_unbind($ldap);
1388
		return false;
1389
	}
1390

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

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

    
1399
	return true;
1400
}
1401

    
1402
function radius_backed($username, $passwd, $authcfg, &$attributes = array()) {
1403
	global $debug, $config;
1404
	$ret = false;
1405

    
1406
	require_once("radius.inc");
1407

    
1408
	$rauth = new Auth_RADIUS_PAP($username, $passwd);
1409
	if ($authcfg) {
1410
		$radiusservers = array();
1411
		$radiusservers[0]['ipaddr'] = $authcfg['host'];
1412
		$radiusservers[0]['port'] = $authcfg['radius_auth_port'];
1413
		$radiusservers[0]['sharedsecret'] = $authcfg['radius_secret'];
1414
		$radiusservers[0]['timeout'] = $authcfg['radius_timeout'];
1415
	} else {
1416
		return false;
1417
	}
1418

    
1419
	/* Add new servers to our instance */
1420
	foreach ($radiusservers as $radsrv) {
1421
		$timeout = (is_numeric($radsrv['timeout'])) ? $radsrv['timeout'] : 5;
1422
		$rauth->addServer($radsrv['ipaddr'], $radsrv['port'], $radsrv['sharedsecret'], $timeout);
1423
	}
1424

    
1425
	if (PEAR::isError($rauth->start())) {
1426
		$retvalue['auth_val'] = 1;
1427
		$retvalue['error'] = $rauth->getError();
1428
		if ($debug) {
1429
			printf(gettext("RADIUS start: %s<br />\n"), $retvalue['error']);
1430
		}
1431
	}
1432

    
1433
	// XXX - billm - somewhere in here we need to handle securid challenge/response
1434

    
1435
	/* Send request */
1436
	$result = $rauth->send();
1437
	if (PEAR::isError($result)) {
1438
		$retvalue['auth_val'] = 1;
1439
		$retvalue['error'] = $result->getMessage();
1440
		if ($debug) {
1441
			printf(gettext("RADIUS send failed: %s<br />\n"), $retvalue['error']);
1442
		}
1443
	} else if ($result === true) {
1444
		if ($rauth->getAttributes()) {
1445
			$attributes = $rauth->listAttributes();
1446
		}
1447
		$retvalue['auth_val'] = 2;
1448
		if ($debug) {
1449
			printf(gettext("RADIUS Auth succeeded")."<br />\n");
1450
		}
1451
		$ret = true;
1452
	} else {
1453
		$retvalue['auth_val'] = 3;
1454
		if ($debug) {
1455
			printf(gettext("RADIUS Auth rejected")."<br />\n");
1456
		}
1457
	}
1458

    
1459
	// close OO RADIUS_AUTHENTICATION
1460
	$rauth->close();
1461

    
1462
	return $ret;
1463
}
1464

    
1465
/*
1466
	$attributes must contain a "class" key containing the groups and local
1467
	groups must exist to match.
1468
*/
1469
function radius_get_groups($attributes) {
1470
	$groups = array();
1471
	if (!empty($attributes) && is_array($attributes) && (!empty($attributes['class']) || !empty($attributes['class_int']))) {
1472
		/* Some RADIUS servers return multiple class attributes, so check them all. */
1473
		$groups = array();
1474
		if (!empty($attributes['class']) && is_array($attributes['class'])) {
1475
			foreach ($attributes['class'] as $class) {
1476
				$groups = array_unique(array_merge($groups, explode(";", $class)));
1477
			}
1478
		}
1479

    
1480
		foreach ($groups as & $grp) {
1481
			$grp = trim($grp);
1482
			if (strtolower(substr($grp, 0, 3)) == "ou=") {
1483
				$grp = substr($grp, 3);
1484
			}
1485
		}
1486
	}
1487
	return $groups;
1488
}
1489

    
1490
function get_user_expiration_date($username) {
1491
	$user = getUserEntry($username);
1492
	if ($user['expires']) {
1493
		return $user['expires'];
1494
	}
1495
}
1496

    
1497
function is_account_expired($username) {
1498
	$expirydate = get_user_expiration_date($username);
1499
	if ($expirydate) {
1500
		if (strtotime("-1 day") > strtotime(date("m/d/Y", strtotime($expirydate)))) {
1501
			return true;
1502
		}
1503
	}
1504

    
1505
	return false;
1506
}
1507

    
1508
function is_account_disabled($username) {
1509
	$user = getUserEntry($username);
1510
	if (isset($user['disabled'])) {
1511
		return true;
1512
	}
1513

    
1514
	return false;
1515
}
1516

    
1517
function auth_get_authserver($name) {
1518
	global $config;
1519

    
1520
	if (is_array($config['system']['authserver'])) {
1521
		foreach ($config['system']['authserver'] as $authcfg) {
1522
			if ($authcfg['name'] == $name) {
1523
				return $authcfg;
1524
			}
1525
		}
1526
	}
1527
	if ($name == "Local Database") {
1528
		return array("name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']);
1529
	}
1530
}
1531

    
1532
function auth_get_authserver_list() {
1533
	global $config;
1534

    
1535
	$list = array();
1536

    
1537
	if (is_array($config['system']['authserver'])) {
1538
		foreach ($config['system']['authserver'] as $authcfg) {
1539
			/* Add support for disabled entries? */
1540
			$list[$authcfg['name']] = $authcfg;
1541
		}
1542
	}
1543

    
1544
	$list["Local Database"] = array("name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']);
1545
	return $list;
1546
}
1547

    
1548
function getUserGroups($username, $authcfg, &$attributes = array()) {
1549
	global $config;
1550

    
1551
	$allowed_groups = array();
1552

    
1553
	switch ($authcfg['type']) {
1554
		case 'ldap':
1555
			$allowed_groups = @ldap_get_groups($username, $authcfg);
1556
			break;
1557
		case 'radius':
1558
			$allowed_groups = @radius_get_groups($attributes);
1559
			break;
1560
		default:
1561
			$user = getUserEntry($username);
1562
			$allowed_groups = @local_user_get_groups($user, true);
1563
			break;
1564
	}
1565

    
1566
	$member_groups = array();
1567
	if (is_array($config['system']['group'])) {
1568
		foreach ($config['system']['group'] as $group) {
1569
			if (in_array($group['name'], $allowed_groups)) {
1570
				$member_groups[] = $group['name'];
1571
			}
1572
		}
1573
	}
1574

    
1575
	return $member_groups;
1576
}
1577

    
1578
function authenticate_user($username, $password, $authcfg = NULL, &$attributes = array()) {
1579

    
1580
	if (is_array($username) || is_array($password)) {
1581
		return false;
1582
	}
1583

    
1584
	if (!$authcfg) {
1585
		return local_backed($username, $password);
1586
	}
1587

    
1588
	$authenticated = false;
1589
	switch ($authcfg['type']) {
1590
		case 'ldap':
1591
			if (ldap_backed($username, $password, $authcfg)) {
1592
				$authenticated = true;
1593
			}
1594
			break;
1595
		case 'radius':
1596
			if (radius_backed($username, $password, $authcfg, $attributes)) {
1597
				$authenticated = true;
1598
			}
1599
			break;
1600
		default:
1601
			/* lookup user object by name */
1602
			if (local_backed($username, $password)) {
1603
				$authenticated = true;
1604
			}
1605
			break;
1606
		}
1607

    
1608
	return $authenticated;
1609
}
1610

    
1611
function session_auth() {
1612
	global $config, $_SESSION, $page;
1613

    
1614
	// Handle HTTPS httponly and secure flags
1615
	$currentCookieParams = session_get_cookie_params();
1616
	session_set_cookie_params(
1617
		$currentCookieParams["lifetime"],
1618
		$currentCookieParams["path"],
1619
		NULL,
1620
		($config['system']['webgui']['protocol'] == "https"),
1621
		true
1622
	);
1623

    
1624
	if (!session_id()) {
1625
		session_start();
1626
	}
1627

    
1628
	// Detect protocol change
1629
	if (!isset($_POST['login']) && !empty($_SESSION['Logged_In']) && $_SESSION['protocol'] != $config['system']['webgui']['protocol']) {
1630
		return false;
1631
	}
1632

    
1633
	/* Validate incoming login request */
1634
	$attributes = array();
1635
	if (isset($_POST['login']) && !empty($_POST['usernamefld']) && !empty($_POST['passwordfld'])) {
1636
		$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
1637
		if (authenticate_user($_POST['usernamefld'], $_POST['passwordfld'], $authcfg, $attributes) ||
1638
		    authenticate_user($_POST['usernamefld'], $_POST['passwordfld'])) {
1639
			// Generate a new id to avoid session fixation
1640
			session_regenerate_id();
1641
			$_SESSION['Logged_In'] = "True";
1642
			$_SESSION['Username'] = $_POST['usernamefld'];
1643
			$_SESSION['user_radius_attributes'] = $attributes;
1644
			$_SESSION['last_access'] = time();
1645
			$_SESSION['protocol'] = $config['system']['webgui']['protocol'];
1646
			if (!isset($config['system']['webgui']['quietlogin'])) {
1647
				log_auth(sprintf(gettext("Successful login for user '%1\$s' from: %2\$s"), $_POST['usernamefld'], $_SERVER['REMOTE_ADDR']));
1648
			}
1649
			if (isset($_POST['postafterlogin'])) {
1650
				return true;
1651
			} else {
1652
				if (empty($page)) {
1653
					$page = "/";
1654
				}
1655
				header("Location: {$page}");
1656
			}
1657
			exit;
1658
		} else {
1659
			/* give the user an error message */
1660
			$_SESSION['Login_Error'] = "Username or Password incorrect";
1661
			log_auth("webConfigurator authentication error for '{$_POST['usernamefld']}' from {$_SERVER['REMOTE_ADDR']}");
1662
			if (isAjax()) {
1663
				echo "showajaxmessage('{$_SESSION['Login_Error']}');";
1664
				return;
1665
			}
1666
		}
1667
	}
1668

    
1669
	/* Show login page if they aren't logged in */
1670
	if (empty($_SESSION['Logged_In'])) {
1671
		return false;
1672
	}
1673

    
1674
	/* If session timeout isn't set, we don't mark sessions stale */
1675
	if (!isset($config['system']['webgui']['session_timeout'])) {
1676
		/* Default to 4 hour timeout if one is not set */
1677
		if ($_SESSION['last_access'] < (time() - 14400)) {
1678
			$_GET['logout'] = true;
1679
			$_SESSION['Logout'] = true;
1680
		} else {
1681
			$_SESSION['last_access'] = time();
1682
		}
1683
	} else if (intval($config['system']['webgui']['session_timeout']) == 0) {
1684
		/* only update if it wasn't ajax */
1685
		if (!isAjax()) {
1686
			$_SESSION['last_access'] = time();
1687
		}
1688
	} else {
1689
		/* Check for stale session */
1690
		if ($_SESSION['last_access'] < (time() - ($config['system']['webgui']['session_timeout'] * 60))) {
1691
			$_GET['logout'] = true;
1692
			$_SESSION['Logout'] = true;
1693
		} else {
1694
			/* only update if it wasn't ajax */
1695
			if (!isAjax()) {
1696
				$_SESSION['last_access'] = time();
1697
			}
1698
		}
1699
	}
1700

    
1701
	/* user hit the logout button */
1702
	if (isset($_GET['logout'])) {
1703

    
1704
		if ($_SESSION['Logout']) {
1705
			log_error(sprintf(gettext("Session timed out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR']));
1706
		} else {
1707
			log_error(sprintf(gettext("User logged out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR']));
1708
		}
1709

    
1710
		/* wipe out $_SESSION */
1711
		$_SESSION = array();
1712

    
1713
		if (isset($_COOKIE[session_name()])) {
1714
			setcookie(session_name(), '', time()-42000, '/');
1715
		}
1716

    
1717
		/* and destroy it */
1718
		session_destroy();
1719

    
1720
		$scriptName = explode("/", $_SERVER["SCRIPT_FILENAME"]);
1721
		$scriptElms = count($scriptName);
1722
		$scriptName = $scriptName[$scriptElms-1];
1723

    
1724
		if (isAjax()) {
1725
			return false;
1726
		}
1727

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

    
1731
		return false;
1732
	}
1733

    
1734
	/*
1735
	 * this is for debugging purpose if you do not want to use Ajax
1736
	 * to submit a HTML form. It basically disables the observation
1737
	 * of the submit event and hence does not trigger Ajax.
1738
	 */
1739
	if ($_GET['disable_ajax']) {
1740
		$_SESSION['NO_AJAX'] = "True";
1741
	}
1742

    
1743
	/*
1744
	 * Same to re-enable Ajax.
1745
	 */
1746
	if ($_GET['enable_ajax']) {
1747
		unset($_SESSION['NO_AJAX']);
1748
	}
1749

    
1750
	return true;
1751
}
1752

    
1753
?>
(4-4/65)