From c0d1b5a004b9949bb455b7dbe17b335b7cab9ead Mon Sep 17 00:00:00 2001 From: "Frank Ch. Eigler" Date: Fri, 12 Feb 2010 10:25:43 -0500 Subject: [PATCH] PR11105 part 2: tighten constraints on stap-server parameters passed to make * util.h, util.cxx (assert_match_regexp): New function. * main.cxx (main): Constrain -R, -r, -a, -D, -S, -q, -B flags. * stap-serverd (listen): Harden stap-server-connect with ulimit/loop. * testsuite/systemtap.server/{client,server}_args.exp: Revised. --- main.cxx | 24 ++++++------ stap-serverd | 22 +++++++---- testsuite/systemtap.server/client_args.exp | 28 +++++++------- testsuite/systemtap.server/server_args.exp | 45 ++++++++++++---------- util.cxx | 36 ++++++++++++++++- util.h | 2 +- 6 files changed, 101 insertions(+), 56 deletions(-) diff --git a/main.cxx b/main.cxx index 8f5ee72e5..2dba179f5 100644 --- a/main.cxx +++ b/main.cxx @@ -57,7 +57,7 @@ version () << "SystemTap translator/driver " << "(version " << VERSION << "/" << dwfl_version (NULL) << " " << GIT_MESSAGE << ")" << endl - << "Copyright (C) 2005-2009 Red Hat, Inc. and others" << endl + << "Copyright (C) 2005-2010 Red Hat, Inc. and others" << endl << "This is free software; see the source for copying conditions." << endl; } @@ -708,12 +708,12 @@ main (int argc, char * const argv []) break; case 'o': + // NB: client_options not a problem, since pass 1-4 does not use output_file. s.output_file = string (optarg); break; case 'R': - if (client_options) - client_options_disallowed += client_options_disallowed.empty () ? "-R" : ", -R"; + if (client_options) { cerr << "ERROR: -R invalid with --client-options" << endl; usage(s,1); } s.runtime_path = string (optarg); break; @@ -722,6 +722,7 @@ main (int argc, char * const argv []) client_options_disallowed += client_options_disallowed.empty () ? "-m" : ", -m"; s.module_name = string (optarg); save_module = true; + // XXX: convert to assert_regexp_match() { string::size_type len = s.module_name.length(); @@ -766,15 +767,14 @@ main (int argc, char * const argv []) break; case 'r': - if (client_options) - client_options_disallowed += client_options_disallowed.empty () ? "-r" : ", -r"; + if (client_options) // NB: no paths! + assert_regexp_match("-r parameter from client", optarg, "^[a-z0-9_\\.-]+$"); setup_kernel_release(s, optarg); break; case 'a': - if (client_options) - client_options_disallowed += client_options_disallowed.empty () ? "-a" : ", -a"; - s.architecture = string(optarg); + assert_regexp_match("-a parameter", optarg, "^[a-z0-9_-]+$"); + s.architecture = string(optarg); break; case 'k': @@ -821,16 +821,19 @@ main (int argc, char * const argv []) break; case 'D': + assert_regexp_match ("-D parameter", optarg, "^[a-z_][a-z_0-9]*(=[a-z_0-9]+)?$"); if (client_options) client_options_disallowed += client_options_disallowed.empty () ? "-D" : ", -D"; s.macros.push_back (string (optarg)); break; case 'S': + assert_regexp_match ("-S parameter", optarg, "^[0-9]+(,[0-9]+)?$"); s.size_option = string (optarg); break; case 'q': + if (client_options) { cerr << "ERROR: -q invalid with --client-options" << endl; usage(s,1); } s.tapset_compile_coverage = true; break; @@ -861,9 +864,8 @@ main (int argc, char * const argv []) break; case 'B': - if (client_options) - client_options_disallowed += client_options_disallowed.empty () ? "-B" : ", -B"; - s.kbuildflags.push_back (string (optarg)); + if (client_options) { cerr << "ERROR: -B invalid with --client-options" << endl; usage(s,1); } + s.kbuildflags.push_back (string (optarg)); break; case 0: diff --git a/stap-serverd b/stap-serverd index eda9711e7..5820286fe 100755 --- a/stap-serverd +++ b/stap-serverd @@ -360,11 +360,19 @@ function advertise_presence { function listen { # The stap-server-connect program will listen forever # accepting requests. - ${stap_pkglibexecdir}stap-server-connect \ - -p $port -n $nss_cert -d $ssl_db -w $nss_pw \ - -s "$stap_options" \ - >> $logfile 2>&1 & - wait '%${stap_pkglibexecdir}stap-server-connect' >> $logfile 2>&1 + # CVE-2009-4273 ... or at least, until resource limits fire + while true; do # NB: loop to avoid DoS by deliberate rlimit-induced halt + # NB: impose resource limits in case of mischevious data inducing + # too much / long computation + (ulimit -f 50000 -s 1000 -t 60 -u 20 -v 500000; + exec ${stap_pkglibexecdir}stap-server-connect \ + -p $port -n $nss_cert -d $ssl_db -w $nss_pw \ + -s "$stap_options") & + stap_server_connect_pid=$! + wait + # NB: avoid superfast spinning in case of a ulimit or other failure + sleep 1 + done >> $logfile 2>&1 } # function: warning [ MESSAGE ] @@ -396,8 +404,8 @@ function terminate { wait '%avahi-publish-service' >> $logfile 2>&1 # Kill any running 'stap-server-connect' job. - kill -s SIGTERM '%${stap_pkglibexecdir}stap-server-connect' >> $logfile 2>&1 - wait '%${stap_pkglibexecdir}stap-server-connect' >> $logfile 2>&1 + kill -s SIGTERM $stap_server_connect_pid >> $logfile 2>&1 + wait $stap_server_connect_pid >> $logfile 2>&1 exit } diff --git a/testsuite/systemtap.server/client_args.exp b/testsuite/systemtap.server/client_args.exp index f41b91cb5..694d546da 100644 --- a/testsuite/systemtap.server/client_args.exp +++ b/testsuite/systemtap.server/client_args.exp @@ -5,33 +5,34 @@ set test "Invalid Server Client Arguments" set test_file $srcdir/systemtap.server/test.stp # Test invalid combinations. -set error_regexp ".*You can't specify .* when --unprivileged is specified.*" +set error_regexp ".*(ERROR)|(You can't specify .* when --unprivileged is specified).*" set invalid_options [list \ - "--unprivileged --client-options -a i386" \ "--unprivileged --client-options -B X=Y" \ "--unprivileged --client-options -D X=Y" \ "--unprivileged --client-options -I /tmp" \ "--unprivileged --client-options -m test" \ "--unprivileged --client-options -R /tmp" \ - "--unprivileged --client-options -r [exec uname -r]" \ - "--unprivileged --client-options -a i386 -B X=Y -D X=Y -I /tmp -m test -R /tmp -r [exec uname -r]" \ - "--client-options --unprivileged -a i386" \ + "--unprivileged --client-options -B X=Y -D X=Y -I /tmp -m test -R /tmp -r [exec uname -r]" \ "--client-options --unprivileged -B X=Y" \ "--client-options --unprivileged -D X=Y" \ "--client-options --unprivileged -I /tmp" \ "--client-options --unprivileged -m test" \ "--client-options --unprivileged -R /tmp" \ - "--client-options --unprivileged -r [exec uname -r]" \ - "--client-options --unprivileged -a i386 -B X=Y -D X=Y -I /tmp -m test -R /tmp -r [exec uname -r]" \ - "--client-options -a i386 --unprivileged" \ + "--client-options --unprivileged -B X=Y -D X=Y -I /tmp -m test -R /tmp -r [exec uname -r]" \ "--client-options -B X=Y --unprivileged" \ "--client-options -D X=Y --unprivileged" \ "--client-options -I /tmp --unprivileged" \ "--client-options -m test --unprivileged" \ "--client-options -R /tmp --unprivileged" \ - "--client-options -r [exec uname -r] --unprivileged" \ - "--client-options -a i386 -B X=Y -D X=Y -I /tmp -m test -R /tmp -r [exec uname -r] --unprivileged" \ + "--client-options -B X=Y -D X=Y -I /tmp -m test -R /tmp -r [exec uname -r] --unprivileged" \ + "--client-options -R /path" \ + "-D \"foo;bar\"" \ + "-D 2=4" \ + "-D \"foo;bar\"" \ + "--client-options -r /path" \ + "-S /path" \ + "--client-options -q" \ ] foreach options $invalid_options { @@ -66,9 +67,8 @@ set valid_options [list \ "-D X=Y" \ "-I /tmp" \ "-m test" \ - "-R /tmp" \ "-r [exec uname -r]" \ - "-a i386 -B X=Y -D X=Y -I /tmp -m test -R /tmp -r [exec uname -r]" \ + "-a i386 -B X=Y -D X=Y -I /tmp -m test -r [exec uname -r]" \ "--unprivileged" \ "--unprivileged -a i386" \ "--unprivileged -B X=Y" \ @@ -80,13 +80,11 @@ set valid_options [list \ "--unprivileged -a i386 -B X=Y -D X=Y -I /tmp -m test -R /tmp -r [exec uname -r]" \ "--client-options" \ "--client-options -a i386" \ - "--client-options -B X=Y" \ "--client-options -D X=Y" \ "--client-options -I /tmp" \ "--client-options -m test" \ - "--client-options -R /tmp" \ "--client-options -r [exec uname -r]" \ - "--client-options -a i386 -B X=Y -D X=Y -I /tmp -m test -R /tmp -r [exec uname -r]" \ + "--client-options -a i386 -D X=Y -I /tmp -m test -r [exec uname -r]" \ "--unprivileged --client-options" \ "--client-options --unprivileged" \ "--unprivileged -a i386 --client-options" \ diff --git a/testsuite/systemtap.server/server_args.exp b/testsuite/systemtap.server/server_args.exp index eac9074c1..81a2c37bc 100644 --- a/testsuite/systemtap.server/server_args.exp +++ b/testsuite/systemtap.server/server_args.exp @@ -114,27 +114,27 @@ if {[installtest_p]} then { # for debugging a currently failing case and helps to ensure that previously # fixed cases do not regress. set previously_fixed [list \ - "-p1 -I=\\w94\nbh -R'-1vo*w- -e -B9 -Dhfuo0iu7 -c" \ - "-p1 -I8o\\2ie -Rtu\\\n -e'1\\ -B*3x8k\; -D\n\" -c" \ - "-p1 -Ira\\3;c g -Rlr\"6/3ho -e0fle'qq -B -Dr/316k\\o8 -cjyoc\n3" \ - "-p1 -I6p3 -Rk3g-t\n89 -elc -Bd -Dqgsgv' -c" \ - "-p1 -I\"vyv;z -Rzvchje2\\ -ej\"/3 -Be -D/ 01qck\n -c3u55zut" \ - "-p1 -I1 -R\n -eo9e\nx047q -B\"*dd;tn\\ -D9xyefk0a -cvl98/x1'i" \ + "-p1 -I=\\w94\nbh -e -Dhfuo0iu7 -c" \ + "-p1 -I8o\\2ie -e'1\\ -D\n\" -c" \ + "-p1 -Ira\\3;c g -e0fle'qq -Dr/316k\\o8 -cjyoc\n3" \ + "-p1 -I6p3 -elc -Dqgsgv' -c" \ + "-p1 -I\"vyv;z -ej\"/3 -D/ 01qck\n -c3u55zut" \ + "-p1 -I1 -eo9e\nx047q -D9xyefk0a -cvl98/x1'i" \ "-p1 -c; test.stp" \ - "-p1 -I4hgy96 -R -e5oo39p -Bile\\vp -Ddx8v -c4;" \ - "-p1 -I -Repwd9 -esq3wors -Btmk;\\t -Dz -c*eibz8h2e" \ - "-p1 -I -Ry a -em339db5 -B;ae41428d -Du2;c0ps -ch9o\\" \ - "-p1 -Ipfjps4 -Rx479 -ebug4dc -Bih;fe2 -Du8vd fvkl -c" \ - "-p1 -I0\"nspzjyf -R -e5r3up8h -Bgqnyjq6w -Dmi;ojp9m -cx;a2fat" \ - "-p1 -Iu -R9 -ek7;r -Big -Dcu\"; -c\"hc" \ - "-p1 -Icd4fidq -Rkj m40mv -edn -B7ria -D;8ha\\cjr -c1*vnq" \ - "-p1 -I;3 -R3lq;vp -er8e -Bgdqjqdy -D -cb6k29z" \ - "-p1 -Ircj -R -e -B -D -c\\vmww" \ - "-p1 -Illc5 -Rug*\\o -e65wof9 -B qr*=x7x5 -D -cgx;" \ - "-p1 -Iyaj420=3 -R -e\" -Bx68j -D -cd'5mi" \ - "-p1 -Ir -Rwd8;;sjl -e -Bxh; -D29\\ -cj2szt;4" \ - "-p1 -Ibno3=b4sk -R*5 -e' -Byl63flos -Dg2-j;e -c2ijx'" \ - "-p1 -I285v7pl -R9a -eo5\\0 -Bfs* -D86s -c-c*v" \ + "-p1 -I4hgy96 -e5oo39p -Ddx8v -c4;" \ + "-p1 -I -esq3wors -Dz -c*eibz8h2e" \ + "-p1 -I a -em339db5 -Du2;c0ps -ch9o\\" \ + "-p1 -Ipfjps4 -ebug4dc -Du8vd fvkl -c" \ + "-p1 -I0\"nspzjyf -e5r3up8h -Dmi;ojp9m -cx;a2fat" \ + "-p1 -Iu -ek7;r -Dcu\"; -c\"hc" \ + "-p1 -Icd4fidq m40mv -edn -D;8ha\\cjr -c1*vnq" \ + "-p1 -I;3 -er8e -D -cb6k29z" \ + "-p1 -Ircj -e -D -c\\vmww" \ + "-p1 -Illc5 -e65wof9 qr*=x7x5 -D -cgx;" \ + "-p1 -Iyaj420=3 -e\" -D -cd'5mi" \ + "-p1 -Ir -e -D29\\ -cj2szt;4" \ + "-p1 -Ibno3=b4sk -e' -Dg2-j;e -c2ijx'" \ + "-p1 -I285v7pl -eo5\\0 -D86s -c-c*v" \ ] set i 0 @@ -150,7 +150,10 @@ foreach options $previously_fixed { # Generate semi-random arguments containing with potential problem characters. # Check that running systemtap with the client/server generates output # comparable to running stap directly. -set dangerous_options [list "-I" "-R" "-e" "-B" "-D" "-c"] +set dangerous_options [list "-I" "-e" "-D" "-c" "-S"] +# NB: Other options could be candidates here, like -r and -B, but +# there stap-server imposes more restrictions than local stap, so +# this simple error-matching test cannot use them. set argchars "0123456789/;*'=-\\\"\n abcdefghijklmnopqrstuvwxyz" for {set i 0} {$i < $iterations} {incr i} { diff --git a/util.cxx b/util.cxx index 736e5a30b..73ba167cb 100644 --- a/util.cxx +++ b/util.cxx @@ -1,5 +1,5 @@ // Copyright (C) Andrew Tridgell 2002 (original file) -// Copyright (C) 2006, 2009 Red Hat Inc. (systemtap changes) +// Copyright (C) 2006-2010 Red Hat Inc. (systemtap changes) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -19,6 +19,8 @@ #include "sys/sdt.h" #include #include +#include +#include extern "C" { #include @@ -31,6 +33,7 @@ extern "C" { #include #include #include +#include } using namespace std; @@ -413,4 +416,35 @@ kill_stap_spawn(int sig) return spawned_pid ? kill(spawned_pid, sig) : 0; } + +void assert_regexp_match (const string& name, const string& value, const string& re) +{ + typedef map cache; + static cache compiled; + cache::iterator it = compiled.find (re); + regex_t* r = 0; + if (it == compiled.end()) + { + r = new regex_t; + int rc = regcomp (r, re.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED); + if (rc) { + cerr << "regcomp " << re << " (" << name << ") error rc=" << rc << endl; + exit(1); + } + compiled[re] = r; + } + else + r = it->second; + + // run regexec + int rc = regexec (r, value.c_str(), 0, 0, 0); + if (rc) + { + cerr << "ERROR: Safety pattern mismatch for " << name + << " ('" << value << "' vs. '" << re << "') rc=" << rc << endl; + exit(1); + } +} + + /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ diff --git a/util.h b/util.h index 8fc64cbd0..75e198cae 100644 --- a/util.h +++ b/util.h @@ -21,7 +21,7 @@ const std::string cmdstr_quoted(const std::string& cmd); std::string git_revision(const std::string& path); int stap_system(int verbose, const std::string& command); int kill_stap_spawn(int sig); - +void assert_regexp_match (const std::string& name, const std::string& value, const std::string& re); // stringification generics -- 2.43.5