Project

General

Profile

Download (51.6 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-2016 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
if (!$do_not_include_config_gui_inc) {
30
	require_once("config.gui.inc");
31
}
32

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

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

    
42
	/* Either a IPv6 address with or without a alternate port */
43
	if (strstr($_SERVER['HTTP_HOST'], "]")) {
44
		$http_host_port = explode("]", $_SERVER['HTTP_HOST']);
45
		/* v6 address has more parts, drop the last part */
46
		if (count($http_host_port) > 1) {
47
			array_pop($http_host_port);
48
			$http_host = str_replace(array("[", "]"), "", implode(":", $http_host_port));
49
		} else {
50
			$http_host = str_replace(array("[", "]"), "", implode(":", $http_host_port));
51
		}
52
	} else {
53
		$http_host = explode(":", $_SERVER['HTTP_HOST']);
54
		$http_host = $http_host[0];
55
	}
56
	if (is_ipaddr($http_host) or $_SERVER['SERVER_ADDR'] == "127.0.0.1" or
57
		strcasecmp($http_host, "localhost") == 0 or $_SERVER['SERVER_ADDR'] == "::1") {
58
		$found_host = true;
59
	}
60
	if (strcasecmp($http_host, $config['system']['hostname'] . "." . $config['system']['domain']) == 0 or
61
		strcasecmp($http_host, $config['system']['hostname']) == 0) {
62
		$found_host = true;
63
	}
64

    
65
	if (is_array($config['dyndnses']['dyndns']) && !$found_host) {
66
		foreach ($config['dyndnses']['dyndns'] as $dyndns) {
67
			if (strcasecmp($dyndns['host'], $http_host) == 0) {
68
				$found_host = true;
69
				break;
70
			}
71
		}
72
	}
73

    
74
	if (is_array($config['dnsupdates']['dnsupdate']) && !$found_host) {
75
		foreach ($config['dnsupdates']['dnsupdate'] as $rfc2136) {
76
			if (strcasecmp($rfc2136['host'], $http_host) == 0) {
77
				$found_host = true;
78
				break;
79
			}
80
		}
81
	}
82

    
83
	if (!empty($config['system']['webgui']['althostnames']) && !$found_host) {
84
		$althosts = explode(" ", $config['system']['webgui']['althostnames']);
85
		foreach ($althosts as $ah) {
86
			if (strcasecmp($ah, $http_host) == 0 or strcasecmp($ah, $_SERVER['SERVER_ADDR']) == 0) {
87
				$found_host = true;
88
				break;
89
			}
90
		}
91
	}
92

    
93
	if ($found_host == false) {
94
		if (!security_checks_disabled()) {
95
			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."));
96
			exit;
97
		}
98
		$security_passed = false;
99
	}
100
}
101

    
102
// If the HTTP_REFERER is something other than ourselves then disallow.
103
if (function_exists("display_error_form") && !isset($config['system']['webgui']['nohttpreferercheck'])) {
104
	if ($_SERVER['HTTP_REFERER']) {
105
		if (file_exists("{$g['tmp_path']}/setupwizard_lastreferrer")) {
106
			if ($_SERVER['HTTP_REFERER'] == file_get_contents("{$g['tmp_path']}/setupwizard_lastreferrer")) {
107
				unlink("{$g['tmp_path']}/setupwizard_lastreferrer");
108
				header("Refresh: 1; url=index.php");
109
?>
110
<!DOCTYPE html>
111
<html lang="en">
112
<head>
113
	<link rel="stylesheet" href="/css/pfSense.css" />
114
	<title><?=gettext("Redirecting..."); ?></title>
115
</head>
116
<body id="error" class="no-menu">
117
	<div id="jumbotron">
118
		<div class="container">
119
			<div class="col-sm-offset-3 col-sm-6 col-xs-12">
120
				<p><?=gettext("Redirecting to the dashboard...")?></p>
121
			</div>
122
		</div>
123
	</div>
124
</body>
125
</html>
126
<?php
127
				exit;
128
			}
129
		}
130
		$found_host = false;
131
		$referrer_host = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);
132
		$referrer_host = str_replace(array("[", "]"), "", $referrer_host);
133
		if ($referrer_host) {
134
			if (strcasecmp($referrer_host, $config['system']['hostname'] . "." . $config['system']['domain']) == 0 ||
135
			    strcasecmp($referrer_host, $config['system']['hostname']) == 0) {
136
				$found_host = true;
137
			}
138

    
139
			if (!empty($config['system']['webgui']['althostnames']) && !$found_host) {
140
				$althosts = explode(" ", $config['system']['webgui']['althostnames']);
141
				foreach ($althosts as $ah) {
142
					if (strcasecmp($referrer_host, $ah) == 0) {
143
						$found_host = true;
144
						break;
145
					}
146
				}
147
			}
148

    
149
			if (is_array($config['dyndnses']['dyndns']) && !$found_host) {
150
				foreach ($config['dyndnses']['dyndns'] as $dyndns) {
151
					if (strcasecmp($dyndns['host'], $referrer_host) == 0) {
152
						$found_host = true;
153
						break;
154
					}
155
				}
156
			}
157

    
158
			if (is_array($config['dnsupdates']['dnsupdate']) && !$found_host) {
159
				foreach ($config['dnsupdates']['dnsupdate'] as $rfc2136) {
160
					if (strcasecmp($rfc2136['host'], $referrer_host) == 0) {
161
						$found_host = true;
162
						break;
163
					}
164
				}
165
			}
166

    
167
			if (!$found_host) {
168
				$interface_list_ips = get_configured_ip_addresses();
169
				foreach ($interface_list_ips as $ilips) {
170
					if (strcasecmp($referrer_host, $ilips) == 0) {
171
						$found_host = true;
172
						break;
173
					}
174
				}
175
				$interface_list_ipv6s = get_configured_ipv6_addresses(true);
176
				foreach ($interface_list_ipv6s as $ilipv6s) {
177
					$ilipv6s = explode('%', $ilipv6s)[0];
178
					if (strcasecmp($referrer_host, $ilipv6s) == 0) {
179
						$found_host = true;
180
						break;
181
					}
182
				}
183
				if ($referrer_host == "127.0.0.1" || $referrer_host == "localhost") {
184
					// allow SSH port forwarded connections and links from localhost
185
					$found_host = true;
186
				}
187
			}
188
		}
189
		if ($found_host == false) {
190
			if (!security_checks_disabled()) {
191
				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.");
192
				exit;
193
			}
194
			$security_passed = false;
195
		}
196
	} else {
197
		$security_passed = false;
198
	}
