Project

General

Profile

Download (73.1 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-2013 BSD Perimeter
10
 * Copyright (c) 2013-2016 Electric Sheep Fencing
11
 * Copyright (c) 2014-2023 Rubicon Communications, LLC (Netgate)
12
 * All rights reserved.
13
 *
14
 * Licensed under the Apache License, Version 2.0 (the "License");
15
 * you may not use this file except in compliance with the License.
16
 * You may obtain a copy of the License at
17
 *
18
 * http://www.apache.org/licenses/LICENSE-2.0
19
 *
20
 * Unless required by applicable law or agreed to in writing, software
21
 * distributed under the License is distributed on an "AS IS" BASIS,
22
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
 * See the License for the specific language governing permissions and
24
 * limitations under the License.
25
 */
26

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

    
37
// Will be changed to false if security checks fail
38
$security_passed = true;
39

    
40
/* Possible user password hash types.
41
 * See https://redmine.pfsense.org/issues/12855
42
 */
43
global $auth_password_hash_types;
44
$auth_password_hash_types = array(
45
	'bcrypt' => gettext('bcrypt -- Blowfish-based crypt'),
46
	'sha512' => gettext('SHA-512 -- SHA-512-based crypt')
47
);
48

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

    
55
	/* Fetch the contents of the lockout table. */
56
	$entries = array();
57
	exec("/sbin/pfctl -t 'sshguard' -T show", $entries);
58

    
59
	/* If the client is in the lockout table, print an error, kill states, and exit */
60
	if (in_array($_SERVER['REMOTE_ADDR'], array_map('trim', $entries))) {
61
		if (!security_checks_disabled()) {
62
			/* They may never see the error since the connection will be cut off, but try to be nice anyhow. */
63
			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."));
64
			/* If they are locked out, they shouldn't have a state. Disconnect their connections. */
65
			$retval = pfSense_kill_states($_SERVER['REMOTE_ADDR']);
66
			if (is_ipaddrv4($_SERVER['REMOTE_ADDR'])) {
67
				$retval = pfSense_kill_states("0.0.0.0/0", $_SERVER['REMOTE_ADDR']);
68
			} elseif (is_ipaddrv6($_SERVER['REMOTE_ADDR'])) {
69
				$retval = pfSense_kill_states("::", $_SERVER['REMOTE_ADDR']);
70
			}
71
			exit;
72
		}
73
		$security_passed = false;
74
	}
75
}
76

    
77
if (function_exists("display_error_form") && !config_path_enabled('system/webgui', 'nodnsrebindcheck')) {
78
	/* DNS ReBinding attack prevention.  https://redmine.pfsense.org/issues/708 */
79
	$found_host = false;
80

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

    
104
	if (!$found_host) {
105
		foreach (config_get_path('dyndnses/dyndns', []) as $dyndns) {
106
			if (!is_array($dyndns) || empty($dyndns)) {
107
				continue;
108
			}
109
			if (strcasecmp($dyndns['host'], $http_host) == 0) {
110
				$found_host = true;
111
				break;
112
			}
113
		}
114
	}
115

    
116
	if (!$found_host) {
117
		foreach (config_get_path('dnsupdates/dnsupdate', []) as $rfc2136) {
118
			if (!is_array($rfc2136) || empty($rfc2136)) {
119
				continue;
120
			}
121
			if (strcasecmp($rfc2136['host'], $http_host) == 0) {
122
				$found_host = true;
123
				break;
124
			}
125
		}
126
	}
127

    
128
	if (!$found_host) {
129
		$althosts = explode(" ", config_get_path('system/webgui/althostnames', ""));
130
		foreach ($althosts as $ah) {
131
			if (strcasecmp($ah, $http_host) == 0 or strcasecmp($ah, $_SERVER['SERVER_ADDR']) == 0) {
132
				$found_host = true;
133
				break;
134
			}
135
		}
136
	}
137

    
138
	if ($found_host == false) {
139
		if (!security_checks_disabled()) {
140
			display_error_form("501", gettext("Potential DNS Rebind attack detected, see https://en.wikipedia.org/wiki/DNS_rebinding<br />Try accessing the router by IP address instead of by hostname."));
141
			exit;
142
		}
143
		$security_passed = false;
144
	}
145
}
146

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

    
184
			if (!$found_host) {
185
				$althosts = explode(" ", config_get_path('system/webgui/althostnames', ""));
186
				foreach ($althosts as $ah) {
187
					if (strcasecmp($referrer_host, $ah) == 0) {
188
						$found_host = true;
189
						break;
190
					}
191
				}
192
			}
193

    
194
			if (!$found_host) {
195
				foreach (config_get_path('dyndnses/dyndns', []) as $dyndns) {
196
					if (!is_array($dyndns) || empty($dyndns)) {
197
						continue;
198
					}
199
					if (strcasecmp($dyndns['host'], $referrer_host) == 0) {
200
						$found_host = true;
201
						break;
202
					}
203
				}
204
			}
205

    
206
			if (!$found_host) {
207
				foreach (config_get_path('dnsupdates/dnsupdate', []) as $rfc2136) {
208
					if (!is_array($rfc2136) || empty($rfc2136)) {
209
						continue;
210
					}
211
					if (strcasecmp($rfc2136['host'], $referrer_host) == 0) {
212
						$found_host = true;
213
						break;
214
					}
215
				}
216
			}
217

    
218
			if (!$found_host) {
219
				$interface_list_ips = get_configured_ip_addresses();
220
				foreach ($interface_list_ips as $ilips) {
221
					if (strcasecmp($referrer_host, $ilips) == 0) {
222
						$found_host = true;
223
						break;
224
					}
225
				}
226
				$interface_list_ipv6s = get_configured_ipv6_addresses(true);
227
				foreach ($interface_list_ipv6s as $ilipv6s) {
228
					$ilipv6s = explode('%', $ilipv6s)[0];
229
					if (strcasecmp($referrer_host, $ilipv6s) == 0) {
230
						$found_host = true;
231
						break;
232
					}
233
				}
234
				if ($referrer_host == "127.0.0.1" || $referrer_host == "localhost") {
235
					// allow SSH port forwarded connections and links from localhost
236
					$found_host = true;
237
				}
238
			}
239

    
240
			/* Fall back to probing active interface addresses rather than config.xml to allow
241
			 * changed addresses that have not yet been applied.
242
			 * See https://redmine.pfsense.org/issues/8822
243
			 */
244
			if (!$found_host) {
245
				$refifs = get_interface_arr();
246
				foreach ($refifs as $rif) {
247
					if (($referrer_host == find_interface_ip($rif)) ||
248
					    ($referrer_host == find_interface_ipv6($rif)) ||
249
					    ($referrer_host == find_interface_ipv6_ll($rif))) {
250
						$found_host = true;
251
						break;
252
					}
253
				}
254
			}
255
		}
256
		if ($found_host == false) {
257
			if (!security_checks_disabled()) {
258
				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 Access.");
259
				exit;
260
			}
261
			$security_passed = false;
262
		}
263
	} else {
264
		$security_passed = false;
265
	}
