🔒 ♻️ Implement secure exec wrapper functions.

This commit is contained in:
Flat 2015-12-02 21:24:34 +09:00
commit 8e951ac72e
115 changed files with 1345 additions and 1986 deletions

85
web/inc/exec.php Normal file
View file

@ -0,0 +1,85 @@
<?php
// Secure `exec` wrapper functions
define('SUDO_CMD', '/usr/bin/sudo');
define('VESTA_BIN_DIR', '/usr/local/vesta/bin/');
define('VESTA_CMD', SUDO_CMD.' '.VESTA_BIN_DIR);
function check_error($return_var) {
if ($return_var > 0) {
header('Location: /error/');
exit;
}
}
function check_return_code($return_var, $output) {
if ($return_var != 0) {
$error = implode('<br>', $output);
if (empty($error)) $error = __('Error code:', $return_var);
$_SESSION['error_msg'] = $error;
}
}
/**
* Build shell command arguments from a string array.
* @param string[] $arguments Unescaped command line arguments. (eg. ['-a', "b'c"], default: [])
* @return string Escaped arguments.
*/
function build_shell_args($arguments=[]) {
$ret = [];
// Convert $arguments to an array
if (!is_array($arguments)) $arguments = !is_null($arguments) ? [$arguments] : [];
foreach ($arguments as $arg) {
// Convert $arg to a string if $arg is an array (for an argument like this: ?abc[def]=ghi)
if (is_array($arg)) $arg = implode('', $arg);
// Convert $arg to a string (just in case)
if (!is_string($arg)) $arg = (string)$arg;
// Append the argument
$ret[] = escapeshellarg($arg);
}
return implode(' ', $ret);
}
/**
* Execute a command.
* @param string $command Command to execute. (eg. ls)
* @param string[] $arguments (optional) Unescaped command line arguments. (eg. ['-a', '/'], default: [])
* @param string &$output (optional) Variable to contain output from the command.
* @return int Exit code (return status) of the executed command.
*/
function safe_exec($command, $arguments=[], &$output=null) {
$cmd = build_shell_args($command);
$arg = build_shell_args($arguments);
if (!empty($arg)) {
$cmd .= ' ' . $arg;
}
// Execute
exec($cmd, $rawOutput, $status);
$output = implode("\n", $rawOutput);
return $status;
}
/**
* Execute a vesta command line APIs (VESTA_CMD/v-*).
* (Wrapper function of `safe_exec`.)
* @see safe_exec
* @param string $command Command to execute. (eg. v-search-object)
* @param string[] $arguments (optional) Unescaped command line arguments. (eg. ["We've", 'json'], default: [])
* @param bool $checkReturn (optional) If this set to true, check_return_code will be called after the command executes. (default: true)
* @param string &$output (optional) Variable to contain output from the command.
* @return int Exit code (return status) of the executed command.
*/
function v_exec($command, $arguments=[], $checkReturn=true, &$output=null) {
// Check command
if (preg_match('#^\.*$|/#', $command)) return -1;
// Convert $arguments to an array
if (!is_array($arguments)) $arguments = !is_null($arguments) ? [$arguments] : [];
// Execute
$status = safe_exec([SUDO_CMD, VESTA_BIN_DIR.$command], $arguments, $output);
if ($checkReturn) {
check_return_code($status, explode("\n", $output));
}
return $status;
}

View file

