xref: /netbsd-src/tests/net/net/t_unix.c (revision 213aa76def5f7e6d8b6925a59d5bc8221df842d9)
1 /*	$NetBSD: t_unix.c,v 1.25 2021/08/08 20:54:49 nia Exp $	*/
2 
3 /*-
4  * Copyright (c) 2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Christos Zoulas.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #define _GNU_SOURCE
40 #include <sys/cdefs.h>
41 #ifdef __RCSID
42 __RCSID("$Id: t_unix.c,v 1.25 2021/08/08 20:54:49 nia Exp $");
43 #else
44 #define getprogname() argv[0]
45 #endif
46 
47 #ifdef __linux__
48 #define LX -1
49 #else
50 #define LX
51 #endif
52 #include <sys/param.h>
53 #include <sys/socket.h>
54 #include <sys/un.h>
55 #include <sys/wait.h>
56 #include <sys/stat.h>
57 #include <stdio.h>
58 #include <err.h>
59 #include <errno.h>
60 #include <string.h>
61 #include <stddef.h>
62 #include <stdint.h>
63 #include <stdlib.h>
64 #include <unistd.h>
65 #include <stdbool.h>
66 
67 #include "test.h"
68 
69 #define UID 666
70 #define GID 999
71 
72 uid_t srvruid, clntuid;
73 gid_t srvrgid, clntgid;
74 
75 #define OF offsetof(struct sockaddr_un, sun_path)
76 
77 static void
print(const char * msg,struct sockaddr_un * addr,socklen_t len)78 print(const char *msg, struct sockaddr_un *addr, socklen_t len)
79 {
80 	size_t i;
81 
82 	printf("%s: client socket length: %zu\n", msg, (size_t)len);
83 	printf("%s: client family %d\n", msg, addr->sun_family);
84 #ifdef BSD4_4
85 	printf("%s: client len %d\n", msg, addr->sun_len);
86 #endif
87 	printf("%s: socket name: ", msg);
88 	for (i = 0; i < len - OF; i++) {
89 		int ch = addr->sun_path[i];
90 		if (ch < ' ' || '~' < ch)
91 			printf("\\x%02x", ch);
92 		else
93 			printf("%c", ch);
94 	}
95 	printf("\n");
96 }
97 
98 static int
acc(int s)99 acc(int s)
100 {
101 	char guard1;
102 	struct sockaddr_un sun;
103 	char guard2;
104 	socklen_t len;
105 
106 	guard1 = guard2 = 's';
107 
108 	memset(&sun, 0, sizeof(sun));
109 	len = sizeof(sun);
110 	if ((s = accept(s, (struct sockaddr *)&sun, &len)) == -1)
111 		FAIL("accept");
112 	if (guard1 != 's')
113 		FAIL("guard1 = '%c'", guard1);
114 	if (guard2 != 's')
115 		FAIL("guard2 = '%c'", guard2);
116 #ifdef DEBUG
117 	print("accept", &sun, len);
118 #endif
119 	if (len != 2)
120 		FAIL("len %d != 2", len);
121 	if (sun.sun_family != AF_UNIX)
122 		FAIL("sun->sun_family %d != AF_UNIX", sun.sun_family);
123 #ifdef BSD4_4
124 	if (sun.sun_len != 2)
125 		FAIL("sun->sun_len %d != 2", sun.sun_len);
126 #endif
127 	for (size_t i = 0; i < sizeof(sun.sun_path); i++)
128 		if (sun.sun_path[i])
129 			FAIL("sun.sun_path[%zu] %d != NULL", i,
130 			    sun.sun_path[i]);
131 	return s;
132 fail:
133 	if (s != -1)
134 		close(s);
135 	return -1;
136 }
137 
138 static int
peercred(int s,uid_t * euid,gid_t * egid,pid_t * pid)139 peercred(int s, uid_t *euid, gid_t *egid, pid_t *pid)
140 {
141 #ifdef SO_PEERCRED
142 	/* Linux */
143 # define unpcbid ucred
144 # define unp_euid uid
145 # define unp_egid gid
146 # define unp_pid pid
147 # define LOCAL_PEEREID SO_PEERCRED
148 # define LEVEL SOL_SOCKET
149 #else
150 # define LEVEL SOL_LOCAL
151 #endif
152 
153 #ifdef LOCAL_PEEREID
154 	/* NetBSD */
155 	struct unpcbid cred;
156 	socklen_t crl;
157 	crl = sizeof(cred);
158 	if (getsockopt(s, LEVEL, LOCAL_PEEREID, &cred, &crl) == -1)
159 		return -1;
160 	*euid = cred.unp_euid;
161 	*egid = cred.unp_egid;
162 	*pid = cred.unp_pid;
163 	return 0;
164 #else
165 	/* FreeBSD */
166 	*pid = -1;
167 	return getpeereid(s, euid, egid);
168 #endif
169 }
170 
171 static int
check_cred(int fd,bool statit,pid_t checkpid,pid_t otherpid,const char * s)172 check_cred(int fd, bool statit, pid_t checkpid, pid_t otherpid, const char *s)
173 {
174 	pid_t pid;
175 	uid_t euid, uid;
176 	gid_t egid, gid;
177 
178 	if (statit) {
179 		struct stat st;
180 		if (fstat(fd, &st) == -1)
181 			FAIL("fstat (%s)", s);
182 		euid = st.st_uid;
183 		egid = st.st_gid;
184 		pid = checkpid;
185 	} else {
186 		if (peercred(fd, &euid, &egid, &pid) == -1)
187 			FAIL("peercred (%s)", s);
188 	}
189 	printf("%s(%s) euid=%jd egid=%jd pid=%jd\n",
190 	    statit ? "fstat" : "peercred", s,
191 	    (intmax_t)euid, (intmax_t)egid, (intmax_t)pid);
192 
193 	if (statit) {
194 		if (strcmp(s, "server") == 0) {
195 			uid = srvruid;
196 			gid = srvrgid;
197 		} else {
198 			uid = clntuid;
199 			gid = clntgid;
200 		}
201 	} else {
202 		if (checkpid != otherpid && strcmp(s, "server") == 0) {
203 			uid = clntuid;
204 			gid = clntgid;
205 		} else {
206 			uid = srvruid;
207 			gid = srvrgid;
208 		}
209 	}
210 	fflush(stdout);
211 
212 	CHECK_EQUAL(euid, uid, s);
213 	CHECK_EQUAL(egid, gid, s);
214 	CHECK_EQUAL(pid, checkpid, s);
215 	return 0;
216 fail:
217 	return -1;
218 }
219 
220 static int
test(bool forkit,bool closeit,bool statit,size_t len)221 test(bool forkit, bool closeit, bool statit, size_t len)
222 {
223 	size_t slen;
224 	socklen_t sl;
225 	int srvr = -1, clnt = -1, acpt = -1;
226 	pid_t srvrpid, clntpid;
227 	struct sockaddr_un *sock_addr = NULL, *sun = NULL;
228 	socklen_t sock_addrlen;
229 	socklen_t peer_addrlen;
230 	struct sockaddr_un peer_addr;
231 
232 	srvruid = geteuid();
233 	srvrgid = getegid();
234 	srvrpid = clntpid = getpid();
235 	srvr = socket(AF_UNIX, SOCK_STREAM, 0);
236 	if (srvr == -1)
237 		FAIL("socket(server)");
238 
239 	slen = len + OF + 1;
240 
241 	if ((sun = calloc(1, slen)) == NULL)
242 		FAIL("calloc");
243 
244 	if (mkdir("sock", 0777) == -1)
245 		FAIL("mkdir");
246 
247 	if (chdir("sock") == -1)
248 		FAIL("chdir");
249 
250 	memset(sun->sun_path, 'a', len);
251 	sun->sun_path[len] = '\0';
252 	(void)unlink(sun->sun_path);
253 
254 	sl = SUN_LEN(sun);
255 #ifdef BSD4_4
256 	sun->sun_len = sl;
257 #endif
258 	sun->sun_family = AF_UNIX;
259 
260 	unlink(sun->sun_path);
261 
262 	if (bind(srvr, (struct sockaddr *)sun, sl) == -1) {
263 		if (errno == EINVAL && sl >= 256) {
264 			close(srvr);
265 			return -1;
266 		}
267 		FAIL("bind");
268 	}
269 	if (chmod(sun->sun_path, 0777) == -1)
270 		FAIL("chmod `%s'", sun->sun_path);
271 
272 	if (listen(srvr, SOMAXCONN) == -1)
273 		FAIL("listen");
274 
275 	if (forkit) {
276 		switch (clntpid = fork()) {
277 		case 0:	/* child */
278 			srvrpid = getppid();
279 			clntpid = getpid();
280 			if (srvruid == 0) {
281 				setgid(clntgid = GID);
282 				setuid(clntuid = UID);
283 			} else {
284 				clntgid = srvrgid;
285 				clntuid = srvruid;
286 			}
287 			break;
288 		case -1:
289 			FAIL("fork");
290 		default:
291 			if (srvruid == 0) {
292 				clntgid = GID;
293 				clntuid = UID;
294 			}
295 			break;
296 		}
297 	}
298 
299 	if (clntpid == getpid()) {
300 		clnt = socket(AF_UNIX, SOCK_STREAM, 0);
301 		if (clnt == -1)
302 			FAIL("socket(client)");
303 
304 		if (connect(clnt, (const struct sockaddr *)sun, sl) == -1)
305 			FAIL("connect");
306 		check_cred(clnt, statit, srvrpid, clntpid, "client");
307 
308 	}
309 
310 	if (srvrpid == getpid()) {
311 		acpt = acc(srvr);
312 		peer_addrlen = sizeof(peer_addr);
313 		memset(&peer_addr, 0, sizeof(peer_addr));
314 		if (getpeername(acpt, (struct sockaddr *)&peer_addr,
315 		    &peer_addrlen) == -1)
316 			FAIL("getpeername");
317 		print("peer", &peer_addr, peer_addrlen);
318 	}
319 
320 	if (clntpid == getpid()) {
321 		if (closeit) {
322 			if (close(clnt) == -1)
323 				FAIL("close");
324 			clnt = -1;
325 		}
326 	}
327 
328 	if (srvrpid == getpid()) {
329 		check_cred(acpt, statit, clntpid, srvrpid, "server");
330 		if ((sock_addr = calloc(1, slen)) == NULL)
331 			FAIL("calloc");
332 		sock_addrlen = slen;
333 		if (getsockname(srvr, (struct sockaddr *)sock_addr,
334 		    &sock_addrlen) == -1)
335 			FAIL("getsockname");
336 		print("sock", sock_addr, sock_addrlen);
337 
338 		if (sock_addr->sun_family != AF_UNIX)
339 			FAIL("sock_addr->sun_family %d != AF_UNIX",
340 			    sock_addr->sun_family);
341 
342 		len += OF;
343 		if (sock_addrlen LX != len)
344 			FAIL("sock_addr_len %zu != %zu", (size_t)sock_addrlen,
345 			    len);
346 #ifdef BSD4_4
347 		if (sock_addr->sun_len != sl)
348 			FAIL("sock_addr.sun_len %d != %zu", sock_addr->sun_len,
349 			    (size_t)sl);
350 #endif
351 		for (size_t i = 0; i < slen - OF; i++)
352 			if (sock_addr->sun_path[i] != sun->sun_path[i])
353 				FAIL("sock_addr.sun_path[%zu] %d != "
354 				    "sun->sun_path[%zu] %d\n", i,
355 				    sock_addr->sun_path[i], i,
356 				    sun->sun_path[i]);
357 
358 		if (acpt != -1)
359 			(void)close(acpt);
360 		if (srvr != -1)
361 			(void)close(srvr);
362 		free(sock_addr);
363 		sock_addr = NULL;
364 		if (forkit && waitpid(clntpid, NULL, 0) == -1)
365 			FAIL("waitpid");
366 	}
367 	if (clnt != -1 && !closeit)
368 		(void)close(clnt);
369 
370 	free(sock_addr);
371 	free(sun);
372 	return 0;
373 fail:
374 	if (srvrpid == getpid()) {
375 		if (acpt != -1)
376 			(void)close(acpt);
377 		if (srvr != -1)
378 			(void)close(srvr);
379 	}
380 	if (clntpid == getpid()) {
381 		if (clnt != -1 && !closeit)
382 			(void)close(clnt);
383 	}
384 	free(sock_addr);
385 	free(sun);
386 	return -1;
387 }
388 
389 #ifndef TEST
390 
391 ATF_TC(sockaddr_un_len_exceed);
ATF_TC_HEAD(sockaddr_un_len_exceed,tc)392 ATF_TC_HEAD(sockaddr_un_len_exceed, tc)
393 {
394 
395 	atf_tc_set_md_var(tc, "descr", "Check that exceeding the size of "
396 	    "unix domain sockets does not trash memory or kernel when "
397 	    "exceeding the size of the fixed sun_path");
398 }
399 
ATF_TC_BODY(sockaddr_un_len_exceed,tc)400 ATF_TC_BODY(sockaddr_un_len_exceed, tc)
401 {
402 	ATF_REQUIRE_MSG(test(false, false, false, 254) == -1,
403 	    "test(false, false, false, 254): %s", strerror(errno));
404 }
405 
406 ATF_TC(sockaddr_un_len_max);
ATF_TC_HEAD(sockaddr_un_len_max,tc)407 ATF_TC_HEAD(sockaddr_un_len_max, tc)
408 {
409 
410 	atf_tc_set_md_var(tc, "descr", "Check that we can use the maximum "
411 	    "unix domain socket pathlen (253): 255 - sizeof(sun_len) - "
412 	    "sizeof(sun_family)");
413 }
414 
ATF_TC_BODY(sockaddr_un_len_max,tc)415 ATF_TC_BODY(sockaddr_un_len_max, tc)
416 {
417 	ATF_REQUIRE_MSG(test(false, false, false, 253) == 0,
418 	    "test(false, false, false, 253): %s", strerror(errno));
419 }
420 
421 ATF_TC(sockaddr_un_closed);
ATF_TC_HEAD(sockaddr_un_closed,tc)422 ATF_TC_HEAD(sockaddr_un_closed, tc)
423 {
424 
425 	atf_tc_set_md_var(tc, "descr", "Check that we can use the accepted "
426 	    "address of unix domain socket when closed");
427 }
428 
ATF_TC_BODY(sockaddr_un_closed,tc)429 ATF_TC_BODY(sockaddr_un_closed, tc)
430 {
431 	ATF_REQUIRE_MSG(test(false, true, false, 100) == 0,
432 	    "test(false, true, false, 100): %s", strerror(errno));
433 }
434 
435 ATF_TC(sockaddr_un_local_peereid);
ATF_TC_HEAD(sockaddr_un_local_peereid,tc)436 ATF_TC_HEAD(sockaddr_un_local_peereid, tc)
437 {
438 
439 	atf_tc_set_md_var(tc, "descr", "Check that we get the right information"
440 	    " from LOCAL_PEEREID");
441 }
442 
ATF_TC_BODY(sockaddr_un_local_peereid,tc)443 ATF_TC_BODY(sockaddr_un_local_peereid, tc)
444 {
445 	ATF_REQUIRE_MSG(test(true, true, false, 100) == 0,
446 	    "test(true, true, false, 100): %s", strerror(errno));
447 }
448 
449 ATF_TC(sockaddr_un_fstat);
ATF_TC_HEAD(sockaddr_un_fstat,tc)450 ATF_TC_HEAD(sockaddr_un_fstat, tc)
451 {
452 
453 	atf_tc_set_md_var(tc, "descr", "Check that we get the right information"
454 	    " from fstat");
455 }
456 
ATF_TC_BODY(sockaddr_un_fstat,tc)457 ATF_TC_BODY(sockaddr_un_fstat, tc)
458 {
459 	ATF_REQUIRE_MSG(test(true, true, true, 100) == 0,
460 	    "test(true, true, true, 100): %s", strerror(errno));
461 }
462 
ATF_TP_ADD_TCS(tp)463 ATF_TP_ADD_TCS(tp)
464 {
465 
466 	ATF_TP_ADD_TC(tp, sockaddr_un_len_exceed);
467 	ATF_TP_ADD_TC(tp, sockaddr_un_len_max);
468 	ATF_TP_ADD_TC(tp, sockaddr_un_closed);
469 	ATF_TP_ADD_TC(tp, sockaddr_un_local_peereid);
470 	ATF_TP_ADD_TC(tp, sockaddr_un_fstat);
471 	return atf_no_error();
472 }
473 #else
474 int
main(int argc,char * argv[])475 main(int argc, char *argv[])
476 {
477 	size_t len;
478 
479 	if (argc == 1) {
480 		fprintf(stderr, "Usage: %s <len>\n", getprogname());
481 		return EXIT_FAILURE;
482 	}
483 	test(false, false, false, atoi(argv[1]));
484 	test(false, true, false, atoi(argv[1]));
485 	test(true, false, false, atoi(argv[1]));
486 	test(true, true, false, atoi(argv[1]));
487 	test(true, true, true, atoi(argv[1]));
488 }
489 #endif
490