199
}
200

    
201
if (function_exists("display_error_form") && $security_passed) {
202
	/* Security checks passed, so it should be OK to turn them back on */
203
	restore_security_checks();
204
}
205
unset($security_passed);
206

    
207
$groupindex = index_groups();
208
$userindex = index_users();
209

    
210
function index_groups() {
211
	global $g, $debug, $config, $groupindex;
212

    
213
	$groupindex = array();
214

    
215
	if (is_array($config['system']['group'])) {
216
		$i = 0;
217
		foreach ($config['system']['group'] as $groupent) {
218
			$groupindex[$groupent['name']] = $i;
219
			$i++;
220
		}
221
	}
222

    
223
	return ($groupindex);
224
}
225

    
226
function index_users() {
227
	global $g, $debug, $config;
228

    
229
	if (is_array($config['system']['user'])) {
230
		$i = 0;
231
		foreach ($config['system']['user'] as $userent) {
232
			$userindex[$userent['name']] = $i;
233
			$i++;
234
		}
235
	}
236

    
237
	return ($userindex);
238
}
239

    
240
function & getUserEntry($name) {
241
	global $debug, $config, $userindex;
242
	$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
243

    
244
	if (isset($userindex[$name])) {
245
		return $config['system']['user'][$userindex[$name]];
246
	} elseif ($authcfg['type'] != "Local Database") {
247
		$user = array();
248
		$user['name'] = $name;
249
		return $user;
250
	}
251
}
252

    
253
function & getUserEntryByUID($uid) {
254
	global $debug, $config;
255

    
256
	if (is_array($config['system']['user'])) {
257
		foreach ($config['system']['user'] as & $user) {
258
			if ($user['uid'] == $uid) {
259
				return $user;
260
			}
261
		}
262
	}
263

    
264
	return false;
265
}
266

    
267
function & getGroupEntry($name) {
268
	global $debug, $config, $groupindex;
269
	if (isset($groupindex[$name])) {
270
		return $config['system']['group'][$groupindex[$name]];
271
	}
272
}
273

    
274
function & getGroupEntryByGID($gid) {
275
	global $debug, $config;
276

    
277
	if (is_array($config['system']['group'])) {
278
		foreach ($config['system']['group'] as & $group) {
279
			if ($group['gid'] == $gid) {
280
				return $group;
281
			}
282
		}
283
	}
284

    
285
	return false;
286
}
287

    
288
function get_user_privileges(& $user) {
289
	global $config;
290

    
291
	$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
292
	$names = array();
293

    
294
	$privs = $user['priv'];
295
	if (!is_array($privs)) {
296
		$privs = array();
297
	}
298

    
299
	if ($authcfg['type'] == "ldap") {
300
		$names = @ldap_get_groups($user['name'], $authcfg);
301
	} elseif ($authcfg['type'] == "radius") {
302
		$names = @radius_get_groups($_SESSION['user_radius_attributes']);
303
	}
304

    
305
	if (empty($names)) {
306
		$names = local_user_get_groups($user, true);
307
	}
308

    
309
	foreach ($names as $name) {
310
		$group = getGroupEntry($name);
311
		if (is_array($group['priv'])) {
312
			$privs = array_merge($privs, $group['priv']);
313
		}
314
	}
315

    
316
	return $privs;
317
}
318

    
319
function userHasPrivilege($userent, $privid = false) {
320

    
321
	if (!$privid || !is_array($userent)) {
322
		return false;
323
	}
324

    
325
	$privs = get_user_privileges($userent);
326

    
327
	if (!is_array($privs)) {
328
		return false;
329
	}
330

    
331
	if (!in_array($privid, $privs)) {
332
		return false;
333
	}
334

    
335
	return true;
336
}
337

    
338
function local_backed($username, $passwd) {
339

    
340
	$user = getUserEntry($username);
341
	if (!$user) {
342
		return false;
343
	}
344

    
345
	if (is_account_disabled($username) || is_account_expired($username)) {
346
		return false;
347
	}
348

    
349
	if ($user['bcrypt-hash']) {
350
		if (password_verify($passwd, $user['bcrypt-hash'])) {
351
			return true;
352
		}
353
	}
354

    
355
	//for backwards compatibility
356
	if ($user['password']) {
357
		if (crypt($passwd, $user['password']) == $user['password']) {
358
			return true;
359
		}
360
	}
361

    
362
	if ($user['md5-hash']) {
363
		if (md5($passwd) == $user['md5-hash']) {
364
			return true;
365
		}
366
	}
367

    
368
	return false;
369
}
370

    
371
function local_sync_accounts() {
372
	global $debug, $config;
373

    
374
	/* remove local users to avoid uid conflicts */
375
	$fd = popen("/usr/sbin/pw usershow -a", "r");
376
	if ($fd) {
377
		while (!feof($fd)) {
378
			$line = explode(":", fgets($fd));
379
			if ($line[0] != "admin") {
380
				if (!strncmp($line[0], "_", 1)) {
381
					continue;
382
				}
383
				if ($line[2] < 2000) {
384
					continue;
385
				}
386
				if ($line[2] > 65000) {
387
					continue;
388
				}
389
			}
390
			/*
391
			 * If a crontab was created to user, pw userdel will be interactive and
392
			 * can cause issues. Just remove crontab before run it when necessary
393
			 */
394
			unlink_if_exists("/var/cron/tabs/{$line[0]}");
395
			$cmd = "/usr/sbin/pw userdel -n " . escapeshellarg($line[0]);
396
			if ($debug) {
397
				log_error(sprintf(gettext("Running: %s"), $cmd));
398
			}
399
			mwexec($cmd);
400
		}
401
		pclose($fd);
402
	}
403

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

    
428
	/* make sure the all group exists */
429
	$allgrp = getGroupEntryByGID(1998);
430
	local_group_set($allgrp, true);
431

    
432
	/* sync all local users */
433
	if (is_array($config['system']['user'])) {
434
		foreach ($config['system']['user'] as $user) {
435
			local_user_set($user);
436
		}
437
	}
438

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

    
446

    
447
}
448

    
449
function local_user_set(& $user) {
450
	global $g, $debug;
451

    
452
	if (empty($user['password']) && empty($user['bcrypt-hash'])) {
453
		log_error("There is something wrong in the config because user {$user['name']} password is missing!");
454
		return;
455
	}
456

    
457

    
458
	$home_base = "/home/";
459
	$user_uid = $user['uid'];
460
	$user_name = $user['name'];
461
	$user_home = "{$home_base}{$user_name}";
462
	$user_shell = "/etc/rc.initial";
463
	$user_group = "nobody";
464

    
465
	// Ensure $home_base exists and is writable
466
	if (!is_dir($home_base)) {
467
		mkdir($home_base, 0755);
468
	}
469

    
470
	$lock_account = false;
471
	/* configure shell type */
472
	/* Cases here should be ordered by most privileged to least privileged. */
473
	if (userHasPrivilege($user, "user-shell-access") || userHasPrivilege($user, "page-all")) {
474
		$user_shell = "/bin/tcsh";
475
	} elseif (userHasPrivilege($user, "user-copy-files-chroot")) {
476
		$user_shell = "/usr/local/sbin/scponlyc";
477
	} elseif (userHasPrivilege($user, "user-copy-files")) {
478
		$user_shell = "/usr/local/bin/scponly";
479
	} elseif (userHasPrivilege($user, "user-ssh-tunnel")) {
480
		$user_shell = "/usr/local/sbin/ssh_tunnel_shell";
481
	} elseif (userHasPrivilege($user, "user-ipsec-xauth-dialin")) {
482
		$user_shell = "/sbin/nologin";
483
	} else {
484
		$user_shell = "/sbin/nologin";
485
		$lock_account = true;
486
	}
487

    
488
	/* Lock out disabled or expired users, unless it's root/admin. */
489
	if ((is_account_disabled($user_name) || is_account_expired($user_name)) && ($user_uid != 0)) {
490
		$user_shell = "/sbin/nologin";
491
		$lock_account = true;
492
	}
493

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

    
512
	/* read from pw db */
513
	$fd = popen("/usr/sbin/pw usershow -n {$user_name} 2>&1", "r");
514
	$pwread = fgets($fd);
515
	pclose($fd);
516
	$userattrs = explode(":", trim($pwread));
517

    
518
	$skel_dir = '/etc/skel';
519

    
520
	/* determine add or mod */
521
	if (($userattrs[0] != $user['name']) || (!strncmp($pwread, "pw:", 3))) {
522
		$user_op = "useradd -m -k " . escapeshellarg($skel_dir) . " -o";
523
	} else {
524
		$user_op = "usermod";
525
	}
526

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

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

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

    
556
	/* Make sure all users have last version of config files */
557
	foreach (glob("{$skel_dir}/dot.*") as $dot_file) {
558
		$target = $user_home . '/' . substr(basename($dot_file), 3);
559
		@copy($dot_file, $target);
560
		@chown($target, $user_name);
561
		@chgrp($target, $user_group);
562
	}
563

    
564
	/* write out ssh authorized key file */
565
	if ($user['authorizedkeys']) {
566
		if (!is_dir("{$user_home}/.ssh")) {
567
			@mkdir("{$user_home}/.ssh", 0700);
568
			@chown("{$user_home}/.ssh", $user_name);
569
		}
570
		$keys = base64_decode($user['authorizedkeys']);
571
		@file_put_contents("{$user_home}/.ssh/authorized_keys", $keys);
572
		@chown("{$user_home}/.ssh/authorized_keys", $user_name);
573
	} else {
574
		unlink_if_exists("{$user_home}/.ssh/authorized_keys");
575
	}
576

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

    
580
}
581

    
582
function local_user_del($user) {
583
	global $debug;
584

    
585
	/* remove all memberships */
586
	local_user_set_groups($user);
587

    
588
	/* Don't remove /root */
589
	if ($user['uid'] != 0) {
590
		$rmhome = "-r";
591
	}
592

    
593
	/* read from pw db */
594
	$fd = popen("/usr/sbin/pw usershow -n {$user['name']} 2>&1", "r");
595
	$pwread = fgets($fd);
596
	pclose($fd);
597
	$userattrs = explode(":", trim($pwread));
598

    
599
	if ($userattrs[0] != $user['name']) {
600
		log_error("Tried to remove user {$user['name']} but got user {$userattrs[0]} instead. Bailing.");
601
		return;
602
	}
603

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

    
607
	if ($debug) {
608
		log_error(sprintf(gettext("Running: %s"), $cmd));
609
	}
610
	mwexec($cmd);
611

    
612
	/* Delete user from groups needs a call to write_config() */
613
	local_group_del_user($user);
614
}
615

    
616
function local_user_set_password(&$user, $password) {
617

    
618
	unset($user['password']);
619
	unset($user['md5-hash']);
620
	$user['bcrypt-hash'] = password_hash($password, PASSWORD_BCRYPT);
621

    
622
	/* Maintain compatibility with FreeBSD - change $2y$ prefix to $2b$
623
	 * https://reviews.freebsd.org/D2742
624
	 * XXX: Can be removed as soon as r284483 is MFC'd.
625
	 */
626
	if ($user['bcrypt-hash'][2] == "y") {
627
		$user['bcrypt-hash'][2] = "b";
628
	}
629

    
630
	// Converts ascii to unicode.
631
	$astr = (string) $password;
632
	$ustr = '';
633
	for ($i = 0; $i < strlen($astr); $i++) {
634
		$a = ord($astr{$i}) << 8;
635
		$ustr .= sprintf("%X", $a);
636
	}
637

    
638
}
639

    
640
function local_user_get_groups($user, $all = false) {
641
	global $debug, $config;
642

    
643
	$groups = array();
644
	if (!is_array($config['system']['group'])) {
645
		return $groups;
646
	}
647

    
648
	foreach ($config['system']['group'] as $group) {
649
		if ($all || (!$all && ($group['name'] != "all"))) {
650
			if (is_array($group['member'])) {
651
				if (in_array($user['uid'], $group['member'])) {
652
					$groups[] = $group['name'];
653
				}
654
			}
655
		}
656
	}
657

    
658
	if ($all) {
659
		$groups[] = "all";
660
	}
661

    
662
	sort($groups);
663

    
664
	return $groups;
665

    
666
}
667

    
668
function local_user_set_groups($user, $new_groups = NULL) {
669
	global $debug, $config, $groupindex;
670

    
671
	if (!is_array($config['system']['group'])) {
672
		return;
673
	}
674

    
675
	$cur_groups = local_user_get_groups($user, true);
676
	$mod_groups = array();
677

    
678
	if (!is_array($new_groups)) {
679
		$new_groups = array();
680
	}
681

    
682
	if (!is_array($cur_groups)) {
683
		$cur_groups = array();
684
	}
685

    
686
	/* determine which memberships to add */
687
	foreach ($new_groups as $groupname) {
688
		if ($groupname == '' || in_array($groupname, $cur_groups)) {
689
			continue;
690
		}
691
		$group = & $config['system']['group'][$groupindex[$groupname]];
692
		$group['member'][] = $user['uid'];
693
		$mod_groups[] = $group;
694
	}
695
	unset($group);
696

    
697
	/* determine which memberships to remove */
698
	foreach ($cur_groups as $groupname) {
699
		if (in_array($groupname, $new_groups)) {
700
			continue;
701
		}
702
		if (!isset($config['system']['group'][$groupindex[$groupname]])) {
703
			continue;
704
		}
705
		$group = & $config['system']['group'][$groupindex[$groupname]];
706
		if (is_array($group['member'])) {
707
			$index = array_search($user['uid'], $group['member']);
708
			array_splice($group['member'], $index, 1);
709
			$mod_groups[] = $group;
710
		}
711
	}
712
	unset($group);
713

    
714
	/* sync all modified groups */
715
	foreach ($mod_groups as $group) {
716
		local_group_set($group);
717
	}
718
}
719

    
720
function local_group_del_user($user) {
721
	global $config;
722

    
723
	if (!is_array($config['system']['group'])) {
724
		return;
725
	}
726

    
727
	foreach ($config['system']['group'] as $group) {
728
		if (is_array($group['member'])) {
729
			foreach ($group['member'] as $idx => $uid) {
730
				if ($user['uid'] == $uid) {
731
					unset($config['system']['group']['member'][$idx]);
732
				}
733
			}
734
		}
735
	}
736
}
737

    
738
function local_group_set($group, $reset = false) {
739
	global $debug;
740

    
741
	$group_name = $group['name'];
742
	$group_gid = $group['gid'];
743
	$group_members = '';
744
	if (!$reset && !empty($group['member']) && count($group['member']) > 0) {
745
		$group_members = implode(",", $group['member']);
746
	}
747

    
748
	if (empty($group_name) || $group['scope'] == "remote") {
749
		return;
750
	}
751

    
752
	/* determine add or mod */
753
	if (mwexec("/usr/sbin/pw groupshow -g " . escapeshellarg($group_gid) . " 2>&1", true) == 0) {
754
		$group_op = "groupmod -l";
755
	} else {
756
		$group_op = "groupadd -n";
757
	}
758

    
759
	/* add or mod group db */
760
	$cmd = "/usr/sbin/pw {$group_op} " .
761
		escapeshellarg($group_name) .
762
		" -g " . escapeshellarg($group_gid) .
763
		" -M " . escapeshellarg($group_members) . " 2>&1";
764

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

    
770
}
771

    
772
function local_group_del($group) {
773
	global $debug;
774

    
775
	/* delete from group db */
776
	$cmd = "/usr/sbin/pw groupdel " . escapeshellarg($group['name']);
777

    
778
	if ($debug) {
779
		log_error(sprintf(gettext("Running: %s"), $cmd));
780
	}
781
	mwexec($cmd);
782
}
783

    
784
function ldap_test_connection($authcfg) {
785
	global $debug, $config, $g;
786

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

    
805
	/* first check if there is even an LDAP server populated */
806
	if (!$ldapserver) {
807
		return false;
808
	}
809

    
810
	/* Setup CA environment if needed. */
811
	ldap_setup_caenv($authcfg);
812

    
813
	/* connect and see if server is up */
814
	$error = false;
815
	if (!($ldap = ldap_connect($ldapserver))) {
816
		$error = true;
817
	}
818

    
819
	if ($error == true) {
820
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
821
		return false;
822
	}
823

    
824
	return true;
825
}
826

    
827
function ldap_setup_caenv($authcfg) {
828
	global $g;
829
	require_once("certs.inc");
830

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

    
858
function ldap_test_bind($authcfg) {
859
	global $debug, $config, $g;
860

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

    
886
	/* first check if there is even an LDAP server populated */
887
	if (!$ldapserver) {
888
		return false;
889
	}
890

    
891
	/* Setup CA environment if needed. */
892
	ldap_setup_caenv($authcfg);
893

    
894
	/* connect and see if server is up */
895
	$error = false;
896
	if (!($ldap = ldap_connect($ldapserver))) {
897
		$error = true;
898
	}
899

    
900
	if ($error == true) {
901
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
902
		return false;
903
	}
904

    
905
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
906
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
907
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
908
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
909
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
910

    
911
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
912
		if (!(ldap_start_tls($ldap))) {
913
			log_error(sprintf(gettext("ERROR! ldap_test_bind() could not STARTTLS to server %s."), $ldapname));
914
			@ldap_close($ldap);
915
			return false;
916
		}
917
	}
918

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

    
931
	@ldap_unbind($ldap);
932

    
933
	return true;
934
}
935

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

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

    
943
	$ous = array();
