Restrict option umask to the address it is applied to

This commit is contained in:
Gerhard Rieger 2023-07-19 21:51:59 +02:00
parent 254958a34d
commit e5cbf2feeb
10 changed files with 208 additions and 14 deletions

View file

@ -143,6 +143,11 @@ Features:
Added option res-nsaddr that overrides /etc/resolv.conf nameserver
address based on an undocumented resolver feature.
Option umask now applies only during opening of its very address, not
for the lifetime of the process; the original umask is restored
afterwards.
Tests: UMASK_ON_CREATE UMASK_ON_SYSTEM
Corrections:
When a sub process (EXEC, SYSTEM) terminated with exit code other than
0, its last sent data might have been lost depending on timing of read/

View file

@ -341,6 +341,7 @@ label(ADDRESS_EXEC)dit(bf(tt(EXEC:<command-line>)))
link(ctty)(OPTION_CTTY),
link(setsid)(OPTION_SETSID),
link(pipes)(OPTION_PIPES),
link(umask)(OPTION_UMASK),
link(login)(OPTION_LOGIN),
link(sigint)(OPTION_SIGINT),
link(sigquit)(OPTION_SIGQUIT),
@ -1111,6 +1112,7 @@ label(ADDRESS_SHELL)dit(bf(tt(SHELL:<shell-command>)))
link(ctty)(OPTION_CTTY),
link(setsid)(OPTION_SETSID),
link(pipes)(OPTION_PIPES),
link(umask)(OPTION_UMASK),
link(sigint)(OPTION_SIGINT),
link(sigquit)(OPTION_SIGQUIT)nl()
See also: link(EXEC)(ADDRESS_EXEC), link(SYSTEM)(ADDRESS_SYSTEM)
@ -1137,6 +1139,7 @@ label(ADDRESS_SYSTEM)dit(bf(tt(SYSTEM:<shell-command>)))
link(ctty)(OPTION_CTTY),
link(setsid)(OPTION_SETSID),
link(pipes)(OPTION_PIPES),
link(umask)(OPTION_UMASK),
link(sigint)(OPTION_SIGINT),
link(sigquit)(OPTION_SIGQUIT),
link(netns)(OPTION_NETNS)nl()
@ -1503,6 +1506,7 @@ label(ADDRESS_UNIX_RECVFROM)dit(bf(tt(UNIX-RECVFROM:<filename>)))
See the link(note about RECVFROM addresses)(NOTE_RECVFROM).nl()
Useful options:
link(fork)(OPTION_FORK)nl()
link(umask)(OPTION_UMASK)nl()
See also:
link(UNIX-SENDTO)(ADDRESS_UNIX_SENDTO),
link(UNIX-RECV)(ADDRESS_UNIX_RECV),
@ -1518,6 +1522,8 @@ label(ADDRESS_UNIX_RECV)dit(bf(tt(UNIX-RECV:<filename>)))
It can be, e.g., addressed by socat UNIX-SENDTO address peers.
It behaves similar to a syslog server.nl()
Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET),link(NAMED)(GROUP_NAMED),link(UNIX)(GROUP_SOCK_UNIX) nl()
Useful options:
link(umask)(OPTION_UMASK)nl()
See also:
link(UNIX-SENDTO)(ADDRESS_UNIX_SENDTO),
link(UNIX-RECVFROM)(ADDRESS_UNIX_RECVFROM),
@ -1856,11 +1862,6 @@ label(OPTION_PERM_EARLY)dit(bf(tt(perm-early=<mode>)))
before accessing it, using the
code(chmod()) system call. This call might require ownership or root
privilege.
label(OPTION_UMASK)dit(bf(tt(umask=<mode>)))
Sets the umask of the process to <mode> [link(mode_t)(TYPE_MODE_T)] before
accessing the file system entry (useful
with unixdomain() sockets!). This call might affect all further operations
of the socat() process!
label(OPTION_UNLINK_EARLY)dit(bf(tt(unlink-early[=<bool>])))
Unlinks (removes) the file before opening it and even before applying
user-early etc.
@ -1983,6 +1984,24 @@ enddit()
startdit()enddit()nl()
label(GROUP_ADDRS)em(bf(General address options))
These options may be applied to all address types. They change some process
properties that are restored after opening the address.
startdit()
label(OPTION_UMASK)dit(bf(tt(umask=<mode>)))
Sets the umask of the process to <mode> [link(mode_t)(TYPE_MODE_T)] before
opening the address. Useful when file system entries are created or a shell
or program is invoked. Usually the value is specified as octal number.nl()
The processes tt(umask) value is inherited by child processes.
Note: umask is an inverted value: creating a file with umask=0026 results in
permissions 0640.
enddit()
startdit()enddit()nl()
label(GROUP_PROCESS)em(bf(PROCESS option group))
Options of this group change the process properties instead of just affecting

155
test.sh
View file

