Project

General

Profile

Download (61.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * auth.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2003-2006 Manuel Kasper <mk@neon1.net>
7
 * Copyright (c) 2005-2006 Bill Marquette <bill.marquette@gmail.com>
8
 * Copyright (c) 2006 Paul Taylor <paultaylor@winn-dixie.com>
9
 * Copyright (c) 2004-2019 Rubicon Communications, LLC (Netgate)
10
 * All rights reserved.
11
 *
12
 * Licensed under the Apache License, Version 2.0 (the "License");
13
 * you may not use this file except in compliance with the License.
14
 * You may obtain a copy of the License at
15
 *
16
 * http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 * Unless required by applicable law or agreed to in writing, software
19
 * distributed under the License is distributed on an "AS IS" BASIS,
20
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 * See the License for the specific language governing permissions and
22
 * limitations under the License.
23
 */
24

    
25
/*
26
 * NOTE : Portions of the mschapv2 support was based on the BSD licensed CHAP.php
27
 * file courtesy of Michael Retterklieber.
28
 */
29
include_once('phpsessionmanager.inc');
30
if (!$do_not_include_config_gui_inc) {
31
	require_once("config.gui.inc");
32
}
33

    
34
// Will be changed to false if security checks fail
35
$security_passed = true;
36

    
37
/* If this function doesn't exist, we're being called from Captive Portal or
38
   another internal subsystem which does not include authgui.inc */
39
if (function_exists("display_error_form")) {
40
	/* Extra layer of lockout protection. Check if the user is in the GUI
41
	 * lockout table before processing a request */
42

    
43
	/* Fetch the contents of the lockout table. */
44
	$entries = array();
45
	exec("/sbin/pfctl -t 'sshguard' -T show", $entries);
46

    
47
	/* If the client is in the lockout table, print an error, kill states, and exit */
48
	if (in_array($_SERVER['REMOTE_ADDR'], array_map('trim', $entries))) {
49
		if (!security_checks_disabled()) {
50
			/* They may never see the error since the connection will be cut off, but try to be nice anyhow. */
51
			display_error_form("501", gettext("Access Denied<br/><br/>Access attempt from a temporarily locked out client address.<br /><br />Try accessing the firewall again after the lockout expires."));
52
			/* If they are locked out, they shouldn't have a state. Disconnect their connections. */
53
			$retval = pfSense_kill_states($_SERVER['REMOTE_ADDR']);
54
			if (is_ipaddrv4($_SERVER['REMOTE_ADDR'])) {
55
				$retval = pfSense_kill_states("0.0.0.0/0", $_SERVER['REMOTE_ADDR']);
56
			} elseif (is_ipaddrv6($_SERVER['REMOTE_ADDR'])) {
57
				$retval = pfSense_kill_states("::", $_SERVER['REMOTE_ADDR']);
58
			}
59
			exit;
60
		}
61
		$security_passed = false;
62
	}
63
}
64

    
65
if (function_exists("display_error_form") && !isset($config['system']['webgui']['nodnsrebindcheck'])) {
66
	/* DNS ReBinding attack prevention.  https://redmine.pfsense.org/issues/708 */
67
	$found_host = false;
68

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

    
92
	if (is_array($config['dyndnses']['dyndns']) && !$found_host) {
93
		foreach ($config['dyndnses']['dyndns'] as $dyndns) {
94
			if (strcasecmp($dyndns['host'], $http_host) == 0) {
95
				$found_host = true;
96
				break;
97
			}
98
		}
99
	}
100

    
101
	if (is_array($config['dnsupdates']['dnsupdate']) && !$found_host) {
102
		foreach ($config['dnsupdates']['dnsupdate'] as $rfc2136) {
103
			if (strcasecmp($rfc2136['host'], $http_host) == 0) {
104
				$found_host = true;
105
				break;
106
			}
107
		}
108
	}
109

    
110
	if (!empty($config['system']['webgui']['althostnames']) && !$found_host) {
111
		$althosts = explode(" ", $config['system']['webgui']['althostnames']);
112
		foreach ($althosts as $ah) {
113
			if (strcasecmp($ah, $http_host) == 0 or strcasecmp($ah, $_SERVER['SERVER_ADDR']) == 0) {
114
				$found_host = true;
115
				break;
116
			}
117
		}
118
	}
119

    
120
	if ($found_host == false) {
121
		if (!security_checks_disabled()) {
122
			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."));
123
			exit;
124
		}
125
		$security_passed = false;
126
	}
127
}
128

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

    
166
			if (!empty($config['system']['webgui']['althostnames']) && !$found_host) {
167
				$althosts = explode(" ", $config['system']['webgui']['althostnames']);
168
				foreach ($althosts as $ah) {
169
					if (strcasecmp($referrer_host, $ah) == 0) {
170
						$found_host = true;
171
						break;
172
					}
173
				}
174
			}
175

    
176
			if (is_array($config['dyndnses']['dyndns']) && !$found_host) {
177
				foreach ($config['dyndnses']['dyndns'] as $dyndns) {
178
					if (strcasecmp($dyndns['host'], $referrer_host) == 0) {
179
						$found_host = true;
180
						break;
181
					}
182
				}
183
			}
184

    
185
			if (is_array($config['dnsupdates']['dnsupdate']) && !$found_host) {
186
				foreach ($config['dnsupdates']['dnsupdate'] as $rfc2136) {
187
					if (strcasecmp($rfc2136['host'], $referrer_host) == 0) {
188
						$found_host = true;
189
						break;
190
					}
191
				}
192
			}
193

    
194
			if (!$found_host) {
195
				$interface_list_ips = get_configured_ip_addresses();
196
				foreach ($interface_list_ips as $ilips) {
197
					if (strcasecmp($referrer_host, $ilips) == 0) {
198
						$found_host = true;
199
						break;
200
					}
201
				}
202
				$interface_list_ipv6s = get_configured_ipv6_addresses(true);
203
				foreach ($interface_list_ipv6s as $ilipv6s) {
204
					$ilipv6s = explode('%', $ilipv6s)[0];
205
					if (strcasecmp($referrer_host, $ilipv6s) == 0) {
206
						$found_host = true;
207
						break;
208
					}
209
				}
210
				if ($referrer_host == "127.0.0.1" || $referrer_host == "localhost") {
211
					// allow SSH port forwarded connections and links from localhost
212
					$found_host = true;
213
				}
214
			}
215

    
216
			/* Fall back to probing active interface addresses rather than config.xml to allow
217
			 * changed addresses that have not yet been applied.
218
			 * See https://redmine.pfsense.org/issues/8822
219
			 */
220
			if (!$found_host) {
221
				$refifs = get_interface_arr();
222
				foreach ($refifs as $rif) {
223
					if (($referrer_host == find_interface_ip($rif)) ||
224
					    ($referrer_host == find_interface_ipv6($rif)) ||
225
					    ($referrer_host == find_interface_ipv6_ll($rif))) {
226
						$found_host = true;
227
						break;
228
					}
229
				}
230
			}
231
		}
232
		if ($found_host == false) {
233
			if (!security_checks_disabled()) {
234
				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.");
235
				exit;
236
			}
237
			$security_passed = false;
238
		}
239
	} else {
240
		$security_passed = false;
241
	}