944

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

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

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

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

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

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

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

    
1001
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
1002
		if (!(ldap_start_tls($ldap))) {
1003
			log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not STARTTLS to server %s."), $ldapname));
1004
			@ldap_close($ldap);
1005
			return false;
1006
		}
1007
	}
1008

    
1009
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1010
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1011
	if ($ldapanon == true) {
1012
		if (!($res = @ldap_bind($ldap))) {
1013
			log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind anonymously to server %s."), $ldapname));
1014
			@ldap_close($ldap);
1015
			return $ous;
1016
		}
1017
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1018
		log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind to server %s."), $ldapname));
1019
		@ldap_close($ldap);
1020
		return $ous;
1021
	}
1022

    
1023
	if ($ldapscope == "one") {
1024
		$ldapfunc = "ldap_list";
1025
	} else {
1026
		$ldapfunc = "ldap_search";
1027
	}
1028

    
1029
	$search = @$ldapfunc($ldap, $ldapbasedn, $ldapfilter);
1030
	$info = @ldap_get_entries($ldap, $search);
1031

    
1032
	if (is_array($info)) {
1033
		foreach ($info as $inf) {
1034
			if (!$show_complete_ou) {
1035
				$inf_split = explode(",", $inf['dn']);
1036
				$ou = $inf_split[0];
1037
				$ou = str_replace("OU=", "", $ou);
1038
				$ou = str_replace("CN=", "", $ou);
1039
			} else {
1040
				if ($inf['dn']) {
1041
					$ou = $inf['dn'];
1042
				}
1043
			}
1044
			if ($ou) {
1045
				$ous[] = $ou;
1046
			}
1047
		}
1048
	}
