* All rights reserved. * * originally based on m0n0wall (http://m0n0.ch/wall) * Copyright (c) 2003-2004 Manuel Kasper . * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ ##|+PRIV ##|*IDENT=page-system-usermanager ##|*NAME=System: User Manager ##|*DESCR=Allow access to the 'System: User Manager' page. ##|*WARN=standard-warning-root ##|*MATCH=system_usermanager.php* ##|-PRIV require_once("certs.inc"); require_once("guiconfig.inc"); require_once("pfsense-utils.inc"); // start admin user code if (isset($_REQUEST['userid']) && is_numericint($_REQUEST['userid'])) { $id = $_REQUEST['userid']; } if (!isset($config['system']['user']) || !is_array($config['system']['user'])) { $config['system']['user'] = array(); } $a_user = &$config['system']['user']; $act = $_REQUEST['act']; if (isset($_SERVER['HTTP_REFERER'])) { $referer = $_SERVER['HTTP_REFERER']; } else { $referer = '/system_usermanager.php'; } if (isset($id) && $a_user[$id]) { $pconfig['usernamefld'] = $a_user[$id]['name']; $pconfig['descr'] = $a_user[$id]['descr']; $pconfig['expires'] = $a_user[$id]['expires']; $pconfig['customsettings'] = isset($a_user[$id]['customsettings']); $pconfig['webguicss'] = $a_user[$id]['webguicss']; $pconfig['webguifixedmenu'] = $a_user[$id]['webguifixedmenu']; $pconfig['webguihostnamemenu'] = $a_user[$id]['webguihostnamemenu']; $pconfig['dashboardcolumns'] = $a_user[$id]['dashboardcolumns']; $pconfig['interfacessort'] = isset($a_user[$id]['interfacessort']); $pconfig['dashboardavailablewidgetspanel'] = isset($a_user[$id]['dashboardavailablewidgetspanel']); $pconfig['systemlogsfilterpanel'] = isset($a_user[$id]['systemlogsfilterpanel']); $pconfig['systemlogsmanagelogpanel'] = isset($a_user[$id]['systemlogsmanagelogpanel']); $pconfig['statusmonitoringsettingspanel'] = isset($a_user[$id]['statusmonitoringsettingspanel']); $pconfig['webguileftcolumnhyper'] = isset($a_user[$id]['webguileftcolumnhyper']); $pconfig['disablealiaspopupdetail'] = isset($a_user[$id]['disablealiaspopupdetail']); $pconfig['pagenamefirst'] = isset($a_user[$id]['pagenamefirst']); $pconfig['groups'] = local_user_get_groups($a_user[$id]); $pconfig['utype'] = $a_user[$id]['scope']; $pconfig['uid'] = $a_user[$id]['uid']; $pconfig['authorizedkeys'] = base64_decode($a_user[$id]['authorizedkeys']); $pconfig['priv'] = $a_user[$id]['priv']; $pconfig['ipsecpsk'] = $a_user[$id]['ipsecpsk']; $pconfig['disabled'] = isset($a_user[$id]['disabled']); } if ($_POST['act'] == "deluser") { if (!isset($_POST['username']) || !isset($a_user[$id]) || ($_POST['username'] != $a_user[$id]['name'])) { pfSenseHeader("system_usermanager.php"); exit; } if ($_POST['username'] == $_SESSION['Username']) { $delete_errors[] = sprintf(gettext("Cannot delete user %s because you are currently logged in as that user."), $_POST['username']); } else { local_user_del($a_user[$id]); $userdeleted = $a_user[$id]['name']; unset($a_user[$id]); write_config(); $savemsg = sprintf(gettext("User %s successfully deleted."), $userdeleted); } } else if ($act == "new") { /* * set this value cause the text field is read only * and the user should not be able to mess with this * setting. */ $pconfig['utype'] = "user"; $pconfig['lifetime'] = 3650; $nonPrvCas = array(); if (is_array($config['ca']) && count($config['ca']) > 0) { foreach ($config['ca'] as $ca) { if (!$ca['prv']) { continue; } $nonPrvCas[ $ca['refid'] ] = $ca['descr']; } } } if (isset($_POST['dellall'])) { $del_users = $_POST['delete_check']; $deleted_users = ""; $deleted_count = 0; $comma = ""; if (!empty($del_users)) { foreach ($del_users as $userid) { if (isset($a_user[$userid]) && $a_user[$userid]['scope'] != "system") { if ($a_user[$userid]['name'] == $_SESSION['Username']) { $delete_errors[] = sprintf(gettext("Cannot delete user %s because you are currently logged in as that user."), $a_user[$userid]['name']); } else { $deleted_users = $deleted_users . $comma . $a_user[$userid]['name']; $comma = ", "; $deleted_count++; local_user_del($a_user[$userid]); unset($a_user[$userid]); } } else { $delete_errors[] = sprintf(gettext("Cannot delete user %s because it is a system user."), $a_user[$userid]['name']); } } if ($deleted_count > 0) { if ($deleted_count == 1) { $savemsg = sprintf(gettext("User %s successfully deleted."), $deleted_users); } else { $savemsg = sprintf(gettext("Users %s successfully deleted."), $deleted_users); } write_config($savemsg); } } } if ($_POST['act'] == "delcert") { if (!$a_user[$id]) { pfSenseHeader("system_usermanager.php"); exit; } $certdeleted = lookup_cert($a_user[$id]['cert'][$_POST['certid']]); $certdeleted = $certdeleted['descr']; unset($a_user[$id]['cert'][$_POST['certid']]); write_config(); $_POST['act'] = "edit"; $savemsg = sprintf(gettext("Certificate %s association removed."), $certdeleted); } if ($_POST['act'] == "delprivid") { $privdeleted = $priv_list[$a_user[$id]['priv'][$_POST['privid']]]['name']; unset($a_user[$id]['priv'][$_POST['privid']]); local_user_set($a_user[$id]); write_config(); $_POST['act'] = "edit"; $savemsg = sprintf(gettext("Privilege %s removed."), $privdeleted); } if ($_POST['save']) { unset($input_errors); $pconfig = $_POST; /* input validation */ if (isset($id) && ($a_user[$id])) { $reqdfields = explode(" ", "usernamefld"); $reqdfieldsn = array(gettext("Username")); } else { if (empty($_POST['name'])) { $reqdfields = explode(" ", "usernamefld passwordfld1"); $reqdfieldsn = array( gettext("Username"), gettext("Password")); } else { $reqdfields = explode(" ", "usernamefld passwordfld1 name caref keylen lifetime"); $reqdfieldsn = array( gettext("Username"), gettext("Password"), gettext("Descriptive name"), gettext("Certificate authority"), gettext("Key length"), gettext("Lifetime")); } } do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); if (preg_match("/[^a-zA-Z0-9\.\-_]/", $_POST['usernamefld'])) { $input_errors[] = gettext("The username contains invalid characters."); } if (strlen($_POST['usernamefld']) > 16) { $input_errors[] = gettext("The username is longer than 16 characters."); } if (($_POST['passwordfld1']) && ($_POST['passwordfld1'] != $_POST['passwordfld2'])) { $input_errors[] = gettext("The passwords do not match."); } if (isset($_POST['ipsecpsk']) && !preg_match('/^[[:ascii:]]*$/', $_POST['ipsecpsk'])) { $input_errors[] = gettext("IPsec Pre-Shared Key contains invalid characters."); } /* Check the POSTed groups to ensure they are valid and exist */ if (is_array($_POST['groups'])) { foreach ($_POST['groups'] as $newgroup) { if (empty(getGroupEntry($newgroup))) { $input_errors[] = gettext("One or more invalid groups was submitted."); } } } if (isset($id) && $a_user[$id]) { $oldusername = $a_user[$id]['name']; } else { $oldusername = ""; } /* make sure this user name is unique */ if (!$input_errors) { foreach ($a_user as $userent) { if ($userent['name'] == $_POST['usernamefld'] && $oldusername != $_POST['usernamefld']) { $input_errors[] = gettext("Another entry with the same username already exists."); break; } } } /* also make sure it is not reserved */ if (!$input_errors) { $system_users = explode("\n", file_get_contents("/etc/passwd")); foreach ($system_users as $s_user) { $ent = explode(":", $s_user); if ($ent[0] == $_POST['usernamefld'] && $oldusername != $_POST['usernamefld']) { $input_errors[] = gettext("That username is reserved by the system."); break; } } } /* * Check for a valid expiration date if one is set at all (valid means, * DateTime puts out a time stamp so any DateTime compatible time * format may be used. to keep it simple for the enduser, we only * claim to accept MM/DD/YYYY as inputs. Advanced users may use inputs * like "+1 day", which will be converted to MM/DD/YYYY based on "now". * Otherwise such an entry would lead to an invalid expiration data. */ if ($_POST['expires']) { try { $expdate = new DateTime($_POST['expires']); //convert from any DateTime compatible date to MM/DD/YYYY $_POST['expires'] = $expdate->format("m/d/Y"); } catch (Exception $ex) { $input_errors[] = gettext("Invalid expiration date format; use MM/DD/YYYY instead."); } } if (!empty($_POST['name'])) { $ca = lookup_ca($_POST['caref']); if (!$ca) { $input_errors[] = gettext("Invalid internal Certificate Authority") . "\n"; } } if (!$input_errors) { $userent = array(); if (isset($id) && $a_user[$id]) { $userent = $a_user[$id]; } isset($_POST['utype']) ? $userent['scope'] = $_POST['utype'] : $userent['scope'] = "system"; /* the user name was modified */ if (!empty($_POST['oldusername']) && ($_POST['usernamefld'] <> $_POST['oldusername'])) { $_SERVER['REMOTE_USER'] = $_POST['usernamefld']; local_user_del($userent); } /* the user password was modified */ if ($_POST['passwordfld1']) { local_user_set_password($userent, $_POST['passwordfld1']); } /* only change description if sent */ if (isset($_POST['descr'])) { $userent['descr'] = $_POST['descr']; } $userent['name'] = $_POST['usernamefld']; $userent['expires'] = $_POST['expires']; $userent['dashboardcolumns'] = $_POST['dashboardcolumns']; $userent['authorizedkeys'] = base64_encode($_POST['authorizedkeys']); $userent['ipsecpsk'] = $_POST['ipsecpsk']; if ($_POST['disabled']) { $userent['disabled'] = true; } else { unset($userent['disabled']); } if ($_POST['customsettings']) { $userent['customsettings'] = true; } else { unset($userent['customsettings']); } if ($_POST['webguicss']) { $userent['webguicss'] = $_POST['webguicss']; } else { unset($userent['webguicss']); } if ($_POST['webguifixedmenu']) { $userent['webguifixedmenu'] = $_POST['webguifixedmenu']; } else { unset($userent['webguifixedmenu']); } if ($_POST['webguihostnamemenu']) { $userent['webguihostnamemenu'] = $_POST['webguihostnamemenu']; } else { unset($userent['webguihostnamemenu']); } if ($_POST['interfacessort']) { $userent['interfacessort'] = true; } else { unset($userent['interfacessort']); } if ($_POST['dashboardavailablewidgetspanel']) { $userent['dashboardavailablewidgetspanel'] = true; } else { unset($userent['dashboardavailablewidgetspanel']); } if ($_POST['systemlogsfilterpanel']) { $userent['systemlogsfilterpanel'] = true; } else { unset($userent['systemlogsfilterpanel']); } if ($_POST['systemlogsmanagelogpanel']) { $userent['systemlogsmanagelogpanel'] = true; } else { unset($userent['systemlogsmanagelogpanel']); } if ($_POST['statusmonitoringsettingspanel']) { $userent['statusmonitoringsettingspanel'] = true; } else { unset($userent['statusmonitoringsettingspanel']); } if ($_POST['webguileftcolumnhyper']) { $userent['webguileftcolumnhyper'] = true; } else { unset($userent['webguileftcolumnhyper']); } if ($_POST['disablealiaspopupdetail']) { $userent['disablealiaspopupdetail'] = true; } else { unset($userent['disablealiaspopupdetail']); } if ($_POST['pagenamefirst']) { $userent['pagenamefirst'] = true; } else { unset($userent['pagenamefirst']); } if (isset($id) && $a_user[$id]) { $a_user[$id] = $userent; } else { if (!empty($_POST['name'])) { $cert = array(); $cert['refid'] = uniqid(); $userent['cert'] = array(); $cert['descr'] = $_POST['name']; $subject = cert_get_subject_array($ca['crt']); $dn = array( 'countryName' => $subject[0]['v'], 'stateOrProvinceName' => $subject[1]['v'], 'localityName' => $subject[2]['v'], 'organizationName' => $subject[3]['v'], 'emailAddress' => $subject[4]['v'], 'commonName' => $userent['name']); $altnames_tmp = array(cert_add_altname_type($userent['name'])); if (!empty($altnames_tmp)) { $dn['subjectAltName'] = implode(",", $altnames_tmp); } cert_create($cert, $_POST['caref'], $_POST['keylen'], (int)$_POST['lifetime'], $dn); if (!is_array($config['cert'])) { $config['cert'] = array(); } $config['cert'][] = $cert; $userent['cert'][] = $cert['refid']; } $userent['uid'] = $config['system']['nextuid']++; /* Add the user to All Users group. */ foreach ($config['system']['group'] as $gidx => $group) { if ($group['name'] == "all") { if (!is_array($config['system']['group'][$gidx]['member'])) { $config['system']['group'][$gidx]['member'] = array(); } $config['system']['group'][$gidx]['member'][] = $userent['uid']; break; } } $a_user[] = $userent; } /* Add user to groups so PHP can see the memberships properly or else the user's shell account does not get proper permissions (if applicable) See #5152. */ local_user_set_groups($userent, $_POST['groups']); local_user_set($userent); /* Add user to groups again to ensure they are set everywhere, otherwise the user may not appear to be a member of the group. See commit:5372d26d9d25d751d16865ed9d46869d3b0ec5e1. */ local_user_set_groups($userent, $_POST['groups']); write_config(); if (is_dir("/etc/inc/privhooks")) { run_plugins("/etc/inc/privhooks"); } pfSenseHeader("system_usermanager.php"); } } function build_priv_table() { global $a_user, $id; $privhtml = '
'; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $i = 0; $user_has_root_priv = false; foreach (get_user_privdesc($a_user[$id]) as $priv) { $group = false; if ($priv['group']) { $group = $priv['group']; } $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; if (!$group) { $i++; } } if ($user_has_root_priv) { $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; $privhtml .= ''; } $privhtml .= ''; $privhtml .= '
' . gettext('Inherited from') . '' . gettext('Name') . '' . gettext('Description') . '' . gettext('Action') . '
' . htmlspecialchars($priv['group']) . '' . htmlspecialchars($priv['name']) . '' . htmlspecialchars($priv['descr']); if (isset($priv['warn']) && ($priv['warn'] == 'standard-warning-root')) { $privhtml .= ' ' . gettext('(admin privilege)'); $user_has_root_priv = true; } $privhtml .= ''; if (!$group) { $privhtml .= ''; } $privhtml .= '
'; $privhtml .= '' . gettext('Security notice: This user effectively has administrator-level access') . ''; $privhtml .= ''; $privhtml .= '
'; $privhtml .= '
'; $privhtml .= ''; return($privhtml); } function build_cert_table() { global $a_user, $id; $certhtml = '
'; $certhtml .= ''; $certhtml .= ''; $certhtml .= ''; $certhtml .= ''; $certhtml .= ''; $certhtml .= ''; $certhtml .= ''; $certhtml .= ''; $certhtml .= ''; $a_cert = $a_user[$id]['cert']; if (is_array($a_cert)) { $i = 0; foreach ($a_cert as $certref) { $cert = lookup_cert($certref); $ca = lookup_ca($cert['caref']); $revokedstr = is_cert_revoked($cert) ? ' Revoked':''; $certhtml .= ''; $certhtml .= ''; $certhtml .= ''; $certhtml .= ''; $certhtml .= ''; $i++; } } $certhtml .= ''; $certhtml .= '
' . gettext('Name') . '' . gettext('CA') . '
' . htmlspecialchars($cert['descr']) . $revokedstr . '' . htmlspecialchars($ca['descr']) . ''; $certhtml .= ''; $certhtml .= '
'; $certhtml .= '
'; $certhtml .= ''; return($certhtml); } $pgtitle = array(gettext("System"), gettext("User Manager"), gettext("Users")); $pglinks = array("", "system_usermanager.php", "system_usermanager.php"); if ($act == "new" || $act == "edit" || $input_errors) { $pgtitle[] = gettext('Edit'); $pglinks[] = "@self"; } include("head.inc"); if ($delete_errors) { print_input_errors($delete_errors); } if ($input_errors) { print_input_errors($input_errors); } if ($savemsg) { print_info_box($savemsg, 'success'); } $tab_array = array(); $tab_array[] = array(gettext("Users"), true, "system_usermanager.php"); $tab_array[] = array(gettext("Groups"), false, "system_groupmanager.php"); $tab_array[] = array(gettext("Settings"), false, "system_usermanager_settings.php"); $tab_array[] = array(gettext("Authentication Servers"), false, "system_authservers.php"); display_top_tabs($tab_array); if (!($act == "new" || $act == "edit" || $input_errors)) { ?>
' . gettext("Additional users can be added here. User permissions for accessing " . "the webConfigurator can be assigned directly or inherited from group memberships. " . "Some system object properties can be modified but they cannot be deleted.") . '

' . '

' . gettext("Accounts added here are also used for other parts of the system " . "such as OpenVPN, IPsec, and Captive Portal.") . '

' ); ?>
addGlobal(new Form_Input( 'act', null, 'hidden', '' )); $form->addGlobal(new Form_Input( 'userid', null, 'hidden', isset($id) ? $id:'' )); $form->addGlobal(new Form_Input( 'privid', null, 'hidden', '' )); $form->addGlobal(new Form_Input( 'certid', null, 'hidden', '' )); $ro = ""; if ($pconfig['utype'] == "system") { $ro = "readonly"; } $section = new Form_Section('User Properties'); $section->addInput(new Form_StaticText( 'Defined by', strtoupper($pconfig['utype']) )); $form->addGlobal(new Form_Input( 'utype', null, 'hidden', $pconfig['utype'] )); $section->addInput(new Form_Checkbox( 'disabled', 'Disabled', 'This user cannot login', $pconfig['disabled'] )); $section->addInput($input = new Form_Input( 'usernamefld', '*Username', 'text', $pconfig['usernamefld'] )); if ($ro) { $input->setReadonly(); } $form->addGlobal(new Form_Input( 'oldusername', null, 'hidden', $pconfig['usernamefld'] )); if ($act == "edit") { $pwd_required = ""; } else { $pwd_required = "*"; } $group = new Form_Group($pwd_required . 'Password'); $group->add(new Form_Input( 'passwordfld1', 'Password', 'password' )); $group->add(new Form_Input( 'passwordfld2', 'Confirm Password', 'password' )); $section->add($group); $section->addInput($input = new Form_Input( 'descr', 'Full name', 'text', htmlspecialchars($pconfig['descr']) ))->setHelp('User\'s full name, for administrative information only'); if ($ro) { $input->setDisabled(); } $section->addInput(new Form_Input( 'expires', 'Expiration date', 'text', $pconfig['expires'] ))->setHelp('Leave blank if the account shouldn\'t expire, otherwise enter '. 'the expiration date as MM/DD/YYYY'); $section->addInput(new Form_Checkbox( 'customsettings', 'Custom Settings', 'Use individual customized GUI options and dashboard layout for this user.', $pconfig['customsettings'] )); gen_user_settings_fields($section, $pconfig); // ==== Group membership ================================================== $group = new Form_Group('Group membership'); // Make a list of all the groups configured on the system, and a list of // those which this user is a member of $systemGroups = array(); $usersGroups = array(); $usergid = [$pconfig['usernamefld']]; foreach ($config['system']['group'] as $Ggroup) { if ($Ggroup['name'] != "all") { if (($act == 'edit') && $Ggroup['member'] && in_array($pconfig['uid'], $Ggroup['member'])) { $usersGroups[ $Ggroup['name'] ] = $Ggroup['name']; // Add it to the user's list } else { $systemGroups[ $Ggroup['name'] ] = $Ggroup['name']; // Add it to the 'not a member of' list } } } $group->add(new Form_Select( 'sysgroups', null, array_combine((array)$pconfig['groups'], (array)$pconfig['groups']), $systemGroups, true ))->setHelp('Not member of'); $group->add(new Form_Select( 'groups', null, array_combine((array)$pconfig['groups'], (array)$pconfig['groups']), $usersGroups, true ))->setHelp('Member of'); $section->add($group); $group = new Form_Group(''); $group->add(new Form_Button( 'movetoenabled', 'Move to "Member of" list', null, 'fa-angle-double-right' ))->setAttribute('type','button')->removeClass('btn-primary')->addClass('btn-info btn-sm'); $group->add(new Form_Button( 'movetodisabled', 'Move to "Not member of" list', null, 'fa-angle-double-left' ))->setAttribute('type','button')->removeClass('btn-primary')->addClass('btn-info btn-sm'); $group->setHelp('Hold down CTRL (PC)/COMMAND (Mac) key to select multiple items.'); $section->add($group); // ==== Button for adding user certificate ================================ if ($act == 'new') { if (count($nonPrvCas) > 0) { $section->addInput(new Form_Checkbox( 'showcert', 'Certificate', 'Click to create a user certificate', false )); } else { $section->addInput(new Form_StaticText( 'Certificate', gettext('No private CAs found. A private CA is required to create a new user certificate. ' . 'Save the user first to import an external certificate.') )); } } $form->add($section); // ==== Effective privileges section ====================================== if (isset($pconfig['uid'])) { // We are going to build an HTML table and add it to an Input_StaticText. It may be ugly, but it // is the best way to make the display we need. $section = new Form_Section('Effective Privileges'); $section->addInput(new Form_StaticText( null, build_priv_table() )); $form->add($section); // ==== Certificate table section ===================================== $section = new Form_Section('User Certificates'); $section->addInput(new Form_StaticText( null, build_cert_table() )); $form->add($section); } // ==== Add user certificate for a new user if (is_array($config['ca']) && count($config['ca']) > 0) { $section = new Form_Section('Create Certificate for User'); $section->addClass('cert-options'); if (!empty($nonPrvCas)) { $section->addInput(new Form_Input( 'name', 'Descriptive name', 'text', $pconfig['name'] )); $section->addInput(new Form_Select( 'caref', 'Certificate authority', null, $nonPrvCas )); $section->addInput(new Form_Select( 'keylen', 'Key length', 2048, array( 512 => '512 bits', 1024 => '1024 bits', 2048 => '2048 bits', 3072 => '3072 bits', 4096 => '4096 bits', 7680 => '7680 bits', 8192 => '8192 bits', 15360 => '15360 bits', 16384 => '16384 bits' ) ))->setHelp('The larger the key, the more security it offers, but larger keys take considerably more time to generate, ' . 'and take slightly longer to validate leading to a slight slowdown in setting up new sessions (not always noticeable). ' . 'As of 2016, 2048 bit is the minimum and most common selection and 4096 is the maximum in common use. ' . 'For more information see %1$s.', 'keylength.com'); $section->addInput(new Form_Input( 'lifetime', 'Lifetime', 'number', $pconfig['lifetime'] )); } $form->add($section); } endif; // ==== Paste a key for the new user $section = new Form_Section('Keys'); $section->addInput(new Form_Checkbox( 'showkey', 'Authorized keys', 'Click to paste an authorized key', false )); $section->addInput(new Form_Textarea( 'authorizedkeys', 'Authorized SSH Keys', $pconfig['authorizedkeys'] ))->setHelp('Enter authorized SSH keys for this user'); $section->addInput(new Form_Input( 'ipsecpsk', 'IPsec Pre-Shared Key', 'text', $pconfig['ipsecpsk'] )); $form->add($section); print $form; $csswarning = sprintf(gettext("%sUser-created themes are unsupported, use at your own risk."), "
"); ?>