1/*-
2 * Copyright (c) 2005 Andrey Simonenko
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/11/tools/regression/sockets/unix_cmsg/uc_common.c 339066 2018-10-01 17:26:41Z sobomax $");
29
30#include <sys/socket.h>
31#include <sys/un.h>
32#include <err.h>
33#include <fcntl.h>
34#include <errno.h>
35#include <inttypes.h>
36#include <stdarg.h>
37#include <stdbool.h>
38#include <stdlib.h>
39#include <stdio.h>
40#include <string.h>
41#include <unistd.h>
42#include <sys/wait.h>
43
44#include "uc_common.h"
45
46#ifndef LISTENQ
47# define LISTENQ	1
48#endif
49
50#ifndef	TIMEOUT
51# define TIMEOUT	2
52#endif
53
54#define	SYNC_SERVER	0
55#define	SYNC_CLIENT	1
56#define	SYNC_RECV	0
57#define	SYNC_SEND	1
58
59#define	LOGMSG_SIZE	128
60
61void
62uc_output(const char *format, ...)
63{
64	char buf[LOGMSG_SIZE];
65	va_list ap;
66
67	va_start(ap, format);
68	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
69		err(EXIT_FAILURE, "output: vsnprintf failed");
70	write(STDOUT_FILENO, buf, strlen(buf));
71	va_end(ap);
72}
73
74void
75uc_logmsg(const char *format, ...)
76{
77	char buf[LOGMSG_SIZE];
78	va_list ap;
79	int errno_save;
80
81	errno_save = errno;
82	va_start(ap, format);
83	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
84		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
85	if (errno_save == 0)
86		uc_output("%s: %s\n", uc_cfg.proc_name, buf);
87	else
88		uc_output("%s: %s: %s\n", uc_cfg.proc_name, buf,
89		    strerror(errno_save));
90	va_end(ap);
91	errno = errno_save;
92}
93
94void
95uc_vlogmsgx(const char *format, va_list ap)
96{
97	char buf[LOGMSG_SIZE];
98
99	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
100		err(EXIT_FAILURE, "uc_logmsgx: vsnprintf failed");
101	uc_output("%s: %s\n", uc_cfg.proc_name, buf);
102}
103
104void
105uc_logmsgx(const char *format, ...)
106{
107	va_list ap;
108
109	va_start(ap, format);
110	uc_vlogmsgx(format, ap);
111	va_end(ap);
112}
113
114void
115uc_dbgmsg(const char *format, ...)
116{
117	va_list ap;
118
119	if (uc_cfg.debug) {
120		va_start(ap, format);
121		uc_vlogmsgx(format, ap);
122		va_end(ap);
123	}
124}
125
126int
127uc_socket_create(void)
128{
129	struct timeval tv;
130	int fd;
131
132	fd = socket(PF_LOCAL, uc_cfg.sock_type, 0);
133	if (fd < 0) {
134		uc_logmsg("socket_create: socket(PF_LOCAL, %s, 0)", uc_cfg.sock_type_str);
135		return (-1);
136	}
137	if (uc_cfg.server_flag)
138		uc_cfg.serv_sock_fd = fd;
139
140	tv.tv_sec = TIMEOUT;
141	tv.tv_usec = 0;
142	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
143	    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
144		uc_logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
145		goto failed;
146	}
147
148	if (uc_cfg.server_flag) {
149		if (bind(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun,
150		    uc_cfg.serv_addr_sun.sun_len) < 0) {
151			uc_logmsg("socket_create: bind(%s)",
152			    uc_cfg.serv_addr_sun.sun_path);
153			goto failed;
154		}
155		if (uc_cfg.sock_type == SOCK_STREAM) {
156			int val;
157
158			if (listen(fd, LISTENQ) < 0) {
159				uc_logmsg("socket_create: listen");
160				goto failed;
161			}
162			val = fcntl(fd, F_GETFL, 0);
163			if (val < 0) {
164				uc_logmsg("socket_create: fcntl(F_GETFL)");
165				goto failed;
166			}
167			if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
168				uc_logmsg("socket_create: fcntl(F_SETFL)");
169				goto failed;
170			}
171		}
172	}
173
174	return (fd);
175
176failed:
177	if (close(fd) < 0)
178		uc_logmsg("socket_create: close");
179	if (uc_cfg.server_flag)
180		if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0)
181			uc_logmsg("socket_close: unlink(%s)",
182			    uc_cfg.serv_addr_sun.sun_path);
183	return (-1);
184}
185
186int
187uc_socket_close(int fd)
188{
189	int rv;
190
191	rv = 0;
192	if (close(fd) < 0) {
193		uc_logmsg("socket_close: close");
194		rv = -1;
195	}
196	if (uc_cfg.server_flag && fd == uc_cfg.serv_sock_fd)
197		if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0) {
198			uc_logmsg("socket_close: unlink(%s)",
199			    uc_cfg.serv_addr_sun.sun_path);
200			rv = -1;
201		}
202	return (rv);
203}
204
205int
206uc_socket_connect(int fd)
207{
208	uc_dbgmsg("connect");
209
210	if (connect(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun,
211	    uc_cfg.serv_addr_sun.sun_len) < 0) {
212		uc_logmsg("socket_connect: connect(%s)", uc_cfg.serv_addr_sun.sun_path);
213		return (-1);
214	}
215	return (0);
216}
217
218int
219uc_sync_recv(void)
220{
221	ssize_t ssize;
222	int fd;
223	char buf;
224
225	uc_dbgmsg("sync: wait");
226
227	fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV];
228
229	ssize = read(fd, &buf, 1);
230	if (ssize < 0) {
231		uc_logmsg("sync_recv: read");
232		return (-1);
233	}
234	if (ssize < 1) {
235		uc_logmsgx("sync_recv: read %zd of 1 byte", ssize);
236		return (-1);
237	}
238
239	uc_dbgmsg("sync: received");
240
241	return (0);
242}
243
244int
245uc_sync_send(void)
246{
247	ssize_t ssize;
248	int fd;
249
250	uc_dbgmsg("sync: send");
251
252	fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND];
253
254	ssize = write(fd, "", 1);
255	if (ssize < 0) {
256		uc_logmsg("uc_sync_send: write");
257		return (-1);
258	}
259	if (ssize < 1) {
260		uc_logmsgx("uc_sync_send: sent %zd of 1 byte", ssize);
261		return (-1);
262	}
263
264	return (0);
265}
266
267int
268uc_message_send(int fd, const struct msghdr *msghdr)
269{
270	const struct cmsghdr *cmsghdr;
271	size_t size;
272	ssize_t ssize;
273
274	size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0;
275	uc_dbgmsg("send: data size %zu", size);
276	uc_dbgmsg("send: msghdr.msg_controllen %u",
277	    (u_int)msghdr->msg_controllen);
278	cmsghdr = CMSG_FIRSTHDR(msghdr);
279	if (cmsghdr != NULL)
280		uc_dbgmsg("send: cmsghdr.cmsg_len %u",
281		    (u_int)cmsghdr->cmsg_len);
282
283	ssize = sendmsg(fd, msghdr, 0);
284	if (ssize < 0) {
285		uc_logmsg("message_send: sendmsg");
286		return (-1);
287	}
288	if ((size_t)ssize != size) {
289		uc_logmsgx("message_send: sendmsg: sent %zd of %zu bytes",
290		    ssize, size);
291		return (-1);
292	}
293
294	if (!uc_cfg.send_data_flag)
295		if (uc_sync_send() < 0)
296			return (-1);
297
298	return (0);
299}
300
301int
302uc_message_sendn(int fd, struct msghdr *msghdr)
303{
304	u_int i;
305
306	for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) {
307		uc_dbgmsg("message #%u", i);
308		if (uc_message_send(fd, msghdr) < 0)
309			return (-1);
310	}
311	return (0);
312}
313
314int
315uc_message_recv(int fd, struct msghdr *msghdr)
316{
317	const struct cmsghdr *cmsghdr;
318	size_t size;
319	ssize_t ssize;
320
321	if (!uc_cfg.send_data_flag)
322		if (uc_sync_recv() < 0)
323			return (-1);
324
325	size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0;
326	ssize = recvmsg(fd, msghdr, MSG_WAITALL);
327	if (ssize < 0) {
328		uc_logmsg("message_recv: recvmsg");
329		return (-1);
330	}
331	if ((size_t)ssize != size) {
332		uc_logmsgx("message_recv: recvmsg: received %zd of %zu bytes",
333		    ssize, size);
334		return (-1);
335	}
336
337	uc_dbgmsg("recv: data size %zd", ssize);
338	uc_dbgmsg("recv: msghdr.msg_controllen %u",
339	    (u_int)msghdr->msg_controllen);
340	cmsghdr = CMSG_FIRSTHDR(msghdr);
341	if (cmsghdr != NULL)
342		uc_dbgmsg("recv: cmsghdr.cmsg_len %u",
343		    (u_int)cmsghdr->cmsg_len);
344
345	if (memcmp(uc_cfg.ipc_msg.buf_recv, uc_cfg.ipc_msg.buf_send, size) != 0) {
346		uc_logmsgx("message_recv: received message has wrong content");
347		return (-1);
348	}
349
350	return (0);
351}
352
353int
354uc_socket_accept(int listenfd)
355{
356	fd_set rset;
357	struct timeval tv;
358	int fd, rv, val;
359
360	uc_dbgmsg("accept");
361
362	FD_ZERO(&rset);
363	FD_SET(listenfd, &rset);
364	tv.tv_sec = TIMEOUT;
365	tv.tv_usec = 0;
366	rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
367	if (rv < 0) {
368		uc_logmsg("socket_accept: select");
369		return (-1);
370	}
371	if (rv == 0) {
372		uc_logmsgx("socket_accept: select timeout");
373		return (-1);
374	}
375
376	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
377	if (fd < 0) {
378		uc_logmsg("socket_accept: accept");
379		return (-1);
380	}
381
382	val = fcntl(fd, F_GETFL, 0);
383	if (val < 0) {
384		uc_logmsg("socket_accept: fcntl(F_GETFL)");
385		goto failed;
386	}
387	if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
388		uc_logmsg("socket_accept: fcntl(F_SETFL)");
389		goto failed;
390	}
391
392	return (fd);
393
394failed:
395	if (close(fd) < 0)
396		uc_logmsg("socket_accept: close");
397	return (-1);
398}
399
400int
401uc_check_msghdr(const struct msghdr *msghdr, size_t size)
402{
403	if (msghdr->msg_flags & MSG_TRUNC) {
404		uc_logmsgx("msghdr.msg_flags has MSG_TRUNC");
405		return (-1);
406	}
407	if (msghdr->msg_flags & MSG_CTRUNC) {
408		uc_logmsgx("msghdr.msg_flags has MSG_CTRUNC");
409		return (-1);
410	}
411	if (msghdr->msg_controllen < size) {
412		uc_logmsgx("msghdr.msg_controllen %u < %zu",
413		    (u_int)msghdr->msg_controllen, size);
414		return (-1);
415	}
416	if (msghdr->msg_controllen > 0 && size == 0) {
417		uc_logmsgx("msghdr.msg_controllen %u > 0",
418		    (u_int)msghdr->msg_controllen);
419		return (-1);
420	}
421	return (0);
422}
423
424int
425uc_check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size)
426{
427	if (cmsghdr == NULL) {
428		uc_logmsgx("cmsghdr is NULL");
429		return (-1);
430	}
431	if (cmsghdr->cmsg_level != SOL_SOCKET) {
432		uc_logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET",
433		    cmsghdr->cmsg_level);
434		return (-1);
435	}
436	if (cmsghdr->cmsg_type != type) {
437		uc_logmsgx("cmsghdr.cmsg_type %d != %d",
438		    cmsghdr->cmsg_type, type);
439		return (-1);
440	}
441	if (cmsghdr->cmsg_len != CMSG_LEN(size)) {
442		uc_logmsgx("cmsghdr.cmsg_len %u != %zu",
443		    (u_int)cmsghdr->cmsg_len, CMSG_LEN(size));
444		return (-1);
445	}
446	return (0);
447}
448
449static void
450uc_msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data)
451{
452	msghdr->msg_name = NULL;
453	msghdr->msg_namelen = 0;
454	if (uc_cfg.send_data_flag) {
455		iov->iov_base = uc_cfg.server_flag ?
456		    uc_cfg.ipc_msg.buf_recv : uc_cfg.ipc_msg.buf_send;
457		iov->iov_len = uc_cfg.ipc_msg.buf_size;
458		msghdr->msg_iov = iov;
459		msghdr->msg_iovlen = 1;
460	} else {
461		msghdr->msg_iov = NULL;
462		msghdr->msg_iovlen = 0;
463	}
464	msghdr->msg_control = cmsg_data;
465	msghdr->msg_flags = 0;
466}
467
468void
469uc_msghdr_init_server(struct msghdr *msghdr, struct iovec *iov,
470    void *cmsg_data, size_t cmsg_size)
471{
472	uc_msghdr_init_generic(msghdr, iov, cmsg_data);
473	msghdr->msg_controllen = cmsg_size;
474	uc_dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ?
475	    msghdr->msg_iov->iov_len : (size_t)0);
476	uc_dbgmsg("init: msghdr.msg_controllen %u",
477	    (u_int)msghdr->msg_controllen);
478}
479
480void
481uc_msghdr_init_client(struct msghdr *msghdr, struct iovec *iov,
482    void *cmsg_data, size_t cmsg_size, int type, size_t arr_size)
483{
484	struct cmsghdr *cmsghdr;
485
486	uc_msghdr_init_generic(msghdr, iov, cmsg_data);
487	if (cmsg_data != NULL) {
488		if (uc_cfg.send_array_flag)
489			uc_dbgmsg("sending an array");
490		else
491			uc_dbgmsg("sending a scalar");
492		msghdr->msg_controllen = uc_cfg.send_array_flag ?
493		    cmsg_size : CMSG_SPACE(0);
494		cmsghdr = CMSG_FIRSTHDR(msghdr);
495		cmsghdr->cmsg_level = SOL_SOCKET;
496		cmsghdr->cmsg_type = type;
497		cmsghdr->cmsg_len = CMSG_LEN(uc_cfg.send_array_flag ? arr_size : 0);
498	} else
499		msghdr->msg_controllen = 0;
500}
501
502int
503uc_client_fork(void)
504{
505	int fd1, fd2;
506
507	if (pipe(uc_cfg.sync_fd[SYNC_SERVER]) < 0 ||
508	    pipe(uc_cfg.sync_fd[SYNC_CLIENT]) < 0) {
509		uc_logmsg("client_fork: pipe");
510		return (-1);
511	}
512	uc_cfg.client_pid = fork();
513	if (uc_cfg.client_pid == (pid_t)-1) {
514		uc_logmsg("client_fork: fork");
515		return (-1);
516	}
517	if (uc_cfg.client_pid == 0) {
518		uc_cfg.proc_name = "CLIENT";
519		uc_cfg.server_flag = false;
520		fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV];
521		fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND];
522	} else {
523		fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND];
524		fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV];
525	}
526	if (close(fd1) < 0 || close(fd2) < 0) {
527		uc_logmsg("client_fork: close");
528		return (-1);
529	}
530	return (uc_cfg.client_pid != 0);
531}
532
533void
534uc_client_exit(int rv)
535{
536	if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
537	    close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
538		uc_logmsg("client_exit: close");
539		rv = -1;
540	}
541	rv = rv == 0 ? EXIT_SUCCESS : -rv;
542	uc_dbgmsg("exit: code %d", rv);
543	_exit(rv);
544}
545
546int
547uc_client_wait(void)
548{
549	int status;
550	pid_t pid;
551
552	uc_dbgmsg("waiting for client");
553
554	if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
555	    close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
556		uc_logmsg("client_wait: close");
557		return (-1);
558	}
559
560	pid = waitpid(uc_cfg.client_pid, &status, 0);
561	if (pid == (pid_t)-1) {
562		uc_logmsg("client_wait: waitpid");
563		return (-1);
564	}
565
566	if (WIFEXITED(status)) {
567		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
568			uc_logmsgx("client exit status is %d",
569			    WEXITSTATUS(status));
570			return (-WEXITSTATUS(status));
571		}
572	} else {
573		if (WIFSIGNALED(status))
574			uc_logmsgx("abnormal termination of client, signal %d%s",
575			    WTERMSIG(status), WCOREDUMP(status) ?
576			    " (core file generated)" : "");
577		else
578			uc_logmsgx("termination of client, unknown status");
579		return (-1);
580	}
581
582	return (0);
583}
584
585int
586uc_check_groups(const char *gid_arr_str, const gid_t *gid_arr,
587    const char *gid_num_str, int gid_num, bool all_gids)
588{
589	int i;
590
591	for (i = 0; i < gid_num; ++i)
592		uc_dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]);
593
594	if (all_gids) {
595		if (gid_num != uc_cfg.proc_cred.gid_num) {
596			uc_logmsgx("%s %d != %d", gid_num_str, gid_num,
597			    uc_cfg.proc_cred.gid_num);
598			return (-1);
599		}
600	} else {
601		if (gid_num > uc_cfg.proc_cred.gid_num) {
602			uc_logmsgx("%s %d > %d", gid_num_str, gid_num,
603			    uc_cfg.proc_cred.gid_num);
604			return (-1);
605		}
606	}
607	if (memcmp(gid_arr, uc_cfg.proc_cred.gid_arr,
608	    gid_num * sizeof(*gid_arr)) != 0) {
609		uc_logmsgx("%s content is wrong", gid_arr_str);
610		for (i = 0; i < gid_num; ++i)
611			if (gid_arr[i] != uc_cfg.proc_cred.gid_arr[i]) {
612				uc_logmsgx("%s[%d] %lu != %lu",
613				    gid_arr_str, i, (u_long)gid_arr[i],
614				    (u_long)uc_cfg.proc_cred.gid_arr[i]);
615				break;
616			}
617		return (-1);
618	}
619	return (0);
620}
621
622int
623uc_check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr)
624{
625	const struct cmsgcred *cmcred;
626	int rc;
627
628	if (uc_check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(struct cmsgcred)) < 0)
629		return (-1);
630
631	cmcred = (struct cmsgcred *)CMSG_DATA(cmsghdr);
632
633	uc_dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmcred->cmcred_pid);
634	uc_dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmcred->cmcred_uid);
635	uc_dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmcred->cmcred_euid);
636	uc_dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmcred->cmcred_gid);
637	uc_dbgmsg("cmsgcred.cmcred_ngroups %d", cmcred->cmcred_ngroups);
638
639	rc = 0;
640
641	if (cmcred->cmcred_pid != uc_cfg.client_pid) {
642		uc_logmsgx("cmsgcred.cmcred_pid %ld != %ld",
643		    (long)cmcred->cmcred_pid, (long)uc_cfg.client_pid);
644		rc = -1;
645	}
646	if (cmcred->cmcred_uid != uc_cfg.proc_cred.uid) {
647		uc_logmsgx("cmsgcred.cmcred_uid %lu != %lu",
648		    (u_long)cmcred->cmcred_uid, (u_long)uc_cfg.proc_cred.uid);
649		rc = -1;
650	}
651	if (cmcred->cmcred_euid != uc_cfg.proc_cred.euid) {
652		uc_logmsgx("cmsgcred.cmcred_euid %lu != %lu",
653		    (u_long)cmcred->cmcred_euid, (u_long)uc_cfg.proc_cred.euid);
654		rc = -1;
655	}
656	if (cmcred->cmcred_gid != uc_cfg.proc_cred.gid) {
657		uc_logmsgx("cmsgcred.cmcred_gid %lu != %lu",
658		    (u_long)cmcred->cmcred_gid, (u_long)uc_cfg.proc_cred.gid);
659		rc = -1;
660	}
661	if (cmcred->cmcred_ngroups == 0) {
662		uc_logmsgx("cmsgcred.cmcred_ngroups == 0");
663		rc = -1;
664	}
665	if (cmcred->cmcred_ngroups < 0) {
666		uc_logmsgx("cmsgcred.cmcred_ngroups %d < 0",
667		    cmcred->cmcred_ngroups);
668		rc = -1;
669	}
670	if (cmcred->cmcred_ngroups > CMGROUP_MAX) {
671		uc_logmsgx("cmsgcred.cmcred_ngroups %d > %d",
672		    cmcred->cmcred_ngroups, CMGROUP_MAX);
673		rc = -1;
674	}
675	if (cmcred->cmcred_groups[0] != uc_cfg.proc_cred.egid) {
676		uc_logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)",
677		    (u_long)cmcred->cmcred_groups[0], (u_long)uc_cfg.proc_cred.egid);
678		rc = -1;
679	}
680	if (uc_check_groups("cmsgcred.cmcred_groups", cmcred->cmcred_groups,
681	    "cmsgcred.cmcred_ngroups", cmcred->cmcred_ngroups, false) < 0)
682		rc = -1;
683	return (rc);
684}
685
686int
687uc_check_scm_creds_sockcred(struct cmsghdr *cmsghdr)
688{
689	const struct sockcred *sc;
690	int rc;
691
692	if (uc_check_cmsghdr(cmsghdr, SCM_CREDS,
693	    SOCKCREDSIZE(uc_cfg.proc_cred.gid_num)) < 0)
694		return (-1);
695
696	sc = (struct sockcred *)CMSG_DATA(cmsghdr);
697
698	rc = 0;
699
700	uc_dbgmsg("sockcred.sc_uid %lu", (u_long)sc->sc_uid);
701	uc_dbgmsg("sockcred.sc_euid %lu", (u_long)sc->sc_euid);
702	uc_dbgmsg("sockcred.sc_gid %lu", (u_long)sc->sc_gid);
703	uc_dbgmsg("sockcred.sc_egid %lu", (u_long)sc->sc_egid);
704	uc_dbgmsg("sockcred.sc_ngroups %d", sc->sc_ngroups);
705
706	if (sc->sc_uid != uc_cfg.proc_cred.uid) {
707		uc_logmsgx("sockcred.sc_uid %lu != %lu",
708		    (u_long)sc->sc_uid, (u_long)uc_cfg.proc_cred.uid);
709		rc = -1;
710	}
711	if (sc->sc_euid != uc_cfg.proc_cred.euid) {
712		uc_logmsgx("sockcred.sc_euid %lu != %lu",
713		    (u_long)sc->sc_euid, (u_long)uc_cfg.proc_cred.euid);
714		rc = -1;
715	}
716	if (sc->sc_gid != uc_cfg.proc_cred.gid) {
717		uc_logmsgx("sockcred.sc_gid %lu != %lu",
718		    (u_long)sc->sc_gid, (u_long)uc_cfg.proc_cred.gid);
719		rc = -1;
720	}
721	if (sc->sc_egid != uc_cfg.proc_cred.egid) {
722		uc_logmsgx("sockcred.sc_egid %lu != %lu",
723		    (u_long)sc->sc_egid, (u_long)uc_cfg.proc_cred.egid);
724		rc = -1;
725	}
726	if (sc->sc_ngroups == 0) {
727		uc_logmsgx("sockcred.sc_ngroups == 0");
728		rc = -1;
729	}
730	if (sc->sc_ngroups < 0) {
731		uc_logmsgx("sockcred.sc_ngroups %d < 0",
732		    sc->sc_ngroups);
733		rc = -1;
734	}
735	if (sc->sc_ngroups != uc_cfg.proc_cred.gid_num) {
736		uc_logmsgx("sockcred.sc_ngroups %d != %u",
737		    sc->sc_ngroups, uc_cfg.proc_cred.gid_num);
738		rc = -1;
739	}
740	if (uc_check_groups("sockcred.sc_groups", sc->sc_groups,
741	    "sockcred.sc_ngroups", sc->sc_ngroups, true) < 0)
742		rc = -1;
743	return (rc);
744}
745