1049

    
1050
	@ldap_unbind($ldap);
1051

    
1052
	return $ous;
1053
}
1054

    
1055
function ldap_get_groups($username, $authcfg) {
1056
	global $debug, $config;
1057

    
1058
	if (!function_exists("ldap_connect")) {
1059
		return;
1060
	}
1061

    
1062
	if (!$username) {
1063
		return false;
1064
	}
1065

    
1066
	if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) {
1067
		$username_split = explode("@", $username);
1068
		$username = $username_split[0];
1069
	}
1070

    
1071
	if (stristr($username, "\\")) {
1072
		$username_split = explode("\\", $username);
1073
		$username = $username_split[0];
1074
	}
1075

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

    
1114
	if (isset($authcfg['ldap_rfc2307'])) {
1115
		$ldapdn = $ldapbasedn;
1116
	} else {
1117
		$ldapdn = $_SESSION['ldapdn'];
1118
	}
1119

    
1120
	/*Convert attribute to lowercase.  php ldap arrays put everything in lowercase */
1121
	$ldapgroupattribute = strtolower($ldapgroupattribute);
1122
	$memberof = array();
1123

    
1124
	/* Setup CA environment if needed. */
1125
	ldap_setup_caenv($authcfg);
1126

    
1127
	/* connect and see if server is up */
