From owner-freebsd-rc@FreeBSD.ORG Wed Oct 20 16:53:52 2010 Return-Path: Delivered-To: freebsd-rc@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 383CE106566C; Wed, 20 Oct 2010 16:53:52 +0000 (UTC) (envelope-from dteske@vicor.com) Received: from postoffice.vicor.com (postoffice.vicor.com [69.26.56.53]) by mx1.freebsd.org (Postfix) with ESMTP id 1CE928FC1B; Wed, 20 Oct 2010 16:53:51 +0000 (UTC) Received: from [208.206.78.30] (port=55329 helo=dt.vicor.com) by postoffice.vicor.com with esmtpsa (SSLv3:RC4-MD5:128) (Exim 4.71) (envelope-from ) id 1P8bvB-0005Sa-2o; Wed, 20 Oct 2010 09:53:51 -0700 From: Devin Teske To: Julian Elischer In-Reply-To: <4CBE6F55.5040609@freebsd.org> References: <1286925182.32724.18.camel@localhost.localdomain> <1286996709.32724.60.camel@localhost.localdomain> <1287448781.5713.3.camel@localhost.localdomain> <1287510629.25599.2.camel@localhost.localdomain> <20101019195225.GB2127@garage.freebsd.pl> <1287540769.25599.73.camel@localhost.localdomain> <4CBE6F55.5040609@freebsd.org> Content-Type: text/plain Organization: Vicor, Inc Date: Wed, 20 Oct 2010 09:53:48 -0700 Message-Id: <1287593628.19873.41.camel@localhost.localdomain> Mime-Version: 1.0 X-Mailer: Evolution 2.0.2 (2.0.2-41.el4) Content-Transfer-Encoding: 7bit X-Scan-Signature: e8093ea28847b015b6a579b3a148cac8 X-Scan-Host: postoffice.vicor.com Cc: Pawel Jakub Dawidek , freebsd-rc@freebsd.org Subject: Re: sysrc(8) -- a sysctl(8)-like utility for managing rc.conf(5) X-BeenThere: freebsd-rc@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Discussion related to /etc/rc.d design and implementation." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 20 Oct 2010 16:53:52 -0000 On Tue, 2010-10-19 at 21:25 -0700, Julian Elischer wrote: > On 10/19/10 7:12 PM, Devin Teske wrote: > > On Tue, 2010-10-19 at 21:52 +0200, Pawel Jakub Dawidek wrote: > >> On Tue, Oct 19, 2010 at 10:50:29AM -0700, Devin Teske wrote: > >>> I added `-j jail' for specifying a jail id or name to operate within > >>> (requires jls(8); overrides `-R dir'). > >> [...] > >> > >> Note that operating on jail files from outside a jail is serious > >> security problem. The files from within the jail can be symbolic links > >> that point to files from outside a jail from your perspective. Even > >> chroot(2) to jail's root is neither safe (running applications that can > >> be modified by jail's root is obvious security hole) nor reliable (jail > >> might not have all the commands). The only safe way is to jexec(8) into > >> a jail, but it of course has the same relialability issue as chroot(8). > >> > > I see the concern, and you're absolutely right. > > > > I did see the need to use either chroot(8) or jexec(8), but for exactly > > the same reasons you mention (handing execution off-to something that > > could have been modified by the jail's root-user), I ended up writing > > routines that just edited the files outside the jail. > > > > It appears that (due to the other aforementioned security problem, > > involving hand-crafted symbolic links with malicious-intent), the only > > proper solution would be: > > > > a. Copy ourselves into some temporary location within the jail > > b. Hand execution off to ourself using either jexec(8) or chroot(8) > > > can you fire off a shell in the jail and somehow shove yourself down > it's throat? > I vaguely remember that one could feed a shell script into the shell > somehow.. > but I forget the details. You bet. $ echo tail -2 /usr/include/osreldate.h | sudo chroot / /bin/sh #define __FreeBSD_version 801000 #endif $ echo tail -2 /usr/include/osreldate.h | sudo chroot /usr/jail/foomaster /bin/sh #define __FreeBSD_version 492101 #endif A more complete (yet simplified) example of a shell-script shoving itself into a chroot'ed shell -- complete with custom-set of positional parameters: ================================= BEGIN FILE: foo.sh #!/bin/sh jailed="$( sysctl -n security.jail.jailed )" echo "jailed: $jailed" echo -n "args: " for arg in "$@"; do echo -n "'$arg' " done echo [ $jailed -eq 1 ] && exit ( echo 'set -- "a b" "c d" "e f"' cat $0 ) | sudo jexec 8 /bin/sh ================================= END FILE When executed: $ uname -spr FreeBSD 8.1-RELEASE-p1 amd64 $ jls jid path 8 /usr/jail/foomaster 9 /usr/jail/foomaster.deluxe $ ./foo.sh "1 2" "3 4" "5 6" jailed: 0 args: '1 2' '3 4' '5 6' jailed: 1 args: 'a b' 'c d' 'e f' This is of-course a simplified example. A productionized version should take into consideration that PATH expansion may have been performed. This is trivial to "undo" as it were: myscript="$0" if [ ! -f "$0" ]; then # NOTE: `-x' should not be used because we could have been # invoked as `sh script' which allows execution of non- # executable shell-scripts # Perform reverse-PATH expansion for dir in $PATH; do # NOTE: If PATH-expansion was performed, the file # must now be executable (where earlier it could # have been non-executable if executed via sh in an # interactive shell) [ -x "$dir/$0" -a -f "$dir/$0" ] || continue # If we hit this point, we found a match myscript="$dir/$0" break done fi # At this point, $myscript should be a valid path to our own script # regardless of whether we were invoked by any of the following: # # sh ./myscript : Relative path (executable-bit optional) # sh myscript : Relative path (executable-bit optional) # sh /path/to/myscript : Absolute path (executable-bit optional) # env sh myscript : Relative path (executable-bit optional) # ./myscript : Relative path # /path/to/myscript : Absolute path # myscript : $PATH expansion performed However, this method is not without pitfalls: 1. Let's say that the script being executed is /usr/local/bin/myscript 2. Let's say that this script was invoked using $PATH expansion ($0 == 'myscript') 3. Let's say that the CWD is ~root 4. Let's say that `./myscript' exists 5. The above code will incorrectly think that we (/usr/local/bin/myscript) are ~root/myscript Unfortunately, there seems little way around this as even the following code fails in interesting ways: myscript="$0" case "$myscript" in */*) : Do nothing (already an absolute or relative path) ;; *) # $0 doesn't contain any slashes -- we were invoked in # one of two ways: # # 1. via $PATH expansion in the interactive shell # 2. via sh(1) and $0 truly is in the CWD # # NOTE: It is not possible to detect when we were invoked # via exec(3) versus sh(1) versus sh(1)-via-env(1) so # testing for #2 above is impossible. for dir in $PATH; do [ -x "$dir/$0" -a -f "$dir/$0" ] || continue myscript="$dir/$0" break done esac The comments in the above code-block explain why it won't work. In the case of: sh myscript $0 will equal "myscript" but, when reverse-PATH expansion is performed, we may end up finding something with the same name and then end up executing something perhaps entirely different. It seems clear that the only safe way to pass off execution to onself is to hard-code the path to yourself. For example: myscript=/usr/local/bin/foo ( echo 'set -- "a b" "c d" "e f"' cat $myscript ) | sudo jexec 8 /bin/sh Can anyone think of anything better? -- Cheers, Devin Teske -> CONTACT INFORMATION <- Business Solutions Consultant II FIS - fisglobal.com 510-735-5650 Mobile 510-621-2038 Office 510-621-2020 Office Fax 909-477-4578 Home/Fax devin.teske@fisglobal.com -> LEGAL DISCLAIMER <- This message contains confidential and proprietary information of the sender, and is intended only for the person(s) to whom it is addressed. Any use, distribution, copying or disclosure by any other person is strictly prohibited. If you have received this message in error, please notify the e-mail sender immediately, and delete the original message without making a copy. -> FUN STUFF <- -----BEGIN GEEK CODE BLOCK----- Version 3.1 GAT/CS d(+) s: a- C++(++++) UB++++$ P++(++++) L++(++++) !E--- W++ N? o? K- w O M+ V- PS+ PE Y+ PGP- t(+) 5? X+(++) R>++ tv(+) b+(++) DI+(++) D(+) G+>++ e>+ h r>++ y+ ------END GEEK CODE BLOCK------ http://www.geekcode.com/ -> END TRANSMISSION <-