3 # This script, noshellinject, creates a mount namespace,
4 # in which common shell commands (/bin/sh, /bin/bash, ...) are bind-mounted over with "notashell".
6 # Using notashell(1) is supposed to prevent shell-injections.
8 # notashell(1) bypasses shell only when the program, which is called by noshellinject, does
9 # directly execute an over-mounted shell (notashell switches NOTASHELL_INTERCEPT environment off).
10 # So in your untrusted-argument-validator script and any of its subprocesses,
11 # sh(1)/bash(1) may be called at your convenience.
12 # This is safe, because it's your control what you pass to them from the validator.
14 # One notable thing in its mechanics is that this re-enablement of real shells
15 # is done not by switching back to the original mount namespace,
16 # because the neglegent program (which calls system(3) inconsiderately) may
17 # switch privilege level so its child process can not switch namespaces,
18 # but by keep calling notashell(1) in guise of sh(1)/bash(1)
19 # but no longer having NOTASHELL_INTERCEPT makes notashell(1) call the real boys
20 # from /var/lib/notashell where they were previously saved (bind-mounted).
21 # Therefore, don't allow users to change NOTASHELL_INTERCEPT environment either.
25 # where to save (bind-mount) read shell executables from /bin
26 real_shells_dir
=/var
/lib
/notashell
27 # may extend if the neglegent program calls something else as shell
28 shellnames
=(sh dash bash
)
32 findmnt
--noheadings --output PROPAGATION
"$1"
40 if type bindmount-v2
>/dev
/null
2>&1
42 bindmount-v2
"$src" "$target"
45 mount
--bind "$src" "$target"
47 echo "$0: $src is a symlink, which may unexpectedly leak out to the parent namespace if bind-mounted. stop." >&2
54 mkdir
-p "$real_shells_dir"
55 # bind-mount this dir over itself to be able to make private mounts under it
56 mount
--bind "$real_shells_dir" "$real_shells_dir"
57 mount
--make-private "$real_shells_dir"
59 for shell
in "${shellnames[@]}"
61 # can bind-mount existing paths only
62 [ -f "$real_shells_dir/real-$shell" ] || true
> "$real_shells_dir/real-$shell"
63 # save the real shell for later use
64 mount
--bind /bin
/$shell "$real_shells_dir/real-$shell"
67 # after sub-mounts are mounted, clean up the parent mount from the parent namespace
68 nsenter
-t $PPID -m umount
-l "$real_shells_dir"
70 # bind-mount this dir over itself to be able to make private mounts under it
71 bind_mount_symlink
/bin
/bin
72 mount
--make-private /bin
74 # over-mount shells to be able to intercept "sh -c commandLine" type calls
75 for shell
in "${shellnames[@]}"
77 bind_mount_symlink
/usr
/tool
/notashell
/bin
/$shell
80 # after sub-mounts are mounted, clean up the parent mount from the parent namespace
81 nsenter
-t $PPID -m umount
-l /bin
84 export NOTASHELL_INTERCEPT
=1
87 propagation
=`propagtype /`
88 if [ "$propagation" != shared
]
90 echo "$0: mount events propagation of the root directory is $propagation, not shared." >&2
94 exec unshare
--mount --propagation=shared
-- "$0" --inner "$@"