Fixed bind with abstract unix domain sockets (Linux)

This commit is contained in:
Gerhard Rieger 2014-09-28 17:53:50 +02:00
parent 2af0495cc6
commit 98028900e0
5 changed files with 135 additions and 58 deletions

View File

@ -18,6 +18,11 @@ security:
Turn off nested signal handler invocations Turn off nested signal handler invocations
Thanks to Peter Lobsinger for reporting and explaining this issue. Thanks to Peter Lobsinger for reporting and explaining this issue.
corrections:
Bind with ABSTRACT commands used non-abstract namespace (Linux).
Test: ABSTRACT_BIND
Thanks to Denis Shatov for reporting this bug.
####################### V 1.7.2.4: ####################### V 1.7.2.4:
corrections: corrections:

48
test.sh
View File

@ -8872,6 +8872,54 @@ esac
N=$((N+1)) N=$((N+1))
# bind with Linux abstract UNIX domain addresses bound to filesystem socket
# instead of abstract namespace
NAME=ABSTRACT_BIND
case "$TESTS" in
*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%unix%*|*%abstract%*|*%$NAME%*)
TEST="$NAME: abstract bind"
# open an abstract client address with bind option, bind to the target socket.
# send a datagram.
# when socat outputs the datagram it got the test succeeded
if ! eval $NUMCOND; then :;
elif [ "$UNAME" != Linux ]; then
$PRINTF "test $F_n $TEST... ${YELLOW}only on Linux${NORMAL}\n" $N
numCANT=$((numCANT+1))
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
ts1="$td/test$N.sock1"
da="test$N $(date) $RANDOM"
CMD1="$TRACE $SOCAT $opts - ABSTRACT-SENDTO:$ts1,bind=$ts1"
printf "test $F_n $TEST... " $N
echo "$da" |$CMD1 >$tf 2>"${te}1"
rc1=$?
if [ $rc1 -ne 0 ]; then
$PRINTF "$FAILED\n"
echo "$CMD1" >&2
echo "rc=$rc1" >&2
cat "${te}1" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
elif echo "$da" |diff -q - $tf; then
$PRINTF "$OK\n"
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "$CMD1" >&2
cat "${te}1" >&2
echo "$da" |diff - "$tf" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
fi
fi # NUMCOND
;;
esac
PORT=$((PORT+1))
N=$((N+1))
NAME=OPENSSLREAD NAME=OPENSSLREAD
# socat determined availability of data using select(). With openssl, the # socat determined availability of data using select(). With openssl, the
# following situation might occur: # following situation might occur:

View File

