Path: blob/main/tools/regression/sockets/unix_cmsg/uc_common.c
96317 views
/*-1* Copyright (c) 2005 Andrey Simonenko2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/cdefs.h>27#include <sys/socket.h>28#include <sys/un.h>29#include <err.h>30#include <fcntl.h>31#include <errno.h>32#include <inttypes.h>33#include <stdarg.h>34#include <stdbool.h>35#include <stdlib.h>36#include <stdio.h>37#include <string.h>38#include <unistd.h>39#include <sys/wait.h>4041#include "uc_common.h"4243#ifndef LISTENQ44# define LISTENQ 145#endif4647#ifndef TIMEOUT48# define TIMEOUT 249#endif5051#define SYNC_SERVER 052#define SYNC_CLIENT 153#define SYNC_RECV 054#define SYNC_SEND 15556#define LOGMSG_SIZE 1285758void59uc_output(const char *format, ...)60{61char buf[LOGMSG_SIZE];62va_list ap;6364va_start(ap, format);65if (vsnprintf(buf, sizeof(buf), format, ap) < 0)66err(EXIT_FAILURE, "output: vsnprintf failed");67write(STDOUT_FILENO, buf, strlen(buf));68va_end(ap);69}7071void72uc_logmsg(const char *format, ...)73{74char buf[LOGMSG_SIZE];75va_list ap;76int errno_save;7778errno_save = errno;79va_start(ap, format);80if (vsnprintf(buf, sizeof(buf), format, ap) < 0)81err(EXIT_FAILURE, "logmsg: vsnprintf failed");82if (errno_save == 0)83uc_output("%s: %s\n", uc_cfg.proc_name, buf);84else85uc_output("%s: %s: %s\n", uc_cfg.proc_name, buf,86strerror(errno_save));87va_end(ap);88errno = errno_save;89}9091void92uc_vlogmsgx(const char *format, va_list ap)93{94char buf[LOGMSG_SIZE];9596if (vsnprintf(buf, sizeof(buf), format, ap) < 0)97err(EXIT_FAILURE, "uc_logmsgx: vsnprintf failed");98uc_output("%s: %s\n", uc_cfg.proc_name, buf);99}100101void102uc_logmsgx(const char *format, ...)103{104va_list ap;105106va_start(ap, format);107uc_vlogmsgx(format, ap);108va_end(ap);109}110111void112uc_dbgmsg(const char *format, ...)113{114va_list ap;115116if (uc_cfg.debug) {117va_start(ap, format);118uc_vlogmsgx(format, ap);119va_end(ap);120}121}122123int124uc_socket_create(void)125{126struct timeval tv;127int fd;128129fd = socket(PF_LOCAL, uc_cfg.sock_type, 0);130if (fd < 0) {131uc_logmsg("socket_create: socket(PF_LOCAL, %s, 0)", uc_cfg.sock_type_str);132return (-1);133}134if (uc_cfg.server_flag)135uc_cfg.serv_sock_fd = fd;136137tv.tv_sec = TIMEOUT;138tv.tv_usec = 0;139if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||140setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {141uc_logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");142goto failed;143}144145if (uc_cfg.server_flag) {146if (bind(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun,147uc_cfg.serv_addr_sun.sun_len) < 0) {148uc_logmsg("socket_create: bind(%s)",149uc_cfg.serv_addr_sun.sun_path);150goto failed;151}152if (uc_cfg.sock_type == SOCK_STREAM) {153int val;154155if (listen(fd, LISTENQ) < 0) {156uc_logmsg("socket_create: listen");157goto failed;158}159val = fcntl(fd, F_GETFL, 0);160if (val < 0) {161uc_logmsg("socket_create: fcntl(F_GETFL)");162goto failed;163}164if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {165uc_logmsg("socket_create: fcntl(F_SETFL)");166goto failed;167}168}169}170171return (fd);172173failed:174if (close(fd) < 0)175uc_logmsg("socket_create: close");176if (uc_cfg.server_flag)177if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0)178uc_logmsg("socket_close: unlink(%s)",179uc_cfg.serv_addr_sun.sun_path);180return (-1);181}182183int184uc_socket_close(int fd)185{186int rv;187188rv = 0;189if (close(fd) < 0) {190uc_logmsg("socket_close: close");191rv = -1;192}193if (uc_cfg.server_flag && fd == uc_cfg.serv_sock_fd)194if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0) {195uc_logmsg("socket_close: unlink(%s)",196uc_cfg.serv_addr_sun.sun_path);197rv = -1;198}199return (rv);200}201202int203uc_socket_connect(int fd)204{205uc_dbgmsg("connect");206207if (connect(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun,208uc_cfg.serv_addr_sun.sun_len) < 0) {209uc_logmsg("socket_connect: connect(%s)", uc_cfg.serv_addr_sun.sun_path);210return (-1);211}212return (0);213}214215int216uc_sync_recv(void)217{218ssize_t ssize;219int fd;220char buf;221222uc_dbgmsg("sync: wait");223224fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV];225226ssize = read(fd, &buf, 1);227if (ssize < 0) {228uc_logmsg("sync_recv: read");229return (-1);230}231if (ssize < 1) {232uc_logmsgx("sync_recv: read %zd of 1 byte", ssize);233return (-1);234}235236uc_dbgmsg("sync: received");237238return (0);239}240241int242uc_sync_send(void)243{244ssize_t ssize;245int fd;246247uc_dbgmsg("sync: send");248249fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND];250251ssize = write(fd, "", 1);252if (ssize < 0) {253uc_logmsg("uc_sync_send: write");254return (-1);255}256if (ssize < 1) {257uc_logmsgx("uc_sync_send: sent %zd of 1 byte", ssize);258return (-1);259}260261return (0);262}263264int265uc_message_send(int fd, const struct msghdr *msghdr)266{267const struct cmsghdr *cmsghdr;268size_t size;269ssize_t ssize;270271size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0;272uc_dbgmsg("send: data size %zu", size);273uc_dbgmsg("send: msghdr.msg_controllen %u",274(u_int)msghdr->msg_controllen);275cmsghdr = CMSG_FIRSTHDR(msghdr);276if (cmsghdr != NULL)277uc_dbgmsg("send: cmsghdr.cmsg_len %u",278(u_int)cmsghdr->cmsg_len);279280ssize = sendmsg(fd, msghdr, 0);281if (ssize < 0) {282uc_logmsg("message_send: sendmsg");283return (-1);284}285if ((size_t)ssize != size) {286uc_logmsgx("message_send: sendmsg: sent %zd of %zu bytes",287ssize, size);288return (-1);289}290291if (!uc_cfg.send_data_flag)292if (uc_sync_send() < 0)293return (-1);294295return (0);296}297298int299uc_message_sendn(int fd, struct msghdr *msghdr)300{301u_int i;302303for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) {304uc_dbgmsg("message #%u", i);305if (uc_message_send(fd, msghdr) < 0)306return (-1);307}308return (0);309}310311int312uc_message_recv(int fd, struct msghdr *msghdr)313{314const struct cmsghdr *cmsghdr;315size_t size;316ssize_t ssize;317318if (!uc_cfg.send_data_flag)319if (uc_sync_recv() < 0)320return (-1);321322size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0;323ssize = recvmsg(fd, msghdr, MSG_WAITALL);324if (ssize < 0) {325uc_logmsg("message_recv: recvmsg");326return (-1);327}328if ((size_t)ssize != size) {329uc_logmsgx("message_recv: recvmsg: received %zd of %zu bytes",330ssize, size);331return (-1);332}333334uc_dbgmsg("recv: data size %zd", ssize);335uc_dbgmsg("recv: msghdr.msg_controllen %u",336(u_int)msghdr->msg_controllen);337cmsghdr = CMSG_FIRSTHDR(msghdr);338if (cmsghdr != NULL)339uc_dbgmsg("recv: cmsghdr.cmsg_len %u",340(u_int)cmsghdr->cmsg_len);341342if (memcmp(uc_cfg.ipc_msg.buf_recv, uc_cfg.ipc_msg.buf_send, size) != 0) {343uc_logmsgx("message_recv: received message has wrong content");344return (-1);345}346347return (0);348}349350int351uc_socket_accept(int listenfd)352{353fd_set rset;354struct timeval tv;355int fd, rv, val;356357uc_dbgmsg("accept");358359FD_ZERO(&rset);360FD_SET(listenfd, &rset);361tv.tv_sec = TIMEOUT;362tv.tv_usec = 0;363rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);364if (rv < 0) {365uc_logmsg("socket_accept: select");366return (-1);367}368if (rv == 0) {369uc_logmsgx("socket_accept: select timeout");370return (-1);371}372373fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);374if (fd < 0) {375uc_logmsg("socket_accept: accept");376return (-1);377}378379val = fcntl(fd, F_GETFL, 0);380if (val < 0) {381uc_logmsg("socket_accept: fcntl(F_GETFL)");382goto failed;383}384if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {385uc_logmsg("socket_accept: fcntl(F_SETFL)");386goto failed;387}388389return (fd);390391failed:392if (close(fd) < 0)393uc_logmsg("socket_accept: close");394return (-1);395}396397int398uc_check_msghdr(const struct msghdr *msghdr, size_t size)399{400if (msghdr->msg_flags & MSG_TRUNC) {401uc_logmsgx("msghdr.msg_flags has MSG_TRUNC");402return (-1);403}404if (msghdr->msg_flags & MSG_CTRUNC) {405uc_logmsgx("msghdr.msg_flags has MSG_CTRUNC");406return (-1);407}408if (msghdr->msg_controllen < size) {409uc_logmsgx("msghdr.msg_controllen %u < %zu",410(u_int)msghdr->msg_controllen, size);411return (-1);412}413if (msghdr->msg_controllen > 0 && size == 0) {414uc_logmsgx("msghdr.msg_controllen %u > 0",415(u_int)msghdr->msg_controllen);416return (-1);417}418return (0);419}420421int422uc_check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size)423{424if (cmsghdr == NULL) {425uc_logmsgx("cmsghdr is NULL");426return (-1);427}428if (cmsghdr->cmsg_level != SOL_SOCKET) {429uc_logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET",430cmsghdr->cmsg_level);431return (-1);432}433if (cmsghdr->cmsg_type != type) {434uc_logmsgx("cmsghdr.cmsg_type %d != %d",435cmsghdr->cmsg_type, type);436return (-1);437}438if (cmsghdr->cmsg_len != CMSG_LEN(size)) {439uc_logmsgx("cmsghdr.cmsg_len %u != %zu",440(u_int)cmsghdr->cmsg_len, CMSG_LEN(size));441return (-1);442}443return (0);444}445446static void447uc_msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data)448{449msghdr->msg_name = NULL;450msghdr->msg_namelen = 0;451if (uc_cfg.send_data_flag) {452iov->iov_base = uc_cfg.server_flag ?453uc_cfg.ipc_msg.buf_recv : uc_cfg.ipc_msg.buf_send;454iov->iov_len = uc_cfg.ipc_msg.buf_size;455msghdr->msg_iov = iov;456msghdr->msg_iovlen = 1;457} else {458msghdr->msg_iov = NULL;459msghdr->msg_iovlen = 0;460}461msghdr->msg_control = cmsg_data;462msghdr->msg_flags = 0;463}464465void466uc_msghdr_init_server(struct msghdr *msghdr, struct iovec *iov,467void *cmsg_data, size_t cmsg_size)468{469uc_msghdr_init_generic(msghdr, iov, cmsg_data);470msghdr->msg_controllen = cmsg_size;471uc_dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ?472msghdr->msg_iov->iov_len : (size_t)0);473uc_dbgmsg("init: msghdr.msg_controllen %u",474(u_int)msghdr->msg_controllen);475}476477void478uc_msghdr_init_client(struct msghdr *msghdr, struct iovec *iov,479void *cmsg_data, size_t cmsg_size, int type, size_t arr_size)480{481struct cmsghdr *cmsghdr;482483uc_msghdr_init_generic(msghdr, iov, cmsg_data);484if (cmsg_data != NULL) {485if (uc_cfg.send_array_flag)486uc_dbgmsg("sending an array");487else488uc_dbgmsg("sending a scalar");489msghdr->msg_controllen = uc_cfg.send_array_flag ?490cmsg_size : CMSG_SPACE(0);491cmsghdr = CMSG_FIRSTHDR(msghdr);492cmsghdr->cmsg_level = SOL_SOCKET;493cmsghdr->cmsg_type = type;494cmsghdr->cmsg_len = CMSG_LEN(uc_cfg.send_array_flag ? arr_size : 0);495} else496msghdr->msg_controllen = 0;497}498499int500uc_client_fork(void)501{502int fd1, fd2;503504if (pipe(uc_cfg.sync_fd[SYNC_SERVER]) < 0 ||505pipe(uc_cfg.sync_fd[SYNC_CLIENT]) < 0) {506uc_logmsg("client_fork: pipe");507return (-1);508}509uc_cfg.client_pid = fork();510if (uc_cfg.client_pid == (pid_t)-1) {511uc_logmsg("client_fork: fork");512return (-1);513}514if (uc_cfg.client_pid == 0) {515uc_cfg.proc_name = "CLIENT";516uc_cfg.server_flag = false;517fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV];518fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND];519} else {520fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND];521fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV];522}523if (close(fd1) < 0 || close(fd2) < 0) {524uc_logmsg("client_fork: close");525return (-1);526}527return (uc_cfg.client_pid != 0);528}529530void531uc_client_exit(int rv)532{533if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||534close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {535uc_logmsg("client_exit: close");536rv = -1;537}538rv = rv == 0 ? EXIT_SUCCESS : -rv;539uc_dbgmsg("exit: code %d", rv);540_exit(rv);541}542543int544uc_client_wait(void)545{546int status;547pid_t pid;548549uc_dbgmsg("waiting for client");550551if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||552close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {553uc_logmsg("client_wait: close");554return (-1);555}556557pid = waitpid(uc_cfg.client_pid, &status, 0);558if (pid == (pid_t)-1) {559uc_logmsg("client_wait: waitpid");560return (-1);561}562563if (WIFEXITED(status)) {564if (WEXITSTATUS(status) != EXIT_SUCCESS) {565uc_logmsgx("client exit status is %d",566WEXITSTATUS(status));567return (-WEXITSTATUS(status));568}569} else {570if (WIFSIGNALED(status))571uc_logmsgx("abnormal termination of client, signal %d%s",572WTERMSIG(status), WCOREDUMP(status) ?573" (core file generated)" : "");574else575uc_logmsgx("termination of client, unknown status");576return (-1);577}578579return (0);580}581582int583uc_check_groups(const char *gid_arr_str, const gid_t *gid_arr,584const char *gid_num_str, int gid_num, bool all_gids)585{586int i;587588for (i = 0; i < gid_num; ++i)589uc_dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]);590591if (all_gids) {592if (gid_num != uc_cfg.proc_cred.gid_num) {593uc_logmsgx("%s %d != %d", gid_num_str, gid_num,594uc_cfg.proc_cred.gid_num);595return (-1);596}597} else {598if (gid_num > uc_cfg.proc_cred.gid_num) {599uc_logmsgx("%s %d > %d", gid_num_str, gid_num,600uc_cfg.proc_cred.gid_num);601return (-1);602}603}604if (memcmp(gid_arr, uc_cfg.proc_cred.gid_arr,605gid_num * sizeof(*gid_arr)) != 0) {606uc_logmsgx("%s content is wrong", gid_arr_str);607for (i = 0; i < gid_num; ++i)608if (gid_arr[i] != uc_cfg.proc_cred.gid_arr[i]) {609uc_logmsgx("%s[%d] %lu != %lu",610gid_arr_str, i, (u_long)gid_arr[i],611(u_long)uc_cfg.proc_cred.gid_arr[i]);612break;613}614return (-1);615}616return (0);617}618619int620uc_check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr)621{622const struct cmsgcred *cmcred;623int rc;624625if (uc_check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(struct cmsgcred)) < 0)626return (-1);627628cmcred = (struct cmsgcred *)CMSG_DATA(cmsghdr);629630uc_dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmcred->cmcred_pid);631uc_dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmcred->cmcred_uid);632uc_dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmcred->cmcred_euid);633uc_dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmcred->cmcred_gid);634uc_dbgmsg("cmsgcred.cmcred_ngroups %d", cmcred->cmcred_ngroups);635636rc = 0;637638if (cmcred->cmcred_pid != uc_cfg.client_pid) {639uc_logmsgx("cmsgcred.cmcred_pid %ld != %ld",640(long)cmcred->cmcred_pid, (long)uc_cfg.client_pid);641rc = -1;642}643if (cmcred->cmcred_uid != uc_cfg.proc_cred.uid) {644uc_logmsgx("cmsgcred.cmcred_uid %lu != %lu",645(u_long)cmcred->cmcred_uid, (u_long)uc_cfg.proc_cred.uid);646rc = -1;647}648if (cmcred->cmcred_euid != uc_cfg.proc_cred.euid) {649uc_logmsgx("cmsgcred.cmcred_euid %lu != %lu",650(u_long)cmcred->cmcred_euid, (u_long)uc_cfg.proc_cred.euid);651rc = -1;652}653if (cmcred->cmcred_gid != uc_cfg.proc_cred.gid) {654uc_logmsgx("cmsgcred.cmcred_gid %lu != %lu",655(u_long)cmcred->cmcred_gid, (u_long)uc_cfg.proc_cred.gid);656rc = -1;657}658if (cmcred->cmcred_ngroups == 0) {659uc_logmsgx("cmsgcred.cmcred_ngroups == 0");660rc = -1;661}662if (cmcred->cmcred_ngroups < 0) {663uc_logmsgx("cmsgcred.cmcred_ngroups %d < 0",664cmcred->cmcred_ngroups);665rc = -1;666}667if (cmcred->cmcred_ngroups > CMGROUP_MAX) {668uc_logmsgx("cmsgcred.cmcred_ngroups %d > %d",669cmcred->cmcred_ngroups, CMGROUP_MAX);670rc = -1;671}672if (cmcred->cmcred_groups[0] != uc_cfg.proc_cred.egid) {673uc_logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)",674(u_long)cmcred->cmcred_groups[0], (u_long)uc_cfg.proc_cred.egid);675rc = -1;676}677if (uc_check_groups("cmsgcred.cmcred_groups", cmcred->cmcred_groups,678"cmsgcred.cmcred_ngroups", cmcred->cmcred_ngroups, false) < 0)679rc = -1;680return (rc);681}682683int684uc_check_scm_creds_sockcred(struct cmsghdr *cmsghdr)685{686const struct sockcred *sc;687int rc;688689if (uc_check_cmsghdr(cmsghdr, SCM_CREDS,690SOCKCREDSIZE(uc_cfg.proc_cred.gid_num)) < 0)691return (-1);692693sc = (struct sockcred *)CMSG_DATA(cmsghdr);694695rc = 0;696697uc_dbgmsg("sockcred.sc_uid %lu", (u_long)sc->sc_uid);698uc_dbgmsg("sockcred.sc_euid %lu", (u_long)sc->sc_euid);699uc_dbgmsg("sockcred.sc_gid %lu", (u_long)sc->sc_gid);700uc_dbgmsg("sockcred.sc_egid %lu", (u_long)sc->sc_egid);701uc_dbgmsg("sockcred.sc_ngroups %d", sc->sc_ngroups);702703if (sc->sc_uid != uc_cfg.proc_cred.uid) {704uc_logmsgx("sockcred.sc_uid %lu != %lu",705(u_long)sc->sc_uid, (u_long)uc_cfg.proc_cred.uid);706rc = -1;707}708if (sc->sc_euid != uc_cfg.proc_cred.euid) {709uc_logmsgx("sockcred.sc_euid %lu != %lu",710(u_long)sc->sc_euid, (u_long)uc_cfg.proc_cred.euid);711rc = -1;712}713if (sc->sc_gid != uc_cfg.proc_cred.gid) {714uc_logmsgx("sockcred.sc_gid %lu != %lu",715(u_long)sc->sc_gid, (u_long)uc_cfg.proc_cred.gid);716rc = -1;717}718if (sc->sc_egid != uc_cfg.proc_cred.egid) {719uc_logmsgx("sockcred.sc_egid %lu != %lu",720(u_long)sc->sc_egid, (u_long)uc_cfg.proc_cred.egid);721rc = -1;722}723if (sc->sc_ngroups == 0) {724uc_logmsgx("sockcred.sc_ngroups == 0");725rc = -1;726}727if (sc->sc_ngroups < 0) {728uc_logmsgx("sockcred.sc_ngroups %d < 0",729sc->sc_ngroups);730rc = -1;731}732if (sc->sc_ngroups != uc_cfg.proc_cred.gid_num) {733uc_logmsgx("sockcred.sc_ngroups %d != %u",734sc->sc_ngroups, uc_cfg.proc_cred.gid_num);735rc = -1;736}737if (uc_check_groups("sockcred.sc_groups", sc->sc_groups,738"sockcred.sc_ngroups", sc->sc_ngroups, true) < 0)739rc = -1;740return (rc);741}742743744