1128
	$error = false;
1129
	if (!($ldap = ldap_connect($ldapserver))) {
1130
		$error = true;
1131
	}
1132

    
1133
	if ($error == true) {
1134
		log_error(sprintf(gettext("ERROR! ldap_get_groups() Could not connect to server %s."), $ldapname));
1135
		return memberof;
1136
	}
1137

    
1138
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1139
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1140
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1141
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1142
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1143

    
1144
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
1145
		if (!(ldap_start_tls($ldap))) {
1146
			log_error(sprintf(gettext("ERROR! ldap_get_groups() could not STARTTLS to server %s."), $ldapname));
1147
			@ldap_close($ldap);
1148
			return false;
1149
		}
1150
	}
1151

    
1152
	/* bind as user that has rights to read group attributes */
1153
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1154
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1155
	if ($ldapanon == true) {
1156
		if (!($res = @ldap_bind($ldap))) {
1157
			log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind anonymously to server %s."), $ldapname));
1158
			@ldap_close($ldap);
1159
			return false;
1160
		}
1161
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1162
		log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind to server %s."), $ldapname));
1163
		@ldap_close($ldap);
1164
		return memberof;
1165
	}
1166

    
1167
	/* get groups from DN found */
1168
	/* use ldap_read instead of search so we don't have to do a bunch of extra work */
1169
	/* since we know the DN is in $_SESSION['ldapdn'] */
1170
	//$search    = ldap_read($ldap, $ldapdn, "(objectclass=*)", array($ldapgroupattribute));
1171
	if ($ldapscope == "one") {
1172
		$ldapfunc = "ldap_list";
1173
	} else {
1174
		$ldapfunc = "ldap_search";
1175
	}
1176

    
1177
	$search = @$ldapfunc($ldap, $ldapdn, $ldapfilter, array($ldapgroupattribute));
1178
	$info = @ldap_get_entries($ldap, $search);
1179

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

    
1182
	if (is_array($gresults)) {
1183
		/* Iterate through the groups and throw them into an array */
1184
		foreach ($gresults as $grp) {
1185
			if (((isset($authcfg['ldap_rfc2307'])) && (stristr($grp["dn"], "CN=") !== false)) ||
1186
			    ((!isset($authcfg['ldap_rfc2307'])) && (stristr($grp, "CN=") !== false))) {
1187
				$grpsplit = isset($authcfg['ldap_rfc2307']) ? explode(",", $grp["dn"]) : explode(",", $grp);
1188
				$memberof[] = preg_replace("/CN=/i", "", $grpsplit[0]);
1189
			}
1190
		}
1191
	}
1192

    
1193
	/* Time to close LDAP connection */
1194
	@ldap_unbind($ldap);
1195

    
1196
	$groups = print_r($memberof, true);
1197

    
1198
	//log_error("Returning groups ".$groups." for user $username");
1199

    
1200
	return $memberof;
1201
}
1202

    
1203
function ldap_format_host($host) {
1204
	return is_ipaddrv6($host) ? "[$host]" : $host ;
1205
}
1206

    
1207
function ldap_backed($username, $passwd, $authcfg) {
1208
	global $debug, $config;
1209

    
1210
	if (!$username) {
1211
		return;
1212
	}
1213

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

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

    
1227
	if ($authcfg) {
1228
		if (strstr($authcfg['ldap_urltype'], "SSL")) {
1229
			$ldapproto = "ldaps";
1230
		} else {
1231
			$ldapproto = "ldap";
1232
		}
1233
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1234
		$ldapport = $authcfg['ldap_port'];
1235
		if (!empty($ldapport)) {
1236
			$ldapserver .= ":{$ldapport}";
1237
		}
1238
		$ldapbasedn = $authcfg['ldap_basedn'];
1239
		$ldapbindun = $authcfg['ldap_binddn'];
1240
		$ldapbindpw = $authcfg['ldap_bindpw'];
1241
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1242
			$ldapanon = true;
1243
		} else {
1244
			$ldapanon = false;
1245
		}
1246
		$ldapauthcont = $authcfg['ldap_authcn'];
1247
		$ldapnameattribute = strtolower($authcfg['ldap_attr_user']);
1248
		$ldapextendedqueryenabled = $authcfg['ldap_extended_enabled'];
1249
		$ldapextendedquery = $authcfg['ldap_extended_query'];
1250
		$ldapfilter = "";
1251
		if (!$ldapextendedqueryenabled) {
1252
			$ldapfilter = "({$ldapnameattribute}={$username})";
1253
		} else {
1254
			$ldapfilter = "(&({$ldapnameattribute}={$username})({$ldapextendedquery}))";
1255
		}
1256
		$ldaptype = "";
1257
		$ldapver = $authcfg['ldap_protver'];
1258
		$ldapname = $authcfg['name'];
1259
		$ldapscope = $authcfg['ldap_scope'];
1260
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
1261
	} else {
1262
		return false;
1263
	}
1264

    
1265
	/* first check if there is even an LDAP server populated */
1266
	if (!$ldapserver) {
1267
		if ($ldapfallback) {
1268
			log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined.  Defaulting to local user database. Visit System -> User Manager."));
1269
			return local_backed($username, $passwd);
1270
		} else {
1271
			log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined."));
1272
		}
1273

    
1274
		return false;
1275
	}
1276

    
1277
	/* Setup CA environment if needed. */
1278
	ldap_setup_caenv($authcfg);
