From 99e46ba0e3e103aea5496dbedd3100e9f9cc1251 Mon Sep 17 00:00:00 2001 From: rich Date: Fri, 8 Dec 2006 15:36:44 +0000 Subject: [PATCH] Added {{shell}} for running external shell commands. --- scripts/Makefile | 5 +- scripts/lib/cocanwiki_func_shell.ml | 92 +++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 scripts/lib/cocanwiki_func_shell.ml diff --git a/scripts/Makefile b/scripts/Makefile index f0a1692..9885b02 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -1,5 +1,5 @@ # Makefile for COCANWIKI. -# $Id: Makefile,v 1.53 2006/12/07 17:05:47 rich Exp $ +# $Id: Makefile,v 1.54 2006/12/08 15:36:44 rich Exp $ include ../Makefile.config @@ -52,7 +52,8 @@ LIB_OBJS := \ lib/cocanwiki_pages.cmo \ lib/cocanwiki_diff.cmo \ lib/cocanwiki_mail.cmo \ - lib/cdvmm_phone_numbers.cmo + lib/cdvmm_phone_numbers.cmo \ + lib/cocanwiki_func_shell.cmo ifneq ($(OCAMLRSS),) LIB_OBJS += lib/cocanwiki_func_rss.cmo diff --git a/scripts/lib/cocanwiki_func_shell.ml b/scripts/lib/cocanwiki_func_shell.ml new file mode 100644 index 0000000..6c708c4 --- /dev/null +++ b/scripts/lib/cocanwiki_func_shell.ml @@ -0,0 +1,92 @@ +(* Run external shell functions. + * $Id: cocanwiki_func_shell.ml,v 1.1 2006/12/08 15:36:44 rich Exp $ + *) + +open Printf +open ExtString +open ExtList + +open Cocanwiki_extensions + +(* This exception is thrown if there is an error during processing. The + * string is an error message. + *) +exception Error of string + +let ws_re = Pcre.regexp "\\s+" + +(* Disallowed in commands, if path is given. *) +let reserved = [ ".."; "/"; "\\" ] + +let shell r dbh hostid arg = + try + (* Is this function enabled for this host? This is a safety feature + * to ensure that people only turn this feature on if they really + * mean to. + *) + let rows = + PGSQL(dbh) "select enable_shell_func from hosts where id = $hostid" in + if rows <> [true] then + raise (Error "Shell function is disabled on this host. You have to enable it manually by setting enable_shell_func and optionally set enable_shell_func_path for the relevant row in the hosts table. This function is dangerous because it allows editors to run arbitrary shell commands on the server."); + + let arg = match arg with + | None -> raise (Error ("missing command name: try {{shell:command}}")) + | Some arg -> arg in + + let args = Pcre.split ~rex:ws_re arg in + let command, args = + match args with + | command :: args -> command, args + | _ -> raise (Error ("missing command name: try {{shell:command}}")) in + + (* If the path is specified, then limit commands to run in this path. + * Disallow '..', '/' and '\'. + * If NO PATH is specified, then allow ARBITRARY commands to run (DANGER!) + *) + let path = + List.hd ( + PGSQL(dbh) + "select enable_shell_func_path from hosts where id = $hostid" + ) in + let command = + match path with + | None -> command (* Any command can run. *) + | Some path -> + (* Disallow reserved characters. *) + if List.exists (String.exists command) reserved then + raise (Error "command contains reserved characters (eg. '..' or '/')."); + Filename.concat path command in + + (* Run the command and capture the output. *) + let cmd = + command ^ " " ^ String.concat " " (List.map Filename.quote args) in + prerr_endline cmd; + let chan = Unix.open_process_in cmd in + let lines = ref [] in + let lines = + try + while true; do + let line = input_line chan in + lines := line :: !lines + done; + List.rev !lines + with + End_of_file -> List.rev !lines in + let stat = Unix.close_process_in chan in + (match stat with + | Unix.WEXITED 0 -> () + | Unix.WEXITED i -> + raise (Error ("command failed with code " ^ string_of_int i)) + | Unix.WSIGNALED i -> + raise (Error ("command killed by signal " ^ string_of_int i)) + | Unix.WSTOPPED i -> + raise (Error ("command stopped by signal " ^ string_of_int i))); + + String.concat "\n" lines + with + Error msg -> + "shell: " ^ Cgi_escape.escape_html msg + +(* Register function. *) +let () = + external_functions := ("shell", shell) :: !external_functions -- 1.8.3.1