@ -57,13 +57,13 @@ const struct addrdesc xioaddr_abstract_recv = { "abstract-recv", 1, xioop
const struct addrdesc xioaddr_abstract_client = { "abstract-client", 3, xioopen_unix_client, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY, 1, 0, 0 HELP(":<filename>") }; const struct addrdesc xioaddr_abstract_client = { "abstract-client", 3, xioopen_unix_client, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY, 1, 0, 0 HELP(":<filename>") };
#endif /* WITH_ABSTRACT_UNIXSOCKET */ #endif /* WITH_ABSTRACT_UNIXSOCKET */
const struct optdesc xioopt_unix_tightsocklen = { "unix-tightsocklen", "tightsocklen", OPT_UNIX_TIGHTSOCKLEN, GROUP_SOCK_UNIX, PH_INIT, TYPE_BOOL, OFUNC_SPEC, 0, 0 }; const struct optdesc xioopt_unix_tightsocklen = { "unix-tightsocklen", "tightsocklen", OPT_UNIX_TIGHTSOCKLEN, GROUP_SOCK_UNIX, PH_PREBIND, TYPE_BOOL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.un.tight), XIO_SIZEOF(para.socket.un.tight) };
/* fills the socket address struct and returns its effective length. /* fills the socket address struct and returns its effective length.
abstract is usually 0; != 0 generates an abstract socket address on Linux. abstract is usually 0; != 0 generates an abstract socket address on Linux.
tight!=0 calculates the resulting length from the path length, not from the tight!=0 calculates the resulting length from the path length, not from the
structures length; this is more common. structures length; this is more common (see option unix-tightsocklen)
the struct need not be initialized when calling this function. the struct need not be initialized when calling this function.
*/ */
socklen_t socklen_t
@ -124,7 +124,6 @@ static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, i
int protocol = 0; int protocol = 0;
struct sockaddr_un us; struct sockaddr_un us;
socklen_t uslen; socklen_t uslen;
bool tight = true;
struct opt *opts0 = NULL; struct opt *opts0 = NULL;
pid_t pid = Getpid(); pid_t pid = Getpid();
bool opt_unlink_early = false; bool opt_unlink_early = false;
@ -136,12 +135,10 @@ static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, i
argv[0], argc-1); argv[0], argc-1);
return STAT_NORETRY; return STAT_NORETRY;
} }
name = argv[1]; name = argv[1];
retropt_socket_pf(opts, &pf);
retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
uslen = xiosetunix(pf, &us, name, abstract, tight);
xfd->para.socket.un.tight = true;
retropt_socket_pf(opts, &pf);
xfd->howtoend = END_SHUTDOWN; xfd->howtoend = END_SHUTDOWN;
if (!(ABSTRACT && abstract)) { if (!(ABSTRACT && abstract)) {
@ -153,8 +150,11 @@ 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; if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
applyopts(-1, opts, PH_INIT); applyopts(-1, opts, PH_INIT);
applyopts_named(name, opts, PH_EARLY); /* umask! */ applyopts_named(name, opts, PH_EARLY); /* umask! */
applyopts_offset(xfd, opts);
applyopts(-1, opts, PH_EARLY); applyopts(-1, opts, PH_EARLY);
uslen = xiosetunix(pf, &us, name, abstract, xfd->para.socket.un.tight);
if (!(ABSTRACT && abstract)) { if (!(ABSTRACT && abstract)) {
if (opt_unlink_early) { if (opt_unlink_early) {
if (Unlink(name) < 0) { if (Unlink(name) < 0) {
@ -214,8 +214,7 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts,
int socktype = SOCK_STREAM; int socktype = SOCK_STREAM;
int protocol = 0; int protocol = 0;
struct sockaddr_un them, us; struct sockaddr_un them, us;
socklen_t themlen, uslen; socklen_t themlen, uslen = sizeof(us);
bool tight = true;
bool needbind = false; bool needbind = false;
bool opt_unlink_close = false; bool opt_unlink_close = false;
int result; int result;
@ -225,19 +224,23 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts,
argv[0], argc-1); argv[0], argc-1);
return STAT_NORETRY; return STAT_NORETRY;
} }
xfd->howtoend = END_SHUTDOWN;
name = argv[1]; name = argv[1];
xfd->para.socket.un.tight = true;
retropt_socket_pf(opts, &pf); retropt_socket_pf(opts, &pf);
retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); xfd->howtoend = END_SHUTDOWN;
themlen = xiosetunix(pf, &them, name, abstract, tight); if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
applyopts(-1, opts, PH_INIT);
applyopts_offset(xfd, opts);
applyopts(-1, opts, PH_EARLY);
themlen = xiosetunix(pf, &them, name, abstract, xfd->para.socket.un.tight);
if (!(ABSTRACT && abstract)) { if (!(ABSTRACT && abstract)) {
/* only for non abstract because abstract do not work in file system */ /* only for non abstract because abstract do not work in file system */
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
} }
if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, 0, 0, 0) if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
!= STAT_NOACTION) { (abstract<<1)|xfd->para.socket.un.tight, 0, 0) == STAT_OK) {
needbind = true; needbind = true;
} }
@ -248,10 +251,6 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts,
xfd->opt_unlink_close = true; xfd->opt_unlink_close = true;
} }
if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
applyopts(-1, opts, PH_INIT);
applyopts(-1, opts, PH_EARLY);
if ((result = if ((result =
xioopen_connect(xfd, xioopen_connect(xfd,
needbind?(struct sockaddr *)&us:NULL, uslen, needbind?(struct sockaddr *)&us:NULL, uslen,
@ -274,8 +273,7 @@ static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, i
int socktype = SOCK_DGRAM; int socktype = SOCK_DGRAM;
int protocol = 0; int protocol = 0;
union sockaddr_union us; union sockaddr_union us;
socklen_t uslen; socklen_t uslen = sizeof(us);
bool tight = true;
bool needbind = false; bool needbind = false;
bool opt_unlink_close = false; bool opt_unlink_close = false;
@ -284,13 +282,14 @@ static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, i
argv[0], argc-1); argv[0], argc-1);
return STAT_NORETRY; return STAT_NORETRY;
} }
retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
name = argv[1]; name = argv[1];
retropt_socket_pf(opts, &pf);
xfd->salen = xiosetunix(pf, &xfd->peersa.un, name, abstract, tight);
xfd->para.socket.un.tight = true;
retropt_socket_pf(opts, &pf);
xfd->howtoend = END_SHUTDOWN; xfd->howtoend = END_SHUTDOWN;
applyopts_offset(xfd, opts);
xfd->salen = xiosetunix(pf, &xfd->peersa.un, name, abstract, xfd->para.socket.un.tight);
if (!(ABSTRACT && abstract)) { if (!(ABSTRACT && abstract)) {
/* only for non abstract because abstract do not work in file system */ /* only for non abstract because abstract do not work in file system */
@ -299,8 +298,8 @@ static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, i
xfd->dtype = XIODATA_RECVFROM; xfd->dtype = XIODATA_RECVFROM;
if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 0, 0, 0) if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
!= STAT_NOACTION) { (abstract<<1)| xfd->para.socket.un.tight, 0, 0) == STAT_OK) {
needbind = true; needbind = true;
} }
@ -333,7 +332,6 @@ int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts,
int protocol = 0; int protocol = 0;
struct sockaddr_un us; struct sockaddr_un us;
socklen_t uslen; socklen_t uslen;
bool tight = true;
bool needbind = true; bool needbind = true;
bool opt_unlink_early = false; bool opt_unlink_early = false;
bool opt_unlink_close = true; bool opt_unlink_close = true;
@ -343,21 +341,30 @@ int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts,
argv[0], argc-1); argv[0], argc-1);
return STAT_NORETRY; return STAT_NORETRY;
} }
name = argv[1]; name = argv[1];
retropt_socket_pf(opts, &pf);
retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
uslen = xiosetunix(pf, &us, name, abstract, tight);
xfd->para.socket.un.tight = true;
retropt_socket_pf(opts, &pf);
xfd->howtoend = END_NONE; xfd->howtoend = END_NONE;
retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
1, 0, 0); applyopts(-1, opts, PH_INIT);
applyopts_named(name, opts, PH_EARLY); /* umask! */
applyopts_offset(xfd, opts);
if (!(ABSTRACT && abstract)) { if (!(ABSTRACT && abstract)) {
/* only for non abstract because abstract do not work in file system */ /* 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_EARLY, &opt_unlink_early);
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
} }
applyopts(-1, opts, PH_EARLY);
uslen = xiosetunix(pf, &us, name, abstract, xfd->para.socket.un.tight);
#if 0
if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
(abstract<<1)|xfd->para.socket.un.tight, 0, 0) == STAT_OK) {
}
#endif
if (!(ABSTRACT && abstract)) { if (!(ABSTRACT && abstract)) {
if (opt_unlink_early) { if (opt_unlink_early) {
@ -411,7 +418,6 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
int protocol = 0; int protocol = 0;
union sockaddr_union us; union sockaddr_union us;
socklen_t uslen; socklen_t uslen;
bool tight = true;
bool opt_unlink_early = false; bool opt_unlink_early = false;
bool opt_unlink_close = true; bool opt_unlink_close = true;
int result; int result;
@ -421,21 +427,33 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
argv[0], argc-1); argv[0], argc-1);
return STAT_NORETRY; return STAT_NORETRY;
} }
name = argv[1]; name = argv[1];
retropt_socket_pf(opts, &pf);
retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
uslen = xiosetunix(pf, &us.un, name, abstract, tight);
#if 1 /*!!! why bind option? */ xfd->para.socket.un.tight = true;
retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 1, 0, 0); retropt_socket_pf(opts, &pf);
#endif xfd->howtoend = END_SHUTDOWN;
if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
applyopts(-1, opts, PH_INIT);
applyopts_named(name, opts, PH_EARLY); /* umask! */
applyopts_offset(xfd, opts);
if (!(ABSTRACT && abstract)) { if (!(ABSTRACT && abstract)) {
/* only for non abstract because abstract do not work in file system */ /* 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_EARLY, &opt_unlink_early);
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
}
applyopts(-1, opts, PH_EARLY);
uslen = xiosetunix(pf, &us.un, name, abstract, xfd->para.socket.un.tight);
#if 0
if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
(abstract<<1)|xfd->para.socket.un.tight, 0, 0)
== STAT_OK) {
}
#endif
if (!(ABSTRACT && abstract)) {
if (opt_unlink_early) { if (opt_unlink_early) {
if (Unlink(name) < 0) { if (Unlink(name) < 0) {
if (errno == ENOENT) { if (errno == ENOENT) {
@ -497,28 +515,27 @@ _xioopen_unix_client(xiosingle_t *xfd, int xioflags, unsigned groups,
int socktype = 0; /* to be determined by server socket type */ int socktype = 0; /* to be determined by server socket type */
int protocol = 0; int protocol = 0;
union sockaddr_union them, us; union sockaddr_union them, us;
socklen_t themlen, uslen; socklen_t themlen, uslen = sizeof(us);
bool tight = true;
bool needbind = false; bool needbind = false;
bool opt_unlink_close = false; bool opt_unlink_close = false;
struct opt *opts0; struct opt *opts0;
int result; int result;
if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; xfd->para.socket.un.tight = true;
applyopts(-1, opts, PH_INIT);
xfd->howtoend = END_SHUTDOWN;
retropt_socket_pf(opts, &pf); retropt_socket_pf(opts, &pf);
xfd->howtoend = END_SHUTDOWN;
if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
applyopts(-1, opts, PH_INIT);
applyopts_offset(xfd, opts);
applyopts(-1, opts, PH_EARLY);
retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); themlen = xiosetunix(pf, &them.un, name, abstract, xfd->para.socket.un.tight);
themlen = xiosetunix(pf, &them.un, name, abstract, tight);
if (!(ABSTRACT && abstract)) { if (!(ABSTRACT && abstract)) {
/* only for non abstract because abstract do not work in file system */ /* only for non abstract because abstract do not work in file system */
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
} }
if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 0, 0, 0) (abstract<<1)|xfd->para.socket.un.tight, 0, 0)
!= STAT_NOACTION) { != STAT_NOACTION) {
needbind = true; needbind = true;
} }

