* * Some or all of this file is based on the m0n0wall project which is * Copyright (c) 2004 Manuel Kasper (BSD 2 clause) * * 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. * * ==================================================================== * */ ##|+PRIV ##|*IDENT=page-system-usermanager ##|*NAME=System: User Manager ##|*DESCR=Allow access to the 'System: User Manager' page. ##|*MATCH=system_usermanager.php* ##|-PRIV require("certs.inc"); require("guiconfig.inc"); // start admin user code if (isset($_POST['userid']) && is_numericint($_POST['userid'])) { $id = $_POST['userid']; } if (isset($_GET['userid']) && is_numericint($_GET['userid'])) { $id = $_GET['userid']; } if (!isset($config['system']['user']) || !is_array($config['system']['user'])) { $config['system']['user'] = array(); } $a_user = &$config['system']['user']; $act = $_GET['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['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 ($_GET['act'] == "deluser") { if (!isset($_GET['username']) || !isset($a_user[$id]) || ($_GET['username'] != $a_user[$id]['name'])) { pfSenseHeader("system_usermanager.php"); exit; } conf_mount_rw(); local_user_del($a_user[$id]); conf_mount_ro(); $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; } if (isset($_POST['dellall'])) { $del_users = $_POST['delete_check']; if (!empty($del_users)) { foreach ($del_users as $userid) { if (isset($a_user[$userid]) && $a_user[$userid]['scope'] != "system") { conf_mount_rw(); local_user_del($a_user[$userid]); conf_mount_ro(); unset($a_user[$userid]); } } $savemsg = gettext("Selected users removed successfully."); 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."); } 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 this is an AJAX caller then handle via JSON */ if (isAjax() && is_array($input_errors)) { input_errors2Ajax($input_errors); exit; } if (!$input_errors) { conf_mount_rw(); $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['authorizedkeys'] = base64_encode($_POST['authorizedkeys']); $userent['ipsecpsk'] = $_POST['ipsecpsk']; if ($_POST['disabled']) { $userent['disabled'] = true; } else { unset($userent['disabled']); } 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']); 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"); } conf_mount_ro(); pfSenseHeader("system_usermanager.php"); } } function build_priv_table() { global $a_user, $id; $privhtml = '