242
}
243

    
244
if (function_exists("display_error_form") && $security_passed) {
245
	/* Security checks passed, so it should be OK to turn them back on */
246
	restore_security_checks();
247
}
248
unset($security_passed);
249

    
250
$groupindex = index_groups();
251
$userindex = index_users();
252

    
253
function index_groups() {
254
	global $g, $debug, $config, $groupindex;
255

    
256
	$groupindex = array();
257

    
258
	if (is_array($config['system']['group'])) {
259
		$i = 0;
260
		foreach ($config['system']['group'] as $groupent) {
261
			$groupindex[$groupent['name']] = $i;
262
			$i++;
263
		}
264
	}
265

    
266
	return ($groupindex);
267
}
268

    
269
function index_users() {
270
	global $g, $debug, $config;
271

    
272
	if (is_array($config['system']['user'])) {
273
		$i = 0;
274
		foreach ($config['system']['user'] as $userent) {
275
			$userindex[$userent['name']] = $i;
276
			$i++;
277
		}
278
	}
279

    
280
	return ($userindex);
281
}
282

    
283
function & getUserEntry($name) {
284
	global $debug, $config, $userindex;
285
	$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
286

    
287
	if (isset($userindex[$name])) {
288
		return $config['system']['user'][$userindex[$name]];
289
	} elseif ($authcfg['type'] != "Local Database") {
290
		$user = array();
291
		$user['name'] = $name;
292
		return $user;
293
	}
294
}
295

    
296
function & getUserEntryByUID($uid) {
297
	global $debug, $config;
298

    
299
	if (is_array($config['system']['user'])) {
300
		foreach ($config['system']['user'] as & $user) {
301
			if ($user['uid'] == $uid) {
302
				return $user;
303
			}
304
		}
305
	}
306

    
307
	return false;
308
}
309

    
310
function & getGroupEntry($name) {
311
	global $debug, $config, $groupindex;
312
	if (isset($groupindex[$name])) {
313
		return $config['system']['group'][$groupindex[$name]];
314
	}
315
}
316

    
317
function & getGroupEntryByGID($gid) {
318
	global $debug, $config;
319

    
320
	if (is_array($config['system']['group'])) {
321
		foreach ($config['system']['group'] as & $group) {
322
			if ($group['gid'] == $gid) {
323
				return $group;
324
			}
325
		}
326
	}
327

    
328
	return false;
329
}
330

    
331
function get_user_privileges(& $user) {
332
	global $config, $_SESSION;
333

    
334
	$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
335
	$allowed_groups = array();
336

    
337
	$privs = $user['priv'];
338
	if (!is_array($privs)) {
339
		$privs = array();
340
	}
341

    
342
	// cache auth results for a short time to ease load on auth services & logs
343
	if (isset($config['system']['webgui']['auth_refresh_time'])) {
344
		$recheck_time = $config['system']['webgui']['auth_refresh_time'];
345
	} else {
346
		$recheck_time = 30;
347
	}
348

    
349
	if ($authcfg['type'] == "ldap") {
350
		if (isset($_SESSION["ldap_allowed_groups"]) &&
351
		    (time() <= $_SESSION["auth_check_time"] + $recheck_time)) {
352
			$allowed_groups = $_SESSION["ldap_allowed_groups"];
353
		} else {
354
			$allowed_groups = @ldap_get_groups($user['name'], $authcfg);
355
			$_SESSION["ldap_allowed_groups"] = $allowed_groups;
356
			$_SESSION["auth_check_time"] = time();
357
		}
358
	} elseif ($authcfg['type'] == "radius") {
359
		if (isset($_SESSION["radius_allowed_groups"]) &&
360
		    (time() <= $_SESSION["auth_check_time"] + $recheck_time)) {
361
			$allowed_groups = $_SESSION["radius_allowed_groups"];
362
		} else {
363
			$allowed_groups = @radius_get_groups($_SESSION['user_radius_attributes']);
364
			$_SESSION["radius_allowed_groups"] = $allowed_groups;
365
			$_SESSION["auth_check_time"] = time();
366
		}
367
	}
368

    
369
	if (empty($allowed_groups)) {
370
		$allowed_groups = local_user_get_groups($user, true);
371
	}
372

    
373
	if (!is_array($allowed_groups)) {
374
		$allowed_groups = array('all');
375
	} else {
376
		$allowed_groups[] = 'all';
377
	}
378

    
379
	foreach ($allowed_groups as $name) {
380
		$group = getGroupEntry($name);
381
		if (is_array($group['priv'])) {
382
			$privs = array_merge($privs, $group['priv']);
383
		}
384
	}
385

    
386
	return $privs;
387
}
388

    
389
function userHasPrivilege($userent, $privid = false) {
390

    
391
	if (!$privid || !is_array($userent)) {
392
		return false;
393
	}
394

    
395
	$privs = get_user_privileges($userent);
396

    
397
	if (!is_array($privs)) {
398
		return false;
399
	}
400

    
401
	if (!in_array($privid, $privs)) {
402
		return false;
403
	}
404

    
405
	return true;
406
}
407

    
408
function local_backed($username, $passwd) {
409

    
410
	$user = getUserEntry($username);
411
	if (!$user) {
412
		return false;
413
	}
414

    
415
	if (is_account_disabled($username) || is_account_expired($username)) {
416
		return false;
417
	}
418

    
419
	if ($user['bcrypt-hash']) {
420
		if (password_verify($passwd, $user['bcrypt-hash'])) {
421
			return true;
422
		}
423
	}
424

    
425
	//for backwards compatibility
426
	if ($user['password']) {
427
		if (crypt($passwd, $user['password']) == $user['password']) {
428
			return true;
429
		}
430
	}
431

    
432
	if ($user['md5-hash']) {
433
		if (md5($passwd) == $user['md5-hash']) {
434
			return true;
435
		}
436
	}
437

    
438
	return false;
439
}
440

    
441
function local_sync_accounts($u2add, $u2del, $g2add, $g2del) {
442
	global $config, $debug;
443

    
444
	if (empty($u2add) && empty($u2del) && empty($g2add) && empty($g2del)) {
445
		/* Nothing to be done here */
446
		return;
447
	}
448

    
449
	foreach($u2del as $user) {
450
		if ($user['uid'] < 2000 || $user['uid'] > 65000) {
451
			continue;
452
		}
453

    
454
		/*
455
		 * If a crontab was created to user, pw userdel will be
456
		 * interactive and can cause issues. Just remove crontab
457
		 * before run it when necessary
458
		 */
459
		unlink_if_exists("/var/cron/tabs/{$user['name']}");
460
		$cmd = "/usr/sbin/pw userdel -n " .
461
		    escapeshellarg($user['name']);
462
		if ($debug) {
463
			log_error(sprintf(gettext("Running: %s"), $cmd));
464
		}
465
		mwexec($cmd);
466
		local_group_del_user($user);
467

    
468
		$system_user = $config['system']['user'];
469
		for ($i = 0; $i < count($system_user); $i++) {
470
			if ($system_user[$i]['name'] == $user['name']) {
471
				log_error("Removing user: {$user['name']}");
472
				unset($config['system']['user'][$i]);
473
				break;
474
			}
475
		}
476
	}
477

    
478
	foreach($g2del as $group) {
479
		if ($group['gid'] < 1999 || $group['gid'] > 65000) {
480
			continue;
481
		}
482

    
483
		$cmd = "/usr/sbin/pw groupdel -g " .
484
		    escapeshellarg($group['name']);
485
		if ($debug) {
486
			log_error(sprintf(gettext("Running: %s"), $cmd));
487
		}
488
		mwexec($cmd);
489

    
490
		$system_group = $config['system']['group'];
491
		for ($i = 0; $i < count($system_group); $i++) {
492
			if ($system_group[$i]['name'] == $group['name']) {
493
				log_error("Removing group: {$group['name']}");
494
				unset($config['system']['group'][$i]);
495
				break;
496
			}
497
		}
498
	}
499

    
500
	foreach ($u2add as $user) {
501
		log_error("Adding user: {$user['name']}");
502
		$config['system']['user'][] = $user;
503
	}
504

    
505
	foreach ($g2add as $group) {
506
		log_error("Adding group: {$group['name']}");
507
		$config['system']['group'][] = $group;
508
	}
509

    
510
	/* Sort it alphabetically */
511
	usort($config['system']['user'], function($a, $b) {
512
		return strcmp($a['name'], $b['name']);
513
	});
514
	usort($config['system']['group'], function($a, $b) {
515
		return strcmp($a['name'], $b['name']);
516
	});
517

    
518
	write_config("Sync'd users and groups via XMLRPC");
519

    
520
	/* make sure the all group exists */
521
	$allgrp = getGroupEntryByGID(1998);
522
	local_group_set($allgrp, true);
523

    
524
	foreach ($u2add as $user) {
525
		local_user_set($user);
526
	}
527

    
528
	foreach ($g2add as $group) {
529
		local_group_set($group);
530
	}
531
}
532

    
533
function local_reset_accounts() {
534
	global $debug, $config;
535

    
536
	/* remove local users to avoid uid conflicts */
537
	$fd = popen("/usr/sbin/pw usershow -a", "r");
538
	if ($fd) {
539
		while (!feof($fd)) {
540
			$line = explode(":", fgets($fd));
541
			if ($line[0] != "admin") {
542
				if (!strncmp($line[0], "_", 1)) {
543
					continue;
544
				}
545
				if ($line[2] < 2000) {
546
					continue;
547
				}
548
				if ($line[2] > 65000) {
549
					continue;
550
				}
551
			}
552
			/*
553
			 * If a crontab was created to user, pw userdel will be interactive and
554
			 * can cause issues. Just remove crontab before run it when necessary
555
			 */
556
			unlink_if_exists("/var/cron/tabs/{$line[0]}");
557
			$cmd = "/usr/sbin/pw userdel -n " . escapeshellarg($line[0]);
558
			if ($debug) {
559
				log_error(sprintf(gettext("Running: %s"), $cmd));
560
			}
561
			mwexec($cmd);
562
		}
563
		pclose($fd);
564
	}
565

    
566
	/* remove local groups to avoid gid conflicts */
567
	$gids = array();
568
	$fd = popen("/usr/sbin/pw groupshow -a", "r");
569
	if ($fd) {
570
		while (!feof($fd)) {
571
			$line = explode(":", fgets($fd));
572
			if (!strncmp($line[0], "_", 1)) {
573
				continue;
574
			}
575
			if ($line[2] < 2000) {
576
				continue;
577
			}
578
			if ($line[2] > 65000) {
579
				continue;
580
			}
581
			$cmd = "/usr/sbin/pw groupdel -g " . escapeshellarg($line[2]);
582
			if ($debug) {
583
				log_error(sprintf(gettext("Running: %s"), $cmd));
584
			}
585
			mwexec($cmd);
586
		}
587
		pclose($fd);
588
	}
589

    
590
	/* make sure the all group exists */
591
	$allgrp = getGroupEntryByGID(1998);
592
	local_group_set($allgrp, true);
593

    
594
	/* sync all local users */
595
	if (is_array($config['system']['user'])) {
596
		foreach ($config['system']['user'] as $user) {
597
			local_user_set($user);
598
		}
599
	}
600

    
601
	/* sync all local groups */
602
	if (is_array($config['system']['group'])) {
603
		foreach ($config['system']['group'] as $group) {
604
			local_group_set($group);
605
		}
606
	}
607
}
608

    
609
function local_user_set(& $user) {
610
	global $g, $debug;
611

    
612
	if (empty($user['password']) && empty($user['bcrypt-hash'])) {
613
		log_error("There is something wrong in the config because user {$user['name']} password is missing!");
614
		return;
615
	}
616

    
617

    
618
	$home_base = "/home/";
619
	$user_uid = $user['uid'];
620
	$user_name = $user['name'];
621
	$user_home = "{$home_base}{$user_name}";
622
	$user_shell = "/etc/rc.initial";
623
	$user_group = "nobody";
624

    
625
	// Ensure $home_base exists and is writable
626
	if (!is_dir($home_base)) {
627
		mkdir($home_base, 0755);
628
	}
629

    
630
	$lock_account = false;
631
	/* configure shell type */
632
	/* Cases here should be ordered by most privileged to least privileged. */
633
	if (userHasPrivilege($user, "user-shell-access") || userHasPrivilege($user, "page-all")) {
634
		$user_shell = "/bin/tcsh";
635
	} elseif (userHasPrivilege($user, "user-copy-files-chroot")) {
636
		$user_shell = "/usr/local/sbin/scponlyc";
637
	} elseif (userHasPrivilege($user, "user-copy-files")) {
638
		$user_shell = "/usr/local/bin/scponly";
639
	} elseif (userHasPrivilege($user, "user-ssh-tunnel")) {
640
		$user_shell = "/usr/local/sbin/ssh_tunnel_shell";
641
	} elseif (userHasPrivilege($user, "user-ipsec-xauth-dialin")) {
642
		$user_shell = "/sbin/nologin";
643
	} else {
644
		$user_shell = "/sbin/nologin";
645
		$lock_account = true;
646
	}
647

    
648
	/* Lock out disabled or expired users, unless it's root/admin. */
649
	if ((is_account_disabled($user_name) || is_account_expired($user_name)) && ($user_uid != 0)) {
650
		$user_shell = "/sbin/nologin";
651
		$lock_account = true;
652
	}
653

    
654
	/* root user special handling */
655
	if ($user_uid == 0) {
656
		$cmd = "/usr/sbin/pw usermod -q -n root -s /bin/sh -H 0";
657
		if ($debug) {
658
			log_error(sprintf(gettext("Running: %s"), $cmd));
659
		}
660
		$fd = popen($cmd, "w");
661
		if (empty($user['bcrypt-hash'])) {
662
			fwrite($fd, $user['password']);
663
		} else {
664
			fwrite($fd, $user['bcrypt-hash']);
665
		}
666
		pclose($fd);
667
		$user_group = "wheel";
668
		$user_home = "/root";
669
		$user_shell = "/etc/rc.initial";
670
	}
671

    
672
	/* read from pw db */
673
	$fd = popen("/usr/sbin/pw usershow -n {$user_name} 2>&1", "r");
674
	$pwread = fgets($fd);
675
	pclose($fd);
676
	$userattrs = explode(":", trim($pwread));
677

    
678
	$skel_dir = '/etc/skel';
679

    
680
	/* determine add or mod */
681
	if (($userattrs[0] != $user['name']) || (!strncmp($pwread, "pw:", 3))) {
682
		$user_op = "useradd -m -k " . escapeshellarg($skel_dir) . " -o";
683
	} else {
684
		$user_op = "usermod";
685
	}
686

    
687
	$comment = str_replace(array(":", "!", "@"), " ", $user['descr']);
688
	/* add or mod pw db */
689
	$cmd = "/usr/sbin/pw {$user_op} -q " .
690
			" -u " . escapeshellarg($user_uid) .
691
			" -n " . escapeshellarg($user_name) .
692
			" -g " . escapeshellarg($user_group) .
693
			" -s " . escapeshellarg($user_shell) .
694
			" -d " . escapeshellarg($user_home) .
695
			" -c " . escapeshellarg($comment) .
696
			" -H 0 2>&1";
697

    
698
	if ($debug) {
699
		log_error(sprintf(gettext("Running: %s"), $cmd));
700
	}
701
	$fd = popen($cmd, "w");
702
	if (empty($user['bcrypt-hash'])) {
703
		fwrite($fd, $user['password']);
704
	} else {
705
		fwrite($fd, $user['bcrypt-hash']);
706
	}
707
	pclose($fd);
708

    
709
	/* create user directory if required */
710
	if (!is_dir($user_home)) {
711
		mkdir($user_home, 0700);
712
	}
713
	@chown($user_home, $user_name);
714
	@chgrp($user_home, $user_group);
715

    
716
	/* Make sure all users have last version of config files */
717
	foreach (glob("{$skel_dir}/dot.*") as $dot_file) {
718
		$target = $user_home . '/' . substr(basename($dot_file), 3);
719
		@copy($dot_file, $target);
720
		@chown($target, $user_name);
721
		@chgrp($target, $user_group);
722
	}
723

    
724
	/* write out ssh authorized key file */
725
	if ($user['authorizedkeys']) {
726
		if (!is_dir("{$user_home}/.ssh")) {
727
			@mkdir("{$user_home}/.ssh", 0700);
728
			@chown("{$user_home}/.ssh", $user_name);
729
		}
730
		$keys = base64_decode($user['authorizedkeys']);
731
		@file_put_contents("{$user_home}/.ssh/authorized_keys", $keys);
732
		@chown("{$user_home}/.ssh/authorized_keys", $user_name);
733
	} else {
734
		unlink_if_exists("{$user_home}/.ssh/authorized_keys");
735
	}
736

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

    
740
}
741

    
742
function local_user_del($user) {
743
	global $debug;
744

    
745
	/* remove all memberships */
746
	local_user_set_groups($user);
747

    
748
	/* Don't remove /root */
749
	if ($user['uid'] != 0) {
750
		$rmhome = "-r";
751
	}
752

    
753
	/* read from pw db */
754
	$fd = popen("/usr/sbin/pw usershow -n {$user['name']} 2>&1", "r");
755
	$pwread = fgets($fd);
756
	pclose($fd);
757
	$userattrs = explode(":", trim($pwread));
758

    
759
	if ($userattrs[0] != $user['name']) {
760
		log_error("Tried to remove user {$user['name']} but got user {$userattrs[0]} instead. Bailing.");
761
		return;
762
	}
763

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

    
767
	if ($debug) {
768
		log_error(sprintf(gettext("Running: %s"), $cmd));
769
	}
770
	mwexec($cmd);
771

    
772
	/* Delete user from groups needs a call to write_config() */
773
	local_group_del_user($user);
774
}
775

    
776
function local_user_set_password(&$user, $password) {
777
	unset($user['password']);
778
	unset($user['md5-hash']);
779
	$user['bcrypt-hash'] = password_hash($password, PASSWORD_BCRYPT);
780
}
781

    
782
function local_user_get_groups($user, $all = false) {
783
	global $debug, $config;
784

    
785
	$groups = array();
786
	if (!is_array($config['system']['group'])) {
787
		return $groups;
788
	}
789

    
790
	foreach ($config['system']['group'] as $group) {
791
		if ($all || (!$all && ($group['name'] != "all"))) {
792
			if (is_array($group['member'])) {
793
				if (in_array($user['uid'], $group['member'])) {
794
					$groups[] = $group['name'];
795
				}
796
			}
797
		}
798
	}
799

    
800
	if ($all) {
801
		$groups[] = "all";
802
	}
803

    
804
	sort($groups);
805

    
806
	return $groups;
807

    
808
}
809

    
810
function local_user_set_groups($user, $new_groups = NULL) {
811
	global $debug, $config, $groupindex, $userindex;
812

    
813
	if (!is_array($config['system']['group'])) {
814
		return;
815
	}
816

    
817
	$cur_groups = local_user_get_groups($user, true);
818
	$mod_groups = array();
819

    
820
	if (!is_array($new_groups)) {
821
		$new_groups = array();
822
	}
823

    
824
	if (!is_array($cur_groups)) {
825
		$cur_groups = array();
826
	}
827

    
828
	/* determine which memberships to add */
829
	foreach ($new_groups as $groupname) {
830
		if ($groupname == '' || in_array($groupname, $cur_groups)) {
831
			continue;
832
		}
833
		$group = &$config['system']['group'][$groupindex[$groupname]];
834
		$group['member'][] = $user['uid'];
835
		$mod_groups[] = $group;
836

    
837
		/*
838
		 * If it's a new user, make sure it is added before try to
839
		 * add it as a member of a group
840
		 */
841
		if (!isset($userindex[$user['uid']])) {
842
			local_user_set($user);
843
		}
844
	}
845
	unset($group);
846

    
847
	/* determine which memberships to remove */
848
	foreach ($cur_groups as $groupname) {
849
		if (in_array($groupname, $new_groups)) {
850
			continue;
851
		}
852
		if (!isset($config['system']['group'][$groupindex[$groupname]])) {
853
			continue;
854
		}
855
		$group = &$config['system']['group'][$groupindex[$groupname]];
856
		if (is_array($group['member'])) {
857
			$index = array_search($user['uid'], $group['member']);
858
			array_splice($group['member'], $index, 1);
859
			$mod_groups[] = $group;
860
		}
861
	}
862
	unset($group);
863

    
864
	/* sync all modified groups */
865
	foreach ($mod_groups as $group) {
866
		local_group_set($group);
867
	}
868
}
869

    
870
function local_group_del_user($user) {
871
	global $config;
872

    
873
	if (!is_array($config['system']['group'])) {
874
		return;
875
	}
876

    
877
	foreach ($config['system']['group'] as $group) {
878
		if (is_array($group['member'])) {
879
			foreach ($group['member'] as $idx => $uid) {
880
				if ($user['uid'] == $uid) {
881
					unset($config['system']['group']['member'][$idx]);
882
				}
883
			}
884
		}
885
	}
886
}
887

    
888
function local_group_set($group, $reset = false) {
889
	global $debug;
890

    
891
	$group_name = $group['name'];
892
	$group_gid = $group['gid'];
893
	$group_members = '';
894

    
895
	if (!$reset && !empty($group['member']) && count($group['member']) > 0) {
896
		$group_members = implode(",", $group['member']);
897
	}
898

    
899
	if (empty($group_name)) {
900
		return;
901
	}
902

    
903
	// If the group is now remote, make sure there is no local group with the same name
904
	if ($group['scope'] == "remote") {
905
		local_group_del($group);
906
		return;
907
	}
908

    
909
	/* determine add or mod */
910
	if (mwexec("/usr/sbin/pw groupshow -g " . escapeshellarg($group_gid) . " 2>&1", true) == 0) {
911
		$group_op = "groupmod -l";
912
	} else {
913
		$group_op = "groupadd -n";
914
	}
915

    
916
	/* add or mod group db */
917
	$cmd = "/usr/sbin/pw {$group_op} " .
918
		escapeshellarg($group_name) .
919
		" -g " . escapeshellarg($group_gid) .
920
		" -M " . escapeshellarg($group_members) . " 2>&1";
921

    
922
	if ($debug) {
923
		log_error(sprintf(gettext("Running: %s"), $cmd));
924
	}
925

    
926
	mwexec($cmd);
927
}
928

    
929
function local_group_del($group) {
930
	global $debug;
931

    
932
	/* delete from group db */
933
	$cmd = "/usr/sbin/pw groupdel " . escapeshellarg($group['name']);
934

    
935
	if ($debug) {
936
		log_error(sprintf(gettext("Running: %s"), $cmd));
937
	}
938
	mwexec($cmd);
939
}
940

    
941
function ldap_test_connection($authcfg) {
942
	if ($authcfg) {
943
		if (strstr($authcfg['ldap_urltype'], "SSL")) {
944
			$ldapproto = "ldaps";
945
		} else {
946
			$ldapproto = "ldap";
947
		}
948
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
949
		$ldapport = $authcfg['ldap_port'];
950
		if (!empty($ldapport)) {
951
			$ldapserver .= ":{$ldapport}";
952
		}
953
	} else {
954
		return false;
955
	}
956

    
957
	/* first check if there is even an LDAP server populated */
958
	if (!$ldapserver) {
959
		return false;
960
	}
961

    
962
	/* connect and see if server is up */
963
	$error = false;
964
	if (!($ldap = ldap_connect($ldapserver))) {
965
		$error = true;
966
	}
967

    
968
	if ($error == true) {
969
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $authcfg['name']));
970
		return false;
