From 98028900e0804c6477831e80562302d8858501a0 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Sun, 28 Sep 2014 17:53:50 +0200 Subject: [PATCH] Fixed bind with abstract unix domain sockets (Linux) --- CHANGES | 5 +++ test.sh | 48 ++++++++++++++++++++ xio-unix.c | 125 ++++++++++++++++++++++++++++++----------------------- xio.h | 3 ++ xioopts.c | 12 +++-- 5 files changed, 135 insertions(+), 58 deletions(-) diff --git a/CHANGES b/CHANGES index 7671566..acaeaee 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,11 @@ security: Turn off nested signal handler invocations 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: corrections: diff --git a/test.sh b/test.sh index 6c9beec..3b47286 100755 --- a/test.sh +++ b/test.sh @@ -8872,6 +8872,54 @@ esac 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 # socat determined availability of data using select(). With openssl, the # following situation might occur: diff --git a/xio-unix.c b/xio-unix.c index a8fbed3..fa6634d 100644 --- a/xio-unix.c +++ b/xio-unix.c @@ -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(":") }; #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. 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 - 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. */ socklen_t @@ -124,7 +124,6 @@ static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, i int protocol = 0; struct sockaddr_un us; socklen_t uslen; - bool tight = true; struct opt *opts0 = NULL; pid_t pid = Getpid(); 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); return STAT_NORETRY; } - 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; 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; applyopts(-1, opts, PH_INIT); applyopts_named(name, opts, PH_EARLY); /* umask! */ + applyopts_offset(xfd, opts); applyopts(-1, opts, PH_EARLY); + uslen = xiosetunix(pf, &us, name, abstract, xfd->para.socket.un.tight); + if (!(ABSTRACT && abstract)) { if (opt_unlink_early) { 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 protocol = 0; struct sockaddr_un them, us; - socklen_t themlen, uslen; - bool tight = true; + socklen_t themlen, uslen = sizeof(us); bool needbind = false; bool opt_unlink_close = false; int result; @@ -225,19 +224,23 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, argv[0], argc-1); return STAT_NORETRY; } - - xfd->howtoend = END_SHUTDOWN; - name = argv[1]; + + xfd->para.socket.un.tight = true; retropt_socket_pf(opts, &pf); - retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); - themlen = xiosetunix(pf, &them, name, abstract, tight); + 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); + + themlen = xiosetunix(pf, &them, name, abstract, xfd->para.socket.un.tight); if (!(ABSTRACT && abstract)) { /* only for non abstract because abstract do not work in file system */ retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); } - if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, 0, 0, 0) - != STAT_NOACTION) { + if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, + (abstract<<1)|xfd->para.socket.un.tight, 0, 0) == STAT_OK) { needbind = true; } @@ -248,10 +251,6 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, 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 = xioopen_connect(xfd, 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 protocol = 0; union sockaddr_union us; - socklen_t uslen; - bool tight = true; + socklen_t uslen = sizeof(us); bool needbind = 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); return STAT_NORETRY; } - - retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); 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; + applyopts_offset(xfd, opts); + + xfd->salen = xiosetunix(pf, &xfd->peersa.un, name, abstract, xfd->para.socket.un.tight); if (!(ABSTRACT && abstract)) { /* 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; - if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 0, 0, 0) - != STAT_NOACTION) { + if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, + (abstract<<1)| xfd->para.socket.un.tight, 0, 0) == STAT_OK) { needbind = true; } @@ -333,7 +332,6 @@ int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts, int protocol = 0; struct sockaddr_un us; socklen_t uslen; - bool tight = true; bool needbind = true; bool opt_unlink_early = false; 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); return STAT_NORETRY; } - 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; - retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, - 1, 0, 0); + 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)) { /* 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); } + 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 (opt_unlink_early) { @@ -411,7 +418,6 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts, int protocol = 0; union sockaddr_union us; socklen_t uslen; - bool tight = true; bool opt_unlink_early = false; bool opt_unlink_close = true; int result; @@ -421,21 +427,33 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts, argv[0], argc-1); return STAT_NORETRY; } - 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? */ - retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 1, 0, 0); -#endif + xfd->para.socket.un.tight = true; + 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_named(name, opts, PH_EARLY); /* umask! */ + applyopts_offset(xfd, 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); + } + 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 (Unlink(name) < 0) { 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 protocol = 0; union sockaddr_union them, us; - socklen_t themlen, uslen; - bool tight = true; + socklen_t themlen, uslen = sizeof(us); bool needbind = false; bool opt_unlink_close = false; struct opt *opts0; int result; - if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; - applyopts(-1, opts, PH_INIT); - - xfd->howtoend = END_SHUTDOWN; + xfd->para.socket.un.tight = true; 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, tight); - + themlen = xiosetunix(pf, &them.un, name, abstract, xfd->para.socket.un.tight); if (!(ABSTRACT && abstract)) { /* only for non abstract because abstract do not work in file system */ retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); } - - if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 0, 0, 0) + if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, + (abstract<<1)|xfd->para.socket.un.tight, 0, 0) != STAT_NOACTION) { needbind = true; } diff --git a/xio.h b/xio.h index 85bbace..a30596e 100644 --- a/xio.h +++ b/xio.h @@ -200,6 +200,9 @@ typedef struct single { char *hosts_deny_table; #endif } ip; + struct { + bool tight; + } un; #endif /* _WITH_IP4 || _WITH_IP6 */ } socket; #endif /* _WITH_SOCKET */ diff --git a/xioopts.c b/xioopts.c index c7ad842..26c2f41 100644 --- a/xioopts.c +++ b/xioopts.c @@ -2816,7 +2816,7 @@ int retropt_string(struct opt *opts, int optcode, char **result) { #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). returns STAT_OK if option exists and could be resolved, STAT_NORETRY if option exists but had error, @@ -2829,7 +2829,10 @@ int retropt_bind(struct opt *opts, struct sockaddr *sa, socklen_t *salen, 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) { const char portsep[] = ":"; const char *ends[] = { portsep, NULL }; @@ -2897,9 +2900,10 @@ int retropt_bind(struct opt *opts, #if WITH_UNIX case AF_UNIX: { - bool tight = false; + bool abstract = (feats&2); + bool tight = (feats&1); 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; #endif /* WITH_UNIX */