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/types.h>
28 #include <sys/limits.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 
32 #include <err.h>
33 #include <inttypes.h>
34 #include <paths.h>
35 #include <signal.h>
36 #include <stdarg.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include "uc_common.h"
44 #include "t_cmsgcred.h"
45 #include "t_bintime.h"
46 #include "t_generic.h"
47 #include "t_peercred.h"
48 #include "t_timeval.h"
49 #include "t_sockcred.h"
50 #include "t_cmsgcred_sockcred.h"
51 #include "t_cmsg_len.h"
52 #include "t_timespec_real.h"
53 #include "t_timespec_mono.h"
54 
55 /*
56  * There are tables with tests descriptions and pointers to test
57  * functions.  Each t_*() function returns 0 if its test passed,
58  * -1 if its test failed, -2 if some system error occurred.
59  * If a test function returns -2, then a program exits.
60  *
61  * If a test function forks a client process, then it waits for its
62  * termination.  If a return code of a client process is not equal
63  * to zero, or if a client process was terminated by a signal, then
64  * a test function returns -1 or -2 depending on exit status of
65  * a client process.
66  *
67  * Each function which can block, is run under TIMEOUT.  If timeout
68  * occurs, then a test function returns -2 or a client process exits
69  * with a non-zero return code.
70  */
71 
72 struct test_func {
73 	int		(*func)(void);
74 	const char	*desc;
75 };
76 
77 static const struct test_func test_stream_tbl[] = {
78 	{
79 	  .func = NULL,
80 	  .desc = "All tests"
81 	},
82 	{
83 	  .func = t_cmsgcred,
84 	  .desc = "Sending, receiving cmsgcred"
85 	},
86 	{
87 	  .func = t_sockcred_1,
88 	  .desc = "Receiving sockcred (listening socket)"
89 	},
90 	{
91 	  .func = t_sockcred_2,
92 	  .desc = "Receiving sockcred (accepted socket)"
93 	},
94 	{
95 	  .func = t_cmsgcred_sockcred,
96 	  .desc = "Sending cmsgcred, receiving sockcred"
97 	},
98 	{
99 	  .func = t_timeval,
100 	  .desc = "Sending, receiving timeval"
101 	},
102 	{
103 	  .func = t_bintime,
104 	  .desc = "Sending, receiving bintime"
105 	},
106 /*
107  * The testcase fails on 64-bit architectures (amd64), but passes on 32-bit
108  * architectures (i386); see bug 206543
109  */
110 #ifndef __LP64__
111 	{
112 	  .func = t_cmsg_len,
113 	  .desc = "Check cmsghdr.cmsg_len"
114 	},
115 #endif
116 	{
117 	  .func = t_peercred,
118 	  .desc = "Check LOCAL_PEERCRED socket option"
119 	},
120 #if defined(SCM_REALTIME)
121 	{
122 	  .func = t_timespec_real,
123 	  .desc = "Sending, receiving realtime"
124 	},
125 #endif
126 #if defined(SCM_MONOTONIC)
127 	{
128 	  .func = t_timespec_mono,
129 	  .desc = "Sending, receiving monotonic time (uptime)"
130 	}
131 #endif
132 };
133 
134 #define TEST_STREAM_TBL_SIZE \
135 	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
136 
137 static const struct test_func test_dgram_tbl[] = {
138 	{
139 	  .func = NULL,
140 	  .desc = "All tests"
141 	},
142 	{
143 	  .func = t_cmsgcred,
144 	  .desc = "Sending, receiving cmsgcred"
145 	},
146 	{
147 	  .func = t_sockcred_2,
148 	  .desc = "Receiving sockcred"
149 	},
150 	{
151 	  .func = t_cmsgcred_sockcred,
152 	  .desc = "Sending cmsgcred, receiving sockcred"
153 	},
154 	{
155 	  .func = t_timeval,
156 	  .desc = "Sending, receiving timeval"
157 	},
158 	{
159 	  .func = t_bintime,
160 	  .desc = "Sending, receiving bintime"
161 	},
162 #ifndef __LP64__
163 	{
164 	  .func = t_cmsg_len,
165 	  .desc = "Check cmsghdr.cmsg_len"
166 	},
167 #endif
168 #if defined(SCM_REALTIME)
169 	{
170 	  .func = t_timespec_real,
171 	  .desc = "Sending, receiving realtime"
172 	},
173 #endif
174 #if defined(SCM_MONOTONIC)
175 	{
176 	  .func = t_timespec_mono,
177 	  .desc = "Sending, receiving monotonic time (uptime)"
178 	}
179 #endif
180 };
181 
182 #define TEST_DGRAM_TBL_SIZE \
183 	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
184 
185 static bool	failed_flag = false;
186 
187 struct uc_cfg uc_cfg;
188 
189 static char	work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
190 
191 #define IPC_MSG_NUM_DEF		5
192 #define IPC_MSG_NUM_MAX		10
193 #define IPC_MSG_SIZE_DEF	7
194 #define IPC_MSG_SIZE_MAX	128
195 
196 static void
usage(bool verbose)197 usage(bool verbose)
198 {
199 	u_int i;
200 
201 	printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
202 	    "[-z value] [testno]\n", getprogname());
203 	if (!verbose)
204 		return;
205 	printf("\n Options are:\n\
206   -d            Output debugging information\n\
207   -h            Output the help message and exit\n\
208   -n num        Number of messages to send\n\
209   -s size       Specify size of data for IPC\n\
210   -t type       Specify socket type (stream, dgram) for tests\n\
211   -z value      Do not send data in a message (bit 0x1), do not send\n\
212                 data array associated with a cmsghdr structure (bit 0x2)\n\
213   testno        Run one test by its number (require the -t option)\n\n");
214 	printf(" Available tests for stream sockets:\n");
215 	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
216 		printf("   %u: %s\n", i, test_stream_tbl[i].desc);
217 	printf("\n Available tests for datagram sockets:\n");
218 	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
219 		printf("   %u: %s\n", i, test_dgram_tbl[i].desc);
220 }
221 
222 static int
run_tests(int type,u_int testno1)223 run_tests(int type, u_int testno1)
224 {
225 	const struct test_func *tf;
226 	u_int i, testno2, failed_num;
227 
228 	uc_cfg.sock_type = type;
229 	if (type == SOCK_STREAM) {
230 		uc_cfg.sock_type_str = "SOCK_STREAM";
231 		tf = test_stream_tbl;
232 		i = TEST_STREAM_TBL_SIZE - 1;
233 	} else {
234 		uc_cfg.sock_type_str = "SOCK_DGRAM";
235 		tf = test_dgram_tbl;
236 		i = TEST_DGRAM_TBL_SIZE - 1;
237 	}
238 	if (testno1 == 0) {
239 		testno1 = 1;
240 		testno2 = i;
241 	} else
242 		testno2 = testno1;
243 
244 	uc_output("Running tests for %s sockets:\n", uc_cfg.sock_type_str);
245 	failed_num = 0;
246 	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
247 		uc_output("  %u: %s\n", i, tf->desc);
248 		switch (tf->func()) {
249 		case -1:
250 			++failed_num;
251 			break;
252 		case -2:
253 			uc_logmsgx("some system error or timeout occurred");
254 			return (-1);
255 		}
256 	}
257 
258 	if (failed_num != 0)
259 		failed_flag = true;
260 
261 	if (testno1 != testno2) {
262 		if (failed_num == 0)
263 			uc_output("-- all tests passed!\n");
264 		else
265 			uc_output("-- %u test%s failed!\n",
266 			    failed_num, failed_num == 1 ? "" : "s");
267 	} else {
268 		if (failed_num == 0)
269 			uc_output("-- test passed!\n");
270 		else
271 			uc_output("-- test failed!\n");
272 	}
273 
274 	return (0);
275 }
276 
277 static int
init(void)278 init(void)
279 {
280 	struct sigaction sigact;
281 	size_t idx;
282 	int rv;
283 
284 	uc_cfg.proc_name = "SERVER";
285 
286 	sigact.sa_handler = SIG_IGN;
287 	sigact.sa_flags = 0;
288 	sigemptyset(&sigact.sa_mask);
289 	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
290 		uc_logmsg("init: sigaction");
291 		return (-1);
292 	}
293 
294 	if (uc_cfg.ipc_msg.buf_size == 0)
295 		uc_cfg.ipc_msg.buf_send = uc_cfg.ipc_msg.buf_recv = NULL;
296 	else {
297 		uc_cfg.ipc_msg.buf_send = malloc(uc_cfg.ipc_msg.buf_size);
298 		uc_cfg.ipc_msg.buf_recv = malloc(uc_cfg.ipc_msg.buf_size);
299 		if (uc_cfg.ipc_msg.buf_send == NULL || uc_cfg.ipc_msg.buf_recv == NULL) {
300 			uc_logmsg("init: malloc");
301 			return (-1);
302 		}
303 		for (idx = 0; idx < uc_cfg.ipc_msg.buf_size; ++idx)
304 			uc_cfg.ipc_msg.buf_send[idx] = (char)idx;
305 	}
306 
307 	uc_cfg.proc_cred.uid = getuid();
308 	uc_cfg.proc_cred.euid = geteuid();
309 	uc_cfg.proc_cred.gid = getgid();
310 	uc_cfg.proc_cred.egid = getegid();
311 	uc_cfg.proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
312 	if (uc_cfg.proc_cred.gid_num < 0) {
313 		uc_logmsg("init: getgroups");
314 		return (-1);
315 	}
316 	uc_cfg.proc_cred.gid_arr = malloc(uc_cfg.proc_cred.gid_num *
317 	    sizeof(*uc_cfg.proc_cred.gid_arr));
318 	if (uc_cfg.proc_cred.gid_arr == NULL) {
319 		uc_logmsg("init: malloc");
320 		return (-1);
321 	}
322 	if (getgroups(uc_cfg.proc_cred.gid_num, uc_cfg.proc_cred.gid_arr) < 0) {
323 		uc_logmsg("init: getgroups");
324 		return (-1);
325 	}
326 
327 	memset(&uc_cfg.serv_addr_sun, 0, sizeof(uc_cfg.serv_addr_sun));
328 	rv = snprintf(uc_cfg.serv_addr_sun.sun_path, sizeof(uc_cfg.serv_addr_sun.sun_path),
329 	    "%s/%s", work_dir, uc_cfg.proc_name);
330 	if (rv < 0) {
331 		uc_logmsg("init: snprintf");
332 		return (-1);
333 	}
334 	if ((size_t)rv >= sizeof(uc_cfg.serv_addr_sun.sun_path)) {
335 		uc_logmsgx("init: not enough space for socket pathname");
336 		return (-1);
337 	}
338 	uc_cfg.serv_addr_sun.sun_family = PF_LOCAL;
339 	uc_cfg.serv_addr_sun.sun_len = SUN_LEN(&uc_cfg.serv_addr_sun);
340 
341 	return (0);
342 }
343 
344 int
main(int argc,char * argv[])345 main(int argc, char *argv[])
346 {
347 	const char *errstr;
348 	u_int testno, zvalue;
349 	int opt, rv;
350 	bool dgram_flag, stream_flag;
351 
352 	memset(&uc_cfg, '\0', sizeof(uc_cfg));
353 	uc_cfg.debug = false;
354 	uc_cfg.server_flag = true;
355 	uc_cfg.send_data_flag = true;
356 	uc_cfg.send_array_flag = true;
357 	uc_cfg.ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
358 	uc_cfg.ipc_msg.msg_num = IPC_MSG_NUM_DEF;
359 	dgram_flag = stream_flag = false;
360 	while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
361 		switch (opt) {
362 		case 'd':
363 			uc_cfg.debug = true;
364 			break;
365 		case 'h':
366 			usage(true);
367 			return (EXIT_SUCCESS);
368 		case 'n':
369 			uc_cfg.ipc_msg.msg_num = strtonum(optarg, 1,
370 			    IPC_MSG_NUM_MAX, &errstr);
371 			if (errstr != NULL)
372 				errx(EXIT_FAILURE, "option -n: number is %s",
373 				    errstr);
374 			break;
375 		case 's':
376 			uc_cfg.ipc_msg.buf_size = strtonum(optarg, 0,
377 			    IPC_MSG_SIZE_MAX, &errstr);
378 			if (errstr != NULL)
379 				errx(EXIT_FAILURE, "option -s: number is %s",
380 				    errstr);
381 			break;
382 		case 't':
383 			if (strcmp(optarg, "stream") == 0)
384 				stream_flag = true;
385 			else if (strcmp(optarg, "dgram") == 0)
386 				dgram_flag = true;
387 			else
388 				errx(EXIT_FAILURE, "option -t: "
389 				    "wrong socket type");
390 			break;
391 		case 'z':
392 			zvalue = strtonum(optarg, 0, 3, &errstr);
393 			if (errstr != NULL)
394 				errx(EXIT_FAILURE, "option -z: number is %s",
395 				    errstr);
396 			if (zvalue & 0x1)
397 				uc_cfg.send_data_flag = false;
398 			if (zvalue & 0x2)
399 				uc_cfg.send_array_flag = false;
400 			break;
401 		default:
402 			usage(false);
403 			return (EXIT_FAILURE);
404 		}
405 
406 	if (optind < argc) {
407 		if (optind + 1 != argc)
408 			errx(EXIT_FAILURE, "too many arguments");
409 		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
410 		if (errstr != NULL)
411 			errx(EXIT_FAILURE, "test number is %s", errstr);
412 		if (stream_flag && testno >= TEST_STREAM_TBL_SIZE)
413 			errx(EXIT_FAILURE, "given test %u for stream "
414 			    "sockets does not exist", testno);
415 		if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE)
416 			errx(EXIT_FAILURE, "given test %u for datagram "
417 			    "sockets does not exist", testno);
418 	} else
419 		testno = 0;
420 
421 	if (!dgram_flag && !stream_flag) {
422 		if (testno != 0)
423 			errx(EXIT_FAILURE, "particular test number "
424 			    "can be used with the -t option only");
425 		dgram_flag = stream_flag = true;
426 	}
427 
428 	if (mkdtemp(work_dir) == NULL)
429 		err(EXIT_FAILURE, "mkdtemp(%s)", work_dir);
430 
431 	rv = EXIT_FAILURE;
432 	if (init() < 0)
433 		goto done;
434 
435 	if (stream_flag)
436 		if (run_tests(SOCK_STREAM, testno) < 0)
437 			goto done;
438 	if (dgram_flag)
439 		if (run_tests(SOCK_DGRAM, testno) < 0)
440 			goto done;
441 
442 	rv = EXIT_SUCCESS;
443 done:
444 	if (rmdir(work_dir) < 0) {
445 		uc_logmsg("rmdir(%s)", work_dir);
446 		rv = EXIT_FAILURE;
447 	}
448 	return (failed_flag ? EXIT_FAILURE : rv);
449 }
450