971
	}
972

    
973
	/* Setup CA environment if needed. */
974
	ldap_setup_caenv($ldap, $authcfg);
975

    
976
	return true;
977
}
978

    
979
function ldap_setup_caenv($ldap, $authcfg) {
980
	global $g;
981
	require_once("certs.inc");
982

    
983
	unset($caref);
984
	if (empty($authcfg['ldap_caref']) || strstr($authcfg['ldap_urltype'], "Standard")) {
985
		ldap_set_option($ldap, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER);
986
		return;
987
	} elseif ($authcfg['ldap_caref'] == "global") {
988
		ldap_set_option($ldap, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_HARD);
989
		ldap_set_option($ldap, LDAP_OPT_X_TLS_CACERTDIR, "/etc/ssl/");
990
		ldap_set_option($ldap, LDAP_OPT_X_TLS_CACERTFILE, "/etc/ssl/cert.pem");
991
	} else {
992
		$caref = lookup_ca($authcfg['ldap_caref']);
993
		$param = array('caref' => $authcfg['ldap_caref']);
994
		$cachain = ca_chain($param);
995
		if (!$caref) {
996
			log_error(sprintf(gettext("LDAP: Could not lookup CA by reference for host %s."), $authcfg['ldap_caref']));
997
			/* XXX: Prevent for credential leaking since we cannot setup the CA env. Better way? */
998
			ldap_set_option($ldap, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_HARD);
999
			return;
1000
		}
1001

    
1002
		safe_mkdir($cert_path);
1003
		unlink_if_exists("{$cert_path}/{$caref['refid']}.ca");
1004
		file_put_contents("{$cert_path}/{$caref['refid']}.ca", $cachain);
1005
		@chmod("{$cert_path}/{$caref['refid']}.ca", 0600);
1006

    
1007
		ldap_set_option($ldap, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_HARD);
1008
		/* XXX: Probably even the hashed link should be created for this? */
1009
		ldap_set_option($ldap, LDAP_OPT_X_TLS_CACERTDIR, $cert_path);
1010
		ldap_set_option($ldap, LDAP_OPT_X_TLS_CACERTFILE, "{$cert_path}/{$caref['refid']}.ca");
1011
	}
1012
}
1013

    
1014
function ldap_test_bind($authcfg) {
1015
	global $debug, $config, $g;
1016

    
1017
	if ($authcfg) {
1018
		if (strstr($authcfg['ldap_urltype'], "SSL")) {
1019
			$ldapproto = "ldaps";
1020
		} else {
1021
			$ldapproto = "ldap";
1022
		}
1023
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1024
		$ldapport = $authcfg['ldap_port'];
1025
		if (!empty($ldapport)) {
1026
			$ldapserver .= ":{$ldapport}";
1027
		}
1028
		$ldapbasedn = $authcfg['ldap_basedn'];
1029
		$ldapbindun = $authcfg['ldap_binddn'];
1030
		$ldapbindpw = $authcfg['ldap_bindpw'];
1031
		$ldapver = $authcfg['ldap_protver'];
1032
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
1033
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1034
			$ldapanon = true;
1035
		} else {
1036
			$ldapanon = false;
1037
		}
1038
	} else {
1039
		return false;
1040
	}