3
xio.h
View File

@ -200,6 +200,9 @@ typedef struct single {
char *hosts_deny_table; char *hosts_deny_table;
#endif #endif
} ip; } ip;
struct {
bool tight;
} un;
#endif /* _WITH_IP4 || _WITH_IP6 */ #endif /* _WITH_IP4 || _WITH_IP6 */
} socket; } socket;
#endif /* _WITH_SOCKET */ #endif /* _WITH_SOCKET */

View File

@ -2816,7 +2816,7 @@ int retropt_string(struct opt *opts, int optcode, char **result) {
#if _WITH_SOCKET #if _WITH_SOCKET
/* looks for an bind option and, if found, overwrites the complete contents of /* looks for a bind option and, if found, overwrites the complete contents of
sa with the appropriate value(s). sa with the appropriate value(s).
returns STAT_OK if option exists and could be resolved, returns STAT_OK if option exists and could be resolved,
STAT_NORETRY if option exists but had error, STAT_NORETRY if option exists but had error,
@ -2829,7 +2829,10 @@ int retropt_bind(struct opt *opts,
struct sockaddr *sa, struct sockaddr *sa,
socklen_t *salen, socklen_t *salen,
int feats, /* TCP etc: 1..address allowed, int feats, /* TCP etc: 1..address allowed,
3..address and port allowed */ 3..address and port allowed
UNIX (or'd): 1..tight
2..abstract
*/
unsigned long res_opts0, unsigned long res_opts1) { unsigned long res_opts0, unsigned long res_opts1) {
const char portsep[] = ":"; const char portsep[] = ":";
const char *ends[] = { portsep, NULL }; const char *ends[] = { portsep, NULL };
@ -2897,9 +2900,10 @@ int retropt_bind(struct opt *opts,
#if WITH_UNIX #if WITH_UNIX
case AF_UNIX: case AF_UNIX:
{ {
bool tight = false; bool abstract = (feats&2);
bool tight = (feats&1);
struct sockaddr_un *s_un = (struct sockaddr_un *)sa; struct sockaddr_un *s_un = (struct sockaddr_un *)sa;
*salen = xiosetunix(af, s_un, bindname, false, tight); *salen = xiosetunix(af, s_un, bindname, abstract, tight);
} }
break; break;
#endif /* WITH_UNIX */ #endif /* WITH_UNIX */