266
}
267

    
268
if (function_exists("display_error_form") && $security_passed) {
269
	/* Security checks passed, so it should be OK to turn them back on */
270
	restore_security_checks();
271
}
272
unset($security_passed);
273

    
274
$groupindex = index_groups();
275
$userindex = index_users();
276

    
277
function index_groups() {
278
	global $g, $debug, $groupindex;
279

    
280
	$groupindex = array();
281

    
282
	$i = 0;
283
	foreach (config_get_path('system/group', []) as $groupent) {
284
		$groupindex[$groupent['name']] = $i;
285
		$i++;
286
	}
287

    
288
	return ($groupindex);
289
}
290

    
291
function index_users() {
292
	$i = 0;
293
	foreach (config_get_path('system/user', []) as $userent) {
294
		$userindex[$userent['name']] = $i;
295
		$i++;
296
	}
297

    
298
	return ($userindex);
299
}
300

    
301
function & getUserEntry($name) {
302
	global $debug, $config, $userindex;
303
	$authcfg = auth_get_authserver($config['system']['webgui']['authmode']);
304

    
305
	if (isset($userindex[$name])) {
306
		return $config['system']['user'][$userindex[$name]];
307
	} elseif ($authcfg['type'] != "Local Database") {
308
		$user = array();
309
		$user['name'] = $name;
310
		return $user;
311
	}
312
}
313

    
314
function & getUserEntryByUID($uid) {
315
	global $debug, $config;
316

    
317
	if (is_array($config['system']['user'])) {
318
		foreach ($config['system']['user'] as & $user) {
319
			if ($user['uid'] == $uid) {
320
				return $user;
321
			}
322
		}
323
	}
324

    
325
	return false;
326
}
327

    
328
function & getGroupEntry($name) {
329
	global $debug, $config, $groupindex;
330
	if (isset($groupindex[$name])) {
331
		return $config['system']['group'][$groupindex[$name]];
332
	}
333
}
334

    
335
function & getGroupEntryByGID($gid) {
336
	global $debug, $config;
337

    
338
	if (is_array($config['system']['group'])) {
339
		foreach ($config['system']['group'] as & $group) {
340
			if ($group['gid'] == $gid) {
341
				return $group;
342
			}
343
		}
344
	}
345

    
346
	return false;
347
}
348

    
349
function get_user_privileges(& $user) {
350
	global $_SESSION;
351

    
352
	$authcfg = auth_get_authserver(config_get_path('system/webgui/authmode'));
353
	$allowed_groups = array();
354

    
355
	$privs = $user['priv'];
356
	if (!is_array($privs)) {
357
		$privs = array();
358
	}
359

    
360
	// cache auth results for a short time to ease load on auth services & logs
361
	$recheck_time = config_get_path('system/webgui/auth_refresh_time', 30);
362

    
363
	if ($authcfg['type'] == "ldap") {
364
		if (isset($_SESSION["ldap_allowed_groups"]) &&
365
		    (time() <= $_SESSION["auth_check_time"] + $recheck_time)) {
366
			$allowed_groups = $_SESSION["ldap_allowed_groups"];
367
		} else {
368
			$allowed_groups = @ldap_get_groups($user['name'], $authcfg);
369
			$_SESSION["ldap_allowed_groups"] = $allowed_groups;
370
			$_SESSION["auth_check_time"] = time();
371
		}
372
	} elseif ($authcfg['type'] == "radius") {
373
		if (isset($_SESSION["radius_allowed_groups"]) &&
374
		    (time() <= $_SESSION["auth_check_time"] + $recheck_time)) {
375
			$allowed_groups = $_SESSION["radius_allowed_groups"];
376
		} else {
377
			$allowed_groups = @radius_get_groups($_SESSION['user_radius_attributes']);
378
			$_SESSION["radius_allowed_groups"] = $allowed_groups;
379
			$_SESSION["auth_check_time"] = time();
380
		}
381
	}
382

    
383
	if (empty($allowed_groups)) {
384
		$allowed_groups = local_user_get_groups($user, true);
385
	}
386

    
387
	if (!is_array($allowed_groups)) {
388
		$allowed_groups = array('all');
389
	} else {
390
		$allowed_groups[] = 'all';
391
	}
392

    
393
	foreach ($allowed_groups as $name) {
394
		$group = getGroupEntry($name);
395
		if (is_array($group['priv'])) {
396
			$privs = array_merge($privs, $group['priv']);
397
		}
398
	}
399

    
400
	return $privs;
401
}
402

    
403
function userHasPrivilege($userent, $privid = false) {
404
	if (!$privid || !is_array($userent)) {
405
		return false;
406
	}
407

    
408
	$privs = get_user_privileges($userent);
409

    
410
	if (!is_array($privs)) {
411
		return false;
412
	}
413

    
414
	if (!in_array($privid, $privs)) {
415
		return false;
416
	}
417

    
418
	/* If someone is in admins group or is admin, do not honor the
419
	 * user-config-readonly privilege to prevent foot-shooting due to a
420
	 * bad privilege config.
421
	 * https://redmine.pfsense.org/issues/10492 */
422
	$userGroups = getUserGroups($userent['name'],
423
			auth_get_authserver(config_get_path('system/webgui/authmode')),
424
			$_SESSION['user_radius_attributes']);
425
	if (($privid == 'user-config-readonly') &&
426
	    (($userent['uid'] === "0") || (in_array('admins', $userGroups)))) {
427
		return false;
428
	}
429

    
430
	return true;
431
}
432

    
433
function local_backed($username, $passwd) {
434

    
435
	$user = getUserEntry($username);
436
	if (!$user) {
437
		return false;
438
	}
439

    
440
	if (is_account_disabled($username) || is_account_expired($username)) {
441
		return false;
442
	}
443

    
444
	if ($user['bcrypt-hash']) {
445
		if (password_verify($passwd, $user['bcrypt-hash'])) {
446
			return true;
447
		}
448
	}
449

    
450
	if ($user['sha512-hash']) {
451
		if (hash_equals($user['sha512-hash'], crypt($passwd, $user['sha512-hash']))) {
452
			return true;
453
		}
454
	}
455

    
456
	// pfSense < 2.3 password hashing, see https://redmine.pfsense.org/issues/4120
457
	if ($user['password']) {
458
		if (hash_equals($user['password'], crypt($passwd, $user['password']))) {
459
			return true;
460
		}
461
	}
462

    
463
	if ($user['md5-hash']) {
464
		if (hash_equals($user['md5-hash'], md5($passwd))) {
465
			return true;
466
		}
467
	}
468

    
469
	return false;
470
}
471

    
472
function local_sync_accounts($u2add, $u2del, $g2add, $g2del) {
473
	global $debug;
474

    
475
	if (empty($u2add) && empty($u2del) && empty($g2add) && empty($g2del)) {
476
		/* Nothing to be done here */
477
		return;
478
	}
479
	$users = config_get_path('system/user', []);
480
	$groups = config_get_path('system/group', []);
481

    
482
	foreach($u2del as $user) {
483
		if ($user['uid'] > 65000) {
484
			continue;
485
		} else if ($user['uid'] < 2000 && !in_array($user, $u2add)) {
486
			continue;
487
		}
488
		/* Don't remove /root */
489
		if ($user['uid'] != 0) {
490
			$rmhome = escapeshellarg('-r');
491
		} else {
492
			$rmhome = '';
493
		}
494

    
495
		/*
496
		 * If a crontab was created to user, pw userdel will be
497
		 * interactive and can cause issues. Just remove crontab
498
		 * before run it when necessary
499
		 */
500
		unlink_if_exists("/var/cron/tabs/{$user['name']}");
501
		$cmd = "/usr/sbin/pw userdel -n " . escapeshellarg($user['name']) . " " . $rmhome;
502
		if ($debug) {
503
			log_error(sprintf(gettext("Running: %s"), $cmd));
504
		}
505
		mwexec($cmd);
506
		local_group_del_user($user);
507

    
508
		for ($i = 0; $i < count($users); $i++) {
509
			if ($users[$i]['name'] == $user['name']) {
510
				log_error("Removing user: {$user['name']}");
511
				unset($users[$i]);
512
				break;
513
			}
514
		}
515
	}
516

    
517
	foreach($g2del as $group) {
518
		if ($group['gid'] < 1999 || $group['gid'] > 65000) {
519
			continue;
520
		}
521

    
522
		$cmd = "/usr/sbin/pw groupdel -g " .
523
		    escapeshellarg($group['name']);
524
		if ($debug) {
525
			log_error(sprintf(gettext("Running: %s"), $cmd));
526
		}
527
		mwexec($cmd);
528

    
529
		for ($i = 0; $i < count($groups); $i++) {
530
			if ($groups[$i]['name'] == $group['name']) {
531
				log_error("Removing group: {$group['name']}");
532
				unset($groups[$i]);
533
				break;
534
			}
535
		}
536
	}
537

    
538
	foreach ($u2add as $user) {
539
		log_error("Adding user: {$user['name']}");
540

    
541
		$users[] = $user;
542
	}
543

    
544
	foreach ($g2add as $group) {
545
		log_error("Adding group: {$group['name']}");
546
		$groups[] = $group;
547
	}
548

    
549
	/* Sort it alphabetically */
550
	usort($users, function($a, $b) {
551
		return strcmp($a['name'], $b['name']);
552
	});
553
	usort($groups, function($a, $b) {
554
		return strcmp($a['name'], $b['name']);
555
	});
556

    
557
	config_set_path('system/user', $users);
558
	config_set_path('system/group', $groups);
559
	write_config("Sync'd users and groups via XMLRPC");
560

    
561
	/* make sure the all group exists */
562
	$allgrp = getGroupEntryByGID(1998);
563
	local_group_set($allgrp, true);
564

    
565
	foreach ($u2add as $user) {
566
		local_user_set($user);
567
	}
568

    
569
	foreach ($g2add as $group) {
570
		local_group_set($group);
571
	}
572
}
573

    
574
function local_reset_accounts() {
575
	global $debug;
576

    
577
	/* remove local users to avoid uid conflicts */
578
	$fd = popen("/usr/sbin/pw usershow -a", "r");
579
	if ($fd) {
580
		while (!feof($fd)) {
581
			$line = explode(":", fgets($fd));
582
			if ($line[0] != "admin") {
583
				if (!strncmp($line[0], "_", 1)) {
584
					continue;
585
				}
586
				if ($line[2] < 2000) {
587
					continue;
588
				}
589
				if ($line[2] > 65000) {
590
					continue;
591
				}
592
			}
593
			/*
594
			 * If a crontab was created to user, pw userdel will be interactive and
595
			 * can cause issues. Just remove crontab before run it when necessary
596
			 */
597
			unlink_if_exists("/var/cron/tabs/{$line[0]}");
598
			$cmd = "/usr/sbin/pw userdel -n " . escapeshellarg($line[0]);
599
			if ($debug) {
600
				log_error(sprintf(gettext("Running: %s"), $cmd));
601
			}
602
			mwexec($cmd);
603
		}
604
		pclose($fd);
605
	}
606

    
607
	/* remove local groups to avoid gid conflicts */
608
	$gids = array();
609
	$fd = popen("/usr/sbin/pw groupshow -a", "r");
610
	if ($fd) {
611
		while (!feof($fd)) {
612
			$line = explode(":", fgets($fd));
613
			if (!strncmp($line[0], "_", 1)) {
614
				continue;
615
			}
616
			if ($line[2] < 2000) {
617
				continue;
618
			}
619
			if ($line[2] > 65000) {
620
				continue;
621
			}
622
			$cmd = "/usr/sbin/pw groupdel -g " . escapeshellarg($line[2]);
623
			if ($debug) {
624
				log_error(sprintf(gettext("Running: %s"), $cmd));
625
			}
626
			mwexec($cmd);
627
		}
628
		pclose($fd);
629
	}
630

    
631
	/* make sure the all group exists */
632
	$allgrp = getGroupEntryByGID(1998);
633
	local_group_set($allgrp, true);
634

    
635
	/* sync all local users */
636
	foreach (config_get_path('system/user', []) as $user) {
637
		local_user_set($user);
638
	}
639

    
640
	/* sync all local groups */
641
	foreach (config_get_path('system/group', []) as $group) {
642
		local_group_set($group);
643
	}
644
}
645

    
646
function local_user_set(& $user) {
647
	global $g, $debug;
648

    
649
	if (empty($user['sha512-hash']) && empty($user['bcrypt-hash']) && empty($user['password'])) {
650
		log_error("There is something wrong in the config because user {$user['name']} password is missing!");
651
		return;
652
	}
653

    
654

    
655
	$home_base = "/home/";
656
	$user_uid = $user['uid'];
657
	$user_name = $user['name'];
658
	$user_home = "{$home_base}{$user_name}";
659
	$user_shell = "/etc/rc.initial";
660
	$user_group = "nobody";
661

    
662
	// Ensure $home_base exists and is writable
663
	if (!is_dir($home_base)) {
664
		mkdir($home_base, 0755);
665
	}
666

    
667
	$lock_account = false;
668
	/* configure shell type */
669
	/* Cases here should be ordered by most privileged to least privileged. */
670
	if (userHasPrivilege($user, "user-shell-access") || userHasPrivilege($user, "page-all")) {
671
		$user_shell = "/bin/tcsh";
672
		$shell_access = true;
673
	} elseif (userHasPrivilege($user, "user-copy-files-chroot")) {
674
		$user_shell = "/usr/local/sbin/scponlyc";
675
	} elseif (userHasPrivilege($user, "user-copy-files")) {
676
		$user_shell = "/usr/local/bin/scponly";
677
	} elseif (userHasPrivilege($user, "user-ssh-tunnel")) {
678
		$user_shell = "/usr/local/sbin/ssh_tunnel_shell";
679
	} elseif (userHasPrivilege($user, "user-ipsec-xauth-dialin")) {
680
		$user_shell = "/sbin/nologin";
681
	} else {
682
		$user_shell = "/sbin/nologin";
683
		$lock_account = true;
684
	}
685

    
686
	/* Lock out disabled or expired users, unless it's root/admin. */
687
	if ((is_account_disabled($user_name) || is_account_expired($user_name)) && ($user_uid != 0)) {
688
		$user_shell = "/sbin/nologin";
689
		$lock_account = true;
690
	}
691

    
692
	/* root user special handling */
693
	if ($user_uid == 0) {
694
		$cmd = "/usr/sbin/pw usermod -q -n root -s /bin/sh -H 0";
695
		if ($debug) {
696
			log_error(sprintf(gettext("Running: %s"), $cmd));
697
		}
698
		$fd = popen($cmd, "w");
699
		if (isset($user['bcrypt-hash'])) {
700
			fwrite($fd, $user['bcrypt-hash']);
701
		} elseif (isset($user['sha512-hash'])) {
702
			fwrite($fd, $user['sha512-hash']);
703
		} else {
704
			fwrite($fd, $user['password']);
705
		}
706
		pclose($fd);
707
		$user_group = "wheel";
708
		$user_home = "/root";
709
		$user_shell = "/etc/rc.initial";
710
		$shell_access = true;
711
	}
712

    
713
	/* read from pw db */
714
	$fd = popen("/usr/sbin/pw usershow -n {$user_name} 2>&1", "r");
715
	$pwread = fgets($fd);
716
	pclose($fd);
717
	$userattrs = explode(":", trim($pwread));
718

    
719
	$skel_dir = '/etc/skel';
720

    
721
	/* determine add or mod */
722
	if (($userattrs[0] != $user['name']) || (!strncmp($pwread, "pw:", 3))) {
723
		$user_op = "useradd -m -k " . escapeshellarg($skel_dir) . " -o";
724
	} else {
725
		$user_op = "usermod";
726
	}
727

    
728
	$comment = str_replace(array(":", "!", "@"), " ", $user['descr']);
729
	/* add or mod pw db */
730
	$cmd = "/usr/sbin/pw {$user_op} -q " .
731
			" -u " . escapeshellarg($user_uid) .
732
			" -n " . escapeshellarg($user_name) .
733
			" -g " . escapeshellarg($user_group) .
734
			" -s " . escapeshellarg($user_shell) .
735
			" -d " . escapeshellarg($user_home) .
736
			" -c " . escapeshellarg($comment) .
737
			" -H 0 2>&1";
738

    
739
	if ($debug) {
740
		log_error(sprintf(gettext("Running: %s"), $cmd));
741
	}
742
	$fd = popen($cmd, "w");
743
	if (isset($user['bcrypt-hash'])) {
744
		fwrite($fd, $user['bcrypt-hash']);
745
	} elseif (isset($user['sha512-hash'])) {
746
		fwrite($fd, $user['sha512-hash']);
747
	} else {
748
		fwrite($fd, $user['password']);
749
	}
750
	pclose($fd);
751

    
752
	/* create user directory if required */
753
	if (!is_dir($user_home)) {
754
		mkdir($user_home, 0700);
755
	}
756
	@chown($user_home, $user_name);
757
	@chgrp($user_home, $user_group);
758

    
759
	/* Make sure all users have last version of config files */
760
	foreach (glob("{$skel_dir}/dot.*") as $dot_file) {
761
		$target = $user_home . '/' . substr(basename($dot_file), 3);
762
		@copy($dot_file, $target);
763
		@chown($target, $user_name);
764
		@chgrp($target, $user_group);
765
	}
766

    
767
	/* write out ssh authorized key file */
768
	$sshdir = "{$user_home}/.ssh";
769
	if ($user['authorizedkeys']) {
770
		if (!is_dir("{$user_home}/.ssh")) {
771
			@mkdir($sshdir, 0700);
772
			@chown($sshdir, $user_name);
773
		} elseif (file_exists($sshdir) && (fileowner($sshdir) != $user['uid'])) {
774
			@chown($sshdir, $user_name);
775
			@chown("{$sshdir}/*", $user_name);
776
		}
777
		$keys = base64_decode($user['authorizedkeys']);
778
		@file_put_contents("{$user_home}/.ssh/authorized_keys", $keys);
779
		@chown("{$user_home}/.ssh/authorized_keys", $user_name);
780
	} else {
781
		unlink_if_exists("{$user_home}/.ssh/authorized_keys");
782
	}
783

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

    
787
}
788

    
789
function local_user_del($user) {
790
	global $debug;
791

    
792
	/* Don't remove /root */
793
	if ($user['uid'] != 0) {
794
		$rmhome = "-r";
795
	}
796

    
797
	/* read from pw db */
798
	$fd = popen("/usr/sbin/pw usershow -n {$user['name']} 2>&1", "r");
799
	$pwread = fgets($fd);
800
	pclose($fd);
801
	$userattrs = explode(":", trim($pwread));
802

    
803
	if ($userattrs[0] != $user['name']) {
804
		log_error("Tried to remove user {$user['name']} but got user {$userattrs[0]} instead. Bailing.");
805
		return;
806
	}
807

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

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

    
816
	/* Delete user from groups needs a call to write_config() */
817
	local_group_del_user($user);
818
}
819

    
820
function local_user_set_password(&$user, $password) {
821
	unset($user['password']);
822
	unset($user['md5-hash']);
823
	unset($user['sha512-hash']);
824
	unset($user['bcrypt-hash']);
825

    
826
	/* Default to bcrypt hashing if unset.
827
	 * See https://redmine.pfsense.org/issues/12855
828
	 */
829
	$hashalgo = config_get_path('system/webgui/pwhash', 'bcrypt');
830

    
831
	switch ($hashalgo) {
832
		case 'sha512':
833
			$salt = substr(bin2hex(random_bytes(16)),0,16);
834
			$user['sha512-hash'] = crypt($password, '$6$'. $salt . '$');
835
			break;
836
		case 'bcrypt':
837
		default:
838
			$user['bcrypt-hash'] = password_hash($password, PASSWORD_BCRYPT);
839
			break;
840
	}
841

    
842
	if (($user['name'] == config_get_path('hasync/username')) &&
843
	    (config_get_path('hasync/adminsync') == 'on')) {
844
		config_set_path('hasync/new_password', $password);
845
	}
846
}
847

    
848
function local_user_get_groups($user, $all = false) {
849
	$sysgroups = config_get_path('system/group', []);
850
	$groups = [];
851
	if(empty($sysgroups)) {
852
		return $groups;
853
	}
854

    
855
	foreach ($sysgroups as $group) {
856
		if ($all || (!$all && ($group['name'] != "all"))) {
857
			if (is_array($group['member'])) {
858
				if (in_array($user['uid'], $group['member'])) {
859
					$groups[] = $group['name'];
860
				}
861
			}
862
		}
863
	}
864

    
865
	if ($all) {
866
		$groups[] = "all";
867
	}
868

    
869
	sort($groups);
870

    
871
	return $groups;
872

    
873
}
874

    
875
function local_user_set_groups($user, $new_groups = NULL) {
876
	global $debug, $groupindex, $userindex;
877

    
878
	$groups = config_get_path('system/group', []);
879
	if (empty($groups)) {
880
		return;
881
	}
882

    
883
	$cur_groups = local_user_get_groups($user, true);
884
	$mod_groups = array();
885

    
886
	if (!is_array($new_groups)) {
887
		$new_groups = array();
888
	}
889

    
890
	if (!is_array($cur_groups)) {
891
		$cur_groups = array();
892
	}
893

    
894
	/* determine which memberships to add */
895
	foreach ($new_groups as $groupname) {
896
		if ($groupname == '' || in_array($groupname, $cur_groups)) {
897
			continue;
898
		}
899
		$ngroup = &$groups[$groupindex[$groupname]];
900
		$ngroup['member'][] = $user['uid'];
901
		$mod_groups[] = $ngroup;
902

    
903
		/*
904
		 * If it's a new user, make sure it is added before try to
905
		 * add it as a member of a group
906
		 */
907
		if (!isset($userindex[$user['uid']])) {
908
			local_user_set($user);
909
		}
910
	}
911
	/* Clear $ngroup reference.
912
	 * https://redmine.pfsense.org/issues/14363 */
913
	unset($ngroup);
914

    
915
	/* determine which memberships to remove */
916
	foreach ($cur_groups as $groupname) {
917
		if (in_array($groupname, $new_groups)) {
918
			continue;
919
		}
920
		if (!isset($groups[$groupindex[$groupname]])) {
921
			continue;
922
		}
923
		$cgroup = &$groups[$groupindex[$groupname]];
924
		if (is_array($cgroup['member'])) {
925
			$index = array_search($user['uid'], $cgroup['member']);
926
			array_splice($cgroup['member'], $index, 1);
927
			$mod_groups[] = $cgroup;
928
		}
929
	}
930
	/* Clear $cgroup reference.
931
	 * https://redmine.pfsense.org/issues/14363 */
932
	unset($cgroup);
933

    
934
	config_set_path('system/group', $groups);
935

    
936
	/* sync all modified groups */
937
	foreach ($mod_groups as $mgroup) {
938
		local_group_set($mgroup);
939
	}
940
}
941

    
942
function local_group_del_user($user) {
943
	foreach (config_get_path('system/group', []) as $gid => $group) {
944
		foreach (array_get_path($group, 'member', []) as $idx => $uid) {
945
			if ($user['uid'] == $uid) {
946
				config_del_path("system/group/{$gid}/member/{$idx}");
947
				break;
948
			}
949
		}
950
	}
951
}
952

    
953
function local_group_set($group, $reset = false) {
954
	global $debug;
955

    
956
	$group_name = $group['name'];
957
	$group_gid = $group['gid'];
958
	$group_members = '';
959

    
960
	if (!$reset && !empty($group['member']) && count($group['member']) > 0) {
961
		$group_members = implode(",", $group['member']);
962
	}
963

    
964
	if (empty($group_name)) {
965
		return;
966
	}
967

    
968
	// If the group is now remote, make sure there is no local group with the same name
969
	if ($group['scope'] == "remote") {
970
		local_group_del($group);
971
		return;
972
	}
973

    
974
	/* determine add or mod */
975
	if (mwexec("/usr/sbin/pw groupshow -g " . escapeshellarg($group_gid) . " 2>&1", true) == 0) {
976
		$group_op = "groupmod -l";
977
	} else {
978
		$group_op = "groupadd -n";
979
	}
980

    
981
	/* add or mod group db */
982
	$cmd = "/usr/sbin/pw {$group_op} " .
983
		escapeshellarg($group_name) .
984
		" -g " . escapeshellarg($group_gid) .
985
		" -M " . escapeshellarg($group_members) . " 2>&1";
986

    
987
	if ($debug) {
988
		log_error(sprintf(gettext("Running: %s"), $cmd));
989
	}
990

    
991
	mwexec($cmd);
992
}
993

    
994
function local_group_del($group) {
995
	global $debug;
996

    
997
	/* delete from group db */
998
	$cmd = "/usr/sbin/pw groupdel " . escapeshellarg($group['name']);
999

    
1000
	if ($debug) {
1001
		log_error(sprintf(gettext("Running: %s"), $cmd));
1002
	}
1003
	mwexec($cmd);
1004
}
1005

    
1006
function ldap_test_connection($authcfg) {
1007
	if ($authcfg) {
1008
		if (strstr($authcfg['ldap_urltype'], "SSL")) {
1009
			$ldapproto = "ldaps";
1010
		} else {
1011
			$ldapproto = "ldap";
1012
		}
1013
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1014
		$ldapport = $authcfg['ldap_port'];
1015
		if (!empty($ldapport)) {
1016
			$ldapserver .= ":{$ldapport}";
1017
		}
1018
	} else {
1019
		return false;
1020
	}
1021

    
1022
	/* first check if there is even an LDAP server populated */
1023
	if (!$ldapserver) {
1024
		return false;
1025
	}
1026

    
1027
	/* connect and see if server is up */
1028
	$error = false;
1029
	if (!($ldap = ldap_connect($ldapserver))) {
1030
		$error = true;
1031
	}
1032

    
1033
	if ($error == true) {
1034
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $authcfg['name']));
1035
		return false;