1041

    
1042
	/* first check if there is even an LDAP server populated */
1043
	if (!$ldapserver) {
1044
		return false;
1045
	}
1046

    
1047
	/* connect and see if server is up */
1048
	$error = false;
1049
	if (!($ldap = ldap_connect($ldapserver))) {
1050
		$error = true;
1051
	}
1052

    
1053
	if ($error == true) {
1054
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
1055
		return false;
1056
	}
1057

    
1058
	/* Setup CA environment if needed. */
1059
	ldap_setup_caenv($ldap, $authcfg);
1060

    
1061
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1062
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1063
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1064
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1065
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1066

    
1067
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
1068
		if (!(@ldap_start_tls($ldap))) {
1069
			log_error(sprintf(gettext("ERROR! ldap_test_bind() could not STARTTLS to server %s."), $ldapname));
1070
			@ldap_close($ldap);
1071
			return false;
1072
		}
1073
	}
1074

    
1075
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1076
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1077
	if ($ldapanon == true) {
1078
		if (!($res = @ldap_bind($ldap))) {
1079
			@ldap_close($ldap);
1080
			return false;
1081
		}
1082
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1083
		@ldap_close($ldap);
1084
		return false;
1085
	}
1086

    
1087
	@ldap_unbind($ldap);
1088

    
1089
	return true;
1090
}
1091

    
1092
function ldap_get_user_ous($show_complete_ou=true, $authcfg) {
1093
	global $debug, $config, $g;
1094

    
1095
	if (!function_exists("ldap_connect")) {
1096
		return;
1097
	}
1098

    
1099
	$ous = array();
1100

    
1101
	if ($authcfg) {
1102
		if (strstr($authcfg['ldap_urltype'], "SSL")) {
1103
			$ldapproto = "ldaps";
1104
		} else {
1105
			$ldapproto = "ldap";
1106
		}
1107
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1108
		$ldapport = $authcfg['ldap_port'];
1109
		if (!empty($ldapport)) {
1110
			$ldapserver .= ":{$ldapport}";
1111
		}
1112
		$ldapbasedn = $authcfg['ldap_basedn'];
1113
		$ldapbindun = $authcfg['ldap_binddn'];
1114
		$ldapbindpw = $authcfg['ldap_bindpw'];
1115
		$ldapver = $authcfg['ldap_protver'];
1116
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1117
			$ldapanon = true;
1118
		} else {
1119
			$ldapanon = false;
1120
		}
1121
		$ldapname = $authcfg['name'];
1122
		$ldapfallback = false;
1123
		$ldapscope = $authcfg['ldap_scope'];
1124
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
1125
	} else {
1126
		return false;
1127
	}
1128

    
1129
	/* first check if there is even an LDAP server populated */
1130
	if (!$ldapserver) {
1131
		log_error(gettext("ERROR!  ldap_get_user_ous() backed selected with no LDAP authentication server defined."));
1132
		return $ous;
1133
	}
1134

    
1135
	/* connect and see if server is up */
1136
	$error = false;
1137
	if (!($ldap = ldap_connect($ldapserver))) {
1138
		$error = true;
1139
	}
1140

    
1141
	if ($error == true) {
1142
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
1143
		return $ous;
1144
	}
1145

    
1146
	/* Setup CA environment if needed. */
1147
	ldap_setup_caenv($ldap, $authcfg);
1148

    
1149
	$ldapfilter = "(|(ou=*)(cn=Users))";
1150

    
1151
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1152
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1153
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1154
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1155
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1156

    
1157
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
1158
		if (!(@ldap_start_tls($ldap))) {
1159
			log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not STARTTLS to server %s."), $ldapname));
1160
			@ldap_close($ldap);
1161
			return false;
1162
		}
1163
	}
1164

    
1165
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1166
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1167
	if ($ldapanon == true) {
1168
		if (!($res = @ldap_bind($ldap))) {
1169
			log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind anonymously to server %s."), $ldapname));
1170
			@ldap_close($ldap);
1171
			return $ous;
1172
		}
1173
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1174
		log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind to server %s."), $ldapname));
1175
		@ldap_close($ldap);
1176
		return $ous;
1177
	}
1178

    
1179
	if ($ldapscope == "one") {
1180
		$ldapfunc = "ldap_list";
1181
	} else {
1182
		$ldapfunc = "ldap_search";
1183
	}
