some file system based addresses failed to apply file options

This commit is contained in:
Gerhard Rieger 2014-03-26 14:02:42 +01:00
parent df152fce75
commit d123461127
5 changed files with 435 additions and 21 deletions

View file

@ -44,6 +44,11 @@ corrections:
fixed some typos and minor issues, including:
Red Hat issue 1021967: formatting error in manual page
UNIX-LISTEN with fork option did not remove the socket file system entry
when exiting. Other file system based passive address types had similar
issues or failed to apply options umask, user e.a.
Thanks to Lorenzo Monti for pointing me to this issue
fixed bug in xio-openssl.c that prevented error handling of bad number
of arguments, thanks to Paulik Tamas for reporting

378
test.sh
View file

@ -52,7 +52,7 @@ esac
#SOCAT_EGD="egd=/dev/egd-pool"
MISCDELAY=1
[ -z "$SOCAT" ] && SOCAT="./socat"
if [ ! -x "$SOCAT" ]; then
if ! [ -x "$SOCAT" ] && ! type $SOCAT >/dev/null 2>&1; then
echo "$SOCAT does not exist" >&2; exit 1;
fi
[ -z "$PROCAN" ] && PROCAN="./procan"
@ -165,6 +165,27 @@ DragonFly) IFCONFIG=/sbin/ifconfig ;;
*) IFCONFIG=/sbin/ifconfig ;;
esac
# need output like "644"
case "$UNAME" in
Linux) fileperms() { stat -L --print "%a\n" "$1" 2>/dev/null; } ;;
FreeBSD) fileperms() { stat -L -x "$1" |grep ' Mode:' |sed 's/.* Mode:[[:space:]]*([0-9]\([0-7][0-7][0-7]\).*/\1/'; } ;;
*) fileperms() {
local p s=0 c
p="$(ls -l -L "$1" |awk '{print($1);}')"
p="${p:1:9}"
while [ "$p" ]; do c=${p:0:1}; p=${p:1}; [ "x$c" == x- ]; let "s=2*s+$?"; done
printf "%03o\n" $s;
} ;;
esac
# need user (owner) of filesystem entry
case "$UNAME" in
Linux) fileuser() { stat -L --print "%U\n" "$tsock" 2>/dev/null; } ;;
FreeBSD) fileuser() { ls -l test.sh |awk '{print($3);}'; } ;;
*) fileuser() { ls -l test.sh |awk '{print($3);}'; } ;;
esac
# for some tests we need a second local IPv4 address
case "$UNAME" in
Linux)
@ -11725,6 +11746,361 @@ PORT=$((PORT+1))
N=$((N+1))
###############################################################################
# tests: option umask with "passive" NAMED group addresses
while read addr fileopt addropts proto diropt ADDR2; do
if [ -z "$addr" ] || [[ "$addr" == \#* ]]; then continue; fi
# some passive (listening...) filesystem based addresses did not implement the
# umask option
ADDR=${addr^^*}
ADDR_=${ADDR/-/_}
PROTO=${proto^^*}
if [ "$diropt" = "." ]; then diropt=; fi
if [ "$fileopt" = "." ]; then fileopt=; fi
if [ "$addropts" = "." ]; then addropts=; fi
NAME=${ADDR_}_UMASK
case "$TESTS" in
*%$N%*|*%functions%*|*%bugs%*|*%proto%*|*%socket%*|*%$proto%*|*%$NAME%*)
TEST="$NAME: $ADDR applies option umask"
# start a socat process with passive/listening file system entry. Check the
# permissions of the FS entry, then terminate the process.
# Test succeeds when FS entry exists and has expected permissions.
if ! eval $NUMCOND; then :; else
if [ $ADDR = PTY ]; then set -xv; fi
tlog="$td/test$N.log"
te0="$td/test$N.0.stderr"
tsock="$td/test$N.sock"
if [ -z "$fileopt" ]; then
CMD0="$SOCAT $opts $diropt $ADDR:$tsock,$addropts,unlink-close=0,umask=177 $ADDR2"
else
CMD0="$SOCAT $opts $diropt $ADDR,$fileopt=$tsock,$addropts,unlink-close=0,umask=177 $ADDR2"
fi
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"$te0" &
pid0=$!
wait${proto} $tsock 1 2>"$tlog"
ERRNOENT=; if ! [ -e "$tsock" ]; then ERRNOENT=1; fi
perms=$(fileperms "$tsock")
kill $pid0 2>>"$tlog"
wait
if [ "$ERRNOENT" ]; then
$PRINTF "${RED}no entry${NORMAL}\n"
echo "$CMD0 &"
cat "$te0"
cat "$tlog"
let numFAIL=numFAIL+1
listFAIL="$listFAIL $N"
elif [ "$perms" != "600" ]; then
$PRINTF "${RED}perms \"$perms\", expected \"600\" ${NORMAL}\n"
echo "$CMD0 &"
cat "$te0"
let numFAIL=numFAIL+1
listFAIL="$listFAIL $N"
else
$PRINTF "$OK\n"
let numOK=numOK+1
fi
fi # NUMCOND
;;
esac
PORT=$((PORT+1))
N=$((N+1))
#
done <<<"
# address fileopt addropts waitfor direction ADDR2
create . . file -U FILE:/dev/null
open . creat file . FILE:/dev/null
gopen . creat file . FILE:/dev/null
unix-listen . . unixport . FILE:/dev/null
unix-recvfrom . . unixport . FILE:/dev/null
unix-recv . . unixport -u FILE:/dev/null
pipe . . file -u FILE:/dev/null
# pty does not seem to honor umask:
#pty link . file . PIPE
"
# tests: option perm with "passive" NAMED group addresses
while read addr fileopt addropts proto diropt; do
if [ -z "$addr" ] || [[ "$addr" == \#* ]]; then continue; fi
# test if passive (listening...) filesystem based addresses implement option perm
ADDR=${addr^^*}
ADDR_=${ADDR/-/_}
PROTO=${proto^^*}
if [ "$diropt" = "." ]; then diropt=; fi
if [ "$fileopt" = "." ]; then fileopt=; fi
if [ "$addropts" = "." ]; then addropts=; fi
NAME=${ADDR_}_PERM
case "$TESTS" in
*%$N%*|*%functions%*|*%bugs%*|*%proto%*|*%socket%*|*%$proto%*|*%$NAME%*)
TEST="$NAME: $ADDR applies option perm"
# start a socat process with passive/listening file system entry. Check the
# permissions of the FS entry, then terminate the process.
# Test succeeds when FS entry exists and has expected permissions.
if ! eval $NUMCOND; then :; else
tlog="$td/test$N.log"
te0="$td/test$N.0.stderr"
tsock="$td/test$N.sock"
#set -vx
if [ -z "$fileopt" ]; then
CMD0="$SOCAT $opts $diropt $ADDR:$tsock,$addropts,perm=511 FILE:/dev/null,ignoreeof"
else
CMD0="$SOCAT $opts $diropt $ADDR,$fileopt=$tsock,$addropts,perm=511 FILE:/dev/null,ignoreeof"
fi
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"$te0" &
pid0=$!
wait${proto} $tsock 1 2>"$tlog"
ERRNOENT=; if ! [ -e "$tsock" ]; then ERRNOENT=1; fi
perms=$(fileperms "$tsock")
kill $pid0 2>>"$tlog"
wait
if [ "$ERRNOENT" ]; then
$PRINTF "${RED}no entry${NORMAL}\n"
echo "$CMD0 &"
cat "$te0"
cat "$tlog"
let numFAIL=numFAIL+1
listFAIL="$listFAIL $N"
elif [ "$perms" != "511" ]; then
$PRINTF "${RED}perms \"$perms\", expected \"511\" ${NORMAL}\n"
echo "$CMD0 &"
cat "$te0"
let numFAIL=numFAIL+1
listFAIL="$listFAIL $N"
else
$PRINTF "$OK\n"
let numOK=numOK+1
fi
set +vx
fi # NUMCOND
;;
esac
PORT=$((PORT+1))
N=$((N+1))
#
done <<<"
# address fileopt addropts waitfor direction
create . . file -U
open . creat file .
gopen . creat file .
unix-listen . . unixport .
unix-recvfrom . . unixport .
unix-recv . . unixport -u
pipe . . file -u
pty link . file .
"
# tests: option user with "passive" NAMED group addresses
while read addr fileopt addropts proto diropt; do
if [ -z "$addr" ] || [[ "$addr" == \#* ]]; then continue; fi
# test if passive (listening...) filesystem based addresses implement option user
ADDR=${addr^^*}
ADDR_=${ADDR/-/_}
PROTO=${proto^^*}
if [ "$diropt" = "." ]; then diropt=; fi
if [ "$fileopt" = "." ]; then fileopt=; fi
if [ "$addropts" = "." ]; then addropts=; fi
NAME=${ADDR_}_USER
case "$TESTS" in
*%$N%*|*%functions%*|*%bugs%*|*%proto%*|*%socket%*|*%$proto%*|*%root%*|*%$NAME%*)
TEST="$NAME: $ADDR applies option user"
# start a socat process with passive/listening file system entry with user option.
# Check the owner of the FS entry, then terminate the process.
# Test succeeds when FS entry exists and has expected owner.
if ! eval $NUMCOND; then :;
elif [ $(id -u) -ne 0 -a "$withroot" -eq 0 ]; then
$PRINTF "test $F_n $TEST... ${YELLOW}must be root${NORMAL}\n" $N
numCANT=$((numCANT+1))
else
tlog="$td/test$N.log"
te0="$td/test$N.0.stderr"
tsock="$td/test$N.sock"
# set -vx
if [ -z "$fileopt" ]; then
CMD0="$SOCAT $opts $diropt $ADDR:$tsock,$addropts,user=$SUBSTUSER FILE:/dev/null,ignoreeof"
else
CMD0="$SOCAT $opts $diropt $ADDR,$fileopt=$tsock,$addropts,user=$SUBSTUSER FILE:/dev/null,ignoreeof"
fi
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"$te0" &
pid0=$!
wait${proto} $tsock 1 2>"$tlog"
ERRNOENT=; if ! [ -e "$tsock" ]; then ERRNOENT=1; fi
user=$(fileuser "$tsock")
kill $pid0 2>>"$tlog"
wait
if [ "$ERRNOENT" ]; then
$PRINTF "${RED}no entry${NORMAL}\n"
echo "$CMD0 &"
cat "$te0"
cat "$tlog"
let numFAIL=numFAIL+1
listFAIL="$listFAIL $N"
elif [ "$user" != "$SUBSTUSER" ]; then
$PRINTF "${RED}user \"$user\", expected \"$SUBSTUSER\" ${NORMAL}\n"
echo "$CMD0 &"
cat "$te0"
let numFAIL=numFAIL+1
listFAIL="$listFAIL $N"
else
$PRINTF "$OK\n"
let numOK=numOK+1
fi
set +vx
fi # NUMCOND
;;
esac
PORT=$((PORT+1))
N=$((N+1))
#
done <<<"
# address fileopt addropts waitfor direction
create . . file -U
open . creat file .
gopen . creat file .
unix-listen . . unixport .
unix-recvfrom . . unixport .
unix-recv . . unixport -u
pipe . . file -u
pty link . file .
"
# tests: is "passive" filesystem entry removed at the end? (without fork)
while read addr fileopt addropts proto diropt crit ADDR2; do
if [ -z "$addr" ] || [[ "$addr" == \#* ]]; then continue; fi
# some passive (listening...) filesystem based addresses did not remove the file
# system entry at the end
ADDR=${addr^^*}
ADDR_=${ADDR/-/_}
PROTO=${proto^^*}
if [ "$diropt" = "." ]; then diropt=; fi
if [ "$fileopt" = "." ]; then fileopt=; fi
if [ "$addropts" = "." ]; then addropts=; fi
# $ADDR removes the file system entry when the process is terminated
NAME=${ADDR_}_REMOVE
case "$TESTS" in
*%$N%*|*%functions%*|*%bugs%*|*%unix%*|*%socket%*|*%$NAME%*)
TEST="$NAME: $ADDR removes socket entry when terminated during accept"
# start a socat process with listening unix domain socket etc. Terminate the
# process and check if the file system socket entry still exists.
# Test succeeds when entry does not exist.
if ! eval $NUMCOND; then :; else
tlog="$td/test$N.log"
te0="$td/test$N.0.stderr"
tsock="$td/test$N.sock"
if [ -z "$fileopt" ]; then
CMD0="$SOCAT $opts $diropt $ADDR:$tsock,$addropts $ADDR2"
else
CMD0="$SOCAT $opts $diropt $ADDR,$fileopt=$tsock,$addropts $ADDR2"
fi
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"$te0" &
pid0=$!
wait${proto} "$crit" $tsock 1 2>"$tlog"
kill $pid0 2>>"$tlog"
rc1=$?
wait >>"$tlog"
if [ $rc1 != 0 ]; then
$PRINTF "${YELLOW}setup failed${NORMAL}\n"
echo "$CMD0 &"
cat "$te0"
cat "$tlog"
let numCANT=numCANT+1
elif ! [ $crit $tsock ]; then
$PRINTF "$OK\n"
let numOK=numOK+1
else
$PRINTF "$FAILED\n"
echo "$CMD0 &"
cat "$te0"
cat "$tlog"
let numFAIL=numFAIL+1
listFAIL="$listFAIL $N"
fi
fi # NUMCOND
;;
esac
PORT=$((PORT+1))
N=$((N+1))
#
done <<<"
# address fileopt addropts waitfor direction crit ADDR2
unix-listen . . unixport . -e FILE:/dev/null
unix-recvfrom . . unixport . -e FILE:/dev/null
unix-recv . . unixport -u -e FILE:/dev/null
pipe . . file -u -e FILE:/dev/null
pty link . file . -L PIPE
"
# tests: is "passive" filesystem entry removed at the end? (with fork)
while read addr fileopt addropts proto diropt crit ADDR2; do
if [ -z "$addr" ] || [[ "$addr" == \#* ]]; then continue; fi
# some passive (listening...) filesystem based addresses with fork did not remove
# the file system entry at the end
ADDR=${addr^^*}
ADDR_=${ADDR/-/_}
PROTO=${proto^^*}
if [ "$diropt" = "." ]; then diropt=; fi
if [ "$fileopt" = "." ]; then fileopt=; fi
if [ "$addropts" = "." ]; then addropts=; fi
# $ADDR with fork removes the file system entry when the process is terminated
NAME=${ADDR_}_REMOVE_FORK
case "$TESTS" in
*%$N%*|*%functions%*|*%bugs%*|*%unix%*|*%socket%*|*%$NAME%*)
TEST="$NAME: $ADDR with fork removes socket entry when terminated during accept"
# start a socat process with listening unix domain socket etc and option fork.
# Terminate the process and check if the file system socket entry still exists.
# Test succeeds when entry does not exist.
if ! eval $NUMCOND; then :; else
tlog="$td/test$N.log"
te0="$td/test$N.0.stderr"
tsock="$td/test$N.sock"
if [ -z "$fileopt" ]; then
CMD0="$SOCAT $opts $diropt $ADDR:$tsock,fork,$addropts $ADDR2"
else
CMD0="$SOCAT $opts $diropt $ADDR,fork,$fileopt=$tsock,$addropts $ADDR2"
fi
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"$te0" &
pid0=$!
wait${proto} "$crit" $tsock 1 2>"$tlog"
kill $pid0 2>>"$tlog"
rc1=$?
wait
if [ $rc1 != 0 ]; then
$PRINTF "${YELLOW}setup failed${NORMAL}\n"
echo "$CMD0 &"
cat "$te0"
cat "$tlog"
let numCANT=numCANT+1
elif ! [ $crit $tsock ]; then
$PRINTF "$OK\n"
let numOK=numOK+1
else
$PRINTF "$FAILED\n"
echo "$CMD0 &"
cat "$te0"
cat "$tlog"
let numFAIL=numFAIL+1
listFAIL="$listFAIL $N"
fi
fi # NUMCOND
;;
esac
PORT=$((PORT+1))
N=$((N+1))
#
done <<<"
# address fileopt addropts waitfor direction crit ADDR2
unix-listen . . unixport . -e FILE:/dev/null
unix-recvfrom . . unixport . -e FILE:/dev/null
"
###############################################################################
# here come tests that might affect your systems integrity. Put normal tests
# before this paragraph.

View file

@ -24,7 +24,7 @@ const struct optdesc opt_unlink_close = { "unlink-close", NULL, OPT_UNLINK_CLOS
const struct optdesc opt_umask = { "umask", NULL, OPT_UMASK, GROUP_NAMED, PH_EARLY, TYPE_MODET, OFUNC_SPEC };
#endif /* WITH_NAMED */
/* applies to fd all options belonging to phase */
/* applies to filesystem entry all options belonging to phase */
int applyopts_named(const char *filename, struct opt *opts, unsigned int phase) {
struct opt *opt;
@ -137,8 +137,8 @@ int _xioopen_named_early(int argc, const char *argv[], xiofile_t *xfd,
}
}
applyopts(-1, opts, PH_EARLY);
applyopts_named(path, opts, PH_EARLY);
applyopts(-1, opts, PH_EARLY);
if (*exists) {
applyopts_named(path, opts, PH_PREOPEN);
} else {

View file

@ -1,5 +1,5 @@
/* source: xio-pipe.c */
/* Copyright Gerhard Rieger 2001-2008 */
/* Copyright Gerhard Rieger */
/* Published under the GNU General Public License V.2, see file COPYING */
/* this file contains the source for opening addresses of pipe type */
@ -96,6 +96,7 @@ static int xioopen_fifo1(int argc, const char *argv[], struct opt *opts, int xio
applyopts(-1, opts, PH_INIT);
retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
applyopts_named(pipename, opts, PH_EARLY); /* umask! */
applyopts(-1, opts, PH_EARLY);
if (opt_unlink_early) {
@ -142,6 +143,8 @@ static int xioopen_fifo1(int argc, const char *argv[], struct opt *opts, int xio
}
#endif
Notice2("created named pipe \"%s\" for %s", pipename, ddirection[rw]);
applyopts_named(pipename, opts, PH_ALL);
}
if (opt_unlink_close) {
if ((fd->stream.unlink_close = strdup(pipename)) == NULL) {

View file

@ -166,6 +166,7 @@ static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, i
if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
applyopts(-1, opts, PH_INIT);
applyopts_named(name, opts, PH_EARLY); /* umask! */
applyopts(-1, opts, PH_EARLY);
if (!(ABSTRACT && abstract)) {
@ -177,15 +178,27 @@ static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, i
Error2("unlink(\"%s\"): %s", name, strerror(errno));
}
}
} else {
struct stat buf;
if (Lstat(name, &buf) == 0) {
Error1("\"%s\" exists", name);
return STAT_RETRYLATER;
}
}
if (opt_unlink_close) {
if ((xfd->unlink_close = strdup(name)) == NULL) {
Error1("strdup(\"%s\"): out of memory", name);
}
xfd->opt_unlink_close = true;
}
/* trying to set user-early, perm-early etc. here is useless because
file system entry is available only past bind() call. */
applyopts_named(name, opts, PH_EARLY); /* umask! */
}
opts0 = copyopts(opts, GROUP_ALL);
/* this may fork() */
if ((result =
xioopen_listen(xfd, xioflags,
(struct sockaddr *)&us, uslen,
@ -193,18 +206,15 @@ static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, i
!= 0)
return result;
/* we set this option as late as now because we should not remove an
existing entry when bind() failed */
if (!(ABSTRACT && abstract)) {
if (opt_unlink_close) {
if (pid == Getpid()) {
if ((xfd->unlink_close = strdup(name)) == NULL) {
Error1("strdup(\"%s\"): out of memory", name);
}
xfd->opt_unlink_close = true;
if (pid != Getpid()) {
/* in a child process - do not unlink-close here! */
xfd->opt_unlink_close = false;
}
}
}
return 0;
}
#endif /* WITH_LISTEN */
@ -380,13 +390,9 @@ int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts,
/* only for non abstract because abstract do not work in file system */
retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
if (opt_unlink_close) {
if ((xfd->unlink_close = strdup(name)) == NULL) {
Error1("strdup(\"%s\"): out of memory", name);
}
xfd->opt_unlink_close = true;
}
}
if (!(ABSTRACT && abstract)) {
if (opt_unlink_early) {
if (Unlink(name) < 0) {
if (errno == ENOENT) {
@ -395,12 +401,30 @@ int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts,
Error2("unlink(\"%s\"): %s", name, strerror(errno));
}
}
} else {
struct stat buf;
if (Lstat(name, &buf) == 0) {
Error1("\"%s\" exists", name);
return STAT_RETRYLATER;
}
}
if (opt_unlink_close) {
if ((xfd->unlink_close = strdup(name)) == NULL) {
Error1("strdup(\"%s\"): out of memory", name);
}
xfd->opt_unlink_close = true;
}
/* trying to set user-early, perm-early etc. here is useless because
file system entry is available only past bind() call. */
}
applyopts_named(name, opts, PH_EARLY); /* umask! */
xfd->para.socket.la.soa.sa_family = pf;
xfd->dtype = XIODATA_RECVFROM_ONE;
/* this may fork */
return
_xioopen_dgram_recvfrom(xfd, xioflags,
needbind?(struct sockaddr *)&us:NULL, uslen,
@ -443,6 +467,8 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
if (!(ABSTRACT && abstract)) {
/* only for non abstract because abstract do not work in file system */
retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
if (opt_unlink_early) {
if (Unlink(name) < 0) {
if (errno == ENOENT) {
@ -451,10 +477,13 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
Error2("unlink(\"%s\"): %s", name, strerror(errno));
}
}
} else {
struct stat buf;
if (Lstat(name, &buf) == 0) {
Error1("\"%s\" exists", name);
return STAT_RETRYLATER;
}
}
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
if (opt_unlink_close) {
if ((xfd->unlink_close = strdup(name)) == NULL) {
Error1("strdup(\"%s\"): out of memory", name);
@ -462,6 +491,7 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
xfd->opt_unlink_close = true;
}
}
applyopts_named(name, opts, PH_EARLY); /* umask! */
xfd->para.socket.la.soa.sa_family = pf;