*
* 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
$pgtitle = array(gettext("System"), gettext("User Manager"), gettext("Users"));
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 = gettext("User")." {$userdeleted} ".
gettext("successfully deleted")."
";
} 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 = gettext("Certificate") . " {$certdeleted} " . gettext("association removed.") . "
";
}
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 = gettext("Privilege ") . $privdeleted . gettext(" removed") . "
";
}
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']);
}
$userent['name'] = $_POST['usernamefld'];
$userent['descr'] = $_POST['descr'];
$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 = '