# Check privilige drop: UID/GID changes, chdir, environment, and file # descriptors # ensure failure to drop privileges doesn't retain root privileges sed -ri 's/^#(user|group)\s*=\s*$/\1 = nonexistent-\1/' /etc/lacme/lacme.conf ! lacme account 2>"$STDERR" || fail grepstderr -Fxq "getgrnam(nonexistent-group)" grepstderr -Fxq "Error: Invalid client version" sed -ri 's/^group\s*=\s*nonexistent.*/#&/' /etc/lacme/lacme.conf ! lacme account 2>"$STDERR" || fail grepstderr -Fxq "getpwnam(nonexistent-user)" grepstderr -Fxq "Error: Invalid client version" # create wrapper to inspect processes STATUSDIR="/dev/shm/lacme-wrap" install -oroot -groot -m0755 /dev/stdin /run/lacme-wrap <<-EOF #!/bin/sh set -ue PATH="/usr/bin:/bin" export PATH prefix="$STATUSDIR/\${1##*[/-]}" cat "\$prefix/status" pwd >"\$prefix/cwd" stat -c "%U:%G %#a" . >"\$prefix/cwd-mod" sort -z "\$prefix/environ" ( find -P /proc/\$\$/fd -mindepth 1 \! -lname "\$0" -printf "%P %#m %U:%G %l\\n" >"\$prefix/fd" ) exec "\$@" EOF # also check privilege drop for the spawned accountd adduser --system --group \ --home /nonexistent --no-create-home \ --gecos "lacme account user" \ --quiet lacme-account sed -ri 's/^#?(user|group)\s*=\s*nonexistent.*/\1 = lacme-account/' /etc/lacme/lacme.conf chown lacme-account: /etc/lacme/account.key install -oroot -groot -dm0755 -- "$STATUSDIR" install -olacme-account -groot -dm0700 -- "$STATUSDIR/accountd" install -o_lacme-client -groot -dm0700 -- "$STATUSDIR/client" install -o_lacme-www -groot -dm0700 -- "$STATUSDIR/webserver" # test with a group that's not the primary group (nogroup) of _lacme-www etc addgroup --system nogroup2 sed -ri 's|^#?group\s*=\s*nogroup$|group = nogroup2|' /etc/lacme/lacme.conf sed -ri 's|^#?command\s*=.*/lacme-accountd$|command = /run/lacme-wrap /usr/bin/lacme-accountd|' /etc/lacme/lacme.conf sed -ri 's|^#?command\s*=.*/lacme/client$|command = /run/lacme-wrap /usr/libexec/lacme/client|' /etc/lacme/lacme.conf sed -ri 's|^#?command\s*=.*/lacme/webserver$|command = /run/lacme-wrap /usr/libexec/lacme/webserver|' /etc/lacme/lacme.conf sed -ri 's|^#?config\s*=\s*$|config = /etc/lacme/lacme-accountd.conf|' /etc/lacme/lacme.conf check_status() { local path="$STATUSDIR/$1/status" user="$2" group="$3" UID="$(getent passwd "$user" | cut -sd: -f3)" GID="$(getent group "$group" | cut -sd: -f3)" [ -n "$UID" -a -n "$GID" ] || return 1 grep -Ex "Uid:\\s+$UID\\s+$UID\\s+$UID\\s+$UID" "$path" || return 1 grep -Ex "Gid:\\s+$GID\\s+$GID\\s+$GID\\s+$GID" "$path" || return 1 grep -Ex "Groups:\s+$GID\s*" "$path" || return 1 } check_cwd() { local path="$STATUSDIR/$1/cwd" dir="$2" cwd cwd="$(cat <"$path")" || return 1 [ "$cwd" = "$dir" ] || return 1 } check_accountd() { local socket_ino stderr prefix="$STATUSDIR/accountd" check_status accountd lacme-account lacme-account || return 1 check_cwd accountd / || return 1 diff --label="a/accountd/environ" --label="b/accountd/environ" \ --color=auto --unified "$prefix/environ" - <<-EOF HOME=/nonexistent LOGNAME=lacme-account PATH=/usr/bin:/bin SHELL=/usr/sbin/nologin TERM=$TERM USER=lacme-account EOF stderr="$(readlink -e "/proc/$$/fd/2")" socket_ino="$(sed -rn '/^0 .* socket:\[([0-9]+)\]$/ {s//\1/p;q}' "$prefix/fd")" [ -n "$socket_ino" ] || return 1 grep -Fxq "0 0700 $UID:$GID socket:[$socket_ino]" "$prefix/fd" || return 1 grep -Fxq "1 0700 $UID:$GID socket:[$socket_ino]" "$prefix/fd" || return 1 grep -Fxq "2 0700 $UID:$GID $stderr" "$prefix/fd" || return 1 sed -ri '\#^[012] #d' "$prefix/fd" ! test -s "$prefix/fd" || return 1 } check_client() { local command="$1" cwd="$2" UID GID stdout stderr prefix="$STATUSDIR/client" check_status client _lacme-client nogroup2 check_cwd client "$cwd" diff --label="a/client/environ" --label="b/client/environ" \ --color=auto --unified "$prefix/environ" - <<-EOF DEBUG=0 HOME=/nonexistent LOGNAME=_lacme-client PATH=/usr/bin:/bin SHELL=/usr/sbin/nologin TERM=$TERM USER=_lacme-client EOF stdout="$(readlink -e "/proc/$$/fd/1")" stderr="$(readlink -e "/proc/$$/fd/2")" if [ "$command" = "account" ]; then # no pipe grep -Fxq "0 0500 $UID:$GID /dev/null" "$prefix/fd" || return 1 grep -Fxq "1 0700 $UID:$GID $stdout" "$prefix/fd" || return 1 elif [ "$command" = "order" ]; then grep -Exq "0 0500 $UID:$GID pipe:\[[0-9]+\]" "$prefix/fd" || return 1 grep -Exq "1 0300 $UID:$GID pipe:\[[0-9]+\]" "$prefix/fd" || return 1 else exit 1 fi grep -Fxq "2 0700 $UID:$GID $stderr" "$prefix/fd" || return 1 sed -ri '\#^[012] #d' "$prefix/fd" grep -Exq "[0-9]+ 0700 $UID:$GID socket:\[[0-9]+\]" "$prefix/fd" || return 1 sed -ri '0,\#^[0-9]+ .* socket:\[[0-9]+\]$# {//d}' "$prefix/fd" grep -Eq "^[0-9]+ 0500 $UID:$GID /tmp/lacme-client.conf\.json-" "$prefix/fd" || return 1 sed -ri '0,\#^[0-9]+ .* /tmp/lacme-client.conf\.json-# {//d}' "$prefix/fd" ! test -s "$prefix/fd" || return 1 } check_webserver() { local cwd="$1" UID GID stdout stderr prefix="$STATUSDIR/webserver" check_status webserver _lacme-www nogroup2 check_cwd webserver "$cwd" diff --label="a/webserver/environ" --label="b/webserver/environ" \ --color=auto --unified "$prefix/environ" - <<-EOF DEBUG=0 HOME=/nonexistent LOGNAME=_lacme-www PATH=/usr/bin:/bin SHELL=/usr/sbin/nologin TERM=$TERM USER=_lacme-www EOF stdout="$(readlink -e "/proc/$$/fd/1")" stderr="$(readlink -e "/proc/$$/fd/2")" grep -Fxq "0 0500 $UID:$GID /dev/null" "$prefix/fd" || return 1 grep -Fxq "1 0700 $UID:$GID $stdout" "$prefix/fd" || return 1 grep -Fxq "2 0700 $UID:$GID $stderr" "$prefix/fd" || return 1 sed -ri '\#^[012] #d' "$prefix/fd" grep -Exq "[0-9]+ 0700 $UID:$GID socket:\[[0-9]+\]" "$prefix/fd" || return 1 sed -ri '0,\#^[0-9]+ .* socket:\[[0-9]+\]$# {//d}' "$prefix/fd" ! test -s "$prefix/fd" || return 1 } lacme account check_accountd check_client account / ! test -e "$STATUSDIR/webserver/status" # account 'command' doesn't start the webserver lacme newOrder check_accountd challenge_dir="$(cat "$STATUSDIR/webserver/cwd")" [ "${challenge_dir#"/tmp/acme-challenge."}" != "$challenge_dir" ] || exit 1 check_client order "$challenge_dir" check_webserver "$challenge_dir" # the temporary challenge directory is created with permissive mode diff --label="a/webserver/cwd" --label="b/webserver/cwd" \ --color=auto --unified "$STATUSDIR/webserver/cwd-mod" - <<-EOF _lacme-client:root 0755 EOF # vim: set filetype=sh :