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