> /var/log/mine.log" that people reasonably write. define('ZM_COMMAND_CHAINING', array(';', '|', '&', '`', '$(')); // The program a command invokes: its first word, with any surrounding quotes // removed. Null when there is not one. // // Only the program is matched, so arguments may vary from run to run -- which // they must, since zmfilter.pl appends the event path to whatever the filter // has stored. function commandProgram($command) { if (!is_string($command)) return null; $command = trim($command); if ($command === '') return null; // A quoted path may contain spaces, so the quoted run has to be taken // before splitting on whitespace -- splitting first would cut // "/usr/local/bin/my script.sh" in half and compare the wrong thing. if (($command[0] === '"') or ($command[0] === "'")) { $quote = $command[0]; $end = strpos($command, $quote, 1); // An unterminated quote is not a program name we can read. if ($end === false) return null; $program = substr($command, 1, $end - 1); return ($program === '') ? null : $program; } $parts = preg_split('/\s+/', $command, 2); $program = $parts[0]; return ($program === '') ? null : $program; } // Parse the configured list into the programs it names. function commandAllowList($setting) { $allowed = array(); if (!is_string($setting)) return $allowed; foreach (explode(',', $setting) as $entry) { $entry = trim($entry); if ($entry !== '') $allowed[] = $entry; } return $allowed; } // May $command be run, given the configured list? // // $allowed is the raw setting; an empty one means no restriction. Pure, so // the rule can be tested without a database. function commandIsAllowed($command, $allowed) { $list = commandAllowList($allowed); // No list configured: unrestricted, as ZoneMinder has always been. if (!count($list)) return true; if (!is_string($command)) return false; foreach (ZM_COMMAND_CHAINING as $chain) { if (strpos($command, $chain) !== false) return false; } $program = commandProgram($command); if ($program === null) return false; return in_array($program, $list, true); } // The same question, asked of the running configuration. function commandAllowedByConfig($command) { return commandIsAllowed($command, defined('ZM_ALLOWED_COMMANDS') ? ZM_ALLOWED_COMMANDS : ''); } // What to tell someone whose command was refused, naming what is allowed so // the answer is actionable rather than just "no". function commandRefusedMessage() { $list = commandAllowList(defined('ZM_ALLOWED_COMMANDS') ? ZM_ALLOWED_COMMANDS : ''); return translate('CommandNotAllowed').' '.implode(', ', $list); }