1184

    
1185
	$search = @$ldapfunc($ldap, $ldapbasedn, $ldapfilter);
1186
	$info = @ldap_get_entries($ldap, $search);
1187

    
1188
	if (is_array($info)) {
1189
		foreach ($info as $inf) {
1190
			if (!$show_complete_ou) {
1191
				$inf_split = explode(",", $inf['dn']);
1192
				$ou = $inf_split[0];
1193
				$ou = str_replace("OU=", "", $ou);
1194
				$ou = str_replace("CN=", "", $ou);
1195
			} else {
1196
				if ($inf['dn']) {
1197
					$ou = $inf['dn'];
1198
				}
1199
			}
1200
			if ($ou) {
1201
				$ous[] = $ou;
1202
			}
1203
		}
1204
	}
1205

    
1206
	@ldap_unbind($ldap);
1207

    
1208
	return $ous;
1209
}
1210

    
1211
function ldap_get_groups($username, $authcfg) {
1212
	global $debug, $config;
1213

    
1214
	if (!function_exists("ldap_connect")) {
1215
		return;
1216
	}
1217

    
1218
	if (!$username) {
1219
		return false;
1220
	}
1221

    
1222
	if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) {
1223
		$username_split = explode("@", $username);
1224
		$username = $username_split[0];
1225
	}
1226

    
1227
	if (stristr($username, "\\")) {
1228
		$username_split = explode("\\", $username);
1229
		$username = $username_split[0];
1230
	}
1231

    
1232
	//log_error("Getting LDAP groups for {$username}.");
1233
	if ($authcfg) {
1234
		if (strstr($authcfg['ldap_urltype'], "SSL")) {
1235
			$ldapproto = "ldaps";
1236
		} else {
1237
			$ldapproto = "ldap";
1238
		}
1239
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1240
		$ldapport = $authcfg['ldap_port'];
1241
		if (!empty($ldapport)) {
1242
			$ldapserver .= ":{$ldapport}";
1243
		}
1244
		$ldapbasedn = $authcfg['ldap_basedn'];
1245
		$ldapbindun = $authcfg['ldap_binddn'];
1246
		$ldapbindpw = $authcfg['ldap_bindpw'];
1247
		$ldapauthcont = $authcfg['ldap_authcn'];
1248
		$ldapnameattribute = strtolower($authcfg['ldap_attr_user']);
1249
		$ldapgroupattribute = strtolower($authcfg['ldap_attr_member']);
1250
		if (isset($authcfg['ldap_rfc2307'])) {
1251
			$ldapfilter         = "(&(objectClass={$authcfg['ldap_attr_groupobj']})({$ldapgroupattribute}={$username}))";
1252
		} else {
1253
			$ldapfilter         = "({$ldapnameattribute}={$username})";
1254
		}
1255
		$ldaptype = "";
1256
		$ldapver = $authcfg['ldap_protver'];
1257
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1258
			$ldapanon = true;
1259
		} else {
1260
			$ldapanon = false;
1261
		}
1262
		$ldapname = $authcfg['name'];
1263
		$ldapfallback = false;
1264
		$ldapscope = $authcfg['ldap_scope'];
1265
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
1266
	} else {
1267
		return false;
1268
	}
1269

    
1270
	if (isset($authcfg['ldap_rfc2307'])) {
1271
		$ldapdn = $ldapbasedn;
1272
	} else {
1273
		$ldapdn = $_SESSION['ldapdn'];
1274
	}
1275

    
1276
	/*Convert attribute to lowercase.  php ldap arrays put everything in lowercase */
1277
	$ldapgroupattribute = strtolower($ldapgroupattribute);
1278
	$memberof = array();
1279

    
1280
	/* connect and see if server is up */
1281
	$error = false;
1282
	if (!($ldap = ldap_connect($ldapserver))) {
1283
		$error = true;
1284
	}
1285

    
1286
	if ($error == true) {
1287
		log_error(sprintf(gettext("ERROR! ldap_get_groups() Could not connect to server %s."), $ldapname));
1288
		return $memberof;
1289
	}
1290

    
1291
	/* Setup CA environment if needed. */
1292
	ldap_setup_caenv($ldap, $authcfg);
1293

    
1294
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1295
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1296
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1297
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1298
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1299

    
1300
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
1301
		if (!(@ldap_start_tls($ldap))) {
1302
			log_error(sprintf(gettext("ERROR! ldap_get_groups() could not STARTTLS to server %s."), $ldapname));
1303
			@ldap_close($ldap);
1304
			return false;
1305
		}
1306
	}
1307

    
1308
	/* bind as user that has rights to read group attributes */
1309
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1310
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1311
	if ($ldapanon == true) {
1312
		if (!($res = @ldap_bind($ldap))) {
1313
			log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind anonymously to server %s."), $ldapname));
1314
			@ldap_close($ldap);
1315
			return false;
1316
		}
1317
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1318
		log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind to server %s."), $ldapname));
1319
		@ldap_close($ldap);
1320
		return $memberof;
1321
	}
1322

    
1323
	/* get groups from DN found */
1324
	/* use ldap_read instead of search so we don't have to do a bunch of extra work */
1325
	/* since we know the DN is in $_SESSION['ldapdn'] */
1326
	//$search    = ldap_read($ldap, $ldapdn, "(objectclass=*)", array($ldapgroupattribute));
1327
	if ($ldapscope == "one") {
1328
		$ldapfunc = "ldap_list";
1329
	} else {
1330
		$ldapfunc = "ldap_search";
1331
	}
1332

    
1333
	$search = @$ldapfunc($ldap, $ldapdn, $ldapfilter, array($ldapgroupattribute));
1334
	$info = @ldap_get_entries($ldap, $search);
1335

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

    
1338
	if (is_array($gresults)) {
1339
		/* Iterate through the groups and throw them into an array */
1340
		foreach ($gresults as $grp) {
1341
			if (((isset($authcfg['ldap_rfc2307'])) && (stristr($grp["dn"], "CN=") !== false)) ||
1342
			    ((!isset($authcfg['ldap_rfc2307'])) && (stristr($grp, "CN=") !== false))) {
1343
				$grpsplit = isset($authcfg['ldap_rfc2307']) ? explode(",", $grp["dn"]) : explode(",", $grp);
1344
				$memberof[] = preg_replace("/CN=/i", "", $grpsplit[0]);
1345
			}
1346
		}
1347
	}
1348

    
1349
	/* Time to close LDAP connection */
1350
	@ldap_unbind($ldap);
1351

    
1352
	$groups = print_r($memberof, true);
1353

    
1354
	//log_error("Returning groups ".$groups." for user $username");
1355

    
1356
	return $memberof;
1357
}
1358

    
1359
function ldap_format_host($host) {
1360
	return is_ipaddrv6($host) ? "[$host]" : $host ;
1361
}
1362

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

    
1366
	if (!$username) {
1367
		$attributes['error_message'] = gettext("Invalid Login.");
1368
		return false;
1369
	}
1370

    
1371
	if (!function_exists("ldap_connect")) {
1372
		log_error(gettext("ERROR! unable to find ldap_connect() function."));
1373
		$attributes['error_message'] = gettext("Internal error during authentication.");
1374
		return null;
1375
	}
1376

    
1377
	if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) {
1378
		$username_split = explode("@", $username);
1379
		$username = $username_split[0];
1380
	}
1381
	if (stristr($username, "\\")) {
1382
		$username_split = explode("\\", $username);
1383
		$username = $username_split[0];
1384
	}
1385

    
1386
	$username = ldap_escape($username, null, LDAP_ESCAPE_FILTER);
1387

    
1388
	if ($authcfg) {
1389
		if (strstr($authcfg['ldap_urltype'], "SSL")) {
1390
			$ldapproto = "ldaps";
1391
		} else {
1392
			$ldapproto = "ldap";
1393
		}
1394
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1395
		$ldapport = $authcfg['ldap_port'];
1396
		if (!empty($ldapport)) {
1397
			$ldapserver .= ":{$ldapport}";
1398
		}
1399
		$ldapbasedn = $authcfg['ldap_basedn'];
1400
		$ldapbindun = $authcfg['ldap_binddn'];
1401
		$ldapbindpw = $authcfg['ldap_bindpw'];
1402
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1403
			$ldapanon = true;
1404
		} else {
1405
			$ldapanon = false;
1406
		}
1407
		$ldapauthcont = $authcfg['ldap_authcn'];
1408
		$ldapnameattribute = strtolower($authcfg['ldap_attr_user']);
1409
		$ldapextendedqueryenabled = $authcfg['ldap_extended_enabled'];
1410
		$ldapextendedquery = $authcfg['ldap_extended_query'];
1411
		$ldapfilter = "";
1412
		if (!$ldapextendedqueryenabled) {
1413
			$ldapfilter = "({$ldapnameattribute}={$username})";
1414
		} else {
1415
			$ldapfilter = "(&({$ldapnameattribute}={$username})({$ldapextendedquery}))";
1416
		}
1417
		$ldaptype = "";
1418
		$ldapver = $authcfg['ldap_protver'];
1419
		$ldapname = $authcfg['name'];
1420
		$ldapscope = $authcfg['ldap_scope'];
1421
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
1422
	} else {
1423
		return null;
1424
	}
1425

    
1426
	/* first check if there is even an LDAP server populated */
1427
	if (!$ldapserver) {
1428
		log_error(gettext("ERROR! could not find details of the LDAP server used for authentication."));
1429
		$attributes['error_message'] =  gettext("Internal error during authentication.");
1430
		return null;
1431
	}
1432

    
1433
	/* Make sure we can connect to LDAP */
1434
	$error = false;
