From eaf2d0e777612ffcff56fa09626fafd1f5364f53 Mon Sep 17 00:00:00 2001 From: William Hubbs Date: Mon, 20 Feb 2017 09:55:05 -0600 Subject: [PATCH 1/8] fix the createdirectory function This function did not actually create a directory. Also, we did not respect that mode could be optional. --- tmpfiles | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tmpfiles b/tmpfiles index 02e3853..5c0ad9f 100644 --- a/tmpfiles +++ b/tmpfiles @@ -87,9 +87,10 @@ _restorecon() { createdirectory() { local mode="$1" uid="$2" gid="$3" path="$4" + dryrun_or_real mkdir "$path" [ "$uid" != - ] && dryrun_or_real chown $uid "$path" [ "$gid" != - ] && dryrun_or_real chgrp $gid "$path" - dryrun_or_real chmod $mode "$path" + [ "$mode" != - ] && dryrun_or_real chmod $mode "$path" } createfile() { From c3841f4f9915aecd3b776e0a88b693ad5b2ec1a4 Mon Sep 17 00:00:00 2001 From: William Hubbs Date: Mon, 20 Feb 2017 10:40:44 -0600 Subject: [PATCH 2/8] make sure modes, Owners and Groups are optional According to the tmpfiles.d man page I've read, Modes, owners and groups are always optional. --- tmpfiles | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tmpfiles b/tmpfiles index 5c0ad9f..13117ca 100644 --- a/tmpfiles +++ b/tmpfiles @@ -98,7 +98,7 @@ createfile() { dryrun_or_real touch "$path" [ "$uid" != - ] && dryrun_or_real chown $uid "$path" [ "$gid" != - ] && dryrun_or_real chgrp $gid "$path" - dryrun_or_real chmod $mode "$path" + [ "$mode" != - ] && dryrun_or_real chmod $mode "$path" } createpipe() { @@ -106,7 +106,7 @@ createpipe() { dryrun_or_real mkfifo "$path" [ "$uid" != - ] && dryrun_or_real chown $uid "$path" [ "$gid" != - ] && dryrun_or_real chgrp $gid "$path" - dryrun_or_real chmod $mode "$path" + [ "$mode" != - ] && dryrun_or_real chmod $mode "$path" } _b() { @@ -115,7 +115,9 @@ _b() { if [ ! -e "$path" ]; then dryrun_or_real mknod -m $mode $path b ${arg%:*} ${arg#*:} _restorecon "$path" - dryrun_or_real chown $uid:$gid $path + [ "$uid" != - ] && dryrun_or_real chown $uid "$path" + [ "$gid" != - ] && dryrun_or_real chgrp $gid "$path" + [ "$mode" != - ] && dryrun_or_real chmod $mode "$path" fi } @@ -125,7 +127,9 @@ _c() { if [ ! -e "$path" ]; then dryrun_or_real mknod -m $mode $path c ${arg%:*} ${arg#*:} _restorecon "$path" - dryrun_or_real chown $uid:$gid $path + [ "$uid" != - ] && dryrun_or_real chown $uid "$path" + [ "$gid" != - ] && dryrun_or_real chgrp $gid "$path" + [ "$mode" != - ] && dryrun_or_real chmod $mode "$path" fi } From eec2e575f9ee636edc29eacb580a743ff0b5bfda Mon Sep 17 00:00:00 2001 From: William Hubbs Date: Tue, 21 Feb 2017 08:24:15 -0600 Subject: [PATCH 3/8] fix default owners, modes and groups If owners, modes or groups are not specified use the defaults from the tmpfiles.d man page --- tmpfiles | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/tmpfiles b/tmpfiles index 13117ca..c1e36b8 100644 --- a/tmpfiles +++ b/tmpfiles @@ -88,48 +88,61 @@ _restorecon() { createdirectory() { local mode="$1" uid="$2" gid="$3" path="$4" dryrun_or_real mkdir "$path" - [ "$uid" != - ] && dryrun_or_real chown $uid "$path" - [ "$gid" != - ] && dryrun_or_real chgrp $gid "$path" - [ "$mode" != - ] && dryrun_or_real chmod $mode "$path" + [ "$uid" = - ] && uid=root + [ "$gid" = - ] && gid=root + [ "$mode" = - ] && mode=0755 + dryrun_or_real chown $uid "$path" + dryrun_or_real chgrp $gid "$path" + dryrun_or_real chmod $mode "$path" } createfile() { local mode="$1" uid="$2" gid="$3" path="$4" dryrun_or_real touch "$path" - [ "$uid" != - ] && dryrun_or_real chown $uid "$path" - [ "$gid" != - ] && dryrun_or_real chgrp $gid "$path" - [ "$mode" != - ] && dryrun_or_real chmod $mode "$path" + [ "$uid" = - ] && uid=root + [ "$gid" = - ] && gid=root + [ "$mode" = - ] && mode=0644 + dryrun_or_real chown $uid "$path" + dryrun_or_real chgrp $gid "$path" + dryrun_or_real chmod $mode "$path" } createpipe() { local mode="$1" uid="$2" gid="$3" path="$4" dryrun_or_real mkfifo "$path" - [ "$uid" != - ] && dryrun_or_real chown $uid "$path" - [ "$gid" != - ] && dryrun_or_real chgrp $gid "$path" - [ "$mode" != - ] && dryrun_or_real chmod $mode "$path" + [ "$uid" = - ] && uid=root + [ "$gid" = - ] && gid=root + [ "$mode" = - ] && mode=0644 + dryrun_or_real chown $uid "$path" + dryrun_or_real chgrp $gid "$path" + dryrun_or_real chmod $mode "$path" } _b() { # Create a block device node if it doesn't exist yet local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 + [ "$uid" = - ] && uid=root + [ "$gid" = - ] && gid=root + [ "$mode" = - ] && mode=0644 if [ ! -e "$path" ]; then dryrun_or_real mknod -m $mode $path b ${arg%:*} ${arg#*:} _restorecon "$path" - [ "$uid" != - ] && dryrun_or_real chown $uid "$path" - [ "$gid" != - ] && dryrun_or_real chgrp $gid "$path" - [ "$mode" != - ] && dryrun_or_real chmod $mode "$path" + dryrun_or_real chown $uid "$path" + dryrun_or_real chgrp $gid "$path" fi } _c() { # Create a character device node if it doesn't exist yet local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 + [ "$uid" = - ] && uid=root + [ "$gid" = - ] && gid=root + [ "$mode" = - ] && mode=0644 if [ ! -e "$path" ]; then dryrun_or_real mknod -m $mode $path c ${arg%:*} ${arg#*:} _restorecon "$path" - [ "$uid" != - ] && dryrun_or_real chown $uid "$path" - [ "$gid" != - ] && dryrun_or_real chgrp $gid "$path" - [ "$mode" != - ] && dryrun_or_real chmod $mode "$path" + dryrun_or_real chown $uid "$path" + dryrun_or_real chgrp $gid "$path" fi } From 0cda2711f599467b535985d378eb2af582292b82 Mon Sep 17 00:00:00 2001 From: William Hubbs Date: Fri, 24 Feb 2017 13:30:00 -0600 Subject: [PATCH 4/8] clean up noisy messages during directory creation --- tmpfiles | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tmpfiles b/tmpfiles index c1e36b8..26ce5bd 100644 --- a/tmpfiles +++ b/tmpfiles @@ -87,7 +87,7 @@ _restorecon() { createdirectory() { local mode="$1" uid="$2" gid="$3" path="$4" - dryrun_or_real mkdir "$path" + dryrun_or_real mkdir -p "$path" [ "$uid" = - ] && uid=root [ "$gid" = - ] && gid=root [ "$mode" = - ] && mode=0755 @@ -188,9 +188,8 @@ _d() { [ $CREATE -gt 0 ] || return 0 if [ ! -d "$path" ]; then - dryrun_or_real mkdir -p "$path" 2>/dev/null - _restorecon "$path" createdirectory "$mode" "$uid" "$gid" "$path" + _restorecon "$path" fi } @@ -204,9 +203,8 @@ _D() { fi if [ $CREATE -gt 0 ]; then - dryrun_or_real mkdir -p "$path" 2>/dev/null - _restorecon "$path" createdirectory "$mode" "$uid" "$gid" "$path" + _restorecon "$path" fi } From 6dacbea6122cf841107f23aa1fa2f58559abca14 Mon Sep 17 00:00:00 2001 From: William Hubbs Date: Wed, 8 Mar 2017 12:26:19 -0600 Subject: [PATCH 5/8] fix return code in multiple functions There is a big difference between how these tests behave: 1. [ foo ] && bar 2. if [ foo ]; then bar; fi The first test will return a failure return code if foo is false, and this was causing issues, so we need the second test. This is for #1. --- tmpfiles | 108 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/tmpfiles b/tmpfiles index 26ce5bd..d116b6f 100644 --- a/tmpfiles +++ b/tmpfiles @@ -41,7 +41,9 @@ invalid_option() { dryrun_or_real() { local dryrun= - [ $DRYRUN -eq 1 ] && dryrun=echo + if [ $DRYRUN -eq 1 ]; then + dryrun=echo + fi $dryrun "$@" } @@ -62,10 +64,18 @@ relabel() { for path in ${paths}; do if [ -e "$path" ]; then - [ -x /sbin/restorecon ] && dryrun_or_real restorecon $CHOPTS "$path" - [ $uid != '-' ] && dryrun_or_real chown $CHOPTS "$uid" "$path" - [ $gid != '-' ] && dryrun_or_real chgrp $CHOPTS "$gid" "$path" - [ $mode != '-' ] && dryrun_or_real chmod $CHOPTS "$mode" "$path" + if [ -x /sbin/restorecon ]; then + dryrun_or_real restorecon $CHOPTS "$path" + fi + if [ $uid != '-' ]; then + dryrun_or_real chown $CHOPTS "$uid" "$path" + fi + if [ $gid != '-' ]; then + dryrun_or_real chgrp $CHOPTS "$gid" "$path" + fi + if [ $mode != '-' ]; then + dryrun_or_real chmod $CHOPTS "$mode" "$path" + fi fi done } @@ -88,9 +98,15 @@ _restorecon() { createdirectory() { local mode="$1" uid="$2" gid="$3" path="$4" dryrun_or_real mkdir -p "$path" - [ "$uid" = - ] && uid=root - [ "$gid" = - ] && gid=root - [ "$mode" = - ] && mode=0755 + if [ "$uid" = - ]; then + uid=root + fi + if [ "$gid" = - ]; then + gid=root + fi + if [ "$mode" = - ]; then + mode=0755 + fi dryrun_or_real chown $uid "$path" dryrun_or_real chgrp $gid "$path" dryrun_or_real chmod $mode "$path" @@ -99,9 +115,15 @@ createdirectory() { createfile() { local mode="$1" uid="$2" gid="$3" path="$4" dryrun_or_real touch "$path" - [ "$uid" = - ] && uid=root - [ "$gid" = - ] && gid=root - [ "$mode" = - ] && mode=0644 + if [ "$uid" = - ]; then + uid=root + fi + if [ "$gid" = - ]; then + gid=root + fi + if [ "$mode" = - ]; then + mode=0644 + fi dryrun_or_real chown $uid "$path" dryrun_or_real chgrp $gid "$path" dryrun_or_real chmod $mode "$path" @@ -110,9 +132,15 @@ createfile() { createpipe() { local mode="$1" uid="$2" gid="$3" path="$4" dryrun_or_real mkfifo "$path" - [ "$uid" = - ] && uid=root - [ "$gid" = - ] && gid=root - [ "$mode" = - ] && mode=0644 + if [ "$uid" = - ]; then + uid=root + fi + if [ "$gid" = - ]; then + gid=root + fi + if [ "$mode" = - ]; then + mode=0644 + fi dryrun_or_real chown $uid "$path" dryrun_or_real chgrp $gid "$path" dryrun_or_real chmod $mode "$path" @@ -121,9 +149,15 @@ createpipe() { _b() { # Create a block device node if it doesn't exist yet local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 - [ "$uid" = - ] && uid=root - [ "$gid" = - ] && gid=root - [ "$mode" = - ] && mode=0644 + if [ "$uid" = - ]; then + uid=root + fi + if [ "$gid" = - ]; then + gid=root + fi + if [ "$mode" = - ]; then + mode=0644 + fi if [ ! -e "$path" ]; then dryrun_or_real mknod -m $mode $path b ${arg%:*} ${arg#*:} _restorecon "$path" @@ -135,9 +169,15 @@ _b() { _c() { # Create a character device node if it doesn't exist yet local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 - [ "$uid" = - ] && uid=root - [ "$gid" = - ] && gid=root - [ "$mode" = - ] && mode=0644 + if [ "$uid" = - ]; then + uid=root + fi + if [ "$gid" = - ]; then + gid=root + fi + if [ "$mode" = - ]; then + mode=0644 + fi if [ ! -e "$path" ]; then dryrun_or_real mknod -m $mode $path c ${arg%:*} ${arg#*:} _restorecon "$path" @@ -152,9 +192,15 @@ _C() { if [ ! -e "$path" ]; then dryrun_or_real cp -r "$arg" "$path" _restorecon "$path" - [ $uid != '-' ] && dryrun_or_real chown "$uid" "$path" - [ $gid != '-' ] && dryrun_or_real chgrp "$gid" "$path" - [ $mode != '-' ] && dryrun_or_real chmod "$mode" "$path" + if [ $uid != '-' ]; then + dryrun_or_real chown "$uid" "$path" + fi + if [ $gid != '-' ]; then + dryrun_or_real chgrp "$gid" "$path" + fi + if [ $mode != '-']; then + dryrun_or_real chmod "$mode" "$path" + fi fi } @@ -166,7 +212,9 @@ _f() { if [ ! -e "$path" ]; then createfile "$mode" "$uid" "$gid" "$path" - [ -z "$arg" ] || _w "$@" + if [ -n "$arg" ]; then + _w "$@" + fi fi } @@ -178,7 +226,9 @@ _F() { dryrun_or_real rm -f "$path" createfile "$mode" "$uid" "$gid" "$path" - [ -z "$arg" ] || _w "$@" + if [ -n "$arg" ]; then + _w "$@" + fi } _d() { @@ -249,7 +299,9 @@ _H() { _L() { # Create a symlink if it doesn't exist yet local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 - [ ! -e "$path" ] && dryrun_or_real ln -s "$arg" "$path" + if [ ! -e "$path" ]; then + dryrun_or_real ln -s "$arg" "$path" + fi _restorecon "$path" } @@ -310,7 +362,9 @@ _R() { [ $REMOVE -gt 0 ] || return 0 for path in ${paths}; do - [ -d "$path" ] && dryrun_or_real rm -rf --one-file-system "$path" + if [ -d "$path" ]; then + dryrun_or_real rm -rf --one-file-system "$path" + fi done } From 0ca4be059e2a19166b399871f8904baef39c34d8 Mon Sep 17 00:00:00 2001 From: William Hubbs Date: Wed, 8 Mar 2017 17:07:05 -0600 Subject: [PATCH 6/8] fix return codes for commands that take globs This fixes #1. --- tmpfiles | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tmpfiles b/tmpfiles index d116b6f..66b50f4 100644 --- a/tmpfiles +++ b/tmpfiles @@ -61,23 +61,42 @@ _chattr() { relabel() { local path local paths=$1 mode=$2 uid=$3 gid=$4 + local status x + status=0 for path in ${paths}; do if [ -e "$path" ]; then if [ -x /sbin/restorecon ]; then dryrun_or_real restorecon $CHOPTS "$path" + x=$? + if [ $x -ne 0 ]; then + status=$x + fi fi if [ $uid != '-' ]; then dryrun_or_real chown $CHOPTS "$uid" "$path" + x=$? + if [ $x -ne 0 ]; then + status=$x + fi fi if [ $gid != '-' ]; then dryrun_or_real chgrp $CHOPTS "$gid" "$path" + x=$? + if [ $x -ne 0 ]; then + status=$x + fi fi if [ $mode != '-' ]; then dryrun_or_real chmod $CHOPTS "$mode" "$path" + x=$? + if [ $x -ne 0 ]; then + status=$x + fi fi fi done + return $status } splitpath() { @@ -341,16 +360,23 @@ _r() { # globs in place of normal path names. local path local paths=$1 + local status x [ $REMOVE -gt 0 ] || return 0 + status=0 for path in ${paths}; do if [ -f "$path" ]; then dryrun_or_real rm -f "$path" elif [ -d "$path" ]; then dryrun_or_real rmdir "$path" fi + x=$? + if [ $x -ne 0 ]; then + status=$x + fi done + return $status } _R() { @@ -358,14 +384,21 @@ _R() { # Lines of this type accept shell-style globs in place of normal path names. local path local paths=$1 + local status x [ $REMOVE -gt 0 ] || return 0 + status=0 for path in ${paths}; do if [ -d "$path" ]; then dryrun_or_real rm -rf --one-file-system "$path" + x=$? + if [ $x -ne 0 ]; then + status=$x + fi fi done + return $status } _w() { From fc2a0d0631e954ca8b6c71c2fbf64bb055d2ac60 Mon Sep 17 00:00:00 2001 From: Sandino Araico Sanchez Date: Fri, 29 Apr 2022 05:01:02 -0500 Subject: [PATCH 7/8] Fix: CVE-2017-18925 Signed-off-by: Sandio Araico Sanchez Gentoo bug #540006 hardened mode for opentmpfiles Ignore some recursive options Refuse to remove root-owned dirs and files Refuse to chmod/chdir/chown user-owned dirs and files Check non-existence before creating a directory Ensure directory has been newly created before chown/chmod/chgrp exit on error --- tmpfiles | 257 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 187 insertions(+), 70 deletions(-) diff --git a/tmpfiles b/tmpfiles index 66b50f4..02dffd8 100644 --- a/tmpfiles +++ b/tmpfiles @@ -29,6 +29,20 @@ checkprefix() { return 1 } +owned_by_root() { + local path="$1" + if [ -z "$path" ] ; then + echo "Missing parameter" >&2 + return 404 + fi + FOUND=$(find "$path" -maxdepth 0 -uid 0 -gid 0) + if [ -z $FOUND ] ; then + echo "Not owned by root" >&2 + return 403 + fi + return 0 +} + warninvalid() { printf "tmpfiles: ignoring invalid entry on line %d of \`%s'\n" "$LINENUM" "$FILE" error=$(( error+1 )) @@ -45,6 +59,7 @@ dryrun_or_real() { dryrun=echo fi $dryrun "$@" + return $? } _chattr() { @@ -55,6 +70,10 @@ _chattr() { *) attr="+$attr" ;; esac local IFS= + if ! owned_by_root $1 ; then + echo "cowardly refusing to chattr" >&2 + return 107 + fi dryrun_or_real chattr $1 "$attr" -- $3 } @@ -66,33 +85,26 @@ relabel() { status=0 for path in ${paths}; do if [ -e "$path" ]; then + if ! owned_by_root $1 ; then + echo "cowardly refusing to relabel" >&2 + return 108 + fi if [ -x /sbin/restorecon ]; then - dryrun_or_real restorecon $CHOPTS "$path" + dryrun_or_real restorecon "$path" x=$? if [ $x -ne 0 ]; then - status=$x + echo "error on restorecon" >&2 + exit $x fi fi if [ $uid != '-' ]; then - dryrun_or_real chown $CHOPTS "$uid" "$path" - x=$? - if [ $x -ne 0 ]; then - status=$x - fi + dryrun_or_real _chown "$uid" "$path" fi if [ $gid != '-' ]; then - dryrun_or_real chgrp $CHOPTS "$gid" "$path" - x=$? - if [ $x -ne 0 ]; then - status=$x - fi + dryrun_or_real _chgrp "$gid" "$path" fi if [ $mode != '-' ]; then - dryrun_or_real chmod $CHOPTS "$mode" "$path" - x=$? - if [ $x -ne 0 ]; then - status=$x - fi + dryrun_or_real _chmod "$mode" "$path" fi fi done @@ -109,14 +121,86 @@ splitpath() { _restorecon() { local path=$1 + if ! owned_by_root $1 ; then + echo "cowardly refusing to restorecon" >&2 + return 103 + fi if [ -x /sbin/restorecon ]; then dryrun_or_real restorecon -F $(splitpath "$path") fi } +_chmod() { + local path=$2 mode=$1 + if ! owned_by_root "$path" ; then + echo "cowardly refusing to chmod" >&2 + return 106 + fi + dryrun_or_real chmod $mode "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on chmod" >&2 + exit $x + fi +} + +_chown() { + local path=$2 uid=$1 + if ! owned_by_root "$path" ; then + echo "cowardly refusing to chmod" >&2 + return 107 + fi + dryrun_or_real chown $uid "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on chown" >&2 + exit $x + fi +} + +_chgrp() { + local path=$2 gid=$1 + if ! owned_by_root "$path" ; then + echo "cowardly refusing to chgrp" >&2 + return 108 + fi + dryrun_or_real chgrp $gid "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on chgrp" >&2 + exit $x + fi +} + +_rm_f() { + local path=$1 + if [ ! -e "$path" ] ; then + echo "file does not exist" >&2 + return 112 + fi + if owned_by_root "$path" ; then + echo "cowardly refusing to rm" >&2 + return 113 + fi + rm -f "$path" +} + createdirectory() { local mode="$1" uid="$2" gid="$3" path="$4" - dryrun_or_real mkdir -p "$path" + # Do nothing if existing directory + # avoids race condition + if [ -e "$path" ] ; then + echo "Directory already exists" >&2 + return 101 + fi + dryrun_or_real mkdir "$path" + # only continue on successful created directory + # avoids rrace condition + x=$? + if [ $x -ne 0 ] ; then + echo "Could not create directory" >&2 + exit $x + fi if [ "$uid" = - ]; then uid=root fi @@ -126,14 +210,18 @@ createdirectory() { if [ "$mode" = - ]; then mode=0755 fi - dryrun_or_real chown $uid "$path" - dryrun_or_real chgrp $gid "$path" - dryrun_or_real chmod $mode "$path" + dryrun_or_real _chown $uid "$path" + dryrun_or_real _chgrp $gid "$path" + dryrun_or_real _chmod $mode "$path" } createfile() { local mode="$1" uid="$2" gid="$3" path="$4" dryrun_or_real touch "$path" + if ! owned_by_root "$path" ; then + echo "cowardly refusing to chown/chgrp/chmod" >&2 + return 106 + fi if [ "$uid" = - ]; then uid=root fi @@ -143,14 +231,27 @@ createfile() { if [ "$mode" = - ]; then mode=0644 fi - dryrun_or_real chown $uid "$path" - dryrun_or_real chgrp $gid "$path" - dryrun_or_real chmod $mode "$path" + dryrun_or_real _chown $uid "$path" + dryrun_or_real _chgrp $gid "$path" + dryrun_or_real _chmod $mode "$path" } createpipe() { local mode="$1" uid="$2" gid="$3" path="$4" + # Do nothing if existing pipe + # avoids race condition + if [ -e "$path" ] ; then + echo "Pipe already exists" >&2 + return 104 + fi dryrun_or_real mkfifo "$path" + # only continue on successful created pipe + # avoids rrace condition + x=$? + if [ $x -ne 0 ] ; then + echo "Could not create pipe" >&2 + exit $x + fi if [ "$uid" = - ]; then uid=root fi @@ -160,9 +261,9 @@ createpipe() { if [ "$mode" = - ]; then mode=0644 fi - dryrun_or_real chown $uid "$path" - dryrun_or_real chgrp $gid "$path" - dryrun_or_real chmod $mode "$path" + dryrun_or_real _chown $uid "$path" + dryrun_or_real _chgrp $gid "$path" + dryrun_or_real _chmod $mode "$path" } _b() { @@ -179,9 +280,14 @@ _b() { fi if [ ! -e "$path" ]; then dryrun_or_real mknod -m $mode $path b ${arg%:*} ${arg#*:} + x=$? + if [ $x -ne 0 ]; then + echo "error on mknod" >&2 + exit $x + fi _restorecon "$path" - dryrun_or_real chown $uid "$path" - dryrun_or_real chgrp $gid "$path" + dryrun_or_real _chown $uid "$path" + dryrun_or_real _chgrp $gid "$path" fi } @@ -199,28 +305,20 @@ _c() { fi if [ ! -e "$path" ]; then dryrun_or_real mknod -m $mode $path c ${arg%:*} ${arg#*:} + x=$? + if [ $x -ne 0 ]; then + echo "error on mknod" >&2 + exit $x + fi _restorecon "$path" - dryrun_or_real chown $uid "$path" - dryrun_or_real chgrp $gid "$path" + dryrun_or_real _chown $uid "$path" + dryrun_or_real _chgrp $gid "$path" fi } _C() { # recursively copy a file or directory - local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 - if [ ! -e "$path" ]; then - dryrun_or_real cp -r "$arg" "$path" - _restorecon "$path" - if [ $uid != '-' ]; then - dryrun_or_real chown "$uid" "$path" - fi - if [ $gid != '-' ]; then - dryrun_or_real chgrp "$gid" "$path" - fi - if [ $mode != '-']; then - dryrun_or_real chmod "$mode" "$path" - fi - fi + return 501 } _f() { @@ -244,6 +342,11 @@ _F() { [ $CREATE -gt 0 ] || return 0 dryrun_or_real rm -f "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on rm" >&2 + exit $x + fi createfile "$mode" "$uid" "$gid" "$path" if [ -n "$arg" ]; then _w "$@" @@ -266,12 +369,12 @@ _D() { # Create or empty a directory local path=$1 mode=$2 uid=$3 gid=$4 - if [ -d "$path" ] && [ $REMOVE -gt 0 ]; then - dryrun_or_real find "$path" -mindepth 1 -maxdepth 1 -xdev -exec rm -rf {} + - _restorecon "$path" - fi - - if [ $CREATE -gt 0 ]; then + if [ -d "$path" ] ; then + if owned_by_root "$path" ; then + echo "Cowardly refusing to remove directory" >&2 + return 102 + fi + dryrun_or_real rm -rf "$path" createdirectory "$mode" "$uid" "$gid" "$path" _restorecon "$path" fi @@ -320,6 +423,11 @@ _L() { local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 if [ ! -e "$path" ]; then dryrun_or_real ln -s "$arg" "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on ln" >&2 + exit $x + fi fi _restorecon "$path" } @@ -367,9 +475,23 @@ _r() { status=0 for path in ${paths}; do if [ -f "$path" ]; then - dryrun_or_real rm -f "$path" + if ! owned_by_root "$path" ; then + dryrun_or_real rm -f "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on rm" >&2 + exit $x + fi + fi elif [ -d "$path" ]; then - dryrun_or_real rmdir "$path" + if ! owned_by_root "$path" ; then + dryrun_or_real rmdir "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on rmdir" >&2 + exit $x + fi + fi fi x=$? if [ $x -ne 0 ]; then @@ -391,10 +513,14 @@ _R() { status=0 for path in ${paths}; do if [ -d "$path" ]; then + if owned_by_root "$path" ; then + echo "cowardly refusing to remove directory" >&2 + return 109 + fi dryrun_or_real rm -rf --one-file-system "$path" x=$? if [ $x -ne 0 ]; then - status=$x + exit $x fi fi done @@ -405,6 +531,10 @@ _w() { # Write the argument parameter to a file, if it exists. local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 if [ -f "$path" ]; then + if owned_by_root "$path" ; then + echo "cowardly refusing to write file" >&2 + return 110 + fi if [ $DRYRUN -eq 1 ]; then echo "echo \"$arg\" >>\"$path\"" else @@ -414,21 +544,8 @@ _w() { } _z() { - # Set ownership, access mode and relabel security context of a file or - # directory if it exists. Lines of this type accept shell-style globs in - # place of normal path names. - [ $CREATE -gt 0 ] || return 0 - - relabel "$@" -} - -_Z() { - # Recursively set ownership, access mode and relabel security context of a - # path and all its subdirectories (if it is a directory). Lines of this type - # accept shell-style globs in place of normal path names. - [ $CREATE -gt 0 ] || return 0 - - CHOPTS=-R relabel "$@" + echo "Ignoring recursively *" + return 111 } BOOT=0 CREATE=0 REMOVE=0 CLEAN=0 VERBOSE=0 DRYRUN=0 error=0 LINENO=0 @@ -565,7 +682,7 @@ for FILE in $tmpfiles_d ; do if [ $FORCE -gt 0 ]; then case $cmd in - p|L|c|b) [ -f "$path" ] && dryrun_or_real rm -f "$path" + p|L|c|b) [ -f "$path" ] && dryrun_or_real _rm_f "$path" esac fi From f92b116100f12695230962f876be0e0a146665c3 Mon Sep 17 00:00:00 2001 From: Sandino Araico Sanchez Date: Fri, 29 Apr 2022 05:01:02 -0500 Subject: [PATCH 8/8] Fix: CVE-2017-18925 Signed-off-by: Sandio Araico Sanchez Bug #540006 hardened mode for opentmpfiles Ignore some recursive options Refuse to remove root-owned dirs and files Refuse to chmod/chdir/chown user-owned dirs and files Check non-existence before creating a directory Ensure directory has been newly created before chown/chmod/chgrp exit on error --- tmpfiles | 257 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 187 insertions(+), 70 deletions(-) diff --git a/tmpfiles b/tmpfiles index 66b50f4..02dffd8 100644 --- a/tmpfiles +++ b/tmpfiles @@ -29,6 +29,20 @@ checkprefix() { return 1 } +owned_by_root() { + local path="$1" + if [ -z "$path" ] ; then + echo "Missing parameter" >&2 + return 404 + fi + FOUND=$(find "$path" -maxdepth 0 -uid 0 -gid 0) + if [ -z $FOUND ] ; then + echo "Not owned by root" >&2 + return 403 + fi + return 0 +} + warninvalid() { printf "tmpfiles: ignoring invalid entry on line %d of \`%s'\n" "$LINENUM" "$FILE" error=$(( error+1 )) @@ -45,6 +59,7 @@ dryrun_or_real() { dryrun=echo fi $dryrun "$@" + return $? } _chattr() { @@ -55,6 +70,10 @@ _chattr() { *) attr="+$attr" ;; esac local IFS= + if ! owned_by_root $1 ; then + echo "cowardly refusing to chattr" >&2 + return 107 + fi dryrun_or_real chattr $1 "$attr" -- $3 } @@ -66,33 +85,26 @@ relabel() { status=0 for path in ${paths}; do if [ -e "$path" ]; then + if ! owned_by_root $1 ; then + echo "cowardly refusing to relabel" >&2 + return 108 + fi if [ -x /sbin/restorecon ]; then - dryrun_or_real restorecon $CHOPTS "$path" + dryrun_or_real restorecon "$path" x=$? if [ $x -ne 0 ]; then - status=$x + echo "error on restorecon" >&2 + exit $x fi fi if [ $uid != '-' ]; then - dryrun_or_real chown $CHOPTS "$uid" "$path" - x=$? - if [ $x -ne 0 ]; then - status=$x - fi + dryrun_or_real _chown "$uid" "$path" fi if [ $gid != '-' ]; then - dryrun_or_real chgrp $CHOPTS "$gid" "$path" - x=$? - if [ $x -ne 0 ]; then - status=$x - fi + dryrun_or_real _chgrp "$gid" "$path" fi if [ $mode != '-' ]; then - dryrun_or_real chmod $CHOPTS "$mode" "$path" - x=$? - if [ $x -ne 0 ]; then - status=$x - fi + dryrun_or_real _chmod "$mode" "$path" fi fi done @@ -109,14 +121,86 @@ splitpath() { _restorecon() { local path=$1 + if ! owned_by_root $1 ; then + echo "cowardly refusing to restorecon" >&2 + return 103 + fi if [ -x /sbin/restorecon ]; then dryrun_or_real restorecon -F $(splitpath "$path") fi } +_chmod() { + local path=$2 mode=$1 + if ! owned_by_root "$path" ; then + echo "cowardly refusing to chmod" >&2 + return 106 + fi + dryrun_or_real chmod $mode "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on chmod" >&2 + exit $x + fi +} + +_chown() { + local path=$2 uid=$1 + if ! owned_by_root "$path" ; then + echo "cowardly refusing to chmod" >&2 + return 107 + fi + dryrun_or_real chown $uid "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on chown" >&2 + exit $x + fi +} + +_chgrp() { + local path=$2 gid=$1 + if ! owned_by_root "$path" ; then + echo "cowardly refusing to chgrp" >&2 + return 108 + fi + dryrun_or_real chgrp $gid "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on chgrp" >&2 + exit $x + fi +} + +_rm_f() { + local path=$1 + if [ ! -e "$path" ] ; then + echo "file does not exist" >&2 + return 112 + fi + if owned_by_root "$path" ; then + echo "cowardly refusing to rm" >&2 + return 113 + fi + rm -f "$path" +} + createdirectory() { local mode="$1" uid="$2" gid="$3" path="$4" - dryrun_or_real mkdir -p "$path" + # Do nothing if existing directory + # avoids race condition + if [ -e "$path" ] ; then + echo "Directory already exists" >&2 + return 101 + fi + dryrun_or_real mkdir "$path" + # only continue on successful created directory + # avoids rrace condition + x=$? + if [ $x -ne 0 ] ; then + echo "Could not create directory" >&2 + exit $x + fi if [ "$uid" = - ]; then uid=root fi @@ -126,14 +210,18 @@ createdirectory() { if [ "$mode" = - ]; then mode=0755 fi - dryrun_or_real chown $uid "$path" - dryrun_or_real chgrp $gid "$path" - dryrun_or_real chmod $mode "$path" + dryrun_or_real _chown $uid "$path" + dryrun_or_real _chgrp $gid "$path" + dryrun_or_real _chmod $mode "$path" } createfile() { local mode="$1" uid="$2" gid="$3" path="$4" dryrun_or_real touch "$path" + if ! owned_by_root "$path" ; then + echo "cowardly refusing to chown/chgrp/chmod" >&2 + return 106 + fi if [ "$uid" = - ]; then uid=root fi @@ -143,14 +231,27 @@ createfile() { if [ "$mode" = - ]; then mode=0644 fi - dryrun_or_real chown $uid "$path" - dryrun_or_real chgrp $gid "$path" - dryrun_or_real chmod $mode "$path" + dryrun_or_real _chown $uid "$path" + dryrun_or_real _chgrp $gid "$path" + dryrun_or_real _chmod $mode "$path" } createpipe() { local mode="$1" uid="$2" gid="$3" path="$4" + # Do nothing if existing pipe + # avoids race condition + if [ -e "$path" ] ; then + echo "Pipe already exists" >&2 + return 104 + fi dryrun_or_real mkfifo "$path" + # only continue on successful created pipe + # avoids rrace condition + x=$? + if [ $x -ne 0 ] ; then + echo "Could not create pipe" >&2 + exit $x + fi if [ "$uid" = - ]; then uid=root fi @@ -160,9 +261,9 @@ createpipe() { if [ "$mode" = - ]; then mode=0644 fi - dryrun_or_real chown $uid "$path" - dryrun_or_real chgrp $gid "$path" - dryrun_or_real chmod $mode "$path" + dryrun_or_real _chown $uid "$path" + dryrun_or_real _chgrp $gid "$path" + dryrun_or_real _chmod $mode "$path" } _b() { @@ -179,9 +280,14 @@ _b() { fi if [ ! -e "$path" ]; then dryrun_or_real mknod -m $mode $path b ${arg%:*} ${arg#*:} + x=$? + if [ $x -ne 0 ]; then + echo "error on mknod" >&2 + exit $x + fi _restorecon "$path" - dryrun_or_real chown $uid "$path" - dryrun_or_real chgrp $gid "$path" + dryrun_or_real _chown $uid "$path" + dryrun_or_real _chgrp $gid "$path" fi } @@ -199,28 +305,20 @@ _c() { fi if [ ! -e "$path" ]; then dryrun_or_real mknod -m $mode $path c ${arg%:*} ${arg#*:} + x=$? + if [ $x -ne 0 ]; then + echo "error on mknod" >&2 + exit $x + fi _restorecon "$path" - dryrun_or_real chown $uid "$path" - dryrun_or_real chgrp $gid "$path" + dryrun_or_real _chown $uid "$path" + dryrun_or_real _chgrp $gid "$path" fi } _C() { # recursively copy a file or directory - local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 - if [ ! -e "$path" ]; then - dryrun_or_real cp -r "$arg" "$path" - _restorecon "$path" - if [ $uid != '-' ]; then - dryrun_or_real chown "$uid" "$path" - fi - if [ $gid != '-' ]; then - dryrun_or_real chgrp "$gid" "$path" - fi - if [ $mode != '-']; then - dryrun_or_real chmod "$mode" "$path" - fi - fi + return 501 } _f() { @@ -244,6 +342,11 @@ _F() { [ $CREATE -gt 0 ] || return 0 dryrun_or_real rm -f "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on rm" >&2 + exit $x + fi createfile "$mode" "$uid" "$gid" "$path" if [ -n "$arg" ]; then _w "$@" @@ -266,12 +369,12 @@ _D() { # Create or empty a directory local path=$1 mode=$2 uid=$3 gid=$4 - if [ -d "$path" ] && [ $REMOVE -gt 0 ]; then - dryrun_or_real find "$path" -mindepth 1 -maxdepth 1 -xdev -exec rm -rf {} + - _restorecon "$path" - fi - - if [ $CREATE -gt 0 ]; then + if [ -d "$path" ] ; then + if owned_by_root "$path" ; then + echo "Cowardly refusing to remove directory" >&2 + return 102 + fi + dryrun_or_real rm -rf "$path" createdirectory "$mode" "$uid" "$gid" "$path" _restorecon "$path" fi @@ -320,6 +423,11 @@ _L() { local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 if [ ! -e "$path" ]; then dryrun_or_real ln -s "$arg" "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on ln" >&2 + exit $x + fi fi _restorecon "$path" } @@ -367,9 +475,23 @@ _r() { status=0 for path in ${paths}; do if [ -f "$path" ]; then - dryrun_or_real rm -f "$path" + if ! owned_by_root "$path" ; then + dryrun_or_real rm -f "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on rm" >&2 + exit $x + fi + fi elif [ -d "$path" ]; then - dryrun_or_real rmdir "$path" + if ! owned_by_root "$path" ; then + dryrun_or_real rmdir "$path" + x=$? + if [ $x -ne 0 ]; then + echo "error on rmdir" >&2 + exit $x + fi + fi fi x=$? if [ $x -ne 0 ]; then @@ -391,10 +513,14 @@ _R() { status=0 for path in ${paths}; do if [ -d "$path" ]; then + if owned_by_root "$path" ; then + echo "cowardly refusing to remove directory" >&2 + return 109 + fi dryrun_or_real rm -rf --one-file-system "$path" x=$? if [ $x -ne 0 ]; then - status=$x + exit $x fi fi done @@ -405,6 +531,10 @@ _w() { # Write the argument parameter to a file, if it exists. local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 if [ -f "$path" ]; then + if owned_by_root "$path" ; then + echo "cowardly refusing to write file" >&2 + return 110 + fi if [ $DRYRUN -eq 1 ]; then echo "echo \"$arg\" >>\"$path\"" else @@ -414,21 +544,8 @@ _w() { } _z() { - # Set ownership, access mode and relabel security context of a file or - # directory if it exists. Lines of this type accept shell-style globs in - # place of normal path names. - [ $CREATE -gt 0 ] || return 0 - - relabel "$@" -} - -_Z() { - # Recursively set ownership, access mode and relabel security context of a - # path and all its subdirectories (if it is a directory). Lines of this type - # accept shell-style globs in place of normal path names. - [ $CREATE -gt 0 ] || return 0 - - CHOPTS=-R relabel "$@" + echo "Ignoring recursively *" + return 111 } BOOT=0 CREATE=0 REMOVE=0 CLEAN=0 VERBOSE=0 DRYRUN=0 error=0 LINENO=0 @@ -565,7 +682,7 @@ for FILE in $tmpfiles_d ; do if [ $FORCE -gt 0 ]; then case $cmd in - p|L|c|b) [ -f "$path" ] && dryrun_or_real rm -f "$path" + p|L|c|b) [ -f "$path" ] && dryrun_or_real _rm_f "$path" esac fi