1036
	}
1037

    
1038
	/* Setup CA environment if needed. */
1039
	ldap_setup_caenv($ldap, $authcfg);
1040

    
1041
	return true;
1042
}
1043

    
1044
function ldap_setup_caenv($ldap, $authcfg) {
1045
	global $g;
1046
	require_once("certs.inc");
1047

    
1048
	unset($caref);
1049
	if (empty($authcfg['ldap_caref']) || strstr($authcfg['ldap_urltype'], "Standard")) {
1050
		ldap_set_option($ldap, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER);
1051
		return;
1052
	} elseif ($authcfg['ldap_caref'] == "global") {
1053
		ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR, "/etc/ssl/");
1054
		ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, "/etc/ssl/cert.pem");
1055
		ldap_set_option($ldap, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_HARD);
1056
	} else {
1057
		$caref = lookup_ca($authcfg['ldap_caref']);
1058
		$cert_details = openssl_x509_parse(base64_decode($caref['crt']));
1059
		$param = array('caref' => $authcfg['ldap_caref']);
1060
		$cachain = ca_chain($param);
1061
		if (!$caref) {
1062
			log_error(sprintf(gettext("LDAP: Could not lookup CA by reference for host %s."), $authcfg['ldap_caref']));
1063
			/* XXX: Prevent for credential leaking since we cannot setup the CA env. Better way? */
1064
			ldap_set_option($ldap, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_HARD);
1065
			return;
1066
		}
1067

    
1068
		$cert_path = "{$g['varrun_path']}/certs";
1069
		$cert_filename = "{$cert_path}/{$cert_details['hash']}.0";
1070
		safe_mkdir($cert_path);
1071
		unlink_if_exists($cert_filename);
1072
		file_put_contents($cert_filename, $cachain);
1073
		@chmod($cert_filename, 0600);
1074

    
1075
		ldap_set_option($ldap, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_HARD);
1076
		ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR, $cert_path);
1077
		ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, $cert_filename);
1078
	}
1079
}
1080

    
1081
function ldap_test_bind($authcfg) {
1082
	if ($authcfg) {
1083
		if (strstr($authcfg['ldap_urltype'], "SSL")) {
1084
			$ldapproto = "ldaps";
1085
		} else {
1086
			$ldapproto = "ldap";
1087
		}
1088
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1089
		$ldapport = $authcfg['ldap_port'];
1090
		if (!empty($ldapport)) {
1091
			$ldapserver .= ":{$ldapport}";
1092
		}
1093
		$ldapbasedn = $authcfg['ldap_basedn'];
1094
		$ldapbindun = $authcfg['ldap_binddn'];
1095
		$ldapbindpw = $authcfg['ldap_bindpw'];
1096
		$ldapver = $authcfg['ldap_protver'];
1097
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
1098
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1099
			$ldapanon = true;
1100
		} else {
1101
			$ldapanon = false;
1102
		}
1103
	} else {
1104
		return false;
1105
	}
