From owner-freebsd-net@FreeBSD.ORG Mon Oct 1 11:30:10 2012 Return-Path: Delivered-To: freebsd-net@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id C35021065672 for ; Mon, 1 Oct 2012 11:30:10 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id A3B7A8FC16 for ; Mon, 1 Oct 2012 11:30:10 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.5/8.14.5) with ESMTP id q91BUABI046475 for ; Mon, 1 Oct 2012 11:30:10 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.5/8.14.5/Submit) id q91BUAxm046466; Mon, 1 Oct 2012 11:30:10 GMT (envelope-from gnats) Date: Mon, 1 Oct 2012 11:30:10 GMT Message-Id: <201210011130.q91BUAxm046466@freefall.freebsd.org> To: freebsd-net@FreeBSD.org From: Andrey Simonenko Cc: Subject: Re: bin/131567: Update for regression/sockets/unix_cmsg X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Andrey Simonenko List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 01 Oct 2012 11:30:10 -0000 The following reply was made to PR bin/131567; it has been noted by GNATS. From: Andrey Simonenko To: bug-followup@freebsd.org Cc: Subject: Re: bin/131567: Update for regression/sockets/unix_cmsg Date: Mon, 1 Oct 2012 14:20:11 +0300 Updated unix_cmsg: all assert() were removed. Verified correctness of unix_cmsg on 9.1-PRERELEASE and 10-CURRENT. diff -ruNp unix_cmsg.orig/README unix_cmsg/README --- unix_cmsg.orig/README 2006-05-29 21:40:55.000000000 +0300 +++ unix_cmsg/README 2012-10-01 13:47:51.000000000 +0300 @@ -1,7 +1,7 @@ $FreeBSD: src/tools/regression/sockets/unix_cmsg/README,v 1.1 2006/05/29 18:40:55 maxim Exp $ About unix_cmsg -================ +=============== This program is a collection of regression tests for ancillary (control) data for PF_LOCAL sockets (local domain or Unix domain sockets). There @@ -13,8 +13,8 @@ is correct in received message. Sometim messages to Server. It is better to change the owner of unix_cmsg to some safe user -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests -can give correct results for wrong implementation. +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that +check credentials can give correct results for wrong implementation. Available options ================= @@ -24,13 +24,13 @@ Available options -h Output help message and exit. --t +-t socktype Run tests only for the given socket type: "stream" or "dgram". With this option it is possible to run only particular test, not all of them. -z Do not send real control data if possible. Struct cmsghdr{} - should be followed by real control data. It is not clear if + should be followed by real control data. It is not clear whether a sender should give control data in all cases (this is not documented and an arbitrary application can choose anything). @@ -90,6 +90,13 @@ For SOCK_STREAM sockets: message with data and control message with SCM_TIMESTAMP type followed by struct timeval{}. + 6: Check LOCAL_PEERCRED socket option + + This test does not use control data for PF_LOCAL sockets, but can be + implemented here. Client connects to Server. Both Client and Server + verify that credentials of the peer are correct using LOCAL_PEERCRED + socket option. + For SOCK_DGRAM sockets: ---------------------- @@ -110,7 +117,7 @@ For SOCK_DGRAM sockets: structure should contain correct information. 3: Sending cmsgcred, receiving sockcred - + Server creates datagram socket and set socket option LOCAL_CREDS for it. Client sends one message with data and control message with SOCK_CREDS type to Server. Server should receive one message with @@ -124,4 +131,4 @@ For SOCK_DGRAM sockets: message with SCM_TIMESTAMP type followed by struct timeval{}. - Andrey Simonenko -simon@comsys.ntu-kpi.kiev.ua +andreysimonenko@users.sourceforge.net diff -ruNp unix_cmsg.orig/unix_cmsg.c unix_cmsg/unix_cmsg.c --- unix_cmsg.orig/unix_cmsg.c 2006-05-31 11:10:34.000000000 +0300 +++ unix_cmsg/unix_cmsg.c 2012-10-01 13:49:59.000000000 +0300 @@ -27,27 +27,28 @@ #include __FBSDID("$FreeBSD: src/tools/regression/sockets/unix_cmsg/unix_cmsg.c,v 1.2 2006/05/31 08:10:34 maxim Exp $"); -#include +#include #include #include +#include #include +#include #include #include -#include #include #include #include +#include #include #include -#include #include #include +#include #include #include #include #include -#include #include /* @@ -68,7 +69,7 @@ __FBSDID("$FreeBSD: src/tools/regression * * Each function which can block, is run under TIMEOUT, if timeout * occurs, then test function returns -2 or a client process exits - * with nonzero return code. + * with non-zero return code. */ #ifndef LISTENQ @@ -76,46 +77,88 @@ __FBSDID("$FreeBSD: src/tools/regression #endif #ifndef TIMEOUT -# define TIMEOUT 60 +# define TIMEOUT 5 #endif #define EXTRA_CMSG_SPACE 512 /* Memory for not expected control data. */ -static int t_cmsgcred(void), t_sockcred_stream1(void); -static int t_sockcred_stream2(void), t_cmsgcred_sockcred(void); -static int t_sockcred_dgram(void), t_timestamp(void); +static int t_cmsgcred(void); +static int t_sockcred_stream1(void); +static int t_sockcred_stream2(void); +static int t_cmsgcred_sockcred(void); +static int t_sockcred_dgram(void); +static int t_timestamp(void); +static int t_peercred(void); struct test_func { - int (*func)(void); /* Pointer to function. */ - const char *desc; /* Test description. */ + int (*func)(void); /* Pointer to function. */ + const char *desc; /* Test description. */ }; -static struct test_func test_stream_tbl[] = { - { NULL, " 0: All tests" }, - { t_cmsgcred, " 1: Sending, receiving cmsgcred" }, - { t_sockcred_stream1, " 2: Receiving sockcred (listening socket has LOCAL_CREDS)" }, - { t_sockcred_stream2, " 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" }, - { t_cmsgcred_sockcred, " 4: Sending cmsgcred, receiving sockcred" }, - { t_timestamp, " 5: Sending, receiving timestamp" }, - { NULL, NULL } +static const struct test_func test_stream_tbl[] = { + { + .func = NULL, + .desc = "All tests" + }, + { + .func = t_cmsgcred, + .desc = "Sending, receiving cmsgcred" + }, + { + .func = t_sockcred_stream1, + .desc = "Receiving sockcred (listening socket has LOCAL_CREDS)" + }, + { + .func = t_sockcred_stream2, + .desc = "Receiving sockcred (accepted socket has LOCAL_CREDS)" + }, + { + .func = t_cmsgcred_sockcred, + .desc = "Sending cmsgcred, receiving sockcred" + }, + { + .func = t_timestamp, + .desc = "Sending, receiving timestamp" + }, + { + .func = t_peercred, + .desc = "Check LOCAL_PEERCRED socket option" + } }; -static struct test_func test_dgram_tbl[] = { - { NULL, " 0: All tests" }, - { t_cmsgcred, " 1: Sending, receiving cmsgcred" }, - { t_sockcred_dgram, " 2: Receiving sockcred" }, - { t_cmsgcred_sockcred, " 3: Sending cmsgcred, receiving sockcred" }, - { t_timestamp, " 4: Sending, receiving timestamp" }, - { NULL, NULL } +#define TEST_STREAM_TBL_SIZE \ + (sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0])) + +static const struct test_func test_dgram_tbl[] = { + { + .func = NULL, + .desc = "All tests" + }, + { + .func = t_cmsgcred, + .desc = "Sending, receiving cmsgcred" + }, + { + .func = t_sockcred_dgram, + .desc = "Receiving sockcred" + }, + { + .func = t_cmsgcred_sockcred, + .desc = "Sending cmsgcred, receiving sockcred" + }, + { + .func = t_timestamp, + .desc = "Sending, receiving timestamp" + } }; -#define TEST_STREAM_NO_MAX (sizeof(test_stream_tbl) / sizeof(struct test_func) - 2) -#define TEST_DGRAM_NO_MAX (sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2) +#define TEST_DGRAM_TBL_SIZE \ + (sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0])) -static const char *myname = "SERVER"; /* "SERVER" or "CLIENT" */ +static const char *myname; /* "SERVER" or "CLIENT" */ -static int debug = 0; /* 1, if -d. */ -static int no_control_data = 0; /* 1, if -z. */ +static bool debug = false; /* -d */ +static bool no_control_data = false;/* -z */ static u_int nfailed = 0; /* Number of failed tests. */ @@ -131,17 +174,11 @@ static char ipc_message[] = "hello"; static struct sockaddr_un servaddr; /* Server address. */ -static sigjmp_buf env_alrm; - static uid_t my_uid; static uid_t my_euid; static gid_t my_gid; static gid_t my_egid; -/* - * my_gids[0] is EGID, next items are supplementary GIDs, - * my_ngids determines valid items in my_gids array. - */ static gid_t my_gids[NGROUPS_MAX]; static int my_ngids; @@ -150,38 +187,35 @@ static pid_t client_pid; /* PID of fork #define dbgmsg(x) do { \ if (debug) \ logmsgx x ; \ -} while (/* CONSTCOND */0) +} while (0) static void logmsg(const char *, ...) __printflike(1, 2); static void logmsgx(const char *, ...) __printflike(1, 2); static void output(const char *, ...) __printflike(1, 2); -extern char *__progname; /* The name of program. */ - /* * Output the help message (-h switch). */ static void -usage(int quick) +usage(bool verbose) { - const struct test_func *test_func; + u_int i; - fprintf(stderr, "Usage: %s [-dhz] [-t ] [testno]\n", - __progname); - if (quick) + fprintf(stderr, "usage: %s [-dhz] [-t socktype] [testno]\n", + getprogname()); + if (!verbose) return; fprintf(stderr, "\n Options are:\n\ -d\t\t\tOutput debugging information\n\ -h\t\t\tOutput this help message and exit\n\ - -t \t\tRun test only for the given socket type:\n\ -\t\t\tstream or dgram\n\ + -t socktype\t\tRun test only for socket type: stream or dgram\n\ -z\t\t\tDo not send real control data if possible\n\n"); fprintf(stderr, " Available tests for stream sockets:\n"); - for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func) - fprintf(stderr, " %s\n", test_func->desc); + for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i) + fprintf(stderr, " %u: %s\n", i, test_stream_tbl[i].desc); fprintf(stderr, "\n Available tests for datagram sockets:\n"); - for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func) - fprintf(stderr, " %s\n", test_func->desc); + for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i) + fprintf(stderr, " %u: %s\n", i, test_dgram_tbl[i].desc); } /* @@ -195,7 +229,7 @@ output(const char *format, ...) va_start(ap, format); if (vsnprintf(buf, sizeof(buf), format, ap) < 0) - err(EX_SOFTWARE, "output: vsnprintf failed"); + err(EXIT_FAILURE, "output: vsnprintf failed"); write(STDOUT_FILENO, buf, strlen(buf)); va_end(ap); } @@ -210,18 +244,16 @@ logmsg(const char *format, ...) va_list ap; int errno_save; - errno_save = errno; /* Save errno. */ - + errno_save = errno; va_start(ap, format); if (vsnprintf(buf, sizeof(buf), format, ap) < 0) - err(EX_SOFTWARE, "logmsg: vsnprintf failed"); + err(EXIT_FAILURE, "logmsg: vsnprintf failed"); if (errno_save == 0) output("%s: %s\n", myname, buf); else output("%s: %s: %s\n", myname, buf, strerror(errno_save)); va_end(ap); - - errno = errno_save; /* Restore errno. */ + errno = errno_save; } /* @@ -235,33 +267,47 @@ logmsgx(const char *format, ...) va_start(ap, format); if (vsnprintf(buf, sizeof(buf), format, ap) < 0) - err(EX_SOFTWARE, "logmsgx: vsnprintf failed"); + err(EXIT_FAILURE, "logmsgx: vsnprintf failed"); output("%s: %s\n", myname, buf); va_end(ap); } /* - * Run tests from testno1 to testno2. + * Run tests for the given socket type. */ static int -run_tests(u_int testno1, u_int testno2) +run_tests(int type, u_int testno1) { - const struct test_func *test_func; - u_int i, nfailed1; + const struct test_func *tf; + u_int i, nfailed1, testno2; - output("Running tests for %s sockets:\n", sock_type_str); - test_func = (sock_type == SOCK_STREAM ? - test_stream_tbl : test_dgram_tbl) + testno1; + sock_type = type; + if (type == SOCK_STREAM) { + sock_type_str = "SOCK_STREAM"; + tf = test_stream_tbl; + i = TEST_STREAM_TBL_SIZE - 1; + } else { + sock_type_str = "SOCK_DGRAM"; + tf = test_dgram_tbl; + i = TEST_DGRAM_TBL_SIZE - 1; + } + if (testno1 == 0) { + testno1 = 1; + testno2 = i; + } else + testno2 = testno1; + output("Running tests for %s sockets:\n", sock_type_str); nfailed1 = 0; - for (i = testno1; i <= testno2; ++test_func, ++i) { - output(" %s\n", test_func->desc); - switch (test_func->func()) { + for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) { + output(" %u: %s\n", i, tf->desc); + switch (tf->func()) { case -1: ++nfailed1; break; case -2: - logmsgx("some system error occurred, exiting"); + logmsgx("some system error or timeout occurred, " + "exiting"); return (-1); } } @@ -284,181 +330,222 @@ run_tests(u_int testno1, u_int testno2) return (0); } -/* ARGSUSED */ +/* + * Initialize signals handlers. + */ static void -sig_alrm(int signo __unused) +sig_init(void) { - siglongjmp(env_alrm, 1); + struct sigaction sigact; + + sigact.sa_handler = SIG_IGN; + sigact.sa_flags = 0; + sigemptyset(&sigact.sa_mask); + if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) + err(EXIT_FAILURE, "sigaction(SIGPIPE)"); } /* - * Initialize signals handlers. + * Output this process UID, GID, groups from getgroups(), EUID and EGID. */ static void -sig_init(void) +show_my_id(void) { - struct sigaction sa; + int i; - sa.sa_handler = SIG_IGN; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) - err(EX_OSERR, "sigaction(SIGPIPE)"); - - sa.sa_handler = sig_alrm; - if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0) - err(EX_OSERR, "sigaction(SIGALRM)"); + if (!debug) + return; + logmsgx("UID %lu, GID %lu, groups from getgroups():", + (u_long)my_uid, (u_long)my_gid); + for (i = 0; i < my_ngids; ++i) + logmsgx(" GID %lu", (u_long)my_gids[i]); + logmsgx("EUID %lu, EGID %lu", (u_long)my_euid, (u_long)my_egid); +} + +static int +fork_client(void) +{ + client_pid = fork(); + if (client_pid == 0) + myname = "CLIENT"; + else if (client_pid == (pid_t)-1) { + logmsg("fork"); + return (-1); + } + return (0); } int main(int argc, char *argv[]) { const char *errstr; - int opt, dgramflag, streamflag; - u_int testno1, testno2; + u_int testno; + int opt, rv; + bool dgramflag, streamflag; - dgramflag = streamflag = 0; + dgramflag = streamflag = false; while ((opt = getopt(argc, argv, "dht:z")) != -1) switch (opt) { case 'd': - debug = 1; + debug = true; break; case 'h': - usage(0); - return (EX_OK); + usage(true); + return (EXIT_SUCCESS); case 't': if (strcmp(optarg, "stream") == 0) - streamflag = 1; + streamflag = true; else if (strcmp(optarg, "dgram") == 0) - dgramflag = 1; + dgramflag = true; else - errx(EX_USAGE, "wrong socket type in -t option"); + errx(EXIT_FAILURE, "option -t: " + "wrong socket type"); break; case 'z': - no_control_data = 1; + no_control_data = true; break; case '?': default: - usage(1); - return (EX_USAGE); + usage(false); + return (EXIT_FAILURE); } if (optind < argc) { if (optind + 1 != argc) - errx(EX_USAGE, "too many arguments"); - testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr); + errx(EXIT_FAILURE, "too many arguments"); + testno = strtonum(argv[optind], 0, UINT_MAX, &errstr); if (errstr != NULL) - errx(EX_USAGE, "wrong test number: %s", errstr); + errx(EXIT_FAILURE, "wrong test number: %s", errstr); } else - testno1 = 0; + testno = 0; - if (dgramflag == 0 && streamflag == 0) - dgramflag = streamflag = 1; + if (!dgramflag && !streamflag) + dgramflag = streamflag = true; - if (dgramflag && streamflag && testno1 != 0) - errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets"); + if (dgramflag && streamflag && testno != 0) + errx(EXIT_FAILURE, "you can use particular test, only " + "with datagram or stream sockets"); if (streamflag) { - if (testno1 > TEST_STREAM_NO_MAX) - errx(EX_USAGE, "given test %u for stream sockets does not exist", - testno1); + if (testno >= TEST_STREAM_TBL_SIZE) + errx(EXIT_FAILURE, "given test %u for stream " + "sockets does not exist", testno); } else { - if (testno1 > TEST_DGRAM_NO_MAX) - errx(EX_USAGE, "given test %u for datagram sockets does not exist", - testno1); + if (testno >= TEST_DGRAM_TBL_SIZE) + errx(EXIT_FAILURE, "given test %u for datagram " + "sockets does not exist", testno); } my_uid = getuid(); my_euid = geteuid(); my_gid = getgid(); my_egid = getegid(); - switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) { + my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids); + switch (my_ngids) { case -1: - err(EX_SOFTWARE, "getgroups"); + err(EXIT_FAILURE, "getgroups"); /* NOTREACHED */ case 0: - errx(EX_OSERR, "getgroups returned 0 groups"); + errx(EXIT_FAILURE, "getgroups returned no groups"); } sig_init(); if (mkdtemp(tempdir) == NULL) - err(EX_OSERR, "mkdtemp"); + err(EXIT_FAILURE, "mkdtemp"); - if (streamflag) { - sock_type = SOCK_STREAM; - sock_type_str = "SOCK_STREAM"; - if (testno1 == 0) { - testno1 = 1; - testno2 = TEST_STREAM_NO_MAX; - } else - testno2 = testno1; - if (run_tests(testno1, testno2) < 0) - goto failed; - testno1 = 0; - } - - if (dgramflag) { - sock_type = SOCK_DGRAM; - sock_type_str = "SOCK_DGRAM"; - if (testno1 == 0) { - testno1 = 1; - testno2 = TEST_DGRAM_NO_MAX; - } else - testno2 = testno1; - if (run_tests(testno1, testno2) < 0) - goto failed; - } + myname = "SERVER"; + rv = EXIT_SUCCESS; + show_my_id(); + if (streamflag) + if (run_tests(SOCK_STREAM, testno) < 0) + rv = EXIT_FAILURE; + if (dgramflag && rv == EXIT_SUCCESS) + if (run_tests(SOCK_DGRAM, testno) < 0) + rv = EXIT_FAILURE; if (rmdir(tempdir) < 0) { logmsg("rmdir(%s)", tempdir); - return (EX_OSERR); + rv = EXIT_FAILURE; } - return (nfailed ? EX_OSERR : EX_OK); + return (nfailed > 0 ? EXIT_FAILURE : rv); +} -failed: - if (rmdir(tempdir) < 0) - logmsg("rmdir(%s)", tempdir); - return (EX_OSERR); +/* + * Close socket descriptor, if sock_path is not equal to NULL, + * then unlink the given path. + */ +static int +close_socket(const char *sock_path, int fd) +{ + int rv; + + if (close(fd) < 0) { + logmsg("close_socket: close"); + rv = -1; + } else + rv = 0; + if (sock_path != NULL) + if (unlink(sock_path) < 0) { + logmsg("close_socket: unlink(%s)", sock_path); + rv = -1; + } + return (rv); } /* * Create PF_LOCAL socket, if sock_path is not equal to NULL, then - * bind() it. Return socket address in addr. Return file descriptor + * bind() it. Return socket address in *un. Return file descriptor * or -1 if some error occurred. */ static int -create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr) +create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *un) { - int rv, fd; + struct timeval tv; + int fd; - if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) { - logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str); + fd = socket(PF_LOCAL, sock_type, 0); + if (fd < 0) { + logmsg("create_socket: socket(PF_LOCAL, %s, 0)", + sock_type_str); return (-1); } + tv.tv_sec = TIMEOUT; + tv.tv_usec = 0; + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 || + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { + logmsg("create_socket: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)"); + goto failed; + } + if (sock_path != NULL) { - if ((rv = snprintf(sock_path, sock_path_len, "%s/%s", - tempdir, myname)) < 0) { + int rv; + + rv = snprintf(sock_path, sock_path_len, "%s/%s", tempdir, + myname); + if (rv < 0) { logmsg("create_socket: snprintf failed"); goto failed; } if ((size_t)rv >= sock_path_len) { - logmsgx("create_socket: too long path name for given buffer"); + logmsgx("create_socket: too long path name for " + "given buffer"); goto failed; } - - memset(addr, 0, sizeof(addr)); - addr->sun_family = AF_LOCAL; - if (strlen(sock_path) >= sizeof(addr->sun_path)) { - logmsgx("create_socket: too long path name (>= %lu) for local domain socket", - (u_long)sizeof(addr->sun_path)); + if (strlen(sock_path) >= sizeof(un->sun_path)) { + logmsgx("create_socket: too long path name (>= %zu) " + "for local domain socket", sizeof(un->sun_path)); goto failed; } - strcpy(addr->sun_path, sock_path); - if (bind(fd, (struct sockaddr *)addr, SUN_LEN(addr)) < 0) { + memset(un, 0, sizeof(un)); + un->sun_family = PF_LOCAL; + strcpy(un->sun_path, sock_path); + un->sun_len = SUN_LEN(un); + + if (bind(fd, (struct sockaddr *)un, un->sun_len) < 0) { logmsg("create_socket: bind(%s)", sock_path); goto failed; } @@ -473,43 +560,49 @@ failed: } /* - * Call create_socket() for server listening socket. - * Return socket descriptor or -1 if some error occurred. + * Create server socket. */ static int create_server_socket(void) { - return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr)); -} + int fd; -/* - * Create unbound socket. - */ -static int -create_unbound_socket(void) -{ - return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL)); + fd = create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr); + if (fd < 0) + return (-1); + + if (sock_type == SOCK_STREAM) { + int val; + + if (listen(fd, LISTENQ) < 0) { + logmsg("create_server_socket: listen"); + goto failed; + } + val = fcntl(fd, F_GETFL, 0); + if (val < 0) { + logmsg("create_server_socket: fcntl(F_GETFL)"); + goto failed; + } + if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) { + logmsg("create_server_socket: fcntl(F_SETFL)"); + goto failed; + } + } + + return (fd); + +failed: + (void)close_socket(serv_sock_path, fd); + return (-1); } /* - * Close socket descriptor, if sock_path is not equal to NULL, - * then unlink the given path. + * Create client socket. */ static int -close_socket(const char *sock_path, int fd) +create_client_socket(void) { - int error = 0; - - if (close(fd) < 0) { - logmsg("close_socket: close"); - error = -1; - } - if (sock_path != NULL) - if (unlink(sock_path) < 0) { - logmsg("close_socket: unlink(%s)", sock_path); - error = -1; - } - return (error); + return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL)); } /* @@ -524,7 +617,7 @@ connect_server(int fd) * If PF_LOCAL listening socket's queue is full, then connect() * returns ECONNREFUSED immediately, do not need timeout. */ - if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { + if (connect(fd, (struct sockaddr *)&servaddr, servaddr.sun_len) < 0) { logmsg("connect_server: connect(%s)", serv_sock_path); return (-1); } @@ -533,34 +626,23 @@ connect_server(int fd) } /* - * sendmsg() with timeout. + * sendmsg() wrapper. */ static int -sendmsg_timeout(int fd, struct msghdr *msg, size_t n) +send_message(int fd, const struct msghdr *msg, size_t n) { ssize_t nsent; - dbgmsg(("sending %lu bytes", (u_long)n)); - - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path); - return (-1); - } - - (void)alarm(TIMEOUT); + dbgmsg(("sending %zu bytes", n)); nsent = sendmsg(fd, msg, 0); - - (void)alarm(0); - if (nsent < 0) { - logmsg("sendmsg_timeout: sendmsg"); + logmsg("send_message: sendmsg"); return (-1); } - if ((size_t)nsent != n) { - logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes", - (long)nsent, (u_long)n); + logmsgx("send_message: sendmsg: short send: " + "%zd of %zu bytes", nsent, n); return (-1); } @@ -568,63 +650,73 @@ sendmsg_timeout(int fd, struct msghdr *m } /* - * accept() with timeout. + * accept() wrapper. */ static int -accept_timeout(int listenfd) +accept_connection(int listenfd) { - int fd; + fd_set rset; + struct timeval tv; + int fd, rv, val; dbgmsg(("accepting connection")); - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("accept_timeout: cannot accept connection (timeout)"); + FD_ZERO(&rset); + FD_SET(listenfd, &rset); + tv.tv_sec = TIMEOUT; + tv.tv_usec = 0; + rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv); + if (rv < 0) { + logmsg("accept_connection: select"); + return (-1); + } + if (rv == 0) { + logmsgx("accept_connection: select timeout"); return (-1); } - - (void)alarm(TIMEOUT); fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL); - - (void)alarm(0); - if (fd < 0) { - logmsg("accept_timeout: accept"); + logmsg("accept_connection: accept"); return (-1); } + val = fcntl(fd, F_GETFL, 0); + if (val < 0) { + logmsg("accept_connection: fcntl(F_GETFL)"); + goto failed; + } + if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) { + logmsg("accept_connection: fcntl(F_SETFL)"); + goto failed; + } + return (fd); + +failed: + if (close(fd) < 0) + logmsg("accept_connection: close"); + return (-1); } /* - * recvmsg() with timeout. + * recvmsg() wrapper. */ static int -recvmsg_timeout(int fd, struct msghdr *msg, size_t n) +recv_message(int fd, struct msghdr *msg, size_t n) { ssize_t nread; - dbgmsg(("receiving %lu bytes", (u_long)n)); - - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("recvmsg_timeout: cannot receive message (timeout)"); - return (-1); - } - - (void)alarm(TIMEOUT); + dbgmsg(("receiving %zu bytes", n)); nread = recvmsg(fd, msg, MSG_WAITALL); - - (void)alarm(0); - if (nread < 0) { - logmsg("recvmsg_timeout: recvmsg"); + logmsg("recv_message: recvmsg"); return (-1); } - if ((size_t)nread != n) { - logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes", - (long)nread, (u_long)n); + logmsgx("recv_message: recvmsg: short read: " + "%zd of %zu bytes", nread, n); return (-1); } @@ -632,7 +724,7 @@ recvmsg_timeout(int fd, struct msghdr *m } /* - * Wait for synchronization message (1 byte) with timeout. + * Wait for synchronization message (1 byte). */ static int sync_recv(int fd) @@ -642,25 +734,13 @@ sync_recv(int fd) dbgmsg(("waiting for sync message")); - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("sync_recv: cannot receive sync message (timeout)"); - return (-1); - } - - (void)alarm(TIMEOUT); - nread = read(fd, &buf, 1); - - (void)alarm(0); - if (nread < 0) { logmsg("sync_recv: read"); return (-1); } - if (nread != 1) { - logmsgx("sync_recv: read: short read: %ld of 1 byte", - (long)nread); + logmsgx("sync_recv: read: short read: %zd of 1 byte", nread); return (-1); } @@ -668,7 +748,7 @@ sync_recv(int fd) } /* - * Send synchronization message (1 byte) with timeout. + * Send synchronization message (1 byte). */ static int sync_send(int fd) @@ -677,25 +757,13 @@ sync_send(int fd) dbgmsg(("sending sync message")); - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("sync_send: cannot send sync message (timeout)"); - return (-1); - } - - (void)alarm(TIMEOUT); - nsent = write(fd, "", 1); - - (void)alarm(0); - if (nsent < 0) { logmsg("sync_send: write"); return (-1); } - if (nsent != 1) { - logmsgx("sync_send: write: short write: %ld of 1 byte", - (long)nsent); + logmsgx("sync_send: write: short write: %zd of 1 byte", nsent); return (-1); } @@ -711,36 +779,29 @@ wait_client(void) int status; pid_t pid; - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)", - (long)client_pid); - return (-1); - } - - (void)alarm(TIMEOUT); + dbgmsg(("waiting for a client")); pid = waitpid(client_pid, &status, 0); - - (void)alarm(0); - if (pid == (pid_t)-1) { logmsg("wait_client: waitpid"); return (-1); } if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) { - logmsgx("wait_client: exit status of client PID %ld is %d", - (long)client_pid, WEXITSTATUS(status)); + if (WEXITSTATUS(status) != EXIT_SUCCESS) { + logmsgx("wait_client: exit status of client PID %ld " + "is %d", (long)client_pid, WEXITSTATUS(status)); return (-1); } } else { if (WIFSIGNALED(status)) - logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s", - (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : ""); + logmsgx("wait_client: abnormal termination of client " + "PID %ld, signal %d%s", (long)client_pid, + WTERMSIG(status), WCOREDUMP(status) ? + " (core file generated)" : ""); else - logmsgx("wait_client: termination of client PID %ld, unknown status", - (long)client_pid); + logmsgx("wait_client: termination of client PID %ld, " + "unknown status", (long)client_pid); return (-1); } @@ -748,28 +809,27 @@ wait_client(void) } /* - * Check if n supplementary GIDs in gids are correct. (my_gids + 1) - * has (my_ngids - 1) supplementary GIDs of current process. + * Check whether n GIDs in gids are correct. */ static int check_groups(const gid_t *gids, int n) { + int i, j, rv; char match[NGROUPS_MAX] = { 0 }; - int error, i, j; - if (n != my_ngids - 1) { - logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)", - n, my_ngids - 1); - error = -1; + if (n != my_ngids) { + logmsgx("wrong number of groups %d != %d (returned from " + "getgroups())", n, my_ngids); + rv = -1; } else - error = 0; + rv = 0; for (i = 0; i < n; ++i) { - for (j = 1; j < my_ngids; ++j) { + for (j = 0; j < my_ngids; ++j) { if (gids[i] == my_gids[j]) { if (match[j]) { logmsgx("duplicated GID %lu", (u_long)gids[i]); - error = -1; + rv = -1; } else match[j] = 1; break; @@ -777,15 +837,15 @@ check_groups(const gid_t *gids, int n) } if (j == my_ngids) { logmsgx("unexpected GID %lu", (u_long)gids[i]); - error = -1; + rv = -1; } } - for (j = 1; j < my_ngids; ++j) + for (j = 0; j < my_ngids; ++j) if (match[j] == 0) { - logmsgx("did not receive supplementary GID %u", my_gids[j]); - error = -1; + logmsgx("did not receive GID %lu", (u_long)my_gids[j]); + rv = -1; } - return (error); + return (rv); } /* @@ -802,16 +862,17 @@ t_cmsgcred_client(u_int n) struct msghdr msg; struct iovec iov[1]; struct cmsghdr *cmptr; - int fd; u_int i; + int fd, rv; - assert(n == 1 || n == 2); + rv = EXIT_FAILURE; - if ((fd = create_unbound_socket()) < 0) + fd = create_client_socket(); + if (fd < 0) goto failed; if (connect_server(fd) < 0) - goto failed_close; + goto done; iov[0].iov_base = ipc_message; iov[0].iov_len = IPC_MESSAGE_SIZE; @@ -834,20 +895,16 @@ t_cmsgcred_client(u_int n) for (i = 0; i < n; ++i) { dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i, (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len)); - if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0) - goto failed_close; + if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0) + goto done; } - if (close_socket((const char *)NULL, fd) < 0) - goto failed; - - _exit(0); - -failed_close: - (void)close_socket((const char *)NULL, fd); - + rv = EXIT_SUCCESS; +done: + if (close_socket((char *)NULL, fd)) + rv = EXIT_FAILURE; failed: - _exit(1); + _exit(rv); } /* @@ -858,28 +915,27 @@ failed: static int t_cmsgcred_server(int fd1) { - char buf[IPC_MESSAGE_SIZE]; union { struct cmsghdr cm; - char control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE]; + char control[CMSG_SPACE(sizeof(struct cmsgcred)) + + EXTRA_CMSG_SPACE]; } control_un; struct msghdr msg; struct iovec iov[1]; struct cmsghdr *cmptr; - const struct cmsgcred *cmcredptr; - socklen_t controllen; - int error, error2, fd2; + const struct cmsgcred *cmcred; u_int i; + int fd2, rv; + char buf[IPC_MESSAGE_SIZE]; if (sock_type == SOCK_STREAM) { - if ((fd2 = accept_timeout(fd1)) < 0) + fd2 = accept_connection(fd1); + if (fd2 < 0) return (-2); } else fd2 = fd1; - error = 0; - - controllen = sizeof(control_un.control); + rv = 0; for (i = 0; i < 2; ++i) { iov[0].iov_base = buf; @@ -890,29 +946,31 @@ t_cmsgcred_server(int fd1) msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = control_un.control; - msg.msg_controllen = controllen; + msg.msg_controllen = sizeof(control_un.control); msg.msg_flags = 0; - controllen = CMSG_SPACE(sizeof(struct cmsgcred)); - - if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0) - goto failed; + if (recv_message(fd2, &msg, sizeof(buf)) < 0) { + rv = -2; + break; + } if (msg.msg_flags & MSG_CTRUNC) { - logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on", - i); - goto next_error; + logmsgx("#%u control data was truncated, MSG_CTRUNC " + "flag is on", i); + goto next; } if (msg.msg_controllen < sizeof(struct cmsghdr)) { - logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))", - i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr)); - goto next_error; + logmsgx("#%u msg_controllen %u < %zu " + "(sizeof(struct cmsghdr))", i, + (u_int)msg.msg_controllen, sizeof(struct cmsghdr)); + goto next; } - if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) { + cmptr = CMSG_FIRSTHDR(&msg); + if (cmptr == NULL) { logmsgx("CMSG_FIRSTHDR is NULL"); - goto next_error; + goto next; } dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i, @@ -921,142 +979,120 @@ t_cmsgcred_server(int fd1) if (cmptr->cmsg_level != SOL_SOCKET) { logmsgx("#%u cmsg_level %d != SOL_SOCKET", i, cmptr->cmsg_level); - goto next_error; + goto next; } if (cmptr->cmsg_type != SCM_CREDS) { logmsgx("#%u cmsg_type %d != SCM_CREDS", i, cmptr->cmsg_type); - goto next_error; + goto next; } if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) { - logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))", - i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred))); - goto next_error; + logmsgx("#%u cmsg_len %u != %zu " + "(CMSG_LEN(sizeof(struct cmsgcred))", i, + (u_int)cmptr->cmsg_len, + CMSG_LEN(sizeof(struct cmsgcred))); + goto next; } - cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr); + cmcred = (struct cmsgcred *)CMSG_DATA(cmptr); - error2 = 0; - if (cmcredptr->cmcred_pid != client_pid) { + if (cmcred->cmcred_pid != client_pid) { logmsgx("#%u cmcred_pid %ld != %ld (PID of client)", - i, (long)cmcredptr->cmcred_pid, (long)client_pid); - error2 = 1; + i, (long)cmcred->cmcred_pid, (long)client_pid); + goto next; } - if (cmcredptr->cmcred_uid != my_uid) { - logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)", - i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid); - error2 = 1; - } - if (cmcredptr->cmcred_euid != my_euid) { - logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)", - i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid); - error2 = 1; - } - if (cmcredptr->cmcred_gid != my_gid) { - logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)", - i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid); - error2 = 1; + if (cmcred->cmcred_uid != my_uid) { + logmsgx("#%u cmcred_uid %lu != %lu (UID of current " + "process)", i, (u_long)cmcred->cmcred_uid, + (u_long)my_uid); + goto next; + } + if (cmcred->cmcred_euid != my_euid) { + logmsgx("#%u cmcred_euid %lu != %lu (EUID of current " + "process)", i, (u_long)cmcred->cmcred_euid, + (u_long)my_euid); + goto next; + } + if (cmcred->cmcred_gid != my_gid) { + logmsgx("#%u cmcred_gid %lu != %lu (GID of current " + "process)", i, (u_long)cmcred->cmcred_gid, + (u_long)my_gid); + goto next; } - if (cmcredptr->cmcred_ngroups == 0) { + if (cmcred->cmcred_ngroups == 0) { logmsgx("#%u cmcred_ngroups = 0, this is wrong", i); - error2 = 1; - } else { - if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) { - logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)", - i, cmcredptr->cmcred_ngroups, NGROUPS_MAX); - error2 = 1; - } else if (cmcredptr->cmcred_ngroups < 0) { - logmsgx("#%u cmcred_ngroups %d < 0", - i, cmcredptr->cmcred_ngroups); - error2 = 1; - } else { - dbgmsg(("#%u cmcred_ngroups = %d", i, - cmcredptr->cmcred_ngroups)); - if (cmcredptr->cmcred_groups[0] != my_egid) { - logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)", - i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid); - error2 = 1; - } - if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) { - logmsgx("#%u cmcred_groups has wrong GIDs", i); - error2 = 1; - } - } + goto next; } - - if (error2) - goto next_error; - - if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) { - logmsgx("#%u control data has extra header", i); - goto next_error; + if (cmcred->cmcred_ngroups < 0) { + logmsgx("#%u cmcred_ngroups %d < 0", + i, cmcred->cmcred_ngroups); + goto next; + } + if (cmcred->cmcred_ngroups > NGROUPS_MAX) { + logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)", + i, cmcred->cmcred_ngroups, NGROUPS_MAX); + goto next; + } + + dbgmsg(("#%u cmcred_ngroups = %d", i, cmcred->cmcred_ngroups)); + if (cmcred->cmcred_groups[0] != my_egid) { + logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of " + "current process)", i, + (u_long)cmcred->cmcred_groups[0], (u_long)my_egid); + goto next; + } + if (check_groups(cmcred->cmcred_groups, + cmcred->cmcred_ngroups) < 0) { + logmsgx("#%u cmcred_groups has wrong GIDs", i); + goto next; + } + + if (CMSG_NXTHDR(&msg, cmptr) != NULL) { + logmsgx("#%u control data has extra header, " + "this is wrong", i); + goto next; } - continue; -next_error: - error = -1; +next: + rv = -1; } if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) { - logmsg("close"); - return (-2); - } - return (error); - -failed: - if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) - logmsg("close"); - return (-2); + if (close_socket((char *)NULL, fd2) < 0) + rv = -2; + return (rv); } static int t_cmsgcred(void) { - int error, fd; + int fd, rv; - if ((fd = create_server_socket()) < 0) + fd = create_server_socket(); + if (fd < 0) return (-2); - if (sock_type == SOCK_STREAM) - if (listen(fd, LISTENQ) < 0) { - logmsg("listen"); - goto failed; - } + rv = -2; - if ((client_pid = fork()) == (pid_t)-1) { - logmsg("fork"); - goto failed; - } + if (fork_client() < 0) + goto done; if (client_pid == 0) { - myname = "CLIENT"; - if (close_socket((const char *)NULL, fd) < 0) - _exit(1); + if (close_socket((char *)NULL, fd) < 0) + _exit(EXIT_FAILURE); t_cmsgcred_client(2); } - if ((error = t_cmsgcred_server(fd)) == -2) { - (void)wait_client(); - goto failed; - } + rv = t_cmsgcred_server(fd); if (wait_client() < 0) - goto failed; - - if (close_socket(serv_sock_path, fd) < 0) { - logmsgx("close_socket failed"); - return (-2); - } - return (error); - -failed: + rv = -2; +done: if (close_socket(serv_sock_path, fd) < 0) - logmsgx("close_socket failed"); - return (-2); + rv = -2; + return (rv); } /* @@ -1067,20 +1103,21 @@ t_sockcred_client(int type) { struct msghdr msg; struct iovec iov[1]; - int fd; u_int i; + int fd, rv; - assert(type == 0 || type == 1); + rv = EXIT_FAILURE; - if ((fd = create_unbound_socket()) < 0) + fd = create_client_socket(); + if (fd < 0) goto failed; if (connect_server(fd) < 0) - goto failed_close; + goto done; if (type == 1) if (sync_recv(fd) < 0) - goto failed_close; + goto done; iov[0].iov_base = ipc_message; iov[0].iov_len = IPC_MESSAGE_SIZE; @@ -1094,19 +1131,15 @@ t_sockcred_client(int type) msg.msg_flags = 0; for (i = 0; i < 2; ++i) - if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0) - goto failed_close; - - if (close_socket((const char *)NULL, fd) < 0) - goto failed; - - _exit(0); - -failed_close: - (void)close_socket((const char *)NULL, fd); + if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0) + goto done; + rv = EXIT_SUCCESS; +done: + if (close_socket((char *)NULL, fd) < 0) + rv = EXIT_FAILURE; failed: - _exit(1); + _exit(rv); } /* @@ -1119,232 +1152,217 @@ failed: static int t_sockcred_server(int type, int fd1, u_int n) { - char buf[IPC_MESSAGE_SIZE]; union { struct cmsghdr cm; - char control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE]; + char control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + + EXTRA_CMSG_SPACE]; } control_un; struct msghdr msg; struct iovec iov[1]; struct cmsghdr *cmptr; const struct sockcred *sockcred; - int error, error2, fd2, optval; u_int i; + int fd2, rv, optval; + char buf[IPC_MESSAGE_SIZE]; - assert(n == 1 || n == 2); - assert(type == 0 || type == 1); + rv = -2; if (sock_type == SOCK_STREAM) { - if ((fd2 = accept_timeout(fd1)) < 0) + fd2 = accept_connection(fd1); + if (fd2 < 0) return (-2); if (type == 1) { optval = 1; - if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) { - logmsg("setsockopt(LOCAL_CREDS) for accepted socket"); - if (errno == ENOPROTOOPT) { - error = -1; - goto done_close; - } - goto failed; + if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, + sizeof(optval)) < 0) { + logmsg("setsockopt(LOCAL_CREDS) for accepted " + "socket"); + if (errno == ENOPROTOOPT) + rv = -1; + goto done; } if (sync_send(fd2) < 0) - goto failed; + goto done; } } else fd2 = fd1; - error = 0; + rv = 0; for (i = 0; i < n; ++i) { iov[0].iov_base = buf; - iov[0].iov_len = sizeof buf; + iov[0].iov_len = sizeof(buf); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = control_un.control; - msg.msg_controllen = sizeof control_un.control; + msg.msg_controllen = sizeof(control_un.control); msg.msg_flags = 0; - if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0) - goto failed; + if (recv_message(fd2, &msg, sizeof(buf)) < 0) { + rv = -2; + break; + } if (msg.msg_flags & MSG_CTRUNC) { - logmsgx("control data was truncated, MSG_CTRUNC flag is on"); - goto next_error; + logmsgx("control data was truncated, MSG_CTRUNC flag " + "is on"); + goto next; } + dbgmsg(("#%u msg_controllen = %u", i, + (u_int)msg.msg_controllen)); + if (i != 0 && sock_type == SOCK_STREAM) { if (msg.msg_controllen != 0) { - logmsgx("second message has control data, this is wrong for stream sockets"); - goto next_error; + logmsgx("second message has control data, " + "this is wrong for stream sockets"); + goto next; } - dbgmsg(("#%u msg_controllen = %u", i, - (u_int)msg.msg_controllen)); continue; } if (msg.msg_controllen < sizeof(struct cmsghdr)) { - logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))", - i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr)); - goto next_error; + logmsgx("#%u msg_controllen %u < %zu " + "(sizeof(struct cmsghdr))", i, + (u_int)msg.msg_controllen, sizeof(struct cmsghdr)); + goto next; } - if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) { + cmptr = CMSG_FIRSTHDR(&msg); + if (cmptr == NULL) { logmsgx("CMSG_FIRSTHDR is NULL"); - goto next_error; + goto next; } - dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i, - (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len)); + dbgmsg(("#%u cmsg_len = %u", i, (u_int)cmptr->cmsg_len)); if (cmptr->cmsg_level != SOL_SOCKET) { logmsgx("#%u cmsg_level %d != SOL_SOCKET", i, cmptr->cmsg_level); - goto next_error; + goto next; } if (cmptr->cmsg_type != SCM_CREDS) { logmsgx("#%u cmsg_type %d != SCM_CREDS", i, cmptr->cmsg_type); - goto next_error; + goto next; } if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) { - logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))", - i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1))); - goto next_error; + logmsgx("#%u cmsg_len %u != %zu " + "(CMSG_LEN(SOCKCREDSIZE(1)))", i, + (u_int)cmptr->cmsg_len, CMSG_LEN(SOCKCREDSIZE(1))); + goto next; } - sockcred = (const struct sockcred *)CMSG_DATA(cmptr); + sockcred = (struct sockcred *)CMSG_DATA(cmptr); - error2 = 0; if (sockcred->sc_uid != my_uid) { - logmsgx("#%u sc_uid %lu != %lu (UID of current process)", - i, (u_long)sockcred->sc_uid, (u_long)my_uid); - error2 = 1; + logmsgx("#%u sc_uid %lu != %lu (UID of current " + "process)", i, (u_long)sockcred->sc_uid, + (u_long)my_uid); + goto next; } if (sockcred->sc_euid != my_euid) { - logmsgx("#%u sc_euid %lu != %lu (EUID of current process)", - i, (u_long)sockcred->sc_euid, (u_long)my_euid); - error2 = 1; + logmsgx("#%u sc_euid %lu != %lu (EUID of current " + "process)", i, (u_long)sockcred->sc_euid, + (u_long)my_euid); + goto next; } if (sockcred->sc_gid != my_gid) { - logmsgx("#%u sc_gid %lu != %lu (GID of current process)", - i, (u_long)sockcred->sc_gid, (u_long)my_gid); - error2 = 1; + logmsgx("#%u sc_gid %lu != %lu (GID of current " + "process)", i, (u_long)sockcred->sc_gid, + (u_long)my_gid); + goto next; } if (sockcred->sc_egid != my_egid) { - logmsgx("#%u sc_egid %lu != %lu (EGID of current process)", - i, (u_long)sockcred->sc_gid, (u_long)my_egid); - error2 = 1; + logmsgx("#%u sc_egid %lu != %lu (EGID of current " + "process)", i, (u_long)sockcred->sc_gid, + (u_long)my_egid); + goto next; + } + if (sockcred->sc_ngroups == 0) { + logmsgx("#%u sc_ngroups %d = 0, this is wrong", + i, sockcred->sc_ngroups); + goto next; + } + if (sockcred->sc_ngroups < 0) { + logmsgx("#%u sc_ngroups %d < 0", + i, sockcred->sc_ngroups); + goto next; } if (sockcred->sc_ngroups > NGROUPS_MAX) { logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)", i, sockcred->sc_ngroups, NGROUPS_MAX); - error2 = 1; - } else if (sockcred->sc_ngroups < 0) { - logmsgx("#%u sc_ngroups %d < 0", - i, sockcred->sc_ngroups); - error2 = 1; - } else { - dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups)); - if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) { - logmsgx("#%u sc_groups has wrong GIDs", i); - error2 = 1; - } + goto next; } - if (error2) - goto next_error; - - if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) { - logmsgx("#%u control data has extra header, this is wrong", - i); - goto next_error; + dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups)); + if (check_groups(sockcred->sc_groups, + sockcred->sc_ngroups) < 0) { + logmsgx("#%u sc_groups has wrong GIDs", i); + goto next; } + if (CMSG_NXTHDR(&msg, cmptr) != NULL) { + logmsgx("#%u control data has extra header, " + "this is wrong", i); + goto next; + } continue; -next_error: - error = -1; +next: + rv = -1; } - -done_close: - if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) { - logmsg("close"); - return (-2); - } - return (error); - -failed: +done: if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) - logmsg("close"); - return (-2); + if (close_socket((char *)NULL, fd2) < 0) + rv = -2; + return (rv); } static int t_sockcred(int type) { - int error, fd, optval; + int fd, rv, optval; - assert(type == 0 || type == 1); - - if ((fd = create_server_socket()) < 0) + fd = create_server_socket(); + if (fd < 0) return (-2); - if (sock_type == SOCK_STREAM) - if (listen(fd, LISTENQ) < 0) { - logmsg("listen"); - goto failed; - } + rv = -2; if (type == 0) { optval = 1; - if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) { + if (setsockopt(fd, 0, LOCAL_CREDS, &optval, + sizeof(optval)) < 0) { logmsg("setsockopt(LOCAL_CREDS) for %s socket", - sock_type == SOCK_STREAM ? "stream listening" : "datagram"); - if (errno == ENOPROTOOPT) { - error = -1; - goto done_close; - } - goto failed; + sock_type_str); + if (errno == ENOPROTOOPT) + rv = -1; + goto done; } } - if ((client_pid = fork()) == (pid_t)-1) { - logmsg("fork"); - goto failed; - } + if (fork_client() < 0) + goto done; if (client_pid == 0) { - myname = "CLIENT"; - if (close_socket((const char *)NULL, fd) < 0) - _exit(1); + if (close_socket((char *)NULL, fd) < 0) + _exit(EXIT_FAILURE); t_sockcred_client(type); } - if ((error = t_sockcred_server(type, fd, 2)) == -2) { - (void)wait_client(); - goto failed; - } + rv = t_sockcred_server(type, fd, 2); if (wait_client() < 0) - goto failed; - -done_close: - if (close_socket(serv_sock_path, fd) < 0) { - logmsgx("close_socket failed"); - return (-2); - } - return (error); - -failed: + rv = -2; +done: if (close_socket(serv_sock_path, fd) < 0) - logmsgx("close_socket failed"); - return (-2); + rv = -2; + return (rv); } static int @@ -1368,59 +1386,39 @@ t_sockcred_dgram(void) static int t_cmsgcred_sockcred(void) { - int error, fd, optval; + int fd, rv, optval; - if ((fd = create_server_socket()) < 0) + fd = create_server_socket(); + if (fd < 0) return (-2); - if (sock_type == SOCK_STREAM) - if (listen(fd, LISTENQ) < 0) { - logmsg("listen"); - goto failed; - } + rv = -2; optval = 1; - if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) { - logmsg("setsockopt(LOCAL_CREDS) for %s socket", - sock_type == SOCK_STREAM ? "stream listening" : "datagram"); - if (errno == ENOPROTOOPT) { - error = -1; - goto done_close; - } - goto failed; + if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof(optval)) < 0) { + logmsg("setsockopt(LOCAL_CREDS) for %s socket", sock_type_str); + if (errno == ENOPROTOOPT) + rv = -1; + goto done; } - if ((client_pid = fork()) == (pid_t)-1) { - logmsg("fork"); - goto failed; - } + if (fork_client() < 0) + goto done; if (client_pid == 0) { - myname = "CLIENT"; - if (close_socket((const char *)NULL, fd) < 0) - _exit(1); + if (close_socket((char *)NULL, fd) < 0) + _exit(EXIT_FAILURE); t_cmsgcred_client(1); } - if ((error = t_sockcred_server(0, fd, 1)) == -2) { - (void)wait_client(); - goto failed; - } + rv = t_sockcred_server(0, fd, 1); if (wait_client() < 0) - goto failed; - -done_close: - if (close_socket(serv_sock_path, fd) < 0) { - logmsgx("close_socket failed"); - return (-2); - } - return (error); - -failed: + rv = -2; +done: if (close_socket(serv_sock_path, fd) < 0) - logmsgx("close_socket failed"); - return (-2); + rv = -2; + return (rv); } /* @@ -1437,13 +1435,16 @@ t_timestamp_client(void) struct msghdr msg; struct iovec iov[1]; struct cmsghdr *cmptr; - int fd; + int fd, rv; + + rv = EXIT_FAILURE; - if ((fd = create_unbound_socket()) < 0) + fd = create_client_socket(); + if (fd < 0) goto failed; if (connect_server(fd) < 0) - goto failed_close; + goto done; iov[0].iov_base = ipc_message; iov[0].iov_len = IPC_MESSAGE_SIZE; @@ -1454,7 +1455,7 @@ t_timestamp_client(void) msg.msg_iovlen = 1; msg.msg_control = control_un.control; msg.msg_controllen = no_control_data ? - sizeof(struct cmsghdr) :sizeof control_un.control; + sizeof(struct cmsghdr) : sizeof(control_un.control); msg.msg_flags = 0; cmptr = CMSG_FIRSTHDR(&msg); @@ -1466,19 +1467,15 @@ t_timestamp_client(void) dbgmsg(("msg_controllen = %u, cmsg_len = %u", (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len)); - if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0) - goto failed_close; - - if (close_socket((const char *)NULL, fd) < 0) - goto failed; - - _exit(0); - -failed_close: - (void)close_socket((const char *)NULL, fd); + if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0) + goto done; + rv = EXIT_SUCCESS; +done: + if (close_socket((char *)NULL, fd) < 0) + rv = EXIT_FAILURE; failed: - _exit(1); + _exit(rv); } /* @@ -1490,36 +1487,40 @@ t_timestamp_server(int fd1) { union { struct cmsghdr cm; - char control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE]; + char control[CMSG_SPACE(sizeof(struct timeval)) + + EXTRA_CMSG_SPACE]; } control_un; - char buf[IPC_MESSAGE_SIZE]; - int error, fd2; struct msghdr msg; struct iovec iov[1]; struct cmsghdr *cmptr; const struct timeval *timeval; + int fd2, rv; + char buf[IPC_MESSAGE_SIZE]; if (sock_type == SOCK_STREAM) { - if ((fd2 = accept_timeout(fd1)) < 0) + fd2 = accept_connection(fd1); + if (fd2 < 0) return (-2); } else fd2 = fd1; iov[0].iov_base = buf; - iov[0].iov_len = sizeof buf; + iov[0].iov_len = sizeof(buf); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = control_un.control; - msg.msg_controllen = sizeof control_un.control;; + msg.msg_controllen = sizeof(control_un.control); msg.msg_flags = 0; - if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0) - goto failed; + if (recv_message(fd2, &msg, sizeof(buf)) < 0) { + rv = -2; + goto done; + } - error = -1; + rv = -1; if (msg.msg_flags & MSG_CTRUNC) { logmsgx("control data was truncated, MSG_CTRUNC flag is on"); @@ -1527,12 +1528,13 @@ t_timestamp_server(int fd1) } if (msg.msg_controllen < sizeof(struct cmsghdr)) { - logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))", - (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr)); + logmsgx("msg_controllen %u < %zu (sizeof(struct cmsghdr))", + (u_int)msg.msg_controllen, sizeof(struct cmsghdr)); goto done; } - if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) { + cmptr = CMSG_FIRSTHDR(&msg); + if (cmptr == NULL) { logmsgx("CMSG_FIRSTHDR is NULL"); goto done; } @@ -1551,80 +1553,202 @@ t_timestamp_server(int fd1) } if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) { - logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))", - (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval))); + logmsgx("cmsg_len %u != %zu (CMSG_LEN(sizeof(struct timeval))", + (u_int)cmptr->cmsg_len, CMSG_LEN(sizeof(struct timeval))); goto done; } - timeval = (const struct timeval *)CMSG_DATA(cmptr); + timeval = (struct timeval *)CMSG_DATA(cmptr); - dbgmsg(("timeval tv_sec %jd, tv_usec %jd", + dbgmsg(("timeval tv_sec %"PRIdMAX", tv_usec %"PRIdMAX, (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec)); - if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) { - logmsgx("control data has extra header"); + if (CMSG_NXTHDR(&msg, cmptr) != NULL) { + logmsgx("control data has extra header, this is wrong"); goto done; } - error = 0; - + rv = 0; done: if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) { - logmsg("close"); - return (-2); - } - return (error); - -failed: - if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) - logmsg("close"); - return (-2); + if (close_socket((char *)NULL, fd2) < 0) + rv = -2; + return (rv); } static int t_timestamp(void) { - int error, fd; + int fd, rv; - if ((fd = create_server_socket()) < 0) + fd = create_server_socket(); + if (fd < 0) return (-2); - if (sock_type == SOCK_STREAM) - if (listen(fd, LISTENQ) < 0) { - logmsg("listen"); - goto failed; - } + rv = -2; - if ((client_pid = fork()) == (pid_t)-1) { - logmsg("fork"); - goto failed; - } + if (fork_client() < 0) + goto done; if (client_pid == 0) { - myname = "CLIENT"; - if (close_socket((const char *)NULL, fd) < 0) - _exit(1); + if (close_socket((char *)NULL, fd) < 0) + _exit(EXIT_FAILURE); t_timestamp_client(); } - if ((error = t_timestamp_server(fd)) == -2) { - (void)wait_client(); - goto failed; - } + rv = t_timestamp_server(fd); if (wait_client() < 0) + rv = -2; +done: + if (close_socket(serv_sock_path, fd) < 0) + rv = -2; + return (rv); +} + +/* + * Verify that xucred structure has correct credentials. + */ +static int +check_xucred(const struct xucred *xucred, socklen_t len) +{ + if (len != sizeof(*xucred)) { + logmsgx("option value size %zu != %zu (sizeof(struct xucred))", + (size_t)len, sizeof(*xucred)); + return (-1); + } + if (xucred->cr_version != XUCRED_VERSION) { + logmsgx("cr_version %u != %d (XUCRED_VERSION)", + xucred->cr_version, XUCRED_VERSION); + return (-1); + } + if (xucred->cr_uid != my_euid) { + logmsgx("cr_uid %lu != %lu (EUID of current process)", + (u_long)xucred->cr_uid, (u_long)my_euid); + return (-1); + } + if (xucred->cr_ngroups == 0) { + logmsgx("cr_ngroups = 0, this is wrong"); + return (-1); + } + if (xucred->cr_ngroups < 0) { + logmsgx("cr_ngroups < 0, this is wrong"); + return (-1); + } + if (xucred->cr_ngroups > NGROUPS_MAX) { + logmsgx("cr_ngroups %hu > %u (NGROUPS_MAX)", + xucred->cr_ngroups, NGROUPS_MAX); + return (-1); + } + if (xucred->cr_groups[0] != my_egid) { + logmsgx("cr_groups[0] %lu != %lu (EGID of current process)", + (u_long)xucred->cr_groups[0], (u_long)my_egid); + return (-1); + } + if (check_groups(xucred->cr_groups, xucred->cr_ngroups) < 0) { + logmsgx("cr_groups has wrong GIDs"); + return (-1); + } + return (0); +} + +/* + * Connect to server and set LOCAL_PEERCRED socket option for connected + * socket. Verify that credentials of the peer are correct. + */ +static void +t_peercred_client(void) +{ + struct xucred xucred; + socklen_t len; + int fd, rv; + + rv = EXIT_FAILURE; + + fd = create_client_socket(); + if (fd < 0) goto failed; - if (close_socket(serv_sock_path, fd) < 0) { - logmsgx("close_socket failed"); - return (-2); + if (connect_server(fd) < 0) + goto done; + + len = sizeof(xucred); + if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) { + logmsg("getsockopt(LOCAL_PEERCRED)"); + goto done; } - return (error); + if (check_xucred(&xucred, len) < 0) + goto done; + + rv = EXIT_SUCCESS; +done: + if (close_socket((char *)NULL, fd) < 0) + rv = EXIT_FAILURE; failed: + _exit(rv); +} + +/* + * Accept connection from client and set LOCAL_PEERCRED socket option for + * connected socket. Verify that credentials of the peer are correct. + */ +static int +t_peercred_server(int fd1) +{ + struct xucred xucred; + socklen_t len; + int fd2, rv; + + fd2 = accept_connection(fd1); + if (fd2 < 0) + return (-2); + + len = sizeof(xucred); + if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) { + logmsg("getsockopt(LOCAL_PEERCRED)"); + rv = -2; + goto done; + } + + if (check_xucred(&xucred, len) < 0) { + rv = -1; + goto done; + } + + rv = 0; +done: + if (close_socket((char *)NULL, fd2) < 0) + rv = -2; + return (rv); +} + +static int +t_peercred(void) +{ + int fd, rv; + + fd = create_server_socket(); + if (fd < 0) + return (-2); + + rv = -2; + + if (fork_client() < 0) + goto done; + + if (client_pid == 0) { + if (close_socket((char *)NULL, fd) < 0) + _exit(EXIT_FAILURE); + t_peercred_client(); + } + + rv = t_peercred_server(fd); + + if (wait_client() < 0) + rv = -2; +done: if (close_socket(serv_sock_path, fd) < 0) - logmsgx("close_socket failed"); - return (-2); + rv = -2; + return (rv); } diff -ruNp unix_cmsg.orig/unix_cmsg.t unix_cmsg/unix_cmsg.t --- unix_cmsg.orig/unix_cmsg.t 2006-05-29 21:40:55.000000000 +0300 +++ unix_cmsg/unix_cmsg.t 2012-10-01 13:47:17.000000000 +0300 @@ -23,14 +23,15 @@ run() done } -echo "1..15" +echo "1..16" for desc in \ "Sending, receiving cmsgcred" \ - "Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \ - "Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \ - "Sending cmsgcred, receiving sockcred # TODO" \ - "Sending, receiving timestamp" + "Receiving sockcred (listening socket has LOCAL_CREDS)" \ + "Receiving sockcred (accepted socket has LOCAL_CREDS)" \ + "Sending cmsgcred, receiving sockcred" \ + "Sending, receiving timestamp" \ + "Check LOCAL_PEERCRED socket option" do n=`expr ${n} + 1` run ${n} stream "" ${n} "STREAM ${desc}" @@ -48,10 +49,10 @@ do run ${n} dgram "" ${i} "DGRAM ${desc}" done -run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)" -run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO" -run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)" +run 11 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)" +run 12 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO" +run 13 stream -z 5 "STREAM Sending, receiving timestamp (no control data)" -run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)" -run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO" -run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)" +run 14 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)" +run 15 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO" +run 16 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"