* Copyright (c) 2006 Paul Taylor * Copyright (c) 2003-2006 Manuel Kasper * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgment: * "This product includes software developed by the pfSense Project * for use in the pfSense software distribution. (http://www.pfsense.org/). * * 4. The names "pfSense" and "pfSense Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * coreteam@pfsense.org. * * 5. Products derived from this software may not be called "pfSense" * nor may "pfSense" appear in their names without prior written * permission of the Electric Sheep Fencing, LLC. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * * "This product includes software developed by the pfSense Project * for use in the pfSense software distribution (http://www.pfsense.org/). * * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * ==================================================================== * */ /* * NOTE : Portions of the mschapv2 support was based on the BSD licensed CHAP.php * file courtesy of Michael Retterklieber. */ if (!$do_not_include_config_gui_inc) { require_once("config.gui.inc"); } // Will be changed to false if security checks fail $security_passed = true; /* If this function doesn't exist, we're being called from Captive Portal or another internal subsystem which does not include authgui.inc */ if (function_exists("display_error_form") && !isset($config['system']['webgui']['nodnsrebindcheck'])) { /* DNS ReBinding attack prevention. https://redmine.pfsense.org/issues/708 */ $found_host = false; /* Either a IPv6 address with or without a alternate port */ if (strstr($_SERVER['HTTP_HOST'], "]")) { $http_host_port = explode("]", $_SERVER['HTTP_HOST']); /* v6 address has more parts, drop the last part */ if (count($http_host_port) > 1) { array_pop($http_host_port); $http_host = str_replace(array("[", "]"), "", implode(":", $http_host_port)); } else { $http_host = str_replace(array("[", "]"), "", implode(":", $http_host_port)); } } else { $http_host = explode(":", $_SERVER['HTTP_HOST']); $http_host = $http_host[0]; } if (is_ipaddr($http_host) or $_SERVER['SERVER_ADDR'] == "127.0.0.1" or strcasecmp($http_host, "localhost") == 0 or $_SERVER['SERVER_ADDR'] == "::1") { $found_host = true; } if (strcasecmp($http_host, $config['system']['hostname'] . "." . $config['system']['domain']) == 0 or strcasecmp($http_host, $config['system']['hostname']) == 0) { $found_host = true; } if (is_array($config['dyndnses']['dyndns']) && !$found_host) { foreach ($config['dyndnses']['dyndns'] as $dyndns) { if (strcasecmp($dyndns['host'], $http_host) == 0) { $found_host = true; break; } } } if (is_array($config['dnsupdates']['dnsupdate']) && !$found_host) { foreach ($config['dnsupdates']['dnsupdate'] as $rfc2136) { if (strcasecmp($rfc2136['host'], $http_host) == 0) { $found_host = true; break; } } } if (!empty($config['system']['webgui']['althostnames']) && !$found_host) { $althosts = explode(" ", $config['system']['webgui']['althostnames']); foreach ($althosts as $ah) { if (strcasecmp($ah, $http_host) == 0 or strcasecmp($ah, $_SERVER['SERVER_ADDR']) == 0) { $found_host = true; break; } } } if ($found_host == false) { if (!security_checks_disabled()) { display_error_form("501", gettext("Potential DNS Rebind attack detected, see http://en.wikipedia.org/wiki/DNS_rebinding
Try accessing the router by IP address instead of by hostname.")); exit; } $security_passed = false; } } // If the HTTP_REFERER is something other than ourselves then disallow. if (function_exists("display_error_form") && !isset($config['system']['webgui']['nohttpreferercheck'])) { if ($_SERVER['HTTP_REFERER']) { if (file_exists("{$g['tmp_path']}/setupwizard_lastreferrer")) { if ($_SERVER['HTTP_REFERER'] == file_get_contents("{$g['tmp_path']}/setupwizard_lastreferrer")) { unlink("{$g['tmp_path']}/setupwizard_lastreferrer"); header("Refresh: 1; url=index.php"); ?> <?=gettext("Redirecting..."); ?>