1435
	if (!($ldap = ldap_connect($ldapserver))) {
1436
		$error = true;
1437
	}
1438

    
1439
	/* Setup CA environment if needed. */
1440
	ldap_setup_caenv($ldap, $authcfg);
1441

    
1442
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1443
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1444
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1445
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1446
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1447

    
1448
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
1449
		if (!(@ldap_start_tls($ldap))) {
1450
			log_error(sprintf(gettext("ERROR! could not connect to LDAP server %s using STARTTLS."), $ldapname));
1451
			$attributes['error_message'] = gettext("Error : could not connect to authentication server.");
1452
			@ldap_close($ldap);
1453
			return null;
1454
		}
1455
	}
1456

    
1457
	if ($error == true) {
1458
		$errormsg = sprintf(gettext("ERROR! Could not connect to server %s."), $ldapname);
1459
		$attributes['error_message'] = gettext("Error : could not connect to authentication server.");
1460
		return null;
1461
	}
1462

    
1463
	/* ok, its up.  now, lets bind as the bind user so we can search it */
1464
	$error = false;
1465
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1466
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1467
	if ($ldapanon == true) {
1468
		if (!($res = @ldap_bind($ldap))) {
1469
			$error = true;
1470
		}
1471
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1472
		$error = true;
1473
	}
1474

    
1475
	if ($error == true) {
1476
		@ldap_close($ldap);
1477
		log_error(sprintf(gettext("ERROR! Could not bind to LDAP server %s. Please check the bind credentials."), $ldapname));
1478
		$attributes['error_message'] = gettext("Error : could not connect to authentication server.");
1479
		return null;
1480
	}
1481

    
1482
	/* Get LDAP Authcontainers and split em up. */
1483
	$ldac_splits = explode(";", $ldapauthcont);
1484

    
1485
	/* setup the usercount so we think we haven't found anyone yet */
1486
	$usercount = 0;
1487

    
1488
	/*****************************************************************/
1489
	/*  We first find the user based on username and filter          */
1490
	/*  then, once we find the first occurrence of that person       */
1491
	/*  we set session variables to point to the OU and DN of the    */
1492
	/*  person.  To later be used by ldap_get_groups.                */
1493
	/*  that way we don't have to search twice.                      */
1494
	/*****************************************************************/
1495
	if ($debug) {
1496
		log_auth(sprintf(gettext("Now Searching for %s in directory."), $username));
1497
	}
1498
	/* Iterate through the user containers for search */
1499
	foreach ($ldac_splits as $i => $ldac_split) {
1500
		$ldac_split = isset($authcfg['ldap_utf8']) ? utf8_encode($ldac_split) : $ldac_split;
1501
		$ldapfilter = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapfilter) : $ldapfilter;
1502
		$ldapsearchbasedn = isset($authcfg['ldap_utf8']) ? utf8_encode("{$ldac_split},{$ldapbasedn}") : "{$ldac_split},{$ldapbasedn}";
1503
		/* Make sure we just use the first user we find */
1504
		if ($debug) {
1505
			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)));
1506
		}
1507
		if ($ldapscope == "one") {
1508
			$ldapfunc = "ldap_list";
1509
		} else {
1510
			$ldapfunc = "ldap_search";
1511
		}
1512
		/* Support legacy auth container specification. */
1513
		if (stristr($ldac_split, "DC=") || empty($ldapbasedn)) {
1514
			$search = @$ldapfunc($ldap, $ldac_split, $ldapfilter);
1515
		} else {
1516
			$search = @$ldapfunc($ldap, $ldapsearchbasedn, $ldapfilter);
1517
		}
1518
		if (!$search) {
1519
			log_error(sprintf(gettext("Search resulted in error: %s"), ldap_error($ldap)));
1520
			continue;
1521
		}
1522
		$info = ldap_get_entries($ldap, $search);
1523
		$matches = $info['count'];
1524
		if ($matches == 1) {
1525
			$userdn = $_SESSION['ldapdn'] = $info[0]['dn'];
1526
			$_SESSION['ldapou'] = $ldac_split[$i];
1527
			$_SESSION['ldapon'] = "true";
1528
			$usercount = 1;
1529
			break;
1530
		}
1531
	}
1532

    
1533
	if ($usercount != 1) {
1534
		@ldap_unbind($ldap);
1535
		if ($debug) {
1536
			if ($usercount === 0) {
1537
				log_error(sprintf(gettext("ERROR! LDAP search failed, no user matching %s was found."), $username));
1538
			} else {
1539
				log_error(sprintf(gettext("ERROR! LDAP search failed, multiple users matching %s were found."), $username));
1540
			}
1541
		}
1542
		$attributes['error_message'] = gettext("Invalid login specified.");
1543
		return false;
1544
	}
1545

    
1546
	/* Now lets bind as the user we found */
1547
	$passwd = isset($authcfg['ldap_utf8']) ? utf8_encode($passwd) : $passwd;
1548
	if (!($res = @ldap_bind($ldap, $userdn, $passwd))) {
1549
		if ($debug) {
1550
			log_error(sprintf(gettext('ERROR! Could not login to server %1$s as user %2$s: %3$s'), $ldapname, $username, ldap_error($ldap)));
1551
		}
1552
		@ldap_unbind($ldap);
1553
		return false;
1554
	}
1555

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

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

    
1564
	return true;