@ -17355,6 +17355,161 @@ else
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
fi
# Test the modified umask option, in particular if umask with first address
# (CREATE) does not affect umask of second address, i.e. original umask is
# recovered
NAME=UMASK_ON_CREATE
case "$TESTS" in
*%$N%*|*%functions%*|*%creat%*|*%system%*|*%umask%*|*%$NAME%*)
TEST="$NAME: test restore after CREAT with umask option"
# Run Socat with first address CREAT with modified umask,
# and second address SYSTEM (shell) with umask command
# Check if the file is created with modified umask but shell has original umask
if ! eval $NUMCOND; then :;
elif ! F=$(testfeats CREAT SYSTEM); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs - CREAT SYSTEM); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! o=$(testoptions umask) >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}Option $o not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tc="$td/test$N.creat"
tdiff="$td/test$N.diff"
tdebug="$td/test$N.debug"
oumask=$(umask)
# Construct a temp umask differing from original umask
case oumask in
*066) tumask=0026 ;;
*) tumask=0066 ;;
esac
CMD0="$TRACE $SOCAT $opts -U CREAT:$tc,umask=$tumask SYSTEM:umask"
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"${te}0"
rc0=$?
tperms=$(fileperms $tc)
case $tperms in
0*) ;;
*) tperms=0$tperms ;;
esac
echo "Original umask: $oumask" >>$tdebug
echo "Temporary umask: $tumask" >>$tdebug
echo "Created umask: $tperms" >>$tdebug
if [ "$rc0" -ne 0 ]; then
$PRINTF "$FAILED\n"
echo "$CMD0 &"
cat "${te}0" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
elif [ $((tumask + tperms - 0666)) -ne 0 ]; then
$PRINTF "$FAILED (umask failed)\n"
echo "$CMD0 &"
cat "${te}0" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
elif ! echo "$oumask" |diff "$tc" - >$tdiff; then
$PRINTF "$FAILED (bad umask)\n"
echo "$CMD0 &"
cat "${te}0" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
else
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
numOK=$((numOK+1))
fi
fi # NUMCOND
;;
esac
N=$((N+1))
# Test the modified umask option, in particular if umask with first address
# (SHELL) does not affect umask of second address, i.e. original umask is
# recovered
NAME=UMASK_ON_SYSTEM
case "$TESTS" in
*%$N%*|*%functions%*|*%shell%*|*%umask%*|*%socket%*|*%$NAME%*)
TEST="$NAME: test restore after SHELL with umask option"
# Run Socat with first address SHELL:"cat >file" with modified umask,
# and second address SYSTEM (shell) with umask command.
# Check if the file is created with modified umask but shell has original umask
if ! eval $NUMCOND; then :;
elif ! F=$(testfeats SHELL SYSTEM); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs SHELL SYSTEM); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! o=$(testoptions umask) >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}Option $o not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tc="$td/test$N.creat"
tdiff="$td/test$N.diff"
tdebug="$td/test$N.debug"
oumask=$(umask)
# Construct a temp umask differing from original umask
case oumask in
*066) tumask=0026 ;;
*) tumask=0066 ;;
esac
CMD0="$TRACE $SOCAT $opts -U SHELL:\"cat\ >$tc\",umask=$tumask SYSTEM:umask"
printf "test $F_n $TEST... " $N
eval "$CMD0" >/dev/null 2>"${te}0"
rc0=$?
tperms=$(fileperms $tc)
case $tperms in
0*) ;;
*) tperms=0$tperms ;;
esac
echo "Original umask: $oumask" >>$tdebug
echo "Temporary umask: $tumask" >>$tdebug
echo "Created umask: $tperms" >>$tdebug
if [ "$rc0" -ne 0 ]; then
$PRINTF "$FAILED\n"
echo "$CMD0 &"
cat "${te}0" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
elif [ $((tumask + tperms - 0666)) -ne 0 ]; then
$PRINTF "$FAILED (umask failed)\n"
echo "$CMD0 &"
cat "${te}0" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
elif ! echo "$oumask" |diff "$tc" - >$tdiff; then
$PRINTF "$FAILED (bad umask)\n"
echo "$CMD0 &"
cat "${te}0" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
else
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
numOK=$((numOK+1))
fi
fi # NUMCOND
;;
esac

View file

@ -21,7 +21,6 @@ const struct optdesc opt_unlink = { "unlink", NULL, OPT_UNLINK, G
const struct optdesc opt_unlink_early= { "unlink-early",NULL, OPT_UNLINK_EARLY,GROUP_NAMED, PH_EARLY, TYPE_BOOL, OFUNC_SPEC };
const struct optdesc opt_unlink_late = { "unlink-late", NULL, OPT_UNLINK_LATE, GROUP_NAMED, PH_PASTOPEN, TYPE_BOOL, OFUNC_SPEC };
const struct optdesc opt_unlink_close = { "unlink-close", NULL, OPT_UNLINK_CLOSE, GROUP_NAMED, PH_LATE, TYPE_BOOL, OFUNC_SPEC };
const struct optdesc opt_umask = { "umask", NULL, OPT_UMASK, GROUP_NAMED, PH_EARLY, TYPE_MODET, OFUNC_SPEC };
#endif /* WITH_NAMED */
/* applies to filesystem entry all options belonging to phase */
@ -71,12 +70,6 @@ int applyopts_named(const char *filename, struct opt *opts, unsigned int phase)
}
}
break;
case OPT_UMASK:
if (Umask(opt->value.u_modet) < 0) {
/* linux docu says it always succeeds, but who believes it? */
Error2("umask("F_mode"): %s", opt->value.u_modet, strerror(errno));
}
break;
default: Error1("applyopts_named(): option \"%s\" not implemented",
opt->desc->defname);
break;