Advanced (" . htmlspecialchars($_SERVER['HTTP_REFERER']) . "). If not needed, this check can be disabled in System -> Advanced -> Admin."); exit; } $security_passed = false; } } else { $security_passed = false; } } if (function_exists("display_error_form") && $security_passed) { /* Security checks passed, so it should be OK to turn them back on */ restore_security_checks(); } unset($security_passed); $groupindex = index_groups(); $userindex = index_users(); function index_groups() { global $g, $debug, $config, $groupindex; $groupindex = array(); if (is_array($config['system']['group'])) { $i = 0; foreach ($config['system']['group'] as $groupent) { $groupindex[$groupent['name']] = $i; $i++; } } return ($groupindex); } function index_users() { global $g, $debug, $config; if (is_array($config['system']['user'])) { $i = 0; foreach ($config['system']['user'] as $userent) { $userindex[$userent['name']] = $i; $i++; } } return ($userindex); } function & getUserEntry($name) { global $debug, $config, $userindex; $authcfg = auth_get_authserver($config['system']['webgui']['authmode']); if (isset($userindex[$name])) { return $config['system']['user'][$userindex[$name]]; } elseif ($authcfg['type'] != "Local Database") { $user = array(); $user['name'] = $name; return $user; } } function & getUserEntryByUID($uid) { global $debug, $config; if (is_array($config['system']['user'])) { foreach ($config['system']['user'] as & $user) { if ($user['uid'] == $uid) { return $user; } } } return false; } function & getGroupEntry($name) { global $debug, $config, $groupindex; if (isset($groupindex[$name])) { return $config['system']['group'][$groupindex[$name]]; } } function & getGroupEntryByGID($gid) { global $debug, $config; if (is_array($config['system']['group'])) { foreach ($config['system']['group'] as & $group) { if ($group['gid'] == $gid) { return $group; } } } return false; } function get_user_privileges(& $user) { global $config; $authcfg = auth_get_authserver($config['system']['webgui']['authmode']); $names = array(); $privs = $user['priv']; if (!is_array($privs)) { $privs = array(); } if ($authcfg['type'] == "ldap") { $names = @ldap_get_groups($user['name'], $authcfg); } elseif ($authcfg['type'] == "radius") { $names = @radius_get_groups($_SESSION['user_radius_attributes']); } if (empty($names)) { $names = local_user_get_groups($user, true); } foreach ($names as $name) { $group = getGroupEntry($name); if (is_array($group['priv'])) { $privs = array_merge($privs, $group['priv']); } } return $privs; } function userHasPrivilege($userent, $privid = false) { if (!$privid || !is_array($userent)) { return false; } $privs = get_user_privileges($userent); if (!is_array($privs)) { return false; } if (!in_array($privid, $privs)) { return false; } return true; } function local_backed($username, $passwd) { $user = getUserEntry($username); if (!$user) { return false; } if (is_account_disabled($username) || is_account_expired($username)) { return false; } if ($user['bcrypt-hash']) { if (password_verify($passwd, $user['bcrypt-hash'])) { return true; } } //for backwards compatibility if ($user['password']) { if (crypt($passwd, $user['password']) == $user['password']) { return true; } } if ($user['md5-hash']) { if (md5($passwd) == $user['md5-hash']) { return true; } } return false; } function local_sync_accounts() { global $debug, $config; conf_mount_rw(); /* remove local users to avoid uid conflicts */ $fd = popen("/usr/sbin/pw usershow -a", "r"); if ($fd) { while (!feof($fd)) { $line = explode(":", fgets($fd)); if ($line[0] != "admin") { if (!strncmp($line[0], "_", 1)) { continue; } if ($line[2] < 2000) { continue; } if ($line[2] > 65000) { continue; } } /* * If a crontab was created to user, pw userdel will be interactive and * can cause issues. Just remove crontab before run it when necessary */ unlink_if_exists("/var/cron/tabs/{$line[0]}"); $cmd = "/usr/sbin/pw userdel -n " . escapeshellarg($line[0]); if ($debug) { log_error(sprintf(gettext("Running: %s"), $cmd)); } mwexec($cmd); } pclose($fd); } /* remove local groups to avoid gid conflicts */ $gids = array(); $fd = popen("/usr/sbin/pw groupshow -a", "r"); if ($fd) { while (!feof($fd)) { $line = explode(":", fgets($fd)); if (!strncmp($line[0], "_", 1)) { continue; } if ($line[2] < 2000) { continue; } if ($line[2] > 65000) { continue; } $cmd = "/usr/sbin/pw groupdel -g " . escapeshellarg($line[2]); if ($debug) { log_error(sprintf(gettext("Running: %s"), $cmd)); } mwexec($cmd); } pclose($fd); } /* make sure the all group exists */ $allgrp = getGroupEntryByGID(1998); local_group_set($allgrp, true); /* sync all local users */ if (is_array($config['system']['user'])) { foreach ($config['system']['user'] as $user) { local_user_set($user); } } /* sync all local groups */ if (is_array($config['system']['group'])) { foreach ($config['system']['group'] as $group) { local_group_set($group); } } conf_mount_ro(); } function local_user_set(& $user) { global $g, $debug; if (empty($user['password']) && empty($user['bcrypt-hash'])) { log_error("There is something wrong in the config because user {$user['name']} password is missing!"); return; } conf_mount_rw(); $home_base = "/home/"; $user_uid = $user['uid']; $user_name = $user['name']; $user_home = "{$home_base}{$user_name}"; $user_shell = "/etc/rc.initial"; $user_group = "nobody"; // Ensure $home_base exists and is writable if (!is_dir($home_base)) { mkdir($home_base, 0755); } $lock_account = false; /* configure shell type */ /* Cases here should be ordered by most privileged to least privileged. */ if (userHasPrivilege($user, "user-shell-access") || userHasPrivilege($user, "page-all")) { $user_shell = "/bin/tcsh"; } elseif (userHasPrivilege($user, "user-copy-files")) { $user_shell = "/usr/local/bin/scponly"; } elseif (userHasPrivilege($user, "user-ssh-tunnel")) { $user_shell = "/usr/local/sbin/ssh_tunnel_shell"; } elseif (userHasPrivilege($user, "user-ipsec-xauth-dialin")) { $user_shell = "/sbin/nologin"; } else { $user_shell = "/sbin/nologin"; $lock_account = true; } /* Lock out disabled or expired users, unless it's root/admin. */ if ((is_account_disabled($user_name) || is_account_expired($user_name)) && ($user_uid != 0)) { $user_shell = "/sbin/nologin"; $lock_account = true; } /* root user special handling */ if ($user_uid == 0) { $cmd = "/usr/sbin/pw usermod -q -n root -s /bin/sh -H 0"; if ($debug) { log_error(sprintf(gettext("Running: %s"), $cmd)); } $fd = popen($cmd, "w"); if (empty($user['bcrypt-hash'])) { fwrite($fd, $user['password']); } else { fwrite($fd, $user['bcrypt-hash']); } pclose($fd); $user_group = "wheel"; $user_home = "/root"; $user_shell = "/etc/rc.initial"; } /* read from pw db */ $fd = popen("/usr/sbin/pw usershow -n {$user_name} 2>&1", "r"); $pwread = fgets($fd); pclose($fd); $userattrs = explode(":", trim($pwread)); /* determine add or mod */ if (($userattrs[0] != $user['name']) || (!strncmp($pwread, "pw:", 3))) { $user_op = "useradd -m -k /etc/skel -o"; } else { $user_op = "usermod"; } $comment = str_replace(array(":", "!", "@"), " ", $user['descr']); /* add or mod pw db */ $cmd = "/usr/sbin/pw {$user_op} -q " . " -u " . escapeshellarg($user_uid) . " -n " . escapeshellarg($user_name) . " -g " . escapeshellarg($user_group) . " -s " . escapeshellarg($user_shell) . " -d " . escapeshellarg($user_home) . " -c " . escapeshellarg($comment) . " -H 0 2>&1"; if ($debug) { log_error(sprintf(gettext("Running: %s"), $cmd)); } $fd = popen($cmd, "w"); if (empty($user['bcrypt-hash'])) { fwrite($fd, $user['password']); } else { fwrite($fd, $user['bcrypt-hash']); } pclose($fd); /* create user directory if required */ if (!is_dir($user_home)) { mkdir($user_home, 0700); } @chown($user_home, $user_name); @chgrp($user_home, $user_group); /* write out ssh authorized key file */ if ($user['authorizedkeys']) { if (!is_dir("{$user_home}/.ssh")) { @mkdir("{$user_home}/.ssh", 0700); @chown("{$user_home}/.ssh", $user_name); } $keys = base64_decode($user['authorizedkeys']); @file_put_contents("{$user_home}/.ssh/authorized_keys", $keys); @chown("{$user_home}/.ssh/authorized_keys", $user_name); } else { unlink_if_exists("{$user_home}/.ssh/authorized_keys"); } $un = $lock_account ? "" : "un"; exec("/usr/sbin/pw {$un}lock " . escapeshellarg($user_name) . " -q 2>/dev/null"); conf_mount_ro(); } function local_user_del($user) { global $debug; /* remove all memberships */ local_user_set_groups($user); /* Don't remove /root */ if ($user['uid'] != 0) { $rmhome = "-r"; } /* read from pw db */ $fd = popen("/usr/sbin/pw usershow -n {$user['name']} 2>&1", "r"); $pwread = fgets($fd); pclose($fd); $userattrs = explode(":", trim($pwread)); if ($userattrs[0] != $user['name']) { log_error("Tried to remove user {$user['name']} but got user {$userattrs[0]} instead. Bailing."); return; } /* delete from pw db */ $cmd = "/usr/sbin/pw userdel -n " . escapeshellarg($user['name']) . " " . escapeshellarg($rmhome); if ($debug) { log_error(sprintf(gettext("Running: %s"), $cmd)); } mwexec($cmd); /* Delete user from groups needs a call to write_config() */ local_group_del_user($user); } function local_user_set_password(&$user, $password) { unset($user['password']); unset($user['md5-hash']); $user['bcrypt-hash'] = password_hash($password, PASSWORD_BCRYPT); /* Maintain compatibility with FreeBSD - change $2y$ prefix to $2b$ * https://reviews.freebsd.org/D2742 * XXX: Can be removed as soon as r284483 is MFC'd. */ if ($user['bcrypt-hash'][2] == "y") { $user['bcrypt-hash'][2] = "b"; } // Converts ascii to unicode. $astr = (string) $password; $ustr = ''; for ($i = 0; $i < strlen($astr); $i++) { $a = ord($astr{$i}) << 8; $ustr .= sprintf("%X", $a); } } function local_user_get_groups($user, $all = false) { global $debug, $config; $groups = array(); if (!is_array($config['system']['group'])) { return $groups; } foreach ($config['system']['group'] as $group) { if ($all || (!$all && ($group['name'] != "all"))) { if (is_array($group['member'])) { if (in_array($user['uid'], $group['member'])) { $groups[] = $group['name']; } } } } if ($all) { $groups[] = "all"; } sort($groups); return $groups; } function local_user_set_groups($user, $new_groups = NULL) { global $debug, $config, $groupindex; if (!is_array($config['system']['group'])) { return; } $cur_groups = local_user_get_groups($user, true); $mod_groups = array(); if (!is_array($new_groups)) { $new_groups = array(); } if (!is_array($cur_groups)) { $cur_groups = array(); } /* determine which memberships to add */ foreach ($new_groups as $groupname) { if ($groupname == '' || in_array($groupname, $cur_groups)) { continue; } $group = & $config['system']['group'][$groupindex[$groupname]]; $group['member'][] = $user['uid']; $mod_groups[] = $group; } unset($group); /* determine which memberships to remove */ foreach ($cur_groups as $groupname) { if (in_array($groupname, $new_groups)) { continue; } if (!isset($config['system']['group'][$groupindex[$groupname]])) { continue; } $group = & $config['system']['group'][$groupindex[$groupname]]; if (is_array($group['member'])) { $index = array_search($user['uid'], $group['member']); array_splice($group['member'], $index, 1); $mod_groups[] = $group; } } unset($group); /* sync all modified groups */ foreach ($mod_groups as $group) { local_group_set($group); } } function local_group_del_user($user) { global $config; if (!is_array($config['system']['group'])) { return; } foreach ($config['system']['group'] as $group) { if (is_array($group['member'])) { foreach ($group['member'] as $idx => $uid) { if ($user['uid'] == $uid) { unset($config['system']['group']['member'][$idx]); } } } } } function local_group_set($group, $reset = false) { global $debug; $group_name = $group['name']; $group_gid = $group['gid']; $group_members = ''; if (!$reset && !empty($group['member']) && count($group['member']) > 0) { $group_members = implode(",", $group['member']); } if (empty($group_name) || $group['scope'] == "remote") { return; } /* determine add or mod */ if (mwexec("/usr/sbin/pw groupshow -g " . escapeshellarg($group_gid) . " 2>&1", true) == 0) { $group_op = "groupmod -l"; } else { $group_op = "groupadd -n"; } /* add or mod group db */ $cmd = "/usr/sbin/pw {$group_op} " . escapeshellarg($group_name) . " -g " . escapeshellarg($group_gid) . " -M " . escapeshellarg($group_members) . " 2>&1"; if ($debug) { log_error(sprintf(gettext("Running: %s"), $cmd)); } mwexec($cmd); } function local_group_del($group) { global $debug; /* delete from group db */ $cmd = "/usr/sbin/pw groupdel " . escapeshellarg($group['name']); if ($debug) { log_error(sprintf(gettext("Running: %s"), $cmd)); } mwexec($cmd); } function ldap_test_connection($authcfg) { global $debug, $config, $g; if ($authcfg) { if (strstr($authcfg['ldap_urltype'], "Standard")) { $ldapproto = "ldap"; } else { $ldapproto = "ldaps"; } $ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']); $ldapport = $authcfg['ldap_port']; if (!empty($ldapport)) { $ldapserver .= ":{$ldapport}"; } $ldapbasedn = $authcfg['ldap_basedn']; $ldapbindun = $authcfg['ldap_binddn']; $ldapbindpw = $authcfg['ldap_bindpw']; } else { return false; } /* first check if there is even an LDAP server populated */ if (!$ldapserver) { return false; } /* Setup CA environment if needed. */ ldap_setup_caenv($authcfg); /* connect and see if server is up */ $error = false; if (!($ldap = ldap_connect($ldapserver))) { $error = true; } if ($error == true) { log_error(sprintf(gettext("ERROR! Could not connect to server %s."), $ldapname)); return false; } return true; } function ldap_setup_caenv($authcfg) { global $g; require_once("certs.inc"); unset($caref); if (empty($authcfg['ldap_caref']) || !strstr($authcfg['ldap_urltype'], "SSL")) { putenv('LDAPTLS_REQCERT=never'); return; } else { $caref = lookup_ca($authcfg['ldap_caref']); if (!$caref) { log_error(sprintf(gettext("LDAP: Could not lookup CA by reference for host %s."), $authcfg['ldap_caref'])); /* XXX: Prevent for credential leaking since we cannot setup the CA env. Better way? */ putenv('LDAPTLS_REQCERT=hard'); return; } if (!is_dir("{$g['varrun_path']}/certs")) { @mkdir("{$g['varrun_path']}/certs"); } if (file_exists("{$g['varrun_path']}/certs/{$caref['refid']}.ca")) { @unlink("{$g['varrun_path']}/certs/{$caref['refid']}.ca"); } file_put_contents("{$g['varrun_path']}/certs/{$caref['refid']}.ca", base64_decode($caref['crt'])); @chmod("{$g['varrun_path']}/certs/{$caref['refid']}.ca", 0600); putenv('LDAPTLS_REQCERT=hard'); /* XXX: Probably even the hashed link should be created for this? */ putenv("LDAPTLS_CACERTDIR={$g['varrun_path']}/certs"); putenv("LDAPTLS_CACERT={$g['varrun_path']}/certs/{$caref['refid']}.ca"); } } function ldap_test_bind($authcfg) { global $debug, $config, $g; if ($authcfg) { if (strstr($authcfg['ldap_urltype'], "Standard")) { $ldapproto = "ldap"; } else { $ldapproto = "ldaps"; } $ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']); $ldapport = $authcfg['ldap_port']; if (!empty($ldapport)) { $ldapserver .= ":{$ldapport}"; } $ldapbasedn = $authcfg['ldap_basedn']; $ldapbindun = $authcfg['ldap_binddn']; $ldapbindpw = $authcfg['ldap_bindpw']; $ldapver = $authcfg['ldap_protver']; $ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5; if (empty($ldapbndun) || empty($ldapbindpw)) { $ldapanon = true; } else { $ldapanon = false; } } else { return false; } /* first check if there is even an LDAP server populated */ if (!$ldapserver) { return false; } /* Setup CA environment if needed. */ ldap_setup_caenv($authcfg); /* connect and see if server is up */ $error = false; if (!($ldap = ldap_connect($ldapserver))) { $error = true; } if ($error == true) { log_error(sprintf(gettext("ERROR! Could not connect to server %s."), $ldapname)); return false; } ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING); ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver); ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout); ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout); $ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun; $ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw; if ($ldapanon == true) { if (!($res = @ldap_bind($ldap))) { @ldap_close($ldap); return false; } } else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) { @ldap_close($ldap); return false; } @ldap_unbind($ldap); return true; } function ldap_get_user_ous($show_complete_ou=true, $authcfg) { global $debug, $config, $g; if (!function_exists("ldap_connect")) { return; } $ous = array(); if ($authcfg) { if (strstr($authcfg['ldap_urltype'], "Standard")) { $ldapproto = "ldap"; } else { $ldapproto = "ldaps"; } $ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']); $ldapport = $authcfg['ldap_port']; if (!empty($ldapport)) { $ldapserver .= ":{$ldapport}"; } $ldapbasedn = $authcfg['ldap_basedn']; $ldapbindun = $authcfg['ldap_binddn']; $ldapbindpw = $authcfg['ldap_bindpw']; $ldapver = $authcfg['ldap_protver']; if (empty($ldapbindun) || empty($ldapbindpw)) { $ldapanon = true; } else { $ldapanon = false; } $ldapname = $authcfg['name']; $ldapfallback = false; $ldapscope = $authcfg['ldap_scope']; $ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5; } else { return false; } /* first check if there is even an LDAP server populated */ if (!$ldapserver) { log_error(gettext("ERROR! ldap_get_user_ous() backed selected with no LDAP authentication server defined.")); return $ous; } /* Setup CA environment if needed. */ ldap_setup_caenv($authcfg); /* connect and see if server is up */ $error = false; if (!($ldap = ldap_connect($ldapserver))) { $error = true; } if ($error == true) { log_error(sprintf(gettext("ERROR! Could not connect to server %s."), $ldapname)); return $ous; } $ldapfilter = "(|(ou=*)(cn=Users))"; ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING); ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver); ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout); ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout); $ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun; $ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw; if ($ldapanon == true) { if (!($res = @ldap_bind($ldap))) { log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind anonymously to server %s."), $ldapname)); @ldap_close($ldap); return $ous; } } else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) { log_error(sprintf(gettext("ERROR! ldap_get_user_ous() could not bind to server %s."), $ldapname)); @ldap_close($ldap); return $ous; } if ($ldapscope == "one") { $ldapfunc = "ldap_list"; } else { $ldapfunc = "ldap_search"; } $search = @$ldapfunc($ldap, $ldapbasedn, $ldapfilter); $info = @ldap_get_entries($ldap, $search); if (is_array($info)) { foreach ($info as $inf) { if (!$show_complete_ou) { $inf_split = explode(",", $inf['dn']); $ou = $inf_split[0]; $ou = str_replace("OU=", "", $ou); $ou = str_replace("CN=", "", $ou); } else { if ($inf['dn']) { $ou = $inf['dn']; } } if ($ou) { $ous[] = $ou; } } } @ldap_unbind($ldap); return $ous; } function ldap_get_groups($username, $authcfg) { global $debug, $config; if (!function_exists("ldap_connect")) { return; } if (!$username) { return false; } if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) { $username_split = explode("@", $username); $username = $username_split[0]; } if (stristr($username, "\\")) { $username_split = explode("\\", $username); $username = $username_split[0]; } //log_error("Getting LDAP groups for {$username}."); if ($authcfg) { if (strstr($authcfg['ldap_urltype'], "Standard")) { $ldapproto = "ldap"; } else { $ldapproto = "ldaps"; } $ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']); $ldapport = $authcfg['ldap_port']; if (!empty($ldapport)) { $ldapserver .= ":{$ldapport}"; } $ldapbasedn = $authcfg['ldap_basedn']; $ldapbindun = $authcfg['ldap_binddn']; $ldapbindpw = $authcfg['ldap_bindpw']; $ldapauthcont = $authcfg['ldap_authcn']; $ldapnameattribute = strtolower($authcfg['ldap_attr_user']); $ldapgroupattribute = strtolower($authcfg['ldap_attr_member']); if (isset($authcfg['ldap_rfc2307'])) { $ldapfilter = "(&(objectClass={$authcfg['ldap_attr_groupobj']})({$ldapgroupattribute}={$username}))"; } else { $ldapfilter = "({$ldapnameattribute}={$username})"; } $ldaptype = ""; $ldapver = $authcfg['ldap_protver']; if (empty($ldapbindun) || empty($ldapbindpw)) { $ldapanon = true; } else { $ldapanon = false; } $ldapname = $authcfg['name']; $ldapfallback = false; $ldapscope = $authcfg['ldap_scope']; $ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5; } else { return false; } if (isset($authcfg['ldap_rfc2307'])) { $ldapdn = $ldapbasedn; } else { $ldapdn = $_SESSION['ldapdn']; } /*Convert attribute to lowercase. php ldap arrays put everything in lowercase */ $ldapgroupattribute = strtolower($ldapgroupattribute); $memberof = array(); /* Setup CA environment if needed. */ ldap_setup_caenv($authcfg); /* connect and see if server is up */ $error = false; if (!($ldap = ldap_connect($ldapserver))) { $error = true; } if ($error == true) { log_error(sprintf(gettext("ERROR! ldap_get_groups() Could not connect to server %s."), $ldapname)); return memberof; } ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING); ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver); ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout); ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout); /* bind as user that has rights to read group attributes */ $ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun; $ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw; if ($ldapanon == true) { if (!($res = @ldap_bind($ldap))) { log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind anonymously to server %s."), $ldapname)); @ldap_close($ldap); return false; } } else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) { log_error(sprintf(gettext("ERROR! ldap_get_groups() could not bind to server %s."), $ldapname)); @ldap_close($ldap); return memberof; } /* get groups from DN found */ /* use ldap_read instead of search so we don't have to do a bunch of extra work */ /* since we know the DN is in $_SESSION['ldapdn'] */ //$search = ldap_read($ldap, $ldapdn, "(objectclass=*)", array($ldapgroupattribute)); if ($ldapscope == "one") { $ldapfunc = "ldap_list"; } else { $ldapfunc = "ldap_search"; } $search = @$ldapfunc($ldap, $ldapdn, $ldapfilter, array($ldapgroupattribute)); $info = @ldap_get_entries($ldap, $search); $gresults = isset($authcfg['ldap_rfc2307']) ? $info : $info[0][$ldapgroupattribute]; if (is_array($gresults)) { /* Iterate through the groups and throw them into an array */ foreach ($gresults as $grp) { if (((isset($authcfg['ldap_rfc2307'])) && (stristr($grp["dn"], "CN=") !== false)) || ((!isset($authcfg['ldap_rfc2307'])) && (stristr($grp, "CN=") !== false))) { $grpsplit = isset($authcfg['ldap_rfc2307']) ? explode(",", $grp["dn"]) : explode(",", $grp); $memberof[] = preg_replace("/CN=/i", "", $grpsplit[0]); } } } /* Time to close LDAP connection */ @ldap_unbind($ldap); $groups = print_r($memberof, true); //log_error("Returning groups ".$groups." for user $username"); return $memberof; } function ldap_format_host($host) { return is_ipaddrv6($host) ? "[$host]" : $host ; } function ldap_backed($username, $passwd, $authcfg) { global $debug, $config; if (!$username) { return; } if (!function_exists("ldap_connect")) { return; } if (!isset($authcfg['ldap_nostrip_at']) && stristr($username, "@")) { $username_split = explode("@", $username); $username = $username_split[0]; } if (stristr($username, "\\")) { $username_split = explode("\\", $username); $username = $username_split[0]; } if ($authcfg) { if (strstr($authcfg['ldap_urltype'], "Standard")) { $ldapproto = "ldap"; } else { $ldapproto = "ldaps"; } $ldapserver = "{$ldapproto}://" . ldap_format_host($authcfg['host']); $ldapport = $authcfg['ldap_port']; if (!empty($ldapport)) { $ldapserver .= ":{$ldapport}"; } $ldapbasedn = $authcfg['ldap_basedn']; $ldapbindun = $authcfg['ldap_binddn']; $ldapbindpw = $authcfg['ldap_bindpw']; if (empty($ldapbindun) || empty($ldapbindpw)) { $ldapanon = true; } else { $ldapanon = false; } $ldapauthcont = $authcfg['ldap_authcn']; $ldapnameattribute = strtolower($authcfg['ldap_attr_user']); $ldapextendedqueryenabled = $authcfg['ldap_extended_enabled']; $ldapextendedquery = $authcfg['ldap_extended_query']; $ldapfilter = ""; if (!$ldapextendedqueryenabled) { $ldapfilter = "({$ldapnameattribute}={$username})"; } else { $ldapfilter = "(&({$ldapnameattribute}={$username})({$ldapextendedquery}))"; } $ldaptype = ""; $ldapver = $authcfg['ldap_protver']; $ldapname = $authcfg['name']; $ldapscope = $authcfg['ldap_scope']; $ldaptimeout = is_numeric($authcfg['ldap_timeout']) ? $authcfg['ldap_timeout'] : 5; } else { return false; } /* first check if there is even an LDAP server populated */ if (!$ldapserver) { if ($ldapfallback) { log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined. Defaulting to local user database. Visit System -> User Manager.")); return local_backed($username, $passwd); } else { log_error(gettext("ERROR! ldap_backed() called with no LDAP authentication server defined.")); } return false; } /* Setup CA environment if needed. */ ldap_setup_caenv($authcfg); ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); ldap_set_option($ldap, LDAP_OPT_DEREF, LDAP_DEREF_SEARCHING); ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, (int)$ldapver); ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, (int)$ldaptimeout); ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, (int)$ldaptimeout); /* Make sure we can connect to LDAP */ $error = false; if (!($ldap = ldap_connect($ldapserver))) { $error = true; } if ($error == true) { log_error(sprintf(gettext("ERROR! Could not connect to server %s."), $ldapname)); return false; } /* ok, its up. now, lets bind as the bind user so we can search it */ $error = false; $ldapbindun = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindun) : $ldapbindun; $ldapbindpw = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapbindpw) : $ldapbindpw; if ($ldapanon == true) { if (!($res = @ldap_bind($ldap))) { $error = true; } } else if (!($res = @ldap_bind($ldap, $ldapbindun, $ldapbindpw))) { $error = true; } if ($error == true) { @ldap_close($ldap); log_error(sprintf(gettext("ERROR! Could not bind to server %s."), $ldapname)); return false; } /* Get LDAP Authcontainers and split em up. */ $ldac_splits = explode(";", $ldapauthcont); /* setup the usercount so we think we haven't found anyone yet */ $usercount = 0; /*****************************************************************/ /* We first find the user based on username and filter */ /* then, once we find the first occurrence of that person */ /* we set session variables to point to the OU and DN of the */ /* person. To later be used by ldap_get_groups. */ /* that way we don't have to search twice. */ /*****************************************************************/ if ($debug) { log_auth(sprintf(gettext("Now Searching for %s in directory."), $username)); } /* Iterate through the user containers for search */ foreach ($ldac_splits as $i => $ldac_split) { $ldac_split = isset($authcfg['ldap_utf8']) ? utf8_encode($ldac_split) : $ldac_split; $ldapfilter = isset($authcfg['ldap_utf8']) ? utf8_encode($ldapfilter) : $ldapfilter; $ldapsearchbasedn = isset($authcfg['ldap_utf8']) ? utf8_encode("{$ldac_split},{$ldapbasedn}") : "{$ldac_split},{$ldapbasedn}"; /* Make sure we just use the first user we find */ if ($debug) { 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))); } if ($ldapscope == "one") { $ldapfunc = "ldap_list"; } else { $ldapfunc = "ldap_search"; } /* Support legacy auth container specification. */ if (stristr($ldac_split, "DC=") || empty($ldapbasedn)) { $search = @$ldapfunc($ldap, $ldac_split, $ldapfilter); } else { $search = @$ldapfunc($ldap, $ldapsearchbasedn, $ldapfilter); } if (!$search) { log_error(sprintf(gettext("Search resulted in error: %s"), ldap_error($ldap))); continue; } $info = ldap_get_entries($ldap, $search); $matches = $info['count']; if ($matches == 1) { $userdn = $_SESSION['ldapdn'] = $info[0]['dn']; $_SESSION['ldapou'] = $ldac_split[$i]; $_SESSION['ldapon'] = "true"; $usercount = 1; break; } } if ($usercount != 1) { @ldap_unbind($ldap); log_error(gettext("ERROR! Either LDAP search failed, or multiple users were found.")); return false; } /* Now lets bind as the user we found */ $passwd = isset($authcfg['ldap_utf8']) ? utf8_encode($passwd) : $passwd; if (!($res = @ldap_bind($ldap, $userdn, $passwd))) { log_error(sprintf(gettext('ERROR! Could not login to server %1$s as user %2$s: %3$s'), $ldapname, $username, ldap_error($ldap))); @ldap_unbind($ldap); return false; } if ($debug) { $userdn = isset($authcfg['ldap_utf8']) ? utf8_decode($userdn) : $userdn; log_auth(sprintf(gettext('Logged in successfully as %1$s via LDAP server %2$s with DN = %3$s.'), $username, $ldapname, $userdn)); } /* At this point we are bound to LDAP so the user was auth'd okay. Close connection. */ @ldap_unbind($ldap); return true; } function radius_backed($username, $passwd, $authcfg, &$attributes = array()) { global $debug, $config; $ret = false; require_once("radius.inc"); $rauth = new Auth_RADIUS_PAP($username, $passwd); if ($authcfg) { $radiusservers = array(); $radiusservers[0]['ipaddr'] = $authcfg['host']; $radiusservers[0]['port'] = $authcfg['radius_auth_port']; $radiusservers[0]['sharedsecret'] = $authcfg['radius_secret']; $radiusservers[0]['timeout'] = $authcfg['radius_timeout']; } else { return false; } /* Add new servers to our instance */ foreach ($radiusservers as $radsrv) { $timeout = (is_numeric($radsrv['timeout'])) ? $radsrv['timeout'] : 5; $rauth->addServer($radsrv['ipaddr'], $radsrv['port'], $radsrv['sharedsecret'], $timeout); } if (PEAR::isError($rauth->start())) { $retvalue['auth_val'] = 1; $retvalue['error'] = $rauth->getError(); if ($debug) { printf(gettext("RADIUS start: %s
\n"), $retvalue['error']); } } // XXX - billm - somewhere in here we need to handle securid challenge/response /* Send request */ $result = $rauth->send(); if (PEAR::isError($result)) { $retvalue['auth_val'] = 1; $retvalue['error'] = $result->getMessage(); if ($debug) { printf(gettext("RADIUS send failed: %s
\n"), $retvalue['error']); } } else if ($result === true) { if ($rauth->getAttributes()) { $attributes = $rauth->listAttributes(); } $retvalue['auth_val'] = 2; if ($debug) { printf(gettext("RADIUS Auth succeeded")."
\n"); } $ret = true; } else { $retvalue['auth_val'] = 3; if ($debug) { printf(gettext("RADIUS Auth rejected")."
\n"); } } // close OO RADIUS_AUTHENTICATION $rauth->close(); return $ret; } /* $attributes must contain a "class" key containing the groups and local groups must exist to match. */ function radius_get_groups($attributes) { $groups = array(); if (!empty($attributes) && is_array($attributes) && (!empty($attributes['class']) || !empty($attributes['class_int']))) { /* Some RADIUS servers return multiple class attributes, so check them all. */ $groups = array(); if (!empty($attributes['class']) && is_array($attributes['class'])) { foreach ($attributes['class'] as $class) { $groups = array_unique(array_merge($groups, explode(";", $class))); } } foreach ($groups as & $grp) { $grp = trim($grp); if (strtolower(substr($grp, 0, 3)) == "ou=") { $grp = substr($grp, 3); } } } return $groups; } function get_user_expiration_date($username) { $user = getUserEntry($username); if ($user['expires']) { return $user['expires']; } } function is_account_expired($username) { $expirydate = get_user_expiration_date($username); if ($expirydate) { if (strtotime("-1 day") > strtotime(date("m/d/Y", strtotime($expirydate)))) { return true; } } return false; } function is_account_disabled($username) { $user = getUserEntry($username); if (isset($user['disabled'])) { return true; } return false; } function auth_get_authserver($name) { global $config; if (is_array($config['system']['authserver'])) { foreach ($config['system']['authserver'] as $authcfg) { if ($authcfg['name'] == $name) { return $authcfg; } } } if ($name == "Local Database") { return array("name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']); } } function auth_get_authserver_list() { global $config; $list = array(); if (is_array($config['system']['authserver'])) { foreach ($config['system']['authserver'] as $authcfg) { /* Add support for disabled entries? */ $list[$authcfg['name']] = $authcfg; } } $list["Local Database"] = array("name" => gettext("Local Database"), "type" => "Local Auth", "host" => $config['system']['hostname']); return $list; } function getUserGroups($username, $authcfg, &$attributes = array()) { global $config; $allowed_groups = array(); switch ($authcfg['type']) { case 'ldap': $allowed_groups = @ldap_get_groups($username, $authcfg); break; case 'radius': $allowed_groups = @radius_get_groups($attributes); break; default: $user = getUserEntry($username); $allowed_groups = @local_user_get_groups($user, true); break; } $member_groups = array(); if (is_array($config['system']['group'])) { foreach ($config['system']['group'] as $group) { if (in_array($group['name'], $allowed_groups)) { $member_groups[] = $group['name']; } } } return $member_groups; } function authenticate_user($username, $password, $authcfg = NULL, &$attributes = array()) { if (is_array($username) || is_array($password)) { return false; } if (!$authcfg) { return local_backed($username, $password); } $authenticated = false; switch ($authcfg['type']) { case 'ldap': if (ldap_backed($username, $password, $authcfg)) { $authenticated = true; } break; case 'radius': if (radius_backed($username, $password, $authcfg, $attributes)) { $authenticated = true; } break; default: /* lookup user object by name */ if (local_backed($username, $password)) { $authenticated = true; } break; } return $authenticated; } function session_auth() { global $config, $_SESSION, $page; // Handle HTTPS httponly and secure flags $currentCookieParams = session_get_cookie_params(); session_set_cookie_params( $currentCookieParams["lifetime"], $currentCookieParams["path"], NULL, ($config['system']['webgui']['protocol'] == "https"), true ); if (!session_id()) { session_start(); } // Detect protocol change if (!isset($_POST['login']) && !empty($_SESSION['Logged_In']) && $_SESSION['protocol'] != $config['system']['webgui']['protocol']) { return false; } /* Validate incoming login request */ $attributes = array(); if (isset($_POST['login']) && !empty($_POST['usernamefld']) && !empty($_POST['passwordfld'])) { $authcfg = auth_get_authserver($config['system']['webgui']['authmode']); if (authenticate_user($_POST['usernamefld'], $_POST['passwordfld'], $authcfg, $attributes) || authenticate_user($_POST['usernamefld'], $_POST['passwordfld'])) { // Generate a new id to avoid session fixation session_regenerate_id(); $_SESSION['Logged_In'] = "True"; $_SESSION['Username'] = $_POST['usernamefld']; $_SESSION['user_radius_attributes'] = $attributes; $_SESSION['last_access'] = time(); $_SESSION['protocol'] = $config['system']['webgui']['protocol']; if (!isset($config['system']['webgui']['quietlogin'])) { log_auth(sprintf(gettext("Successful login for user '%1\$s' from: %2\$s"), $_POST['usernamefld'], $_SERVER['REMOTE_ADDR'])); } if (isset($_POST['postafterlogin'])) { return true; } else { if (empty($page)) { $page = "/"; } header("Location: {$page}"); } exit; } else { /* give the user an error message */ $_SESSION['Login_Error'] = "Username or Password incorrect"; log_auth("webConfigurator authentication error for '{$_POST['usernamefld']}' from {$_SERVER['REMOTE_ADDR']}"); if (isAjax()) { echo "showajaxmessage('{$_SESSION['Login_Error']}');"; return; } } } /* Show login page if they aren't logged in */ if (empty($_SESSION['Logged_In'])) { return false; } /* If session timeout isn't set, we don't mark sessions stale */ if (!isset($config['system']['webgui']['session_timeout'])) { /* Default to 4 hour timeout if one is not set */ if ($_SESSION['last_access'] < (time() - 14400)) { $_GET['logout'] = true; $_SESSION['Logout'] = true; } else { $_SESSION['last_access'] = time(); } } else if (intval($config['system']['webgui']['session_timeout']) == 0) { /* only update if it wasn't ajax */ if (!isAjax()) { $_SESSION['last_access'] = time(); } } else { /* Check for stale session */ if ($_SESSION['last_access'] < (time() - ($config['system']['webgui']['session_timeout'] * 60))) { $_GET['logout'] = true; $_SESSION['Logout'] = true; } else { /* only update if it wasn't ajax */ if (!isAjax()) { $_SESSION['last_access'] = time(); } } } /* user hit the logout button */ if (isset($_GET['logout'])) { if ($_SESSION['Logout']) { log_error(sprintf(gettext("Session timed out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR'])); } else { log_error(sprintf(gettext("User logged out for user '%1\$s' from: %2\$s"), $_SESSION['Username'], $_SERVER['REMOTE_ADDR'])); } /* wipe out $_SESSION */ $_SESSION = array(); if (isset($_COOKIE[session_name()])) { setcookie(session_name(), '', time()-42000, '/'); } /* and destroy it */ session_destroy(); $scriptName = explode("/", $_SERVER["SCRIPT_FILENAME"]); $scriptElms = count($scriptName); $scriptName = $scriptName[$scriptElms-1]; if (isAjax()) { return false; } /* redirect to page the user is on, it'll prompt them to login again */ header("Location: {$scriptName}"); return false; } /* * this is for debugging purpose if you do not want to use Ajax * to submit a HTML form. It basically disables the observation * of the submit event and hence does not trigger Ajax. */ if ($_GET['disable_ajax']) { $_SESSION['NO_AJAX'] = "True"; } /* * Same to re-enable Ajax. */ if ($_GET['enable_ajax']) { unset($_SESSION['NO_AJAX']); } return true; } ?>