1565
}
1566

    
1567
function radius_backed($username, $password, $authcfg, &$attributes = array()) {
1568
	global $debug, $config;
1569
	$ret = false;
1570

    
1571
	require_once("Auth/RADIUS.php");
1572
	require_once("Crypt/CHAP.php");
1573

    
1574
	if ($authcfg) {
1575
		$radiusservers = array();
1576
		$radiusservers[0]['ipaddr'] = $authcfg['host'];
1577
		$radiusservers[0]['port'] = $authcfg['radius_auth_port'];
1578
		$radiusservers[0]['sharedsecret'] = $authcfg['radius_secret'];
1579
		$radiusservers[0]['timeout'] = $authcfg['radius_timeout'];
1580
		if(isset($authcfg['radius_protocol'])) {
1581
			$radius_protocol = $authcfg['radius_protocol'];
1582
		} else {
1583
			$radius_protocol = 'PAP';
1584
		}
1585
	} else {
1586
		log_error(gettext("ERROR! could not find details of the RADIUS server used for authentication."));
1587
		$attributes['error_message'] =  gettext("Internal error during authentication.");
1588
		return null;
1589
	}
1590

    
1591
	// Create our instance
1592
	$classname = 'Auth_RADIUS_' . $radius_protocol;
1593
	$rauth = new $classname($username, $password);
1594

    
1595
	/* Add new servers to our instance */
1596
	foreach ($radiusservers as $radsrv) {
1597
		$timeout = (is_numeric($radsrv['timeout'])) ? $radsrv['timeout'] : 5;
1598
		$rauth->addServer($radsrv['ipaddr'], $radsrv['port'], $radsrv['sharedsecret'], $timeout);
1599
	}
1600

    
1601
	// Construct data package
1602
	$rauth->username = $username;
1603
	switch ($radius_protocol) {
1604
		case 'CHAP_MD5':
1605
		case 'MSCHAPv1':
1606
			$classname = $radius_protocol == 'MSCHAPv1' ? 'Crypt_CHAP_MSv1' : 'Crypt_CHAP_MD5';
1607
			$crpt = new $classname;
1608
			$crpt->username = $username;
1609
			$crpt->password = $password;
1610
			$rauth->challenge = $crpt->challenge;
1611
			$rauth->chapid = $crpt->chapid;
1612
			$rauth->response = $crpt->challengeResponse();
1613
			$rauth->flags = 1;
1614
			break;
1615

    
1616
		case 'MSCHAPv2':
1617
			$crpt = new Crypt_CHAP_MSv2;
1618
			$crpt->username = $username;
1619
			$crpt->password = $password;
1620
			$rauth->challenge = $crpt->authChallenge;
1621
			$rauth->peerChallenge = $crpt->peerChallenge;
1622
			$rauth->chapid = $crpt->chapid;
1623
			$rauth->response = $crpt->challengeResponse();
1624
			break;
1625

    
1626
		default:
1627
			$rauth->password = $password;
1628
			break;
1629
	}
1630

    
1631
	if (PEAR::isError($rauth->start())) {
1632
		$ret = null;
1633
		log_error(sprintf(gettext("Error during RADIUS authentication : %s"), $rauth->getError()));
1634
		$attributes['error_message'] = gettext("Error : could not connect to authentication server.");
1635
	} else {
1636
		$nasid = $attributes['nas_identifier'];
1637
		$nasip = $authcfg['radius_nasip_attribute'];
1638
		if (empty($nasid)) {
1639
			$nasid = gethostname(); //If no RADIUS NAS-Identifier is given : we use pfsense's hostname as NAS-Identifier
1640
		}
1641
		if (!is_ipaddr($nasip)) {
1642
			$nasip = get_interface_ip($nasip);
1643
			
1644
			if (!is_ipaddr($nasip)) {
1645
				$nasip = get_interface_ip();//We use wan interface IP as fallback for NAS-IP-Address
1646
			}
1647
		}
1648
		$nasmac = get_interface_mac(find_ip_interface($nasip));
1649

    
1650
		$rauth->putAttribute(RADIUS_NAS_IP_ADDRESS, $nasip, "addr");
1651
		$rauth->putAttribute(RADIUS_NAS_IDENTIFIER, $nasid);
1652
		
1653
		if(!empty($attributes['calling_station_id'])) {
1654
			$rauth->putAttribute(RADIUS_CALLING_STATION_ID, $attributes['calling_station_id']);
1655
		}
1656
		// Carefully check that interface has a MAC address
1657
		if(!empty($nasmac)) {
1658
			$nasmac = mac_format($nasmac);
1659
			$rauth->putAttribute(RADIUS_CALLED_STATION_ID, $nasmac.':'.gethostname());
1660
		}
1661
		if(!empty($attributes['nas_port_type'])) {
1662
			$rauth->putAttribute(RADIUS_NAS_PORT_TYPE, $attributes['nas_port_type']);
1663
		}		
1664
		if(!empty($attributes['nas_port'])) {
1665
			$rauth->putAttribute(RADIUS_NAS_PORT, intval($attributes['nas_port']), 'integer');
1666
		}
1667
		if(!empty($attributes['framed_ip']) && is_ipaddr($attributes['framed_ip'])) {
1668
			$rauth->putAttribute(RADIUS_FRAMED_IP_ADDRESS, $attributes['framed_ip'], "addr");
1669
		}
1670
	}
1671

    
1672
	// XXX - billm - somewhere in here we need to handle securid challenge/response
1673

    
1674
	/* Send request */
1675
	$result = $rauth->send();
1676
	if (PEAR::isError($result)) {
1677
		log_error(sprintf(gettext("Error during RADIUS authentication : %s"), $rauth->getError()));
1678
		$attributes['error_message'] = gettext("Error : could not connect to authentication server.");
1679
		$ret = null;
1680
	} else if ($result === true) {
1681
		$ret = true;
1682
	} else {
1683
		$ret = false;
1684
	}
1685

    
1686
	
1687
	// Get attributes, even if auth failed.
1688
	if ($rauth->getAttributes()) {
1689
	$attributes = array_merge($attributes,$rauth->listAttributes());
1690

    
1691
	// We convert the session_terminate_time to unixtimestamp if its set before returning the whole array to our caller
1692
	if (!empty($attributes['session_terminate_time'])) {
1693
			$stt = &$attributes['session_terminate_time'];
1694
			$stt = strtotime(preg_replace("/\+(\d+):(\d+)$/", " +\${1}\${2}", preg_replace("/(\d+)T(\d+)/", "\${1} \${2}",$stt)));
1695
		}
1696
	}
1697
	
1698
	// close OO RADIUS_AUTHENTICATION
1699
	$rauth->close();
1700

    
1701
	return $ret;
1702
}
1703

    
1704
/*
1705
	$attributes must contain a "class" key containing the groups and local
1706
	groups must exist to match.
1707
*/
1708
function radius_get_groups($attributes) {
1709
	$groups = array();
1710
	if (!empty($attributes) && is_array($attributes) && (!empty($attributes['class']) || !empty($attributes['class_int']))) {
1711
		/* Some RADIUS servers return multiple class attributes, so check them all. */
1712
		$groups = array();
1713
		if (!empty($attributes['class']) && is_array($attributes['class'])) {
1714
			foreach ($attributes['class'] as $class) {
1715
				$groups = array_unique(array_merge($groups, explode(";", $class)));
1716
			}
1717
		}
1718

    
1719
		foreach ($groups as & $grp) {
1720
			$grp = trim($grp);
1721
			if (strtolower(substr($grp, 0, 3)) == "ou=") {
1722
				$grp = substr($grp, 3);
1723
			}
1724
		}
1725
	}
1726
	return $groups;
1727
}
1728

    
1729
function get_user_expiration_date($username) {
1730
	$user = getUserEntry($username);
1731
	if ($user['expires']) {
1732
		return $user['expires'];
1733
	}
1734
}
1735

    
1736
function is_account_expired($username) {
1737
	$expirydate = get_user_expiration_date($username);
1738
	if ($expirydate) {
1739
		if (strtotime("-1 day") > strtotime(date("m/d/Y", strtotime($expirydate)))) {
1740
			return true;
1741
		}
1742
	}
1743

    
1744
	return false;
1745
}
1746

    
1747
function is_account_disabled($username) {
1748
	$user = getUserEntry($username);
1749
	if (isset($user['disabled'])) {
1750
		return true;
1751
	}
1752

    
1753
	return false;
1754
}
1755

    
1756
function get_user_settings($username) {
1757
	global $config;
1758
	$settings = array();
1759
	$settings['widgets'] = $config['widgets'];
1760
	$settings['webgui']['dashboardcolumns'] = $config['system']['webgui']['dashboardcolumns'];
1761
	$settings['webgui']['webguihostnamemenu'] = $config['system']['webgui']['webguihostnamemenu'];
1762
	$settings['webgui']['webguicss'] = $config['system']['webgui']['webguicss'];
1763
	$settings['webgui']['logincss'] = $config['system']['webgui']['logincss'];
1764
	$settings['webgui']['interfacessort'] = isset($config['system']['webgui']['interfacessort']);
1765
	$settings['webgui']['dashboardavailablewidgetspanel'] = isset($config['system']['webgui']['dashboardavailablewidgetspanel']);
1766
	$settings['webgui']['webguifixedmenu'] = isset($config['system']['webgui']['webguifixedmenu']);
1767
	$settings['webgui']['webguileftcolumnhyper'] = isset($config['system']['webgui']['webguileftcolumnhyper']);
1768
	$settings['webgui']['disablealiaspopupdetail'] = isset($config['system']['webgui']['disablealiaspopupdetail']);
1769
	$settings['webgui']['systemlogsfilterpanel'] = isset($config['system']['webgui']['systemlogsfilterpanel']);
1770
	$settings['webgui']['systemlogsmanagelogpanel'] = isset($config['system']['webgui']['systemlogsmanagelogpanel']);
1771
	$settings['webgui']['statusmonitoringsettingspanel'] = isset($config['system']['webgui']['statusmonitoringsettingspanel']);
1772
	$settings['webgui']['pagenamefirst'] = isset($config['system']['webgui']['pagenamefirst']);
1773
	$user = getUserEntry($username);
1774
	if (isset($user['customsettings'])) {
1775
		$settings['customsettings'] = true;
1776
		if (isset($user['widgets'])) {
1777
			// This includes the 'sequence', and any widgetname-config per-widget settings.
1778
			$settings['widgets'] = $user['widgets'];
1779
		}
1780
		if (isset($user['dashboardcolumns'])) {
1781
			$settings['webgui']['dashboardcolumns'] = $user['dashboardcolumns'];
1782
		}
1783
		if (isset($user['webguicss'])) {
1784
			$settings['webgui']['webguicss'] = $user['webguicss'];
1785
		}
1786
		if (isset($user['webguihostnamemenu'])) {
1787
			$settings['webgui']['webguihostnamemenu'] = $user['webguihostnamemenu'];
1788
		}
1789
		$settings['webgui']['interfacessort'] = isset($user['interfacessort']);
1790
		$settings['webgui']['dashboardavailablewidgetspanel'] = isset($user['dashboardavailablewidgetspanel']);
1791
		$settings['webgui']['webguifixedmenu'] = isset($user['webguifixedmenu']);
1792
		$settings['webgui']['webguileftcolumnhyper'] = isset($user['webguileftcolumnhyper']);
1793
		$settings['webgui']['disablealiaspopupdetail'] = isset($user['disablealiaspopupdetail']);
1794
		$settings['webgui']['systemlogsfilterpanel'] = isset($user['systemlogsfilterpanel']);
1795
		$settings['webgui']['systemlogsmanagelogpanel'] = isset($user['systemlogsmanagelogpanel']);
1796
		$settings['webgui']['statusmonitoringsettingspanel'] = isset($user['statusmonitoringsettingspanel']);
1797
		$settings['webgui']['pagenamefirst'] = isset($user['pagenamefirst']);
1798
	} else {
1799
		$settings['customsettings'] = false;
1800
	}
1801

    
1802
	if ($settings['webgui']['dashboardcolumns'] < 1) {
1803
		$settings['webgui']['dashboardcolumns'] = 2;
1804
	}
1805

    
1806
	return $settings;
1807
}
1808

    
1809
function save_widget_settings($username, $settings, $message = "") {
1810
	global $config, $userindex;
1811
	$user = getUserEntry($username);
1812

    
1813
	if (strlen($message) > 0) {
1814
		$msgout = $message;
1815
	} else {
1816
		$msgout = gettext("Widget configuration has been changed.");
1817
	}
1818

    
1819
	if (isset($user['customsettings'])) {
1820
		$config['system']['user'][$userindex[$username]]['widgets'] = $settings;
1821
		write_config($msgout . " " . sprintf(gettext("(User %s)"), $username));
1822
	} else {
1823
		$config['widgets'] = $settings;
1824
		write_config($msgout);
1825
	}
1826
}
1827

    
1828
function auth_get_authserver($name) {
1829
	global $config;
1830

    
1831
	if (is_array($config['system']['authserver'])) {
1832
		foreach ($config['system']['authserver'] as $authcfg) {
1833
			if ($authcfg['name'] == $name) {
1834
				return $authcfg;
1835
			}
1836
		}
1837
	}
1838
	if ($name == "Local Database") {
1839
		return array("name" => "Local Database", "type" => "Local Auth", "host" => $config['system']['hostname']);
1840
	}
1841
}
1842

    
1843
function auth_get_authserver_list() {
1844
	global $config;
1845

    
1846
	$list = array();
1847

    
1848
	if (is_array($config['system']['authserver'])) {
1849
		foreach ($config['system']['authserver'] as $authcfg) {
1850
			/* Add support for disabled entries? */
1851
			$list[$authcfg['name']] = $authcfg;
1852
		}
1853
	}
1854

    
1855
	$list["Local Database"] = array("name" => "Local Database", "type" => "Local Auth", "host" => $config['system']['hostname']);
1856
	return $list;
1857
}
1858

    
1859
function getUserGroups($username, $authcfg, &$attributes = array()) {
1860
	global $config;
1861

    
1862
	$allowed_groups = array();
1863

    
1864
	switch ($authcfg['type']) {
1865
		case 'ldap':
1866
			$allowed_groups = @ldap_get_groups($username, $authcfg);
1867
			break;
1868
		case 'radius':
1869
			$allowed_groups = @radius_get_groups($attributes);
1870
			break;
1871
		default:
1872
			$user = getUserEntry($username);
1873
			$allowed_groups = @local_user_get_groups($user, true);
1874
			break;
1875
	}
1876

    
1877
	$member_groups = array();
1878
	if (is_array($config['system']['group'])) {
1879
		foreach ($config['system']['group'] as $group) {
1880
			if (in_array($group['name'], $allowed_groups)) {
1881
				$member_groups[] = $group['name'];
1882
			}
1883
		}
1884
	}
1885

    
1886
	return $member_groups;
1887
}
1888

    
1889
/*
1890
Possible return values : 
1891
true : authentication worked
1892
false : authentication failed (invalid login/password, not enought permission, etc...)
1893
null : error during authentication process (unable to reach remote server, etc...)
1894
*/
1895
function authenticate_user($username, $password, $authcfg = NULL, &$attributes = array()) {
1896

    
1897
	if (is_array($username) || is_array($password)) {
1898
		return false;
1899
	}
1900

    
1901
	if (!$authcfg) {
1902
		return local_backed($username, $password, $attributes);
1903
	}
1904

    
1905
	$authenticated = false;
1906
	switch ($authcfg['type']) {
1907
		case 'ldap':
1908
			$authenticated = ldap_backed($username, $password, $authcfg, $attributes);
1909
			break;
1910
		case 'radius':
1911
			$authenticated = radius_backed($username, $password, $authcfg, $attributes);
1912
			break;
1913
		default:
1914
			/* lookup user object by name */
1915
			$authenticated = local_backed($username, $password, $attributes);
1916
			break;
1917
		}
1918

    
1919
	return $authenticated;
1920
}
1921

    
1922
function session_auth() {
1923
	global $config, $_SESSION, $page;
1924

    
1925
	// Handle HTTPS httponly and secure flags
1926
	$currentCookieParams = session_get_cookie_params();
1927
	session_set_cookie_params(
1928
		$currentCookieParams["lifetime"],
1929
		$currentCookieParams["path"],
1930
		NULL,
1931
		($config['system']['webgui']['protocol'] == "https"),
1932
		true
1933
	);
1934

    
1935
	phpsession_begin();
1936

    
1937
	// Detect protocol change
1938
	if (!isset($_POST['login']) && !empty($_SESSION['Logged_In']) && $_SESSION['protocol'] != $config['system']['webgui']['protocol']) {
1939
		phpsession_end();
1940
		return false;
1941
	}
1942

    
1943
	/* Validate incoming login request */
1944
	$attributes = array();
1945
	if (isset($_POST['login']) && !empty($_POST['usernamefld']) && !empty($_POST['passwordfld'])) {
1946
		$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
1947
		$remoteauth = authenticate_user($_POST['usernamefld'], $_POST['passwordfld'], $authcfg, $attributes);
1948
		if ($remoteauth || authenticate_user($_POST['usernamefld'], $_POST['passwordfld'])) {
1949
			// Generate a new id to avoid session fixation
1950
			session_regenerate_id();
1951
			$_SESSION['Logged_In'] = "True";
1952
			$_SESSION['remoteauth'] = $remoteauth;
1953
			if ($remoteauth) {
1954
				if (empty($authcfg['type']) || ($authcfg['type'] == "Local Auth")) {
1955
					$_SESSION['authsource'] = "Local Database";
1956
				} else {
1957
					$_SESSION['authsource'] = strtoupper($authcfg['type']) . "/{$authcfg['name']}";
1958
				}
1959
			} else {
1960
				$_SESSION['authsource'] = 'Local Database Fallback';
1961
			}
1962
			$_SESSION['Username'] = $_POST['usernamefld'];
1963
			$_SESSION['user_radius_attributes'] = $attributes;
1964
			$_SESSION['last_access'] = time();
1965
			$_SESSION['protocol'] = $config['system']['webgui']['protocol'];
1966
			phpsession_end(true);
1967
			if (!isset($config['system']['webgui']['quietlogin'])) {
1968
				log_auth(sprintf(gettext("Successful login for user '%1\$s' from: %2\$s"), $_POST['usernamefld'], get_user_remote_address() . get_user_remote_authsource()));
1969
			}
1970
			if (isset($_POST['postafterlogin'])) {
1971
				return true;
1972
			} else {
1973
				if (empty($page)) {
1974
					$page = "/";
1975
				}
1976
				header("Location: {$page}");
1977
			}
1978
			exit;
1979
		} else {
1980
			/* give the user an error message */
1981
			$_SESSION['Login_Error'] = gettext("Username or Password incorrect");
1982
			log_auth(sprintf(gettext("webConfigurator authentication error for user '%1\$s' from: %2\$s"), $_POST['usernamefld'], get_user_remote_address() . get_user_remote_authsource()));
1983
			if (isAjax()) {
1984
				echo "showajaxmessage('{$_SESSION['Login_Error']}');";
1985
				return;
1986
			}
1987
		}
1988
	}
1989

    
1990
	/* Show login page if they aren't logged in */
1991
	if (empty($_SESSION['Logged_In'])) {
1992
		phpsession_end(true);
1993
		return false;
1994
	}
1995

    
1996
	/* If session timeout isn't set, we don't mark sessions stale */
1997
	if (!isset($config['system']['webgui']['session_timeout'])) {
1998
		/* Default to 4 hour timeout if one is not set */
1999
		if ($_SESSION['last_access'] < (time() - 14400)) {
2000
			$_POST['logout'] = true;
2001
			$_SESSION['Logout'] = true;
2002
		} else {
2003
			$_SESSION['last_access'] = time();
2004
		}
2005
	} else if (intval($config['system']['webgui']['session_timeout']) == 0) {
2006
		/* only update if it wasn't ajax */
2007
		if (!isAjax()) {
2008
			$_SESSION['last_access'] = time();
2009
		}
2010
	} else {
2011
		/* Check for stale session */
2012
		if ($_SESSION['last_access'] < (time() - ($config['system']['webgui']['session_timeout'] * 60))) {
2013
			$_POST['logout'] = true;
2014
			$_SESSION['Logout'] = true;
2015
		} else {
2016
			/* only update if it wasn't ajax */
2017
			if (!isAjax()) {
2018
				$_SESSION['last_access'] = time();
2019
			}
2020
		}
2021
	}
2022

    
2023
	/* user hit the logout button */
2024
	if (isset($_POST['logout'])) {
2025

    
2026
		if ($_SESSION['Logout']) {
2027
			log_error(sprintf(gettext("Session timed out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], get_user_remote_address() . get_user_remote_authsource()));
2028
		} else {
2029
			log_error(sprintf(gettext("User logged out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], get_user_remote_address() . get_user_remote_authsource()));
2030
		}
2031

    
2032
		/* wipe out $_SESSION */
2033
		$_SESSION = array();
2034

    
2035
		if (isset($_COOKIE[session_name()])) {
2036
			setcookie(session_name(), '', time()-42000, '/');
2037
		}
2038

    
2039
		/* and destroy it */
2040
		phpsession_destroy();
2041

    
2042
		$scriptName = explode("/", $_SERVER["SCRIPT_FILENAME"]);
2043
		$scriptElms = count($scriptName);
2044
		$scriptName = $scriptName[$scriptElms-1];
2045

    
2046
		if (isAjax()) {
2047
			return false;
2048
		}
2049

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

    
2053
		return false;
2054
	}
2055

    
2056
	/*
2057
	 * this is for debugging purpose if you do not want to use Ajax
2058
	 * to submit a HTML form. It basically disables the observation
2059
	 * of the submit event and hence does not trigger Ajax.
2060
	 */
2061
	if ($_REQUEST['disable_ajax']) {
2062
		$_SESSION['NO_AJAX'] = "True";
2063
	}
2064

    
2065
	/*
2066
	 * Same to re-enable Ajax.
2067
	 */
2068
	if ($_REQUEST['enable_ajax']) {
2069
		unset($_SESSION['NO_AJAX']);
2070
	}
2071
	phpsession_end(true);
2072
	return true;
2073
}
2074

    
2075
function print_credit() {
2076
	global $g;
2077

    
2078
	return  '<a target="_blank" href="https://pfsense.org">' . $g["product_name"] . '</a>' .
2079
			gettext(' is developed and maintained by ') .
2080
			'<a target="_blank" href="https://netgate.com">Netgate. </a>' . ' &copy; ESF ' . $g["product_copyright_years"] .
2081
			'<a target="_blank" href="https://pfsense.org/license">' .
2082
			gettext(' View license.') . '</a>';
2083
}
2084
function get_user_remote_address() {
2085
	$remote_address = $_SERVER['REMOTE_ADDR'];
2086
	if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
2087
		$remote_address .= "[{$_SERVER['HTTP_CLIENT_IP']}]";
2088
	} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2089
		$remote_address .= "[{$_SERVER['HTTP_X_FORWARDED_FOR']}]";
2090
	}
2091
	return $remote_address;
2092
}
2093
function get_user_remote_authsource() {
2094
	$authsource = "";
2095
	if (!empty($_SESSION['authsource'])) {
2096
		$authsource .= " ({$_SESSION['authsource']})";
2097
	}
2098
	return $authsource;
2099
}
2100
?>
(2-2/59)