1106

    
1107
	/* first check if there is even an LDAP server populated */
1108
	if (!$ldapserver) {
1109
		return false;
1110
	}
1111

    
1112
	/* connect and see if server is up */
1113
	$error = false;
1114
	if (!($ldap = ldap_connect($ldapserver))) {
1115
		$error = true;
1116
	}
1117

    
1118
	if ($error == true) {
1119
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
1120
		return false;
1121
	}
1122

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

    
1126
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1127
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1128
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1129
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1130
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1131

    
1132
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
1133
		if (!(@ldap_start_tls($ldap))) {
1134
			log_error(sprintf(gettext("ERROR! ldap_test_bind() could not STARTTLS to server %s."), $ldapname));
1135
			@ldap_close($ldap);
1136
			return false;
1137
		}
1138
	}
1139

    
1140
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1141
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1142
	if ($ldapanon == true) {
1143
		if (!($res = @ldap_bind($ldap))) {
1144
			@ldap_close($ldap);
1145
			return false;
1146
		}
1147
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1148
		@ldap_close($ldap);
1149
		return false;
1150
	}
1151

    
1152
	@ldap_unbind($ldap);
1153

    
1154
	return true;
1155
}
1156

    
1157
function ldap_get_user_ous($show_complete_ou, $authcfg) {
1158
	if (!function_exists("ldap_connect")) {
1159
		return;
1160
	}
1161

    
1162
	$ous = array();
1163

    
1164
	if ($authcfg) {
1165
		if (strstr($authcfg['ldap_urltype'], "SSL")) {
1166
			$ldapproto = "ldaps";
1167
		} else {
1168
			$ldapproto = "ldap";
1169
		}
1170
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1171
		$ldapport = $authcfg['ldap_port'];
1172
		if (!empty($ldapport)) {
1173
			$ldapserver .= ":{$ldapport}";
1174
		}
1175
		$ldapbasedn = $authcfg['ldap_basedn'];
1176
		$ldapbindun = $authcfg['ldap_binddn'];
1177
		$ldapbindpw = $authcfg['ldap_bindpw'];
1178
		$ldapver = $authcfg['ldap_protver'];
1179
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1180
			$ldapanon = true;
1181
		} else {
1182
			$ldapanon = false;
1183
		}
1184
		$ldapname = $authcfg['name'];
1185
		$ldapfallback = false;
1186
		$ldapscope = $authcfg['ldap_scope'];
1187
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
1188
	} else {
1189
		return false;
1190
	}
1191

    
1192
	/* first check if there is even an LDAP server populated */
1193
	if (!$ldapserver) {
1194
		log_error(gettext("ERROR!  ldap_get_user_ous() backed selected with no LDAP authentication server defined."));
1195
		return $ous;
1196
	}
1197

    
1198
	/* connect and see if server is up */
1199
	$error = false;
1200
	if (!($ldap = ldap_connect($ldapserver))) {
1201
		$error = true;
1202
	}
1203

    
1204
	if ($error == true) {
1205
		log_error(sprintf(gettext("ERROR!  Could not connect to server %s."), $ldapname));
1206
		return $ous;
1207
	}
1208

    
1209
	/* Setup CA environment if needed. */
1210
	ldap_setup_caenv($ldap, $authcfg);
1211

    
1212
	$ldapfilter = "(|(ou=*)(cn=Users))";
1213

    
1214
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1215
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1216
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1217
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1218
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1219

    
1220
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
1221
		if (!(@ldap_start_tls($ldap))) {
1222
			log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not STARTTLS to server %s."), $ldapname));
1223
			@ldap_close($ldap);
1224
			return false;
1225
		}
1226
	}
1227

    
1228
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1229
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1230
	if ($ldapanon == true) {
1231
		if (!($res = @ldap_bind($ldap))) {
1232
			log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind anonymously to server %s."), $ldapname));
1233
			@ldap_close($ldap);
1234
			return $ous;
1235
		}
1236
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1237
		log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind to server %s."), $ldapname));
1238
		@ldap_close($ldap);
1239
		return $ous;
1240
	}
1241

    
1242
	if ($ldapscope == "one") {
1243
		$ldapfunc = "ldap_list";
1244
	} else {
1245
		$ldapfunc = "ldap_search";
1246
	}
1247

    
1248
	if (!($search = @$ldapfunc($ldap, $ldapbasedn, $ldapfilter))) {
1249
		goto errout;
1250
	}
1251

    
1252
	if (!is_array($info = @ldap_get_entries($ldap, $search))) {
1253
		goto errout;
1254
	}
1255

    
1256
	foreach ($info as $inf) {
1257
		if (!$show_complete_ou) {
1258
			$inf_split = explode(",", $inf['dn']);
1259
			$ou = $inf_split[0];
1260
			$ou = str_replace("OU=", "", $ou);
1261
			$ou = str_replace("CN=", "", $ou);
1262
		} else {
1263
			if ($inf['dn']) {
1264
				$ou = $inf['dn'];
1265
			}
1266
		}
1267
		if ($ou) {
1268
			$ous[] = $ou;
1269
		}
1270
	}
1271

    
1272
errout: // goto to bailout early
1273

    
1274
	@ldap_unbind($ldap);
1275

    
1276
	return $ous;
1277
}
1278

    
1279
function ldap_get_groups($username, $authcfg) {
1280
	if (!function_exists("ldap_connect")) {
1281
		return array();
1282
	}
1283

    
1284
	if (!$username) {
1285
		return array();
1286
	}
1287

    
1288
	if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) {
1289
		$username_split = explode("@", $username);
1290
		$username = $username_split[0];
1291
	}
1292

    
1293
	if (stristr($username, "\\")) {
1294
		$username_split = explode("\\", $username);
1295
		$username = $username_split[0];
1296
	}
1297

    
1298
	//log_error("Getting LDAP groups for {$username}.");
1299
	if ($authcfg) {
1300
		if (strstr($authcfg['ldap_urltype'], "SSL")) {
1301
			$ldapproto = "ldaps";
1302
		} else {
1303
			$ldapproto = "ldap";
1304
		}
1305
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1306
		$ldapport = $authcfg['ldap_port'];
1307
		if (!empty($ldapport)) {
1308
			$ldapserver .= ":{$ldapport}";
1309
		}
1310
		$ldapbasedn = $authcfg['ldap_basedn'];
1311
		$ldapbindun = $authcfg['ldap_binddn'];
1312
		$ldapbindpw = $authcfg['ldap_bindpw'];
1313
		$ldapauthcont = $authcfg['ldap_authcn'];
1314
		$ldapnameattribute = strtolower($authcfg['ldap_attr_user']);
1315
		$ldapgroupattribute = strtolower($authcfg['ldap_attr_member']);
1316
		$ldapver = $authcfg['ldap_protver'];
1317
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1318
			$ldapanon = true;
1319
		} else {
1320
			$ldapanon = false;
1321
		}
1322
		$ldapname = $authcfg['name'];
1323
		$ldapfallback = false;
1324
		$ldapscope = $authcfg['ldap_scope'];
1325
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
1326
	} else {
1327
		return array();
1328
	}
1329

    
1330
	if (isset($authcfg['ldap_rfc2307'])) {
1331
		$ldapdn = $ldapbasedn;
1332
	} else {
1333
		$ldapdn = $_SESSION['ldapdn'];
1334
	}
1335

    
1336
	/*Convert attribute to lowercase.  php ldap arrays put everything in lowercase */
1337
	$ldapgroupattribute = strtolower($ldapgroupattribute);
1338
	$memberof = array();
1339

    
1340
	/* connect and see if server is up */
1341
	$error = false;
1342
	if (!($ldap = ldap_connect($ldapserver))) {
1343
		$error = true;
1344
	}
1345

    
1346
	if ($error == true) {
1347
		log_error(sprintf(gettext("ERROR! ldap_get_groups() Could not connect to server %s."), $ldapname));
1348
		return $memberof;
1349
	}
1350

    
1351
	/* Setup CA environment if needed. */
1352
	ldap_setup_caenv($ldap, $authcfg);
1353

    
1354
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1355
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1356
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1357
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1358
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1359

    
1360
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
1361
		if (!(@ldap_start_tls($ldap))) {
1362
			log_error(sprintf(gettext("ERROR! ldap_get_groups() could not STARTTLS to server %s."), $ldapname));
1363
			@ldap_close($ldap);
1364
			return array();
1365
		}
1366
	}
1367

    
1368
	/* bind as user that has rights to read group attributes */
1369
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1370
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1371
	if ($ldapanon == true) {
1372
		if (!($res = @ldap_bind($ldap))) {
1373
			log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind anonymously to server %s."), $ldapname));
1374
			@ldap_close($ldap);
1375
			return array();
1376
		}
1377
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1378
		log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind to server %s."), $ldapname));
1379
		@ldap_close($ldap);
1380
		return $memberof;
1381
	}
1382

    
1383
	/* get groups from DN found */
1384
	/* use ldap_read instead of search so we don't have to do a bunch of extra work */
1385
	/* since we know the DN is in $_SESSION['ldapdn'] */
1386
	//$search    = ldap_read($ldap, $ldapdn, "(objectclass=*)", array($ldapgroupattribute));
1387
	if ($ldapscope == "one") {
1388
		$ldapfunc = "ldap_list";
1389
	} else {
1390
		$ldapfunc = "ldap_search";
1391
	}
1392

    
1393
	if (isset($authcfg['ldap_rfc2307'])) {
1394
		if (isset($authcfg['ldap_rfc2307_userdn'])) {
1395
			$has_userinfo = false;
1396
			$ldac_splits = explode(";", $ldapauthcont);
1397
			foreach ($ldac_splits as $i => $ldac_split) {
1398
				$ldac_split = isset($authcfg['ldap_utf8']) ? utf8_encode($ldac_split) : $ldac_split;
1399
				$ldapsearchbasedn = isset($authcfg['ldap_utf8']) ? utf8_encode("{$ldac_split},{$ldapbasedn}") : "{$ldac_split},{$ldapbasedn}";
1400
				$ldapfilter = "({$ldapnameattribute}={$username})";
1401
				if (stristr($ldac_split, "DC=") || empty($ldapbasedn)) {
1402
					$ldapdn = $ldac_split;
1403
				} else {
1404
					$ldapdn = $ldapsearchbasedn;
1405
				}
1406
				if (($usersearch = @$ldapfunc($ldap, $ldapdn, $ldapfilter))) {
1407
					$userinfo = @ldap_get_entries($ldap, $usersearch);
1408
					$has_userinfo = true;
1409
				}
1410
			}
1411
			/* we can bailout early if the above query fails in rfc2307 mode */
1412
			if (!$has_userinfo) {
1413
				goto errout;
1414
			}
1415
			$username = $userinfo[0]['dn'];
1416
		}
1417
		$ldapfilter = "(&(objectClass={$authcfg['ldap_attr_groupobj']})({$ldapgroupattribute}={$username}))";
1418
	} else {
1419
		$ldapfilter = "({$ldapnameattribute}={$username})";
1420
	}
1421

    
1422
	if (!($search = @$ldapfunc($ldap, $ldapdn, $ldapfilter, array($ldapgroupattribute)))) {
1423
		goto errout;
1424
	}
1425

    
1426
	$info = @ldap_get_entries($ldap, $search);
1427
	$gresults = isset($authcfg['ldap_rfc2307']) ? $info : $info[0][$ldapgroupattribute];
