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