From bd0aeed0626aa2f9785ba52aaaaf9759e9976e80 Mon Sep 17 00:00:00 2001 From: Assaf Gordon Date: Sun, 24 Apr 2016 21:06:57 -0400 Subject: [PATCH] sed: new --sandbox option In sandbox mode, r/w/e commands are not allowed. This ensures sed operates only on files designated on the command line, and cannot execute external programs. * sed/sed.c (sandbox): New option variable. (usage): List new option. (main): Accept new option in getopt. * sed/sed.h (sandbox): New extern option variable. * sed/compile.c (DISALLOWED_CMD): New error message. (read_filename): Abort in sandbox mode (r/w commands). (compile_program): Abort on 'e' in sandbox mode. * testsuite/sandbox.sh: Test new option. * testsuite/local.mk: Add new test. * NEWS: Mention new option. * doc/sed-in.texi, doc/sed.texi: Document new option. --- NEWS | 5 +++ doc/sed-in.texi | 9 +++++ doc/sed.texi | 9 +++++ sed/compile.c | 17 ++++++++-- sed/sed.c | 13 ++++++++ sed/sed.h | 3 ++ testsuite/local.mk | 1 + testsuite/sandbox.sh | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 147 insertions(+), 2 deletions(-) create mode 100755 testsuite/sandbox.sh diff --git a/NEWS b/NEWS index ab11060..e9b4584 100644 --- a/NEWS +++ b/NEWS @@ -82,6 +82,11 @@ GNU sed NEWS -*- outline -*- diffutils and parted for a few years. +** New Features + + new --sandbox option rejects programs with r/w/e commands. + + * Noteworthy changes in release 4.2.2 (2012-12-22) [stable] * don't misbehave (truncate input) for lines of length 2^31 and longer diff --git a/doc/sed-in.texi b/doc/sed-in.texi index 1c5e0b2..b5f0033 100644 --- a/doc/sed-in.texi +++ b/doc/sed-in.texi @@ -367,6 +367,15 @@ of each file, @code{$} refers to the last line of each file, and files invoked from the @code{R} commands are rewound at the start of each file. address@hidden --sandbox address@hidden --sandbox address@hidden Sandbox mode +In sandbox mode, @code{e/w/r} commands are rejected - programs containing +them will be aborted without being run. Sandbox mode ensures @command{sed} +operates only on the input files designated on the command line, and +cannot run external programs. + + @item -u @itemx --unbuffered @opindex -u diff --git a/doc/sed.texi b/doc/sed.texi index bf45cfc..fc2afac 100644 --- a/doc/sed.texi +++ b/doc/sed.texi @@ -368,6 +368,15 @@ of each file, @code{$} refers to the last line of each file, and files invoked from the @code{R} commands are rewound at the start of each file. address@hidden --sandbox address@hidden --sandbox address@hidden Sandbox mode +In sandbox mode, @code{e/w/r} commands are rejected - programs containing +them will be aborted without being run. Sandbox mode ensures @command{sed} +operates only on the input files designated on the command line, and +cannot run external programs. + + @item -u @itemx --unbuffered @opindex -u diff --git a/sed/compile.c b/sed/compile.c index 36bdec6..2d5c4b1 100644 --- a/sed/compile.c +++ b/sed/compile.c @@ -139,7 +139,8 @@ static const char errors[] = "unknown command: `%c'\0" "incomplete command\0" "\":\" lacks a label\0" - "recursive escaping after \\c not allowed"; + "recursive escaping after \\c not allowed\0" + "e/r/w commands disabled in sandbox mode"; #define BAD_BANG (errors) #define BAD_COMMA (BAD_BANG + sizeof(N_("multiple `!'s"))) @@ -185,7 +186,10 @@ static const char errors[] = + sizeof(N_("incomplete command"))) #define RECURSIVE_ESCAPE_C (COLON_LACKS_LABEL \ + sizeof(N_("\":\" lacks a label"))) -/* #define END_ERRORS (COLON_LACKS_LABEL + sizeof(N_("\":\" lacks a label"))) */ +#define DISALLOWED_CMD (RECURSIVE_ESCAPE_C \ + + sizeof(N_("recursive escaping after \\c not allowed"))) +/* #define END_ERRORS (DISALLOWED_CMD \ + + sizeof(N_( "e/r/w commands disabled in sandbox mode"))) */ static struct output *file_read = NULL; static struct output *file_write = NULL; @@ -340,6 +344,9 @@ read_filename(void) struct buffer *b; int ch; + if (sandbox) + bad_prog(_(DISALLOWED_CMD)); + b = init_buffer(); ch = in_nonblank(); while (ch != EOF && ch != '\n') @@ -1106,6 +1113,9 @@ compile_program(struct vector *vector) break; case 'e': + if (sandbox) + bad_prog(_(DISALLOWED_CMD)); + ch = in_nonblank(); if (ch == EOF || ch == '\n') { @@ -1235,6 +1245,9 @@ compile_program(struct vector *vector) cur_cmd->x.cmd_subst->regx = compile_regex(b, flags, cur_cmd->x.cmd_subst->max_id + 1); free_buffer(b); + + if (cur_cmd->x.cmd_subst->eval && sandbox) + bad_prog(_(DISALLOWED_CMD)); } break; diff --git a/sed/sed.c b/sed/sed.c index 44efaab..ed11049 100644 --- a/sed/sed.c +++ b/sed/sed.c @@ -19,6 +19,7 @@ #include "sed.h" +#include #include #include #include @@ -53,6 +54,9 @@ bool separate_files = false; /* If set, follow symlinks when processing in place */ bool follow_symlinks = false; +/* If set, opearate in 'sandbox' mode */ +bool sandbox = false; + /* How do we edit files in-place? (we don't if NULL) */ char *in_place_extension = NULL; @@ -161,6 +165,8 @@ Usage: %s [OPTION]... {script-only-if-no-other-script} [input-file]...\n\ fprintf(out, _(" -s, --separate\n\ consider files as separate rather than as a single,\n\ continuous long stream.\n")); + fprintf(out, _(" --sandbox\n\ + operate in sandbox mode.\n")); fprintf(out, _(" -u, --unbuffered\n\ load minimal amounts of data from the input files and flush\n\ the output buffers more often\n")); @@ -189,6 +195,8 @@ main (int argc, char **argv) #define SHORTOPTS "bsnrzuEe:f:l:i::V:" #endif + enum { SANDBOX_OPTION = CHAR_MAX+1 }; + static const struct option longopts[] = { {"binary", 0, NULL, 'b'}, {"regexp-extended", 0, NULL, 'r'}, @@ -204,6 +212,7 @@ main (int argc, char **argv) {"quiet", 0, NULL, 'n'}, {"posix", 0, NULL, 'p'}, {"silent", 0, NULL, 'n'}, + {"sandbox", 0, NULL, SANDBOX_OPTION}, {"separate", 0, NULL, 's'}, {"unbuffered", 0, NULL, 'u'}, {"version", 0, NULL, 'v'}, @@ -329,6 +338,10 @@ main (int argc, char **argv) separate_files = true; break; + case SANDBOX_OPTION: + sandbox = true; + break; + case 'u': unbuffered = true; break; diff --git a/sed/sed.h b/sed/sed.h index 5c249cf..8579a86 100644 --- a/sed/sed.h +++ b/sed/sed.h @@ -240,6 +240,9 @@ extern bool use_extended_syntax_p; extern int mb_cur_max; extern bool is_utf8; +/* If set, operate in 'sandbox' mode - disable e/r/w commands */ +extern bool sandbox; + #define MBRTOWC(pwc, s, n, ps) \ (mb_cur_max == 1 ? \ (*(pwc) = btowc (*(unsigned char *) (s)), 1) : \ diff --git a/testsuite/local.mk b/testsuite/local.mk index e274d46..6ae11e4 100644 --- a/testsuite/local.mk +++ b/testsuite/local.mk @@ -49,6 +49,7 @@ T = \ testsuite/range-overlap.sh \ testsuite/recursive-escape-c.sh \ testsuite/regex-errors.sh \ + testsuite/sandbox.sh \ testsuite/stdin-prog.sh \ testsuite/subst-options.sh \ testsuite/subst-mb-incomplete.sh \ diff --git a/testsuite/sandbox.sh b/testsuite/sandbox.sh new file mode 100755 index 0000000..3e529fe --- /dev/null +++ b/testsuite/sandbox.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# Test --sandbox mode + +# Copyright (C) 2016 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +. "${srcdir=.}/testsuite/init.sh"; path_prepend_ ./sed +print_ver_ sed + +echo a > a || framework_failure_ +echo b > b || framework_failure_ + +# Same error message, different character position in the sed program. +for i in 1 6 14 ; do + err="sed: -e expression #1, char $i: e/r/w commands disabled in sandbox mode" + echo "$err" > exp-err$i || framework_failure_ +done + +fail=0 + +# read command - without sandbox +printf "a\nb\n" > exp || framework_failure_ +sed rb a > out || fail=1 +compare exp out || fail=1 + +# read command - with sandbox +returns_ 1 sed --sandbox -e 'ra' b >/dev/null 2>err1 || fail=1 +compare exp-err1 err1 || fail=1 + + +# write command (create file 'c') - without sandbox +sed wc a > out || fail=1 +compare a c || fail=1 +compare out a || fail=1 + +# write command - with sandbox +returns_ 1 sed --sandbox -e 'wd' a >/dev/null 2>err2 || fail=1 +compare exp-err1 err1 || fail=1 +# ensure file 'd' was not created +test -e d && fail=1 + + + +# execute command - without sandbox +sed 'etouch e' b > out || fail=1 +compare b out || fail=1 +# ensure 'e' was created +test -e e || fail=1 + +# execute command - with sandbox +returns_ 1 sed --sandbox -e 'etouch f' b >/dev/null 2>err3 || fail=1 +compare exp-err1 err3 || fail=1 +# ensure 'f' was not created +test -e f && fail=1 + + + +# substitute+write option - without sandbox +sed 's/^//wg' a > out || fail=1 +test -e g || fail=1 + +# substitute+write option - with sandbox +returns_ 1 sed --sandbox 's/^//wh' a >/dev/null 2>err4 || fail=1 +compare exp-err6 err4 || fail=1 +# ensure file 'h' was not created +test -e h && fail=1 + + + +# substitute+execute option - without sandbox +sed 's/.*/touch i/e' a > out || fail=1 +test -e i || fail=1 + +# substitute+execute option - with sandbox +returns_ 1 sed --sandbox 's/.*/touch j/e' a >/dev/null 2>err5 || fail=1 +compare exp-err14 err5 || fail=1 +# ensure file 'j' was not created +test -e j && fail=1 + + +Exit $fail -- 2.8.2