aboutsummaryrefslogtreecommitdiffstats
path: root/config/sudo
diff options
context:
space:
mode:
Diffstat (limited to 'config/sudo')
-rw-r--r--config/sudo/sudo.inc179
-rw-r--r--config/sudo/sudo.xml89
2 files changed, 268 insertions, 0 deletions
diff --git a/config/sudo/sudo.inc b/config/sudo/sudo.inc
new file mode 100644
index 00000000..a65753a1
--- /dev/null
+++ b/config/sudo/sudo.inc
@@ -0,0 +1,179 @@
+<?php
+/*
+ sudo.inc
+
+ Copyright (C) 2013 Jim Pingle (jpingle@gmail.com)
+ All rights reserved.
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS 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
+ AUTHOR 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.
+*/
+
+$pfs_version = substr(trim(file_get_contents("/etc/version")),0,3);
+switch ($pfs_version) {
+ case "1.2":
+ case "2.0":
+ define('SUDO_BASE','/usr/local');
+ break;
+ default:
+ // Hackish way to detect if someone manually did pkg_add rather than use pbi.
+ if (is_dir('/usr/pbi/sudo-' . php_uname("m")))
+ define('SUDO_BASE', '/usr/pbi/sudo-' . php_uname("m"));
+ else
+ define('SUDO_BASE','/usr/local');
+}
+
+define('SUDO_CONFIG_DIR', SUDO_BASE . '/etc');
+define('SUDO_SUDOERS', SUDO_CONFIG_DIR . '/sudoers');
+
+function sudo_install() {
+ global $g, $config;
+ /* If we don't have a config, pre-load some good default sudo entries. */
+ if (!is_array($config['installedpackages']['sudo']['config'][0]['row'])) {
+ $config['installedpackages']['sudo']['config'][0]['row'] = array(
+ 0 => array(
+ "username" => "user:root",
+ "runas" => "user:root",
+ "cmdlist" => "ALL"
+ ),
+ 1 => array(
+ "username" => "user:admin",
+ "runas" => "user:root",
+ "cmdlist" => "ALL"
+ ),
+ 2 => array(
+ "username" => "group:admins",
+ "runas" => "user:root",
+ "cmdlist" => "ALL"
+ )
+ );
+ }
+}
+
+function sudo_write_config() {
+ global $config;
+ $sudoers = "";
+ if (!is_array($config['installedpackages']['sudo']['config'][0]['row'])) {
+ /* No config, wipe sudoers file and bail. */
+ unlink(SUDO_SUDOERS);
+ log_error("No sudo configuration found, removing sudoers file to prevent unpredictable results.");
+ return;
+ }
+ $sudocfg = &$config['installedpackages']['sudo']['config'][0]['row'];
+ /* Parse the config and massage it into proper sudo config lines. */
+ foreach ($sudocfg as $sudo_commands) {
+ // (user|group) ALL=(ALL|user spec) ALL|command list
+ list($etype, $ename) = explode(":", $sudo_commands['username']);
+ $user = ($etype == "group") ? "%{$ename}" : $ename;
+ list($rtype, $rname) = explode(":", $sudo_commands['runas']);
+ $runas = ($rtype == "group") ? ":{$rname}" : $rname;
+ $nopasswd = ($sudo_commands['nopasswd'] == "ON") ? "NOPASSWD:" : "";
+ $commands = (empty($sudo_commands['cmdlist'])) ? "ALL" : $sudo_commands['cmdlist'];
+ $commands = ($commands == "all") ? "ALL" : $commands;
+ $sudoers .= "{$user} ALL=({$runas}) {$nopasswd} {$commands}\n";
+ }
+
+ /* Check validity of the sudoers data created above. */
+ $tmpsudoers = tempnam("/tmp", "sudoers");
+ file_put_contents($tmpsudoers, $sudoers);
+ $result = exec("/usr/local/sbin/visudo -c -f {$tmpsudoers}");
+
+ /* If the file is OK, move it into place with the correct permissions, otherwise log an error and trash it. */
+ if (stristr($result, "parsed OK")) {
+ rename($tmpsudoers, SUDO_SUDOERS);
+ chmod(SUDO_SUDOERS, 0440);
+ } else {
+ log_error("Sudoers file invalid: {$result}");
+ unlink($tmpsudoers);
+ }
+}
+
+/* Get a list of users and groups in a format we can use to make proper sudoers entries.
+Optionally include "ALL" as a user (for use by the Run As list)
+ */
+function sudo_get_users($list_all_user = false) {
+ global $config;
+ if (!is_array($config['system']['user']))
+ $config['system']['user'] = array();
+ $a_user = &$config['system']['user'];
+ if (!is_array($config['system']['group']))
+ $config['system']['group'] = array();
+ $a_group = &$config['system']['group'];
+ $users = array();
+
+ /* Make an entry for root, even though admin is essentially the same as root, they are distinct. */
+ $tmpuser = array();
+ $tmpuser["name"] = "user:root";
+ $tmpuser["descr"] = "User: root";
+ $users[] = $tmpuser;
+
+ /* Add the all user if we want it */
+ if ($list_all_user) {
+ $tmpuser = array();
+ $tmpuser["name"] = "user:ALL";
+ $tmpuser["descr"] = "User: ALL Users";
+ $users[] = $tmpuser;
+ }
+
+ foreach ($a_user as $user) {
+ $tmpuser = array();
+ $tmpuser["name"] = "user:{$user['name']}";
+ $tmpuser["descr"] = "User: {$user['name']}";
+ $users[] = $tmpuser;
+ }
+
+ /* Add the wheel group here. We may need other manual groups later (e.g. operator) */
+ $tmpuser = array();
+ $tmpuser["name"] = "group:wheel";
+ $tmpuser["descr"] = "Group: wheel";
+ $users[] = $tmpuser;
+
+ foreach ($a_group as $group) {
+ /* The "all" group is internal and doesn't make sense to use here. */
+ if ($group['name'] == "all")
+ continue;
+ $tmpgroup = array();
+ $tmpgroup["name"] = "group:{$group['name']}";
+ $tmpgroup["descr"] = "Group: {$group['name']}";
+ $users[] = $tmpgroup;
+ }
+
+ return $users;
+}
+
+/* Make sure commands passed in are valid executables to help ensure a valid sudoers file and expected behavior.
+ This also forces the user to give full paths to executables, which they should be doing anyhow.
+ */
+function sudo_validate_commands($input_errors) {
+ $idx = 0;
+ while(isset($_POST["cmdlist{$idx}"])) {
+ $commands = $_POST["cmdlist" . $idx++];
+ if (strtoupper($commands) == "ALL")
+ continue;
+ $commands = explode(",", $commands);
+ foreach ($commands as $command) {
+ list($cmd, $params) = explode(" ", trim($command), 2);
+ if (!is_executable($cmd))
+ $input_errors[] = htmlspecialchars($cmd) . " is not an executable command.";
+ }
+ }
+}
+?>
diff --git a/config/sudo/sudo.xml b/config/sudo/sudo.xml
new file mode 100644
index 00000000..56163abf
--- /dev/null
+++ b/config/sudo/sudo.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<packagegui>
+ <description>Sudo Command Control</description>
+ <requirements>None</requirements>
+ <name>sudo</name>
+ <version>0.1</version>
+ <title>Sudo - Shell Command Privilege Delegation Utility</title>
+ <include_file>/usr/local/pkg/sudo.inc</include_file>
+ <menu>
+ <name>sudo</name>
+ <tooltiptext></tooltiptext>
+ <section>System</section>
+ <url>/pkg_edit.php?xml=sudo.xml</url>
+ </menu>
+ <configpath>installedpackages->package->sudo</configpath>
+ <additional_files_needed>
+ <prefix>/usr/local/pkg/</prefix>
+ <chmod>077</chmod>
+ <item>http://www.pfsense.com/packages/config/sudo/sudo.inc</item>
+ </additional_files_needed>
+ <fields>
+ <field>
+ <type>listtopic</type>
+ <name>Sudo Options</name>
+ </field>
+ <field>
+ <type>info</type>
+ <description><![CDATA[
+User permission definitions for allowing the use of sudo by shell users to run commands as other users, such as root.
+<br /><br />More information on the full command options may be found in the <a href="http://www.sudo.ws/sudoers.man.html">sudoers manual</a>.
+<br /><br />By default the command is "ALL" meaning the user can run any commands. Leaving the commands field blank assumes "ALL". A comma-separated list of commands can be supplied to limit the user to individual binaries. Full paths to binaries must be used.
+ ]]></description>
+ </field>
+ <field>
+ <fielddescr>User Permissions</fielddescr>
+ <fieldname>none</fieldname>
+ <type>rowhelper</type>
+ <rowhelper>
+ <rowhelperfield>
+ <fielddescr>User/Group</fielddescr>
+ <fieldname>username</fieldname>
+ <type>select_source</type>
+ <source><![CDATA[sudo_get_users()]]></source>
+ <source_name>descr</source_name>
+ <source_value>name</source_value>
+ <required/>
+ </rowhelperfield>
+ <rowhelperfield>
+ <fielddescr>Run As</fielddescr>
+ <fieldname>runas</fieldname>
+ <type>select_source</type>
+ <source><![CDATA[sudo_get_users(true)]]></source>
+ <source_name>descr</source_name>
+ <source_value>name</source_value>
+ <required/>
+ </rowhelperfield>
+ <rowhelperfield>
+ <fielddescr>No Password</fielddescr>
+ <fieldname>nopasswd</fieldname>
+ <type>checkbox</type>
+ </rowhelperfield>
+ <rowhelperfield>
+ <fielddescr>Command List</fielddescr>
+ <fieldname>cmdlist</fieldname>
+ <description>Commands the user may run. Comma-separated list, full paths preferred. Default: ALL</description>
+ <type>input</type>
+ <size>30</size>
+ <value>ALL</value>
+ </rowhelperfield>
+ </rowhelper>
+ </field>
+ </fields>
+ <custom_php_install_command>
+ <![CDATA[
+ sudo_install();
+ sudo_write_config();
+ ]]>
+ </custom_php_install_command>
+ <custom_php_resync_config_command>
+ <![CDATA[
+ sudo_write_config();
+ ]]>
+ </custom_php_resync_config_command>
+ <custom_php_validation_command>
+ <![CDATA[
+ sudo_validate_commands(&$input_errors);
+ ]]>
+ </custom_php_validation_command>
+</packagegui>