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$");
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 
61 void
62 uc_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 
74 void
75 uc_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 
94 void
95 uc_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 
104 void
105 uc_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 
114 void
115 uc_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 
126 int
127 uc_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 
176 failed:
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 
186 int
187 uc_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 
205 int
206 uc_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 
218 int
219 uc_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 
244 int
245 uc_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 
267 int
268 uc_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 
301 int
302 uc_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 
314 int
315 uc_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 
353 int
354 uc_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 
394 failed:
395 	if (close(fd) < 0)
396 		uc_logmsg("socket_accept: close");
397 	return (-1);
398 }
399 
400 int
401 uc_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 
424 int
425 uc_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 
449 static void
450 uc_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 
468 void
469 uc_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 
480 void
481 uc_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 
502 int
503 uc_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 
533 void
534 uc_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 
546 int
547 uc_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 
585 int
586 uc_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 
622 int
623 uc_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 
686 int
687 uc_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