View file

@ -14,7 +14,6 @@ extern const struct optdesc opt_unlink;
extern const struct optdesc opt_unlink_early;
extern const struct optdesc opt_unlink_late;
extern const struct optdesc opt_unlink_close;
extern const struct optdesc opt_umask;
extern int
applyopts_named(const char *filename, struct opt *opts, unsigned int phase);

View file

@ -24,3 +24,4 @@ const struct optdesc opt_intervall = { "interval", NULL, OPT_INTERVALL, GROUP_R
const struct optdesc opt_retry = { "retry", NULL, OPT_RETRY, GROUP_RETRY, PH_INIT, TYPE_UINT, OFUNC_EXT, XIO_OFFSETOF(retry), XIO_SIZEOF(retry) };
#endif
const struct optdesc opt_umask = { "umask", NULL, OPT_UMASK, GROUP_ADDR, PH_INIT, TYPE_MODET, OFUNC_SPEC };

View file

@ -15,5 +15,7 @@ extern const struct optdesc opt_escape;
extern const struct optdesc opt_forever;
extern const struct optdesc opt_intervall;
extern const struct optdesc opt_retry;
extern const struct optdesc opt_umask;
extern const struct optdesc opt_un_umask;
#endif /* !defined(__xiolayer_h_included) */

View file

@ -621,6 +621,9 @@ int xioopen_single(xiofile_t *xfd, int xioflags) {
struct single *sfd = &xfd->stream;
const struct addrdesc *addrdesc;
const char *modetext[4] = { "none", "read-only", "write-only", "read-write" } ;
/* Values to be saved until xioopen() is finished */
bool have_umask = false;
mode_t orig_umask, tmp_umask;
int result;
/* Values to be saved until xioopen() is finished */
#if WITH_RESOLVE && HAVE_RESOLV_H
@ -633,6 +636,8 @@ int xioopen_single(xiofile_t *xfd, int xioflags) {
#endif
int rc;
/* Apply "temporary" process properties, save value for later restore */
if (applyopts_single(sfd, sfd->opts, PH_OFFSET) < 0)
return -1;
@ -676,10 +681,23 @@ int xioopen_single(xiofile_t *xfd, int xioflags) {
}
xfd->stream.flags &= (~XIO_ACCMODE);
xfd->stream.flags |= (xioflags & XIO_ACCMODE);
if (retropt_mode(xfd->stream.opts, OPT_UMASK, &tmp_umask) >= 0) {
Info1("changing umask to 0%3o", tmp_umask);
orig_umask = Umask(tmp_umask);
have_umask = true;
}
result = (*addrdesc->func)(xfd->stream.argc, xfd->stream.argv,
xfd->stream.opts, xioflags, xfd,
addrdesc);
/* Restore process properties */
if (have_umask) {
Info1("restoring umask to 0%3o", orig_umask);
Umask(orig_umask);
}
#if WITH_RESOLVE && HAVE_RESOLV_H
if (do_res)
xio_res_restore(&save_res);

View file

@ -2074,7 +2074,7 @@ int parseopts_table(const char **a, groups_t groups, struct opt **opts,
}
if (!(ent->desc->group & groups) && !(ent->desc->group & GROUP_ANY) &&
!xioopts_ignoregroups) {
(ent->desc->group != GROUP_ADDR) && !xioopts_ignoregroups) {
Error1("parseopts_table(): option \"%s\" not supported with this address type",
token /*a0*/);
Info2("parseopts_table() groups="F_groups_t", ent->group="F_groups_t,

View file

@ -148,6 +148,7 @@ enum e_func {
/* keep consistent with xiohelp.c:addressgroupnames[] ! */
/* a dummy group */
#define GROUP_NONE 0x00000000
#define GROUP_ADDR 0x00000000 /* options that apply to all addresses */
#define GROUP_FD 0x00000001 /* everything applyable to a fd */
#define GROUP_FIFO 0x00000002
@ -862,6 +863,7 @@ enum e_optcode {
OPT_UNLINK_CLOSE,
OPT_UNLINK_EARLY,
OPT_UNLINK_LATE,
OPT_UN_UMASK,
OPT_USER,
OPT_USER_EARLY,
OPT_USER_LATE,