1279

    
1280
	/* Make sure we can connect to LDAP */
1281
	$error = false;
1282
	if (!($ldap = ldap_connect($ldapserver))) {
1283
		$error = true;
1284
	}
1285

    
1286
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1287
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1288
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1289
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1290
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1291

    
1292
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
1293
		if (!(ldap_start_tls($ldap))) {
1294
			log_error(sprintf(gettext("ERROR! ldap_backed() could not STARTTLS to server %s."), $ldapname));
1295
			@ldap_close($ldap);
1296
			return false;
1297
		}
1298
	}
1299

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

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

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

    
1323
	/* Get LDAP Authcontainers and split em up. */
1324
	$ldac_splits = explode(";", $ldapauthcont);
1325

    
1326
	/* setup the usercount so we think we haven't found anyone yet */
1327
	$usercount = 0;
1328

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

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

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

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

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

    
1396
	return true;
1397
}
1398

    
1399
function radius_backed($username, $password, $authcfg, &$attributes = array()) {
1400
	global $debug, $config;
1401
	$ret = false;
1402

    
1403
	require_once("radius.inc");
1404
	require_once("Crypt/CHAP.php");
1405

    
1406
	if ($authcfg) {
1407
		$radiusservers = array();
1408
		$radiusservers[0]['ipaddr'] = $authcfg['host'];
1409
		$radiusservers[0]['port'] = $authcfg['radius_auth_port'];
1410
		$radiusservers[0]['sharedsecret'] = $authcfg['radius_secret'];
1411
		$radiusservers[0]['timeout'] = $authcfg['radius_timeout'];
1412
		if(isset($authcfg['radius_protocol'])) {
1413
			$radius_protocol = $authcfg['radius_protocol'];
1414
		} else {
1415
			$radius_protocol = 'PAP';
1416
		}
1417
	} else {
1418
		return false;
1419
	}
1420

    
1421
	// Create our instance
1422
	$classname = 'Auth_RADIUS_' . $radius_protocol;
1423
	$rauth = new $classname($username, $password);
1424

    
1425
	/* Add new servers to our instance */
1426
	foreach ($radiusservers as $radsrv) {
1427
		$timeout = (is_numeric($radsrv['timeout'])) ? $radsrv['timeout'] : 5;
1428
		$rauth->addServer($radsrv['ipaddr'], $radsrv['port'], $radsrv['sharedsecret'], $timeout);
1429
	}
1430

    
1431
	// Construct data package
1432
	$rauth->username = $username;
1433
	switch ($radius_protocol) {
1434
		case 'CHAP_MD5':
1435
		case 'MSCHAPv1':
1436
			$classname = $radius_protocol == 'MSCHAPv1' ? 'Crypt_CHAP_MSv1' : 'Crypt_CHAP_MD5';
1437
			$crpt = new $classname;
1438
			$crpt->username = $username;
1439
			$crpt->password = $password;
1440
			$rauth->challenge = $crpt->challenge;
1441
			$rauth->chapid = $crpt->chapid;
1442
			$rauth->response = $crpt->challengeResponse();
1443
			$rauth->flags = 1;
1444
			break;
1445

    
1446
		case 'MSCHAPv2':
1447
			$crpt = new Crypt_CHAP_MSv2;
1448
			$crpt->username = $username;
1449
			$crpt->password = $password;
1450
			$rauth->challenge = $crpt->authChallenge;
1451
			$rauth->peerChallenge = $crpt->peerChallenge;
1452
			$rauth->chapid = $crpt->chapid;
1453
			$rauth->response = $crpt->challengeResponse();
1454
			break;
1455

    
1456
		default:
1457
			$rauth->password = $password;
1458
			break;
1459
	}
1460

    
1461
	if (PEAR::isError($rauth->start())) {
1462
		$retvalue['auth_val'] = 1;
1463
		$retvalue['error'] = $rauth->getError();
1464
		if ($debug) {
1465
			printf(gettext("RADIUS start: %s") . "<br />\n", $retvalue['error']);
1466
		}
1467
	}
1468

    
1469
	// XXX - billm - somewhere in here we need to handle securid challenge/response
1470

    
1471
	/* Send request */
1472
	$result = $rauth->send();
1473
	if (PEAR::isError($result)) {
1474
		$retvalue['auth_val'] = 1;
1475
		$retvalue['error'] = $result->getMessage();
1476
		if ($debug) {
1477
			printf(gettext("RADIUS send failed: %s") . "<br />\n", $retvalue['error']);
1478
		}
1479
	} else if ($result === true) {
1480
		if ($rauth->getAttributes()) {
1481
			$attributes = $rauth->listAttributes();
1482
		}
1483
		$retvalue['auth_val'] = 2;
1484
		if ($debug) {
1485
			printf(gettext("RADIUS Auth succeeded")."<br />\n");
1486
		}
1487
		$ret = true;
1488
	} else {
1489
		$retvalue['auth_val'] = 3;
1490
		if ($debug) {
1491
			printf(gettext("RADIUS Auth rejected")."<br />\n");
1492
		}
1493
	}
1494

    
1495
	// close OO RADIUS_AUTHENTICATION
1496
	$rauth->close();
1497

    
1498
	return $ret;
1499
}
1500

    
1501
/*
1502
	$attributes must contain a "class" key containing the groups and local
1503
	groups must exist to match.
1504
*/
1505
function radius_get_groups($attributes) {
1506
	$groups = array();
1507
	if (!empty($attributes) && is_array($attributes) && (!empty($attributes['class']) || !empty($attributes['class_int']))) {
1508
		/* Some RADIUS servers return multiple class attributes, so check them all. */
1509
		$groups = array();
1510
		if (!empty($attributes['class']) && is_array($attributes['class'])) {
1511
			foreach ($attributes['class'] as $class) {
1512
				$groups = array_unique(array_merge($groups, explode(";", $class)));
1513
			}
1514
		}
1515

    
1516
		foreach ($groups as & $grp) {
1517
			$grp = trim($grp);
1518
			if (strtolower(substr($grp, 0, 3)) == "ou=") {
1519
				$grp = substr($grp, 3);
1520
			}
1521
		}
1522
	}
1523
	return $groups;
1524
}
1525

    
1526
function get_user_expiration_date($username) {
1527
	$user = getUserEntry($username);
1528
	if ($user['expires']) {
1529
		return $user['expires'];
1530
	}
1531
}
1532

    
1533
function is_account_expired($username) {
1534
	$expirydate = get_user_expiration_date($username);
1535
	if ($expirydate) {
1536
		if (strtotime("-1 day") > strtotime(date("m/d/Y", strtotime($expirydate)))) {
1537
			return true;
1538
		}
1539
	}
1540

    
1541
	return false;
1542
}
1543

    
1544
function is_account_disabled($username) {
1545
	$user = getUserEntry($username);
1546
	if (isset($user['disabled'])) {
1547
		return true;
1548
	}
1549

    
1550
	return false;
1551
}
1552

    
1553
function get_user_settings($username) {
1554
	global $config;
1555
	$settings = array();
1556
	$settings['widgets'] = $config['widgets'];
1557
	$settings['webgui']['dashboardcolumns'] = $config['system']['webgui']['dashboardcolumns'];
1558
	$settings['webgui']['webguihostnamemenu'] = $config['system']['webgui']['webguihostnamemenu'];
1559
	$settings['webgui']['webguicss'] = $config['system']['webgui']['webguicss'];
1560
	$settings['webgui']['dashboardavailablewidgetspanel'] = isset($config['system']['webgui']['dashboardavailablewidgetspanel']);
1561
	$settings['webgui']['webguifixedmenu'] = isset($config['system']['webgui']['webguifixedmenu']);
1562
	$settings['webgui']['webguileftcolumnhyper'] = isset($config['system']['webgui']['webguileftcolumnhyper']);
1563
	$settings['webgui']['systemlogsfilterpanel'] = isset($config['system']['webgui']['systemlogsfilterpanel']);
1564
	$settings['webgui']['systemlogsmanagelogpanel'] = isset($config['system']['webgui']['systemlogsmanagelogpanel']);
1565
	$settings['webgui']['statusmonitoringsettingspanel'] = isset($config['system']['webgui']['statusmonitoringsettingspanel']);
1566
	$settings['webgui']['pagenamefirst'] = isset($config['system']['webgui']['pagenamefirst']);
1567
	$user = getUserEntry($username);
1568
	if (isset($user['customsettings'])) {
1569
		$settings['customsettings'] = true;
1570
		if (isset($user['widgets'])) {
1571
			// This includes the 'sequence', and any widgetname-config per-widget settings.
1572
			$settings['widgets'] = $user['widgets'];
1573
		}
1574
		if (isset($user['dashboardcolumns'])) {
1575
			$settings['webgui']['dashboardcolumns'] = $user['dashboardcolumns'];
1576
		}
1577
		if (isset($user['webguicss'])) {
1578
			$settings['webgui']['webguicss'] = $user['webguicss'];
1579
		}
1580
		if (isset($user['webguihostnamemenu'])) {
1581
			$settings['webgui']['webguihostnamemenu'] = $user['webguihostnamemenu'];
1582
		}
1583
		$settings['webgui']['dashboardavailablewidgetspanel'] = isset($user['dashboardavailablewidgetspanel']);
1584
		$settings['webgui']['webguifixedmenu'] = isset($user['webguifixedmenu']);
1585
		$settings['webgui']['webguileftcolumnhyper'] = isset($user['webguileftcolumnhyper']);
1586
		$settings['webgui']['systemlogsfilterpanel'] = isset($user['systemlogsfilterpanel']);
1587
		$settings['webgui']['systemlogsmanagelogpanel'] = isset($user['systemlogsmanagelogpanel']);
1588
		$settings['webgui']['statusmonitoringsettingspanel'] = isset($user['statusmonitoringsettingspanel']);
1589
		$settings['webgui']['pagenamefirst'] = isset($user['pagenamefirst']);
1590
	} else {
1591
		$settings['customsettings'] = false;
1592
	}
1593

    
1594
	if ($settings['webgui']['dashboardcolumns'] < 1) {
1595
		$settings['webgui']['dashboardcolumns'] = 2;
1596
	}
1597

    
1598
	return $settings;
1599
}
1600

    
1601
function save_widget_settings($username, $settings, $message = "") {
1602
	global $config, $userindex;
1603
	$user = getUserEntry($username);
1604

    
1605
	if (strlen($message) > 0) {
1606
		$msgout = $message;
1607
	} else {
1608
		$msgout = gettext("Widget configuration has been changed.");
1609
	}
1610

    
1611
	if (isset($user['customsettings'])) {
1612
		$config['system']['user'][$userindex[$username]]['widgets'] = $settings;
1613
		write_config($msgout . " " . sprintf(gettext("(User %s)"), $username));
1614
	} else {
1615
		$config['widgets'] = $settings;
1616
		write_config($msgout);
1617
	}
1618
}
1619

    
1620
function auth_get_authserver($name) {
1621
	global $config;
1622

    
1623
	if (is_array($config['system']['authserver'])) {
1624
		foreach ($config['system']['authserver'] as $authcfg) {
1625
			if ($authcfg['name'] == $name) {
1626
				return $authcfg;
1627
			}
1628
		}
1629
	}
1630
	if ($name == "Local Database") {
1631
		return array("name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']);
1632
	}
1633
}
1634

    
1635
function auth_get_authserver_list() {
1636
	global $config;
1637

    
1638
	$list = array();
1639

    
1640
	if (is_array($config['system']['authserver'])) {
1641
		foreach ($config['system']['authserver'] as $authcfg) {
1642
			/* Add support for disabled entries? */
1643
			$list[$authcfg['name']] = $authcfg;
1644
		}
1645
	}
1646

    
1647
	$list["Local Database"] = array("name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']);
1648
	return $list;
1649
}
1650

    
1651
function getUserGroups($username, $authcfg, &$attributes = array()) {
1652
	global $config;
1653

    
1654
	$allowed_groups = array();
1655

    
1656
	switch ($authcfg['type']) {
1657
		case 'ldap':
1658
			$allowed_groups = @ldap_get_groups($username, $authcfg);
1659
			break;
1660
		case 'radius':
1661
			$allowed_groups = @radius_get_groups($attributes);
1662
			break;
1663
		default:
1664
			$user = getUserEntry($username);
1665
			$allowed_groups = @local_user_get_groups($user, true);
1666
			break;
1667
	}
1668

    
1669
	$member_groups = array();
1670
	if (is_array($config['system']['group'])) {
1671
		foreach ($config['system']['group'] as $group) {
1672
			if (in_array($group['name'], $allowed_groups)) {
1673
				$member_groups[] = $group['name'];
1674
			}
1675
		}
1676
	}
1677

    
1678
	return $member_groups;
1679
}
1680

    
1681
function authenticate_user($username, $password, $authcfg = NULL, &$attributes = array()) {
1682

    
1683
	if (is_array($username) || is_array($password)) {
1684
		return false;
1685
	}
1686

    
1687
	if (!$authcfg) {
1688
		return local_backed($username, $password);
1689
	}
1690

    
1691
	$authenticated = false;
1692
	switch ($authcfg['type']) {
1693
		case 'ldap':
1694
			if (ldap_backed($username, $password, $authcfg)) {
1695
				$authenticated = true;
1696
			}
1697
			break;
1698
		case 'radius':
1699
			if (radius_backed($username, $password, $authcfg, $attributes)) {
1700
				$authenticated = true;
1701
			}
1702
			break;
1703
		default:
1704
			/* lookup user object by name */
1705
			if (local_backed($username, $password)) {
1706
				$authenticated = true;
1707
			}
1708
			break;
1709
		}
1710

    
1711
	return $authenticated;
1712
}
1713

    
1714
function session_auth() {
1715
	global $config, $_SESSION, $page;
1716

    
1717
	// Handle HTTPS httponly and secure flags
1718
	$currentCookieParams = session_get_cookie_params();
1719
	session_set_cookie_params(
1720
		$currentCookieParams["lifetime"],
1721
		$currentCookieParams["path"],
1722
		NULL,
1723
		($config['system']['webgui']['protocol'] == "https"),
1724
		true
1725
	);
1726

    
1727
	if (!session_id()) {
1728
		session_start();
1729
	}
1730

    
1731
	// Detect protocol change
1732
	if (!isset($_POST['login']) && !empty($_SESSION['Logged_In']) && $_SESSION['protocol'] != $config['system']['webgui']['protocol']) {
1733
		return false;
1734
	}
1735

    
1736
	/* Validate incoming login request */
1737
	$attributes = array();
1738
	if (isset($_POST['login']) && !empty($_POST['usernamefld']) && !empty($_POST['passwordfld'])) {
1739
		$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
1740
		$remoteauth = authenticate_user($_POST['usernamefld'], $_POST['passwordfld'], $authcfg, $attributes);
1741
		if ($remoteauth || authenticate_user($_POST['usernamefld'], $_POST['passwordfld'])) {
1742
			// Generate a new id to avoid session fixation
1743
			session_regenerate_id();
1744
			$_SESSION['Logged_In'] = "True";
1745
			$_SESSION['remoteauth'] = $remoteauth;
1746
			$_SESSION['Username'] = $_POST['usernamefld'];
1747
			$_SESSION['user_radius_attributes'] = $attributes;
1748
			$_SESSION['last_access'] = time();
1749
			$_SESSION['protocol'] = $config['system']['webgui']['protocol'];
1750
			if (!isset($config['system']['webgui']['quietlogin'])) {
1751
				log_auth(sprintf(gettext("Successful login for user '%1\$s' from: %2\$s"), $_POST['usernamefld'], $_SERVER['REMOTE_ADDR']));
1752
			}
1753
			if (isset($_POST['postafterlogin'])) {
1754
				return true;
1755
			} else {
1756
				if (empty($page)) {
1757
					$page = "/";
1758
				}
1759
				header("Location: {$page}");
1760
			}
1761
			exit;
1762
		} else {
1763
			/* give the user an error message */
1764
			$_SESSION['Login_Error'] = "Username or Password incorrect";
1765
			log_auth("webConfigurator authentication error for '{$_POST['usernamefld']}' from {$_SERVER['REMOTE_ADDR']}");
1766
			if (isAjax()) {
1767
				echo "showajaxmessage('{$_SESSION['Login_Error']}');";
1768
				return;
1769
			}
1770
		}
1771
	}
1772

    
1773
	/* Show login page if they aren't logged in */
1774
	if (empty($_SESSION['Logged_In'])) {
1775
		return false;
1776
	}
1777

    
1778
	/* If session timeout isn't set, we don't mark sessions stale */
1779
	if (!isset($config['system']['webgui']['session_timeout'])) {
1780
		/* Default to 4 hour timeout if one is not set */
1781
		if ($_SESSION['last_access'] < (time() - 14400)) {
1782
			$_GET['logout'] = true;
1783
			$_SESSION['Logout'] = true;
1784
		} else {
1785
			$_SESSION['last_access'] = time();
1786
		}
1787
	} else if (intval($config['system']['webgui']['session_timeout']) == 0) {
1788
		/* only update if it wasn't ajax */
1789
		if (!isAjax()) {
1790
			$_SESSION['last_access'] = time();
1791
		}
1792
	} else {
1793
		/* Check for stale session */
1794
		if ($_SESSION['last_access'] < (time() - ($config['system']['webgui']['session_timeout'] * 60))) {
1795
			$_GET['logout'] = true;
1796
			$_SESSION['Logout'] = true;
1797
		} else {
1798
			/* only update if it wasn't ajax */
1799
			if (!isAjax()) {
1800
				$_SESSION['last_access'] = time();
1801
			}
1802
		}
1803
	}
1804

    
1805
	/* user hit the logout button */
1806
	if (isset($_GET['logout'])) {
1807

    
1808
		if ($_SESSION['Logout']) {
1809
			log_error(sprintf(gettext("Session timed out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR']));
1810
		} else {
1811
			log_error(sprintf(gettext("User logged out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR']));
1812
		}
1813

    
1814
		/* wipe out $_SESSION */
1815
		$_SESSION = array();
1816

    
1817
		if (isset($_COOKIE[session_name()])) {
1818
			setcookie(session_name(), '', time()-42000, '/');
1819
		}
1820

    
1821
		/* and destroy it */
1822
		session_destroy();
1823

    
1824
		$scriptName = explode("/", $_SERVER["SCRIPT_FILENAME"]);
1825
		$scriptElms = count($scriptName);
1826
		$scriptName = $scriptName[$scriptElms-1];
1827

    
1828
		if (isAjax()) {
1829
			return false;
1830
		}
1831

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

    
1835
		return false;
1836
	}
1837

    
1838
	/*
1839
	 * this is for debugging purpose if you do not want to use Ajax
1840
	 * to submit a HTML form. It basically disables the observation
1841
	 * of the submit event and hence does not trigger Ajax.
1842
	 */
1843
	if ($_GET['disable_ajax']) {
1844
		$_SESSION['NO_AJAX'] = "True";
1845
	}
1846

    
1847
	/*
1848
	 * Same to re-enable Ajax.
1849
	 */
1850
	if ($_GET['enable_ajax']) {
1851
		unset($_SESSION['NO_AJAX']);
1852
	}
1853

    
1854
	return true;
1855
}
1856

    
1857
?>
(1-1/51)