1428

    
1429
	if (is_array($gresults)) {
1430
		/* Iterate through the groups and throw them into an array */
1431
		foreach ($gresults as $grp) {
1432
			if (((isset($authcfg['ldap_rfc2307'])) && (stristr($grp["dn"], "CN=") !== false)) ||
1433
			    ((!isset($authcfg['ldap_rfc2307'])) && (stristr($grp, "CN=") !== false))) {
1434
				$grpsplit = isset($authcfg['ldap_rfc2307']) ? explode(",", $grp["dn"]) : explode(",", $grp);
1435
				$memberof[] = preg_replace("/CN=/i", "", $grpsplit[0]);
1436
			}
1437
		}
1438
	}
1439

    
1440
errout: // goto to bailout early
1441

    
1442
	/* Time to close LDAP connection */
1443
	@ldap_unbind($ldap);
1444

    
1445
	$groups = print_r($memberof, true);
1446

    
1447
	return $memberof;
1448
}
1449

    
1450
function ldap_format_host($host) {
1451
	return is_ipaddrv6($host) ? "[$host]" : $host ;
1452
}
1453

    
1454
function ldap_backed($username, $passwd, $authcfg, &$attributes = array()) {
1455
	global $debug;
1456

    
1457
	if (!$username) {
1458
		$attributes['error_message'] = gettext("Invalid Login.");
1459
		return false;
1460
	}
1461

    
1462
	if (!isset($authcfg['ldap_allow_unauthenticated']) && $passwd == '') {
1463
		$attributes['error_message'] = gettext("Invalid credentials.");
1464
		return false;
1465
	}
1466

    
1467
	if (!function_exists("ldap_connect")) {
1468
		log_error(gettext("ERROR! unable to find ldap_connect() function."));
1469
		$attributes['error_message'] = gettext("Internal error during authentication.");
1470
		return null;
1471
	}
1472

    
1473
	if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) {
1474
		$username_split = explode("@", $username);
1475
		$username = $username_split[0];
1476
	}
1477
	if (stristr($username, "\\")) {
1478
		$username_split = explode("\\", $username);
1479
		$username = $username_split[0];
1480
	}
1481

    
1482
	$username = ldap_escape($username, null, LDAP_ESCAPE_FILTER);
1483

    
1484
	if ($authcfg) {
1485
		if (strstr($authcfg['ldap_urltype'], "SSL")) {
1486
			$ldapproto = "ldaps";
1487
		} else {
1488
			$ldapproto = "ldap";
1489
		}
1490
		$ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']);
1491
		$ldapport = $authcfg['ldap_port'];
1492
		if (!empty($ldapport)) {
1493
			$ldapserver .= ":{$ldapport}";
1494
		}
1495
		$ldapbasedn = $authcfg['ldap_basedn'];
1496
		$ldapbindun = $authcfg['ldap_binddn'];
1497
		$ldapbindpw = $authcfg['ldap_bindpw'];
1498
		if (empty($ldapbindun) || empty($ldapbindpw)) {
1499
			$ldapanon = true;
1500
		} else {
1501
			$ldapanon = false;
1502
		}
1503
		$ldapauthcont = $authcfg['ldap_authcn'];
1504
		$ldapnameattribute = strtolower($authcfg['ldap_attr_user']);
1505
		$ldapgroupattribute = $authcfg['ldap_attr_member'];
1506
		$ldapextendedqueryenabled = $authcfg['ldap_extended_enabled'];
1507
		if ($ldapextendedqueryenabled) {
1508
			$ldapextendedquery = $authcfg['ldap_extended_query'];
1509
		}
1510
		if (!$ldapextendedqueryenabled) {
1511
			$ldapfilter = "({$ldapnameattribute}={$username})";
1512
		} else {
1513
			if (isset($authcfg['ldap_rfc2307'])) {
1514
				$ldapfilter = "({$ldapnameattribute}={$username})";
1515
				$ldapgroupfilter = "(&({$ldapgroupattribute}={$username})({$ldapextendedquery}))";
1516
			} else {
1517
				$ldapfilter = "(&({$ldapnameattribute}={$username})({$ldapextendedquery}))";
1518
			}
1519
		}
1520
		$ldapver = $authcfg['ldap_protver'];
1521
		$ldapname = $authcfg['name'];
1522
		$ldapscope = $authcfg['ldap_scope'];
1523
		$ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5;
1524
	} else {
1525
		return null;
1526
	}
1527

    
1528
	/* first check if there is even an LDAP server populated */
1529
	if (!$ldapserver) {
1530
		log_error(gettext("ERROR! could not find details of the LDAP server used for authentication."));
1531
		$attributes['error_message'] =  gettext("Internal error during authentication.");
1532
		return null;
1533
	}
1534

    
1535
	if ($debug) {
1536
		log_error(sprintf(gettext("LDAP Debug: Attempting to authenticate %s on %s"), $username, $ldapname));
1537
		log_error(sprintf(gettext("LDAP Debug: URI: %s (v%s)"), $ldapserver, $ldapver));
1538
		log_error(sprintf(gettext("LDAP Debug: Base DN: %s"), $ldapbasedn));
1539
		log_error(sprintf(gettext("LDAP Debug: Scope: %s"), $ldapscope));
1540
		log_error(sprintf(gettext("LDAP Debug: Auth Bind DN: %s"), $ldapbindun));
1541
		log_error(sprintf(gettext("LDAP Debug: Container: %s"), $ldapauthcont));
1542
		log_error(sprintf(gettext("LDAP Debug: Attrs: Name: %s / Group: %s"), $ldapnameattribute, $ldapgroupattribute));
1543
		log_error(sprintf(gettext("LDAP Debug: Extended Query: %s"), $ldapextendedquery));
1544
		log_error(sprintf(gettext("LDAP Debug: Filter: %s"), $ldapfilter));
1545
		log_error(sprintf(gettext("LDAP Debug: Group Filter: %s"), $ldapgroupfilter));
1546
	}
1547

    
1548
	/* Make sure we can connect to LDAP */
1549
	$error = false;
1550
	if (!($ldap = ldap_connect($ldapserver))) {
1551
		$error = true;
1552
	}
1553

    
1554
	if ($debug) {
1555
		log_error(sprintf(gettext("LDAP Debug: LDAP connection error flag: %s"), var_export($error, true)));
1556
	}
1557

    
1558
	/* Setup CA environment if needed. */
1559
	ldap_setup_caenv($ldap, $authcfg);
1560

    
1561
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
1562
	ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING);
1563
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver);
1564
	ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout);
1565
	ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout);
1566

    
1567
	if (strstr($authcfg['ldap_urltype'], "STARTTLS")) {
1568
		if (!(@ldap_start_tls($ldap))) {
1569
			log_error(sprintf(gettext("ERROR! could not connect to LDAP server %s using STARTTLS."), $ldapname));
1570
			$attributes['error_message'] = gettext("Error : could not connect to authentication server.");
1571
			@ldap_close($ldap);
1572
			return null;
1573
		}
1574
	}
1575

    
1576
	if ($error == true) {
1577
		$errormsg = sprintf(gettext("ERROR! Could not connect to server %s."), $ldapname);
1578
		$attributes['error_message'] = gettext("Error : could not connect to authentication server.");
1579
		return null;
1580
	}
1581

    
1582
	/* ok, its up.  now, lets bind as the bind user so we can search it */
1583
	$error = false;
1584
	$ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun;
1585
	$ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw;
1586
	if ($ldapanon == true) {
1587
		if (!($res = @ldap_bind($ldap))) {
1588
			$error = true;
1589
		}
1590
	} else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) {
1591
		$error = true;
1592
	}
1593

    
1594
	if ($error == true) {
1595
		@ldap_close($ldap);
1596
		log_error(sprintf(gettext("ERROR! Could not bind to LDAP server %s. Please check the bind credentials."), $ldapname));
1597
		$attributes['error_message'] = gettext("Error : could not connect to authentication server.");
1598
		return null;
1599
	}
1600

    
1601
	/* Get LDAP Authcontainers and split em up. */
1602
	$ldac_splits = explode(";", $ldapauthcont);
1603

    
1604
	/* setup the usercount so we think we haven't found anyone yet */
1605
	$usercount = 0;
1606

    
1607
	/*****************************************************************/
1608
	/*  We first find the user based on username and filter          */
1609
	/*  then, once we find the first occurrence of that person       */
1610
	/*  we set session variables to point to the OU and DN of the    */
1611
	/*  person.  To later be used by ldap_get_groups.                */
1612
	/*  that way we don't have to search twice.                      */
1613
	/*****************************************************************/
1614
	if ($debug) {
1615
		log_error(sprintf(gettext("LDAP Debug: Now Searching for %s in directory."), $username));
1616
	}
1617
	/* Iterate through the user containers for search */
1618
	foreach ($ldac_splits as $i => $ldac_split) {
1619
		$ldac_split = isset($authcfg['ldap_utf8']) ? utf8_encode($ldac_split) : $ldac_split;
1620
		$ldapfilter = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapfilter) : $ldapfilter;
1621
		$ldapsearchbasedn = isset($authcfg['ldap_utf8']) ? utf8_encode("{$ldac_split},{$ldapbasedn}") : "{$ldac_split},{$ldapbasedn}";
1622
		/* Make sure we just use the first user we find */
1623
		if ($debug) {
1624
			log_error(sprintf(gettext('LDAP Debug: Now searching in server %1$s, container %2$s with filter %3$s.'), $ldapname, utf8_decode($ldac_split), utf8_decode($ldapfilter)));
1625
		}
1626
		if ($ldapscope == "one") {
1627
			$ldapfunc = "ldap_list";
1628
		} else {
1629
			$ldapfunc = "ldap_search";
1630
		}
1631
		/* Support legacy auth container specification. */
1632
		if (stristr($ldac_split, "DC=") || empty($ldapbasedn)) {
1633
			$ldapdn = $ldac_split;
1634
		} else {
1635
			$ldapdn = $ldapsearchbasedn;
1636
		}
1637
		if (!($search = @$ldapfunc($ldap, $ldapdn, $ldapfilter))) {
1638
			log_error(sprintf(gettext("Search resulted in error: %s"), ldap_error($ldap)));
1639
			continue;
1640
		}
1641
		if (isset($authcfg['ldap_rfc2307']) && isset($ldapgroupfilter)) {
1642
			if (isset($authcfg['ldap_rfc2307_userdn'])) {
1643
				$info = ldap_get_entries($ldap, $search);
1644
				$username = $info[0]['dn'];
1645
			}
1646
			$ldapgroupfilter = "(&({$ldapgroupattribute}={$username})({$ldapextendedquery}))";
1647
			$groupsearch = @$ldapfunc($ldap, $ldapdn, $ldapgroupfilter);
1648
			if ($debug) {
1649
				log_error(sprintf(gettext("LDAP Debug: RFC2307 group filter search: %s"), $ldapgroupfilter));
1650
			}
1651
		}
1652

    
1653
		if (isset($ldapgroupfilter) && !$groupsearch) {
1654
			log_error(sprintf(gettext("LDAP Debug: Extended group search resulted in error: %s"), ldap_error($ldap)));
1655
			continue;
1656
		}
1657
		if (isset($groupsearch)) {
1658
			$validgroup = ldap_count_entries($ldap, $groupsearch);
1659
			if ($debug) {
1660
				log_error(sprintf(gettext("LDAP Debug: Group search contains %s results."), $validgroup));
1661
			}
1662
		}
1663
		$info = ldap_get_entries($ldap, $search);
1664
		$matches = $info['count'];
1665
		if ($matches == 1) {
1666
			$userdn = $_SESSION['ldapdn'] = $info[0]['dn'];
1667
			$_SESSION['ldapou'] = $ldac_split[$i];
1668
			$_SESSION['ldapon'] = "true";
1669
			$usercount = 1;
1670
			break;
1671
		}
