diff options
Diffstat (limited to 'config/sudo')
-rw-r--r-- | config/sudo/sudo.inc | 179 | ||||
-rw-r--r-- | config/sudo/sudo.xml | 89 |
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> |