@ -1,6 +1,8 @@
<?php
// Functions for internationalization
require_once(__DIR__.'/exec.php');
/**
* Translates string to given language in first parameter, key given in second parameter (dynamically loads required language). Works like spritf from second parameter
* @global array $LANG Associative array of language pharses
@ -16,19 +18,19 @@ function _translate() {
$key = $args[1];
if (!isset($LANG[$l])) {
require_once($_SERVER['DOCUMENT_ROOT'].'/inc/i18n/'.$l.'.php');
require_once(__DIR__."/i18n/$l.php");
}
if (!isset($LANG[$l][$key])) {
$text=$key;
$text = $key;
} else {
$text=$LANG[$l][$key];
$text = $LANG[$l][$key];
}
array_shift($args);
if (count($args)>1) {
if (count($args) > 1) {
$args[0] = $text;
return call_user_func_array("sprintf",$args);
return call_user_func_array('sprintf', $args);
} else {
return $text;
}
@ -42,8 +44,8 @@ function _translate() {
*/
function __() {
$args = func_get_args();
array_unshift($args,$_SESSION['language']);
return call_user_func_array("_translate",$args);
array_unshift($args, $_SESSION['language']);
return call_user_func_array('_translate', $args);
}
/**
@ -86,16 +88,15 @@ function detect_user_language($fallback='en') {
arsort($accept_langs_sorted);
// List languages
exec (VESTA_CMD."v-list-sys-languages json", $output, $return_var);
$languages = json_decode(implode('', $output), true);
unset($output);
v_exec('v-list-sys-languages', ['json'], false, $output);
$languages = json_decode($output, true);
// Find best matching language
foreach ($accept_langs_sorted as $user_lang => $dummy) {
foreach ($accept_langs_sorted as $req_lang => $dummy) {
$decision = '';
foreach ($languages as $prov_lang) {
if (strlen($decision) > strlen($prov_lang)) continue;
if (strpos($user_lang, $prov_lang) !== false) {
if (stripos($req_lang, $prov_lang) !== false) {
$decision = $prov_lang;
}
}
@ -109,4 +110,4 @@ function detect_user_language($fallback='en') {
// Store result for reusing
$user_lang = $fallback;
return $user_lang;
}
}

View file

@ -8,14 +8,15 @@ if (empty($argv[1])) {
$options = getopt("s:f:");
require_once(__DIR__.'/exec.php');
define('NO_AUTH_REQUIRED',true);
define('NO_AUTH_REQUIRED', true);
include("/usr/local/vesta/web/inc/main.php");
// Set system language
exec (VESTA_CMD . "v-list-sys-config json", $output, $return_var);
$data = json_decode(implode('', $output), true);
if (!empty( $data['config']['LANGUAGE'])) {
v_exec('v-list-sys-config', ['json'], false, $output);
$data = json_decode($output, true);
if (!empty($data['config']['LANGUAGE'])) {
$_SESSION['language'] = $data['config']['LANGUAGE'];
} else {
$_SESSION['language'] = 'en';

View file

@ -1,7 +1,8 @@
<?php
session_start();
require_once($_SERVER['DOCUMENT_ROOT'].'/inc/i18n.php');
require_once(__DIR__ . '/exec.php');
require_once(__DIR__ . '/i18n.php');
// Check system settings
if ((!isset($_SESSION['VERSION'])) && (!defined('NO_AUTH_REQUIRED'))) {
@ -25,8 +26,6 @@ if (isset($_SESSION['user'])) {
}
}
define('VESTA_CMD', '/usr/bin/sudo /usr/local/vesta/bin/');
$i = 0;
if (isset($_SESSION['language'])) {
@ -60,10 +59,10 @@ if (isset($_SESSION['look']) && ( $_SESSION['look'] != 'admin' )) {
}
function get_favourites(){
exec (VESTA_CMD."v-list-user-favourites ".$_SESSION['user']." json", $output, $return_var);
// $data = json_decode(implode('', $output).'}', true);
$data = json_decode(implode('', $output), true);
$data = array_reverse($data,true);
v_exec('v-list-user-favourites', [$_SESSION['user'], 'json'], false, $output);
// $data = json_decode($output.'}', true);
$data = json_decode($output, true);
$data = array_reverse($data, true);
$favourites = array();
foreach($data['Favourites'] as $key => $favourite){
@ -71,7 +70,7 @@ function get_favourites(){
$items = explode(',', $favourite);
foreach($items as $item){
if($item)
if ($item)
$favourites[$key][trim($item)] = 1;
}
}
@ -79,34 +78,15 @@ function get_favourites(){
$_SESSION['favourites'] = $favourites;
}
function check_error($return_var) {
if ( $return_var > 0 ) {
header("Location: /error/");
exit;
}
}
function check_return_code($return_var,$output) {
if ($return_var != 0) {
$error = implode('<br>', $output);
if (empty($error)) $error = __('Error code:',$return_var);
$_SESSION['error_msg'] = $error;
}
}
function top_panel($user, $TAB) {
global $panel;
$command = VESTA_CMD."v-list-user '".$user."' 'json'";
exec ($command, $output, $return_var);
if ( $return_var > 0 ) {
header("Location: /error/");
$return_var = v_exec('v-list-user', [$user, 'json'], false, $output);
if ($return_var > 0) {
header('Location: /error/');
exit;
}
$panel = json_decode(implode('', $output), true);
unset($output);
if ( $user == 'admin' ) {
$panel = json_decode($output, true);
if ($user == 'admin') {
include($_SERVER['DOCUMENT_ROOT'].'/templates/admin/panel.html');
} else {
include($_SERVER['DOCUMENT_ROOT'].'/templates/user/panel.html');