1672
	}
1673

    
1674
	if ($usercount != 1) {
1675
		@ldap_unbind($ldap);
1676
		if ($debug) {
1677
			if ($usercount === 0) {
1678
				log_error(sprintf(gettext("LDAP Debug: ERROR! LDAP search failed, no user matching %s was found."), $username));
1679
			} else {
1680
				log_error(sprintf(gettext("LDAP Debug: ERROR! LDAP search failed, multiple users matching %s were found."), $username));
1681
			}
1682
		}
1683
		$attributes['error_message'] = gettext("Invalid login specified.");
1684
		return false;
1685
	}
1686

    
1687
	/* Now lets bind as the user we found */
1688
	$passwd = isset($authcfg['ldap_utf8']) ? utf8_encode($passwd) : $passwd;
1689
	if (!($res = @ldap_bind($ldap, $userdn, $passwd))) {
1690
		if ($debug) {
1691
			log_error(sprintf(gettext('LDAP Debug: ERROR! Could not login to server %1$s as user %2$s: %3$s'), $ldapname, $username, ldap_error($ldap)));
1692
		}
1693
		@ldap_unbind($ldap);
1694
		return false;
1695
	}
1696

    
1697
	if ($debug) {
1698
		$userdn = isset($authcfg['ldap_utf8']) ? utf8_decode($userdn) : $userdn;
1699
		log_error(sprintf(gettext('LDAP Debug: Logged in successfully as %1$s via LDAP server %2$s with DN = %3$s.'), $username, $ldapname, $userdn));
1700
	}
1701

    
1702
	if ($debug && isset($ldapgroupfilter) && $validgroup < 1) {
1703
		log_error(sprintf(gettext('LDAP Debug: Logged in successfully as %1$s but did not match any field in extended query.'), $username));
1704
	}
1705

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

    
1709
	if (isset($ldapgroupfilter) && $validgroup < 1) {
1710
		return false;
1711
	}
1712

    
1713
	return true;
1714
}
1715

    
1716
function radius_backed($username, $password, $authcfg, &$attributes = array()) {
1717
	$ret = false;
1718

    
1719
	require_once("Auth/RADIUS.php");
1720
	require_once("Crypt/CHAP.php");
1721

    
1722
	if ($authcfg) {
1723
		$radiusservers = array();
1724
		$radiusservers[0]['ipaddr'] = $authcfg['host'];
1725
		$radiusservers[0]['port'] = $authcfg['radius_auth_port'];
1726
		$radiusservers[0]['sharedsecret'] = $authcfg['radius_secret'];
1727
		$radiusservers[0]['timeout'] = $authcfg['radius_timeout'];
1728
		if(isset($authcfg['radius_protocol'])) {
1729
			$radius_protocol = $authcfg['radius_protocol'];
1730
		} else {
1731
			$radius_protocol = 'PAP';
1732
		}
1733
	} else {
1734
		log_error(gettext("ERROR! could not find details of the RADIUS server used for authentication."));
1735
		$attributes['error_message'] =  gettext("Internal error during authentication.");
1736
		return null;
1737
	}
1738

    
1739
	// Create our instance
1740
	$classname = 'Auth_RADIUS_' . $radius_protocol;
1741
	$rauth = new $classname($username, $password);
1742

    
1743
	/* Add new servers to our instance */
1744
	foreach ($radiusservers as $radsrv) {
1745
		$timeout = (is_numeric($radsrv['timeout'])) ? $radsrv['timeout'] : 5;
1746
		$rauth->addServer($radsrv['ipaddr'], $radsrv['port'], $radsrv['sharedsecret'], $timeout);
1747
	}
1748

    
1749
	// Construct data package
1750
	$rauth->username = $username;
1751
	switch ($radius_protocol) {
1752
		case 'CHAP_MD5':
1753
		case 'MSCHAPv1':
1754
			$classname = $radius_protocol == 'MSCHAPv1' ? 'Crypt_CHAP_MSv1' : 'Crypt_CHAP_MD5';
1755
			$crpt = new $classname;
1756
			$crpt->username = $username;
1757
			$crpt->password = $password;
1758
			$rauth->challenge = $crpt->challenge;
1759
			$rauth->chapid = $crpt->chapid;
1760
			$rauth->response = $crpt->challengeResponse();
1761
			$rauth->flags = 1;
1762
			break;
1763

    
1764
		case 'MSCHAPv2':
1765
			$crpt = new Crypt_CHAP_MSv2;
1766
			$crpt->username = $username;
1767
			$crpt->password = $password;
1768
			$rauth->challenge = $crpt->authChallenge;
1769
			$rauth->peerChallenge = $crpt->peerChallenge;
1770
			$rauth->chapid = $crpt->chapid;
1771
			$rauth->response = $crpt->challengeResponse();
1772
			break;
1773

    
1774
		default:
1775
			$rauth->password = $password;
1776
			break;
1777
	}
1778

    
1779
	if (PEAR::isError($rauth->start())) {
1780
		$ret = null;
1781
		log_error(sprintf(gettext("Error during RADIUS authentication : %s"), $rauth->getError()));
1782
		$attributes['error_message'] = gettext("Error : could not connect to authentication server.");
1783
	} else {
1784
		$nasid = $attributes['nas_identifier'];
1785
		if (empty($nasid)) {
1786
			$nasid = gethostname(); //If no RADIUS NAS-Identifier is given : we use pfsense's hostname as NAS-Identifier
1787
		}
1788
		$nasip = nasip_fallback($authcfg['radius_nasip_attribute']);
1789
		$nasmac = get_interface_mac(find_ip_interface($nasip));
1790

    
1791
		$rauth->putAttribute(RADIUS_NAS_IP_ADDRESS, $nasip, "addr");
1792
		$rauth->putAttribute(RADIUS_NAS_IDENTIFIER, $nasid);
1793

    
1794
		if(!empty($attributes['calling_station_id'])) {
1795
			$rauth->putAttribute(RADIUS_CALLING_STATION_ID, $attributes['calling_station_id']);
1796
		}
1797
		// Carefully check that interface has a MAC address
1798
		if(!empty($nasmac)) {
1799
			$nasmac = mac_format($nasmac);
1800
			$rauth->putAttribute(RADIUS_CALLED_STATION_ID, $nasmac.':'.gethostname());
1801
		}
1802
		if(!empty($attributes['nas_port_type'])) {
1803
			$rauth->putAttribute(RADIUS_NAS_PORT_TYPE, $attributes['nas_port_type']);
1804
		}
1805
		if(!empty($attributes['nas_port'])) {
1806
			$rauth->putAttribute(RADIUS_NAS_PORT, intval($attributes['nas_port']), 'integer');
1807
		}
1808
		if(!empty($attributes['framed_ip']) && is_ipaddr($attributes['framed_ip'])) {
1809
			$rauth->putAttribute(RADIUS_FRAMED_IP_ADDRESS, $attributes['framed_ip'], "addr");
1810
		}
1811
	}
1812

    
1813
	// XXX - billm - somewhere in here we need to handle securid challenge/response
1814

    
1815
	/* Send request */
1816
	$result = $rauth->send();
1817
	if (PEAR::isError($result)) {
1818
		log_error(sprintf(gettext("Error during RADIUS authentication : %s"), $rauth->getError()));
1819
		$attributes['error_message'] = gettext("Error : could not connect to authentication server.");
1820
		$ret = null;
1821
	} else if ($result === true) {
1822
		$ret = true;
1823
	} else {
1824
		$ret = false;
1825
	}
1826

    
1827

    
1828
	// Get attributes, even if auth failed.
1829
	if ($rauth->getAttributes()) {
1830
	$attributes = array_merge($attributes,$rauth->listAttributes());
1831

    
1832
	// We convert the session_terminate_time to unixtimestamp if its set before returning the whole array to our caller
1833
	if (!empty($attributes['session_terminate_time'])) {
1834
			$stt = &$attributes['session_terminate_time'];
1835
			$stt = strtotime(preg_replace("/\+(\d+):(\d+)$/", " +\${1}\${2}", preg_replace("/(\d+)T(\d+)/", "\${1} \${2}",$stt)));
1836
		}
1837
	}
1838

    
1839
	// close OO RADIUS_AUTHENTICATION
1840
	$rauth->close();
1841

    
1842
	return $ret;
1843
}
1844

    
1845
function nasip_fallback($ip) {
1846
	if (!is_ipaddr($ip)) {
1847
		$nasip = get_interface_ip($ip);
1848

    
1849
		if (!is_ipaddr($nasip)) {
1850
			/* use first interface with IP as fallback for NAS-IP-Address
1851
			 * see https://redmine.pfsense.org/issues/11109 */
1852
			foreach (get_configured_interface_list() as $if) {
1853
				$nasip = get_interface_ip($if);
1854
				if (is_ipaddr($nasip)) {
1855
					break;
1856
				}
1857
			}
1858
		}
1859
	} else {
1860
		$nasip = $ip;
1861
	}
1862

    
1863
	return $nasip;
1864
}
1865

    
1866
/*
1867
	$attributes must contain a "class" key containing the groups and local
1868
	groups must exist to match.
1869
*/
1870
function radius_get_groups($attributes) {
1871
	$groups = array();
1872
	if (!empty($attributes) && is_array($attributes) && (!empty($attributes['class']) || !empty($attributes['class_int']))) {
1873
		/* Some RADIUS servers return multiple class attributes, so check them all. */
1874
		$groups = array();
1875
		if (!empty($attributes['class']) && is_array($attributes['class'])) {
1876
			foreach ($attributes['class'] as $class) {
1877
				$groups = array_unique(array_merge($groups, explode(";", $class)));
1878
			}
1879
		}
1880

    
1881
		foreach ($groups as & $grp) {
1882
			$grp = trim($grp);
1883
			if (strtolower(substr($grp, 0, 3)) == "ou=") {
1884
				$grp = substr($grp, 3);
1885
			}
1886
		}
1887
	}
1888
	return $groups;
1889
}
1890

    
1891
function get_user_expiration_date($username) {
1892
	$user = getUserEntry($username);
1893
	if ($user['expires']) {
1894
		return $user['expires'];
1895
	}
1896
}
1897

    
1898
function is_account_expired($username) {
1899
	$expirydate = get_user_expiration_date($username);
1900
	if ($expirydate) {
1901
		if (strtotime("-1 day") > strtotime(date("m/d/Y", strtotime($expirydate)))) {
1902
			return true;
1903
		}
1904
	}
1905

    
1906
	return false;
1907
}
1908

    
1909
function is_account_disabled($username) {
1910
	$user = getUserEntry($username);
1911
	if (isset($user['disabled'])) {
1912
		return true;
1913
	}
1914

    
1915
	return false;
1916
}
1917

    
1918
function get_user_settings($username) {
1919
	$settings = array();
1920
	$settings['widgets'] = config_get_path('widgets');
1921
	$settings['webgui']['dashboardcolumns'] = config_get_path('system/webgui/dashboardcolumns');
1922
	$settings['webgui']['webguihostnamemenu'] = config_get_path('system/webgui/webguihostnamemenu');
1923
	$settings['webgui']['webguicss'] = config_get_path('system/webgui/webguicss');
1924
	$settings['webgui']['logincss'] = config_get_path('system/webgui/logincss');
1925
	$settings['webgui']['interfacessort'] = config_path_enabled('system/webgui', 'interfacessort');
1926
	$settings['webgui']['dashboardavailablewidgetspanel'] = config_path_enabled('system/webgui', 'dashboardavailablewidgetspanel');
1927
	$settings['webgui']['webguifixedmenu'] = config_path_enabled('system/webgui', 'webguifixedmenu');
1928
	$settings['webgui']['webguileftcolumnhyper'] = config_path_enabled('system/webgui', 'webguileftcolumnhyper');
1929
	$settings['webgui']['disablealiaspopupdetail'] = config_path_enabled('system/webgui', 'disablealiaspopupdetail');
1930
	$settings['webgui']['systemlogsfilterpanel'] = config_path_enabled('system/webgui', 'systemlogsfilterpanel');
1931
	$settings['webgui']['systemlogsmanagelogpanel'] = config_path_enabled('system/webgui', 'systemlogsmanagelogpanel');
1932
	$settings['webgui']['statusmonitoringsettingspanel'] = config_path_enabled('system/webgui', 'statusmonitoringsettingspanel');
1933
	$settings['webgui']['pagenamefirst'] = config_path_enabled('system/webgui', 'pagenamefirst');
1934
	$user = getUserEntry($username);
1935
	if (isset($user['customsettings'])) {
1936
		$settings['customsettings'] = true;
1937
		if (isset($user['widgets'])) {
1938
			// This includes the 'sequence', and any widgetname-config per-widget settings.
1939
			$settings['widgets'] = $user['widgets'];
1940
		}
1941
		if (isset($user['dashboardcolumns'])) {
1942
			$settings['webgui']['dashboardcolumns'] = $user['dashboardcolumns'];
1943
		}
1944
		if (isset($user['webguicss'])) {
1945
			$settings['webgui']['webguicss'] = $user['webguicss'];
1946
		}
1947
		if (isset($user['webguihostnamemenu'])) {
1948
			$settings['webgui']['webguihostnamemenu'] = $user['webguihostnamemenu'];
1949
		}
1950
		$settings['webgui']['interfacessort'] = isset($user['interfacessort']);
1951
		$settings['webgui']['dashboardavailablewidgetspanel'] = isset($user['dashboardavailablewidgetspanel']);
1952
		$settings['webgui']['webguifixedmenu'] = isset($user['webguifixedmenu']);
1953
		$settings['webgui']['webguileftcolumnhyper'] = isset($user['webguileftcolumnhyper']);
1954
		$settings['webgui']['disablealiaspopupdetail'] = isset($user['disablealiaspopupdetail']);
1955
		$settings['webgui']['systemlogsfilterpanel'] = isset($user['systemlogsfilterpanel']);
1956
		$settings['webgui']['systemlogsmanagelogpanel'] = isset($user['systemlogsmanagelogpanel']);
1957
		$settings['webgui']['statusmonitoringsettingspanel'] = isset($user['statusmonitoringsettingspanel']);
1958
		$settings['webgui']['pagenamefirst'] = isset($user['pagenamefirst']);
1959
	} else {
1960
		$settings['customsettings'] = false;
1961
	}
1962

    
1963
	if ($settings['webgui']['dashboardcolumns'] < 1) {
1964
		$settings['webgui']['dashboardcolumns'] = 2;
1965
	}
1966

    
1967
	return $settings;
1968
}
1969

    
1970
function save_widget_settings($username, $settings, $message = "") {
1971
	global $userindex;
1972
	$user = getUserEntry($username);
1973

    
1974
	if (strlen($message) > 0) {
1975
		$msgout = $message;
1976
	} else {
1977
		$msgout = gettext("Widget configuration has been changed.");
1978
	}
1979

    
1980
	if (isset($user['customsettings'])) {
1981
		config_set_path("system/user/{$userindex[$username]}/widgets", $settings);
1982
		write_config($msgout . " " . sprintf(gettext("(User %s)"), $username));
1983
	} else {
1984
		config_set_path('widgets', $settings);
1985
		write_config($msgout);
1986
	}
1987
}
1988

    
1989
function auth_get_authserver($name) {
1990
	foreach (config_get_path('system/authserver', []) as $authcfg) {
1991
		if ($authcfg['name'] == $name) {
1992
			return $authcfg;
1993
		}
1994
	}
1995
	if ($name == "Local Database") {
1996
		return array("name" => "Local Database", "type" => "Local Auth", "host" => config_get_path('system/hostname'));
1997
	}
1998
}
1999

    
2000
function auth_get_authserver_list() {
2001
	$list = array();
2002

    
2003
	foreach (config_get_path('system/authserver', []) as $authcfg) {
2004
		/* Add support for disabled entries? */
2005
		$list[$authcfg['name']] = $authcfg;
2006
	}
2007

    
2008
	$list["Local Database"] = array("name" => "Local Database", "type" => "Local Auth", "host" => config_get_path('system/hostname'));
2009
	return $list;
2010
}
2011

    
2012
function getUserGroups($username, $authcfg, &$attributes = array()) {
2013
	$allowed_groups = array();
2014

    
2015
	switch ($authcfg['type']) {
2016
		case 'ldap':
2017
			$allowed_groups = @ldap_get_groups($username, $authcfg);
2018
			break;
2019
		case 'radius':
2020
			$allowed_groups = @radius_get_groups($attributes);
2021
			break;
2022
		default:
2023
			$user = getUserEntry($username);
2024
			$allowed_groups = @local_user_get_groups($user, true);
2025
			break;
2026
	}
2027

    
2028
	$member_groups = array();
2029
	foreach (config_get_path('system/group', []) as $group) {
2030
		if (in_array($group['name'], $allowed_groups)) {
2031
			$member_groups[] = $group['name'];
2032
		}
2033
	}
2034

    
2035
	return $member_groups;
2036
}
2037

    
2038
/*
2039
Possible return values :
2040
true : authentication worked
2041
false : authentication failed (invalid login/password, not enough permission, etc...)
2042
null : error during authentication process (unable to reach remote server, etc...)
2043
*/
2044
function authenticate_user($username, $password, $authcfg = NULL, &$attributes = array()) {
2045

    
2046
	if (is_array($username) || is_array($password)) {
2047
		return false;
2048
	}
2049

    
2050
	if (!$authcfg) {
2051
		return local_backed($username, $password, $attributes);
2052
	}
2053

    
2054
	$authenticated = false;
2055
	switch ($authcfg['type']) {
2056
		case 'ldap':
2057
			try {
2058
				$authenticated = ldap_backed($username, $password, $authcfg, $attributes);
2059
			} catch (Exception $e) {
2060
				log_error(sprintf(gettext("LDAP authentication error: %s"), $e->getMessage()));
2061
			}
2062
			break;
2063

    
2064
			break;
2065
		case 'radius':
2066
			try {
2067
				$authenticated = radius_backed($username, $password, $authcfg, $attributes);
2068
			} catch (Exception $e) {
2069
				log_error(sprintf(gettext("RADIUS authentication error: %s"), $e->getMessage()));
2070
			}
2071
			break;
2072
		default:
2073
			/* lookup user object by name */
2074
			try {
2075
				$authenticated = local_backed($username, $password, $attributes);
2076
			} catch (Exception $e) {
2077
				log_error(sprintf(gettext("Local authentication error: %s"), $e->getMessage()));
2078
			}
2079
			break;
2080
		}
2081

    
2082
	return $authenticated;
2083
}
2084

    
2085
function session_auth() {
2086
	global $_SESSION, $page;
2087

    
2088
	// Handle HTTPS httponly and secure flags
2089
	$currentCookieParams = session_get_cookie_params();
2090
	session_set_cookie_params(
2091
		$currentCookieParams["lifetime"],
2092
		$currentCookieParams["path"],
2093
		NULL,
2094
		(config_get_path('system/webgui/protocol') == "https"),
2095
		true
2096
	);
2097

    
2098
	phpsession_begin();
2099

    
2100
	// Detect protocol change
2101
	if (!isset($_POST['login']) && !empty($_SESSION['Logged_In']) && $_SESSION['protocol'] != config_get_path('system/webgui/protocol')) {
2102
		phpsession_end();
2103
		return false;
2104
	}
2105

    
2106
	/* Validate incoming login request */
2107
	$attributes = array('nas_identifier' => 'webConfigurator-' . gethostname());
2108
	if (isset($_POST['login']) && !empty($_POST['usernamefld'])) {
2109
		$authcfg = auth_get_authserver(config_get_path('system/webgui/authmode'));
2110
		$remoteauth = authenticate_user($_POST['usernamefld'], $_POST['passwordfld'], $authcfg, $attributes);
2111
		if ($remoteauth || authenticate_user($_POST['usernamefld'], $_POST['passwordfld'])) {
2112
			// Generate a new id to avoid session fixation
2113
			session_regenerate_id();
2114
			$_SESSION['Logged_In'] = "True";
2115
			$_SESSION['remoteauth'] = $remoteauth;
2116
			if ($remoteauth) {
2117
				if (empty($authcfg['type']) || ($authcfg['type'] == "Local Auth")) {
2118
					$_SESSION['authsource'] = "Local Database";
2119
				} else {
2120
					$_SESSION['authsource'] = strtoupper($authcfg['type']) . "/{$authcfg['name']}";
2121
				}
2122
			} else {
2123
				$_SESSION['authsource'] = 'Local Database Fallback';
2124
			}
2125
			$_SESSION['Username'] = $_POST['usernamefld'];
2126
			$_SESSION['user_radius_attributes'] = $attributes;
2127
			$_SESSION['last_access'] = time();
2128
			$_SESSION['protocol'] = config_get_path('system/webgui/protocol');
2129
			phpsession_end(true);
2130
			log_auth(sprintf(gettext("Successful login for user '%1\$s' from: %2\$s"), $_POST['usernamefld'], get_user_remote_address() . get_user_remote_authsource()));
2131
			if (isset($_POST['postafterlogin'])) {
2132
				return true;
2133
			} else {
2134
				if (empty($page)) {
2135
					$page = "/";
2136
				}
2137
				header("Location: {$page}");
2138
			}
2139
			exit;
2140
		} else {
2141
			/* give the user an error message */
2142
			$_SESSION['Login_Error'] = gettext("Username or Password incorrect");
2143
			log_auth(sprintf(gettext("webConfigurator authentication error for user '%1\$s' from: %2\$s"), $_POST['usernamefld'], get_user_remote_address(false)));
2144
			if (isAjax()) {
2145
				echo "showajaxmessage('{$_SESSION['Login_Error']}');";
2146
				return;
2147
			}
2148
		}
2149
	}
2150

    
2151
	/* Show login page if they aren't logged in */
2152
	if (empty($_SESSION['Logged_In'])) {
2153
		phpsession_end(true);
2154
		return false;
2155
	}
2156

    
2157
	$session_timeout = config_get_path('system/webgui/session_timeout', 240);
2158
	/* If session timeout isn't set, we don't mark sessions stale */
2159
	if (intval($session_timeout) == 0) {
2160
		/* only update if it wasn't ajax */
2161
		if (!isAjax()) {
2162
			$_SESSION['last_access'] = time();
2163
		}
2164
	} else {
2165
		/* Check for stale session */
2166
		if ($_SESSION['last_access'] < (time() - ($session_timeout * 60))) {
2167
			$_POST['logout'] = true;
2168
			$_SESSION['Logout'] = true;
2169
		} else {
2170
			/* only update if it wasn't ajax */
2171
			if (!isAjax()) {
2172
				$_SESSION['last_access'] = time();
2173
			}
2174
		}
2175
	}
2176

    
2177
	/* user hit the logout button */
2178
	if (isset($_POST['logout'])) {
2179

    
2180
		if ($_SESSION['Logout']) {
2181
			log_error(sprintf(gettext("Session timed out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], get_user_remote_address() . get_user_remote_authsource()));
2182
		} else {
2183
			log_error(sprintf(gettext("User logged out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], get_user_remote_address() . get_user_remote_authsource()));
2184
		}
2185

    
2186
		/* wipe out $_SESSION */
2187
		$_SESSION = array();
2188

    
2189
		if (isset($_COOKIE[session_name()])) {
2190
			setcookie(session_name(), '', time()-42000, '/');
2191
		}
2192

    
2193
		/* and destroy it */
2194
		phpsession_destroy();
2195

    
2196
		$scriptName = explode("/", $_SERVER["SCRIPT_FILENAME"]);
2197
		$scriptElms = count($scriptName);
2198
		$scriptName = $scriptName[$scriptElms-1];
2199

    
2200
		if (isAjax()) {
2201
			return false;
2202
		}
2203

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

    
2207
		return false;
2208
	}
2209

    
2210
	/*
2211
	 * this is for debugging purpose if you do not want to use Ajax
2212
	 * to submit a HTML form. It basically disables the observation
2213
	 * of the submit event and hence does not trigger Ajax.
2214
	 */
2215
	if ($_REQUEST['disable_ajax']) {
2216
		$_SESSION['NO_AJAX'] = "True";
2217
	}
2218

    
2219
	/*
2220
	 * Same to re-enable Ajax.
2221
	 */
2222
	if ($_REQUEST['enable_ajax']) {
2223
		unset($_SESSION['NO_AJAX']);
2224
	}
2225
	phpsession_end(true);
2226
	return true;
2227
}
2228

    
2229
function print_credit() {
2230
	global $g;
2231

    
2232
	return  '<a target="_blank" href="https://pfsense.org">' . g_get('product_label') . '</a>' .
2233
			gettext(' is developed and maintained by ') .
2234
			'<a target="_blank" href="https://netgate.com">Netgate. </a>' . ' &copy; ESF ' . g_get('product_copyright_years') .
2235
			'<a target="_blank" href="https://pfsense.org/license">' .
2236
			gettext(' View license.') . '</a>';
2237
}
2238
function get_user_remote_address($print_all=true) {
2239
	$remote_address = $_SERVER['REMOTE_ADDR'];
2240
	/* Add extra identifying header information upon request */
2241
	if ($print_all) {
2242
		if (!empty($_SERVER['HTTP_CLIENT_IP'] &&
2243
		    is_ipaddr($_SERVER['HTTP_CLIENT_IP']))) {
2244
			$remote_address .= "[{$_SERVER['HTTP_CLIENT_IP']}]";
2245
		} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) &&
2246
			  is_ipaddr($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2247
			$remote_address .= "[{$_SERVER['HTTP_X_FORWARDED_FOR']}]";
2248
		}
2249
	}
2250
	return $remote_address;
2251
}
2252
function get_user_remote_authsource() {
2253
	$authsource = "";
2254
	if (!empty($_SESSION['authsource'])) {
2255
		$authsource .= " ({$_SESSION['authsource']})";
2256
	}
2257
	return $authsource;
2258
}
2259

    
2260
function set_pam_auth() {
2261
	$authcfg = auth_get_authserver(config_get_path('system/webgui/authmode'));
2262

    
2263
	unlink_if_exists("/etc/radius.conf");
2264
	unlink_if_exists("/var/etc/pam_ldap.conf");
2265
	unlink_if_exists("/var/etc/pam_ldap_ca.crt");
2266

    
2267
	$header = "# This file is automatically generated. Do not edit.\n\n";
2268
	$pam_sshd =<<<EOD
2269
# auth
2270
auth		required	pam_unix.so		no_warn try_first_pass
2271

    
2272
# account
2273
account		required	pam_nologin.so
2274
account		required	pam_login_access.so
2275
account		required	pam_unix.so
2276

    
2277
# session
2278
session		required	pam_permit.so
2279

    
2280
# password
2281
password	required	pam_unix.so		no_warn try_first_pass
2282

    
2283
EOD;
2284

    
2285
	$pam_system =<<<EOD
2286
# auth
2287
auth		required	pam_unix.so		no_warn try_first_pass
2288

    
2289
# account
2290
account		required	pam_login_access.so
2291
account		required	pam_unix.so
2292

    
2293
# session
2294
session		required	pam_lastlog.so		no_fail
2295

    
2296
# password
2297
password	required	pam_unix.so		no_warn try_first_pass
2298

    
2299
EOD;
2300

    
2301
	$nsswitch =<<<EOD
2302
group: files
2303
hosts: files dns
2304
netgroup: files
2305
networks: files
2306
passwd: files
2307
shells: files
2308
services: files
2309
protocols: files
2310
rpc: files
2311

    
2312
EOD;
2313

    
2314
	if (config_path_enabled('system/webgui', 'shellauth')) {
2315
		if (($authcfg['type'] == "radius") && isset($authcfg['radius_auth_port'])) {
2316
			$radius_conf = "auth {$authcfg['host']}:{$authcfg['radius_auth_port']} " .
2317
					"{$authcfg['radius_secret']} {$authcfg['radius_timeout']}\n";
2318
			if (isset($authcfg['radius_acct_port'])) {
2319
				$radius_conf .= "acct {$authcfg['host']}:{$authcfg['radius_acct_port']} " .
2320
					"{$authcfg['radius_secret']} {$authcfg['radius_timeout']}\n";
2321
			}
2322

    
2323
			$pam_sshd =<<<EOD
2324
# auth
2325
auth            sufficient      pam_radius.so
2326
auth		required	pam_unix.so		no_warn try_first_pass
2327

    
2328
# account
2329
account		required	pam_nologin.so
2330
account		required	pam_login_access.so
2331
account         sufficient      pam_radius.so
2332

    
2333
# session
2334
session		required	pam_permit.so
2335

    
2336
# password
2337
password        sufficient      pam_radius.so
2338
password	required	pam_unix.so		no_warn try_first_pass
2339

    
2340
EOD;
2341

    
2342
			$pam_system =<<<EOD
2343
# auth
2344
auth            sufficient      pam_radius.so
2345
auth		required	pam_unix.so		no_warn try_first_pass
2346

    
2347
# account
2348
account		required	pam_login_access.so
2349
account         sufficient      pam_radius.so
2350

    
2351
# session
2352
session		required	pam_lastlog.so		no_fail
2353

    
2354
# password
2355
password        sufficient      pam_radius.so
2356
password	required	pam_unix.so		no_warn try_first_pass
2357

    
2358
EOD;
2359

    
2360
			@file_put_contents("/etc/radius.conf", $header . $radius_conf);
2361
		} elseif (($authcfg['type'] == "ldap") && !empty($authcfg['ldap_pam_groupdn'])) {
2362
			// do not try to reconnect
2363
			$ldapconf = "bind_policy soft\n";
2364
			// Bind/connect timelimit
2365
			$ldapconf .= "bind_timelimit {$authcfg['ldap_timeout']}\n";
2366
			$uri = ($authcfg['ldap_urltype'] == 'SSL/TLS Encrypted') ? 'ldaps' : 'ldap';
2367
			$ldapconf .= "uri {$uri}://{$authcfg['host']}/\n";
2368
			$ldapconf .= "port {$authcfg['ldap_port']}\n";
2369
			if ($authcfg['ldap_urltype'] == 'STARTTLS Encrypted')  {
2370
				$ldapconf .= "ssl start_tls\n";
2371
			} elseif ($authcfg['ldap_urltype'] == 'SSL/TLS Encrypted')  {
2372
				$ldapconf .= "ssl on\n";
2373
			}
2374
			if ($authcfg['ldap_urltype'] != 'Standard TCP') {
2375
				if ($authcfg['ldap_caref'] == 'global') {
2376
					$ldapconf .= "tls_cacertfile /etc/ssl/cert.pem\n";
2377
				} else {
2378
					$ca = array();
2379
					$ldappamcafile = "/var/etc/pam_ldap_ca.crt";
2380
					$ca['caref'] = $authcfg['ldap_caref'];
2381
					$cacrt = ca_chain($ca);
2382
					@file_put_contents($ldappamcafile, $cacrt);
2383
					$ldapconf .= "tls_cacertfile {$ldappamcafile}\n";
2384
				}
2385
				$ldapconf .= "tls_checkpeer yes\n";
2386
			}
2387
			$ldapconf .= "ldap_version {$authcfg['ldap_protver']}\n";
2388
			$ldapconf .= "timelimit {$authcfg['ldap_timeout']}\n";
2389
			$ldapconf .= "base {$authcfg['ldap_basedn']}\n";
2390
			$scope = ($authcfg['ldap_scope' == 'one']) ? 'one' : 'sub';
2391
			$ldapconf .= "scope {$scope}\n";
2392
			$ldapconf .= "binddn {$authcfg['ldap_binddn']}\n";
2393
			$ldapconf .= "bindpw {$authcfg['ldap_bindpw']}\n";
2394
			$ldapconf .= "pam_login_attribute {$authcfg['ldap_attr_user']}\n";
2395
			$ldapconf .= "pam_member_attribute {$authcfg['ldap_attr_member']}\n";
2396
			//$ldapconf .= "pam_filter objectclass={$authcfg['ldap_attr_user']}\n";
2397
			$ldapconf .= "pam_groupdn {$authcfg['ldap_pam_groupdn']}\n";
2398
			//$ldapconf .= "pam_password ad\n";
2399

    
2400
			$pam_sshd =<<<EOD
2401
# auth
2402
auth            sufficient      /usr/local/lib/pam_ldap.so	config=/var/etc/pam_ldap.conf
2403
auth		required	pam_unix.so		no_warn try_first_pass
2404

    
2405
# account
2406
account		required	pam_nologin.so
2407
account		required	pam_login_access.so
2408
account         sufficient      /usr/local/lib/pam_ldap.so	ignore_authinfo_unavail ignore_unknown_user config=/var/etc/pam_ldap.conf
2409

    
2410
# session
2411
session		required	/usr/local/lib/pam_mkhomedir.so	umask=0077 skel=/etc/skel/ silent
2412
session		sufficient	/usr/local/lib/pam_ldap.so	config=/var/etc/pam_ldap.conf
2413
session		required	pam_permit.so
2414

    
2415
# password
2416
password        sufficient      /usr/local/lib/pam_ldap.so	config=/var/etc/pam_ldap.conf
2417
password	required	pam_unix.so		no_warn try_first_pass
2418

    
2419
EOD;
2420

    
2421
			$pam_system =<<<EOD
2422
# auth
2423
auth            sufficient      /usr/local/lib/pam_ldap.so	config=/var/etc/pam_ldap.conf
2424
auth		required	pam_unix.so		no_warn try_first_pass
2425

    
2426
# account
2427
account		required	pam_login_access.so
2428
account         sufficient      pam_radius.so
2429
account         sufficient      /usr/local/lib/pam_ldap.so	ignore_authinfo_unavail ignore_unknown_user config=/var/etc/pam_ldap.conf
2430

    
2431
# session
2432
session		required	/usr/local/lib/pam_mkhomedir.so	umask=0077 skel=/etc/skel/ silent
2433
session		sufficient	/usr/local/lib/pam_ldap.so	config=/var/etc/pam_ldap.conf
2434
session		required	pam_lastlog.so		no_fail
2435

    
2436
# password
2437
password        sufficient      /usr/local/lib/pam_ldap.so	config=/var/etc/pam_ldap.conf
2438
password	required	pam_unix.so		no_warn try_first_pass
2439

    
2440
EOD;
2441

    
2442
			$nsswitch =<<<EOD
2443
group: files ldap
2444
hosts: files dns
2445
netgroup: files
2446
networks: files
2447
passwd: files ldap
2448
shells: files
2449
services: files
2450
protocols: files
2451
rpc: files
2452

    
2453
EOD;
2454

    
2455
			@file_put_contents("/var/etc/pam_ldap.conf", $ldapconf);
2456
			@chmod("/var/etc/pam_ldap.conf", 0600);
2457
			@unlink_if_exists("/usr/local/etc/nss_ldap.conf");
2458
			@symlink("/var/etc/pam_ldap.conf", "/usr/local/etc/nss_ldap.conf");
2459
		}
2460
	}
2461

    
2462
	@file_put_contents("/etc/pam.d/sshd", $header . $pam_sshd);
2463
	@file_put_contents("/etc/pam.d/system", $header . $pam_system);
2464
	@file_put_contents("/etc/nsswitch.conf", $header . $nsswitch);
2465
}
2466
?>
(2-2/61)