xref: /freebsd/libexec/tftpd/tests/functional.c (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2018 Alan Somers.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/socket.h>
30 #include <sys/stat.h>
31 #include <sys/wait.h>
32 
33 #include <netinet/in.h>
34 
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 #include <stdalign.h>
39 #include <stdio.h>
40 #include <unistd.h>
41 
42 #include <atf-c.h>
43 #include <libutil.h>
44 
45 static const uint16_t BASEPORT = 6969;
46 static const char pidfile[] = "tftpd.pid";
47 static int protocol = PF_UNSPEC;
48 static int s = -1;	/* tftp client socket */
49 static struct sockaddr_storage addr; /* Destination address for the client */
50 static bool s_flag = false;	/* Pass -s to tftpd */
51 static bool w_flag = false;	/* Pass -w to tftpd */
52 
53 /* Helper functions*/
54 static void require_bufeq(const char *expected, ssize_t expected_len,
55     const char *actual, ssize_t len);
56 
57 /*
58  * Receive a response from tftpd
59  * @param	hdr		The reply's expected header, as a char array
60  * @param	contents	The reply's expected contents, as a char array
61  * @param	contents_len	Length of contents
62  */
63 #define RECV(hdr, contents, contents_len) do { \
64 	char buffer[1024]; \
65 	struct sockaddr_storage from; \
66 	socklen_t fromlen = sizeof(from); \
67 	ssize_t r = recvfrom(s, buffer, sizeof(buffer), 0, \
68 	    (struct sockaddr*)&from, &fromlen); \
69 	ATF_REQUIRE(r > 0); \
70 	require_bufeq((hdr), sizeof(hdr), buffer, \
71 	    MIN(r, (ssize_t)sizeof(hdr))); \
72 	require_bufeq((const char*) (contents), (contents_len), \
73 	    &buffer[sizeof(hdr)], r - sizeof(hdr)); \
74 	if (protocol == PF_INET) { \
75 		((struct sockaddr_in*)&addr)->sin_port = \
76 		    ((struct sockaddr_in*)&from)->sin_port; \
77 	} else { \
78 		((struct sockaddr_in6*)&addr)->sin6_port = \
79 		    ((struct sockaddr_in6*)&from)->sin6_port; \
80 	} \
81 } while(0)
82 
83 static void
84 recv_ack(uint16_t blocknum)
85 {
86 	char hdr[] = {0, 4, blocknum >> 8, blocknum & 0xFF};
87 	RECV(hdr, NULL, 0);
88 }
89 
90 static void
91 recv_oack(const char *options, size_t options_len)
92 {
93 	char hdr[] = {0, 6};
94 	RECV(hdr, options, options_len);
95 }
96 
97 /*
98  * Receive a data packet from tftpd
99  * @param	blocknum	Expected block number to be received
100  * @param	contents	Pointer to expected contents
101  * @param	contents_len	Length of contents expected to receive
102  */
103 static void
104 recv_data(uint16_t blocknum, const char* contents, size_t contents_len)
105 {
106 	char hdr[] = {0, 3, blocknum >> 8, blocknum & 0xFF};
107 	RECV(hdr, contents, contents_len);
108 }
109 
110 #define RECV_ERROR(code, msg) do { \
111 	char hdr[] = {0, 5, code >> 8, code & 0xFF}; \
112 	RECV(hdr, msg, sizeof(msg)); \
113 } while (0)
114 
115 /*
116  * send a command to tftpd.
117  * @param	cmd		Command to send, as a char array
118  */
119 static void
120 send_bytes(const void* cmd, ssize_t len)
121 {
122 	ssize_t r;
123 
124 	r = sendto(s, cmd, len, 0, (struct sockaddr*)(&addr), addr.ss_len);
125 	ATF_REQUIRE_EQ(r, len);
126 }
127 
128 static void
129 send_data(uint16_t blocknum, const char* contents, size_t contents_len)
130 {
131 	char buffer[1024];
132 
133 	buffer[0] = 0;	/* DATA opcode high byte */
134 	buffer[1] = 3;	/* DATA opcode low byte */
135 	buffer[2] = blocknum >> 8;
136 	buffer[3] = blocknum & 0xFF;
137 	memmove(&buffer[4], contents, contents_len);
138 	send_bytes(buffer, 4 + contents_len);
139 }
140 
141 /*
142  * send a command to tftpd.
143  * @param	cmd		Command to send, as a const string
144  *				(terminating NUL will be ignored)
145  */
146 #define SEND_STR(cmd) ATF_REQUIRE_EQ( \
147 	sendto(s, (cmd), sizeof(cmd) - 1, 0, (struct sockaddr*)(&addr), \
148 	    addr.ss_len), \
149 	sizeof(cmd) - 1)
150 
151 /*
152  * Acknowledge block blocknum
153  */
154 static void
155 send_ack(uint16_t blocknum)
156 {
157 	char packet[] = {
158 	    0, 4,	/* ACK opcode in BE */
159 	    blocknum >> 8,
160 	    blocknum & 0xFF
161 	};
162 
163 	send_bytes(packet, sizeof(packet));
164 
165 }
166 
167 /*
168  * build an option string
169  */
170 #define OPTION_STR(name, value)	name "\000" value "\000"
171 
172 /*
173  * send a read request to tftpd.
174  * @param	filename	filename as a string, absolute or relative
175  * @param	mode		either "octet" or "netascii"
176  */
177 #define SEND_RRQ(filename, mode) SEND_STR("\0\001" filename "\0" mode "\0")
178 
179 /*
180  * send a read request with options
181  */
182 #define SEND_RRQ_OPT(filename, mode, options) SEND_STR("\0\001" filename "\0" mode "\000" options)
183 
184 /*
185  * send a write request to tftpd.
186  * @param	filename	filename as a string, absolute or relative
187  * @param	mode		either "octet" or "netascii"
188  */
189 #define SEND_WRQ(filename, mode) SEND_STR("\0\002" filename "\0" mode "\0")
190 
191 /*
192  * send a write request with options
193  */
194 #define SEND_WRQ_OPT(filename, mode, options) SEND_STR("\0\002" filename "\0" mode "\000" options)
195 
196 /* Define a test case, for both IPv4 and IPv6 */
197 #define TFTPD_TC_DEFINE(name, head, ...) \
198 static void \
199 name ## _body(void); \
200 ATF_TC_WITH_CLEANUP(name ## _v4); \
201 ATF_TC_HEAD(name ## _v4, tc) \
202 { \
203 	head \
204 } \
205 ATF_TC_BODY(name ## _v4, tc) \
206 { \
207 	__VA_ARGS__; \
208 	protocol = AF_INET; \
209 	s = setup(&addr, __COUNTER__); \
210 	name ## _body(); \
211 	close(s); \
212 } \
213 ATF_TC_CLEANUP(name ## _v4, tc) \
214 { \
215 	cleanup(); \
216 } \
217 ATF_TC_WITH_CLEANUP(name ## _v6); \
218 ATF_TC_HEAD(name ## _v6, tc) \
219 { \
220 	head \
221 } \
222 ATF_TC_BODY(name ## _v6, tc) \
223 { \
224 	__VA_ARGS__; \
225 	protocol = AF_INET6; \
226 	s = setup(&addr, __COUNTER__); \
227 	name ## _body(); \
228 	close(s); \
229 } \
230 ATF_TC_CLEANUP(name ## _v6, tc) \
231 { \
232 	cleanup(); \
233 } \
234 static void \
235 name ## _body(void)
236 
237 /* Add the IPv4 and IPv6 versions of a test case */
238 #define TFTPD_TC_ADD(tp, name ) \
239 do { \
240 	ATF_TP_ADD_TC(tp, name ## _v4); \
241 	ATF_TP_ADD_TC(tp, name ## _v6); \
242 } while (0)
243 
244 /* Standard cleanup used by all testcases */
245 static void
246 cleanup(void)
247 {
248 	FILE *f;
249 	pid_t pid;
250 
251 	f = fopen(pidfile, "r");
252 	if (f == NULL)
253 		return;
254 	if (fscanf(f, "%d", &pid) == 1) {
255 		kill(pid, SIGTERM);
256 		waitpid(pid, NULL, 0);
257 	}
258 	fclose(f);
259 	unlink(pidfile);
260 }
261 
262 /* Assert that two binary buffers are identical */
263 static void
264 require_bufeq(const char *expected, ssize_t expected_len, const char *actual,
265     ssize_t len)
266 {
267 	ssize_t i;
268 
269 	ATF_REQUIRE_EQ_MSG(expected_len, len,
270 	    "Expected %zd bytes but got %zd", expected_len, len);
271 	for (i = 0; i < len; i++) {
272 		ATF_REQUIRE_EQ_MSG(actual[i], expected[i],
273 		    "Expected %#hhx at position %zd; got %hhx instead",
274 		    expected[i], i, actual[i]);
275 	}
276 }
277 
278 /*
279  * Start tftpd and return its communicating socket
280  * @param	to	Will be filled in for use with sendto
281  * @param	idx	Unique identifier of the test case
282  * @return		Socket ready to use
283  */
284 static int
285 setup(struct sockaddr_storage *to, uint16_t idx)
286 {
287 	int client_s, server_s, pid, argv_idx;
288 	char execname[] = "/usr/libexec/tftpd";
289 	char s_flag_str[] = "-s";
290 	char w_flag_str[] = "-w";
291 	char pwd[MAXPATHLEN];
292 	char *argv[10];
293 	struct sockaddr_in addr4;
294 	struct sockaddr_in6 addr6;
295 	struct sockaddr *server_addr;
296 	struct pidfh *pfh;
297 	uint16_t port = BASEPORT + idx;
298 	socklen_t len;
299 
300 	if (protocol == PF_INET) {
301 		len = sizeof(addr4);
302 		bzero(&addr4, len);
303 		addr4.sin_len = len;
304 		addr4.sin_family = PF_INET;
305 		addr4.sin_port = htons(port);
306 		server_addr = (struct sockaddr*)&addr4;
307 	} else {
308 		len = sizeof(addr6);
309 		bzero(&addr6, len);
310 		addr6.sin6_len = len;
311 		addr6.sin6_family = PF_INET6;
312 		addr6.sin6_port = htons(port);
313 		server_addr = (struct sockaddr*)&addr6;
314 	}
315 
316 	ATF_REQUIRE_EQ(getcwd(pwd, sizeof(pwd)), pwd);
317 
318 	/* Must bind(2) pre-fork so it happens before the client's send(2) */
319 	server_s = socket(protocol, SOCK_DGRAM, 0);
320 	if (server_s < 0 && errno == EAFNOSUPPORT) {
321 		atf_tc_skip("This test requires IPv%d support",
322 		    protocol == PF_INET ? 4 : 6);
323 	}
324 	ATF_REQUIRE_MSG(server_s >= 0,
325 	    "socket failed with error %s", strerror(errno));
326 	ATF_REQUIRE_EQ_MSG(bind(server_s, server_addr, len), 0,
327 	    "bind failed with error %s", strerror(errno));
328 
329 	pid = fork();
330 	switch (pid) {
331 	case -1:
332 		atf_tc_fail("fork failed");
333 		break;
334 	case 0:
335 		/* In child */
336 		pfh = pidfile_open(pidfile, 0644, NULL);
337 		ATF_REQUIRE_MSG(pfh != NULL,
338 		    "pidfile_open: %s", strerror(errno));
339 		ATF_REQUIRE_EQ(pidfile_write(pfh), 0);
340 		ATF_REQUIRE_EQ(pidfile_close(pfh), 0);
341 
342 		bzero(argv, sizeof(argv));
343 		argv[0] = execname;
344 		argv_idx = 1;
345 		if (w_flag)
346 			argv[argv_idx++] = w_flag_str;
347 		if (s_flag)
348 			argv[argv_idx++] = s_flag_str;
349 		argv[argv_idx++] = pwd;
350 		ATF_REQUIRE_EQ(dup2(server_s, STDOUT_FILENO), STDOUT_FILENO);
351 		ATF_REQUIRE_EQ(dup2(server_s, STDIN_FILENO), STDIN_FILENO);
352 		ATF_REQUIRE_EQ(dup2(server_s, STDERR_FILENO), STDERR_FILENO);
353 		execv(execname, argv);
354 		atf_tc_fail("exec failed");
355 		break;
356 	default:
357 		/* In parent */
358 		bzero(to, sizeof(*to));
359 		if (protocol == PF_INET) {
360 			struct sockaddr_in *to4 = (struct sockaddr_in*)to;
361 			to4->sin_len = sizeof(*to4);
362 			to4->sin_family = PF_INET;
363 			to4->sin_port = htons(port);
364 			to4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
365 		} else {
366 			struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT;
367 			struct sockaddr_in6 *to6 = (struct sockaddr_in6*)to;
368 			to6->sin6_len = sizeof(*to6);
369 			to6->sin6_family = PF_INET6;
370 			to6->sin6_port = htons(port);
371 			to6->sin6_addr = loopback;
372 		}
373 
374 		close(server_s);
375 		ATF_REQUIRE((client_s = socket(protocol, SOCK_DGRAM, 0)) > 0);
376 		break;
377 	}
378 
379 	/* Clear the client's umask.  Test cases will specify exact modes */
380 	umask(0000);
381 
382 	return (client_s);
383 }
384 
385 /* Like write(2), but never returns less than the requested length */
386 static void
387 write_all(int fd, const void *buf, size_t nbytes)
388 {
389 	ssize_t r;
390 
391 	while (nbytes > 0) {
392 		r = write(fd, buf, nbytes);
393 		ATF_REQUIRE(r > 0);
394 		nbytes -= r;
395 		buf = (const char*)buf + r;
396 	}
397 }
398 
399 
400 /*
401  * Test Cases
402  */
403 
404 /*
405  * Read a file, specified by absolute pathname.
406  */
407 TFTPD_TC_DEFINE(abspath,)
408 {
409 	int fd;
410 	char command[1024];
411 	size_t pathlen;
412 	char suffix[] = {'\0', 'o', 'c', 't', 'e', 't', '\0'};
413 
414 	command[0] = 0;		/* RRQ high byte */
415 	command[1] = 1;		/* RRQ low byte */
416 	ATF_REQUIRE(getcwd(&command[2], sizeof(command) - 2) != NULL);
417 	pathlen = strlcat(&command[2], "/abspath.txt", sizeof(command) - 2);
418 	ATF_REQUIRE(pathlen + sizeof(suffix) < sizeof(command) - 2);
419 	memmove(&command[2 + pathlen], suffix, sizeof(suffix));
420 
421 	fd = open("abspath.txt", O_CREAT | O_RDONLY, 0644);
422 	ATF_REQUIRE(fd >= 0);
423 	close(fd);
424 
425 	send_bytes(command, 2 + pathlen + sizeof(suffix));
426 	recv_data(1, NULL, 0);
427 	send_ack(1);
428 }
429 
430 /*
431  * Attempt to read a file outside of the allowed directory(ies)
432  */
433 TFTPD_TC_DEFINE(dotdot,)
434 {
435 	ATF_REQUIRE_EQ(mkdir("subdir", 0777), 0);
436 	SEND_RRQ("../disallowed.txt", "octet");
437 	RECV_ERROR(2, "Access violation");
438 	s = setup(&addr, __COUNTER__); \
439 	SEND_RRQ("subdir/../../disallowed.txt", "octet");
440 	RECV_ERROR(2, "Access violation");
441 	s = setup(&addr, __COUNTER__); \
442 	SEND_RRQ("/etc/passwd", "octet");
443 	RECV_ERROR(2, "Access violation");
444 }
445 
446 /*
447  * With "-s", tftpd should chroot to the specified directory
448  */
449 TFTPD_TC_DEFINE(s_flag, atf_tc_set_md_var(tc, "require.user", "root");,
450 		s_flag = true)
451 {
452 	int fd;
453 	char contents[] = "small";
454 
455 	fd = open("small.txt", O_RDWR | O_CREAT, 0644);
456 	ATF_REQUIRE(fd >= 0);
457 	write_all(fd, contents, strlen(contents) + 1);
458 	close(fd);
459 
460 	SEND_RRQ("/small.txt", "octet");
461 	recv_data(1, contents, strlen(contents) + 1);
462 	send_ack(1);
463 }
464 
465 /*
466  * Read a file, and simulate a dropped ACK packet
467  */
468 TFTPD_TC_DEFINE(rrq_dropped_ack,)
469 {
470 	int fd;
471 	char contents[] = "small";
472 
473 	fd = open("small.txt", O_RDWR | O_CREAT, 0644);
474 	ATF_REQUIRE(fd >= 0);
475 	write_all(fd, contents, strlen(contents) + 1);
476 	close(fd);
477 
478 	SEND_RRQ("small.txt", "octet");
479 	recv_data(1, contents, strlen(contents) + 1);
480 	/*
481 	 * client "sends" the ack, but network drops it
482 	 * Eventually, tftpd should resend the data packet
483 	 */
484 	recv_data(1, contents, strlen(contents) + 1);
485 	send_ack(1);
486 }
487 
488 /*
489  * Read a file, and simulate a dropped DATA packet
490  */
491 TFTPD_TC_DEFINE(rrq_dropped_data,)
492 {
493 	int fd;
494 	size_t i;
495 	uint32_t contents[192];
496 	char buffer[1024];
497 
498 	for (i = 0; i < nitems(contents); i++)
499 		contents[i] = i;
500 
501 	fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
502 	ATF_REQUIRE(fd >= 0);
503 	write_all(fd, contents, sizeof(contents));
504 	close(fd);
505 
506 	SEND_RRQ("medium.txt", "octet");
507 	recv_data(1, (const char*)&contents[0], 512);
508 	send_ack(1);
509 	(void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL);
510 	/*
511 	 * server "sends" the data, but network drops it
512 	 * Eventually, client should resend the last ACK
513 	 */
514 	send_ack(1);
515 	recv_data(2, (const char*)&contents[128], 256);
516 	send_ack(2);
517 }
518 
519 /*
520  * Read a medium file, and simulate a duplicated ACK packet
521  */
522 TFTPD_TC_DEFINE(rrq_duped_ack,)
523 {
524 	int fd;
525 	size_t i;
526 	uint32_t contents[192];
527 
528 	for (i = 0; i < nitems(contents); i++)
529 		contents[i] = i;
530 
531 	fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
532 	ATF_REQUIRE(fd >= 0);
533 	write_all(fd, contents, sizeof(contents));
534 	close(fd);
535 
536 	SEND_RRQ("medium.txt", "octet");
537 	recv_data(1, (const char*)&contents[0], 512);
538 	send_ack(1);
539 	send_ack(1);	/* Dupe an ACK packet */
540 	recv_data(2, (const char*)&contents[128], 256);
541 	recv_data(2, (const char*)&contents[128], 256);
542 	send_ack(2);
543 }
544 
545 
546 /*
547  * Attempt to read a file without read permissions
548  */
549 TFTPD_TC_DEFINE(rrq_eaccess,)
550 {
551 	int fd;
552 
553 	fd = open("empty.txt", O_CREAT | O_RDONLY, 0000);
554 	ATF_REQUIRE(fd >= 0);
555 	close(fd);
556 
557 	SEND_RRQ("empty.txt", "octet");
558 	RECV_ERROR(2, "Access violation");
559 }
560 
561 /*
562  * Read an empty file
563  */
564 TFTPD_TC_DEFINE(rrq_empty,)
565 {
566 	int fd;
567 
568 	fd = open("empty.txt", O_CREAT | O_RDONLY, 0644);
569 	ATF_REQUIRE(fd >= 0);
570 	close(fd);
571 
572 	SEND_RRQ("empty.txt", "octet");
573 	recv_data(1, NULL, 0);
574 	send_ack(1);
575 }
576 
577 /*
578  * Read a medium file of more than one block
579  */
580 TFTPD_TC_DEFINE(rrq_medium,)
581 {
582 	int fd;
583 	size_t i;
584 	uint32_t contents[192];
585 
586 	for (i = 0; i < nitems(contents); i++)
587 		contents[i] = i;
588 
589 	fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
590 	ATF_REQUIRE(fd >= 0);
591 	write_all(fd, contents, sizeof(contents));
592 	close(fd);
593 
594 	SEND_RRQ("medium.txt", "octet");
595 	recv_data(1, (const char*)&contents[0], 512);
596 	send_ack(1);
597 	recv_data(2, (const char*)&contents[128], 256);
598 	send_ack(2);
599 }
600 
601 /*
602  * Read a medium file with a window size of 2.
603  */
604 TFTPD_TC_DEFINE(rrq_medium_window,)
605 {
606 	int fd;
607 	size_t i;
608 	uint32_t contents[192];
609 	char options[] = OPTION_STR("windowsize", "2");
610 
611 	for (i = 0; i < nitems(contents); i++)
612 		contents[i] = i;
613 
614 	fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
615 	ATF_REQUIRE(fd >= 0);
616 	write_all(fd, contents, sizeof(contents));
617 	close(fd);
618 
619 	SEND_RRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2"));
620 	recv_oack(options, sizeof(options) - 1);
621 	send_ack(0);
622 	recv_data(1, (const char*)&contents[0], 512);
623 	recv_data(2, (const char*)&contents[128], 256);
624 	send_ack(2);
625 }
626 
627 /*
628  * Read a file in netascii format
629  */
630 TFTPD_TC_DEFINE(rrq_netascii,)
631 {
632 	int fd;
633 	char contents[] = "foo\nbar\rbaz\n";
634 	/*
635 	 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed
636 	 * is not intended
637 	 */
638 	char expected[] = "foo\r\nbar\r\0baz\r\n";
639 
640 	fd = open("unix.txt", O_RDWR | O_CREAT, 0644);
641 	ATF_REQUIRE(fd >= 0);
642 	write_all(fd, contents, strlen(contents) + 1);
643 	close(fd);
644 
645 	SEND_RRQ("unix.txt", "netascii");
646 	recv_data(1, expected, sizeof(expected));
647 	send_ack(1);
648 }
649 
650 /*
651  * Read a file that doesn't exist
652  */
653 TFTPD_TC_DEFINE(rrq_nonexistent,)
654 {
655 	SEND_RRQ("nonexistent.txt", "octet");
656 	RECV_ERROR(1, "File not found");
657 }
658 
659 /*
660  * Attempt to read a file whose name exceeds PATH_MAX
661  */
662 TFTPD_TC_DEFINE(rrq_path_max,)
663 {
664 #define AReallyBigFileName \
665 	    "AReallyBigFileNameXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
666 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
667 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
668 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
669 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
670 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
671 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
672 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
673 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
674 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
675 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
676 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
677 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
678 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
679 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
680 	    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\
681 	    ".txt"
682 	ATF_REQUIRE_MSG(strlen(AReallyBigFileName) > PATH_MAX,
683 	    "Somebody increased PATH_MAX.  Update the test");
684 	SEND_RRQ(AReallyBigFileName, "octet");
685 	RECV_ERROR(4, "Illegal TFTP operation");
686 }
687 
688 /*
689  * Read a small file of less than one block
690  */
691 TFTPD_TC_DEFINE(rrq_small,)
692 {
693 	int fd;
694 	char contents[] = "small";
695 
696 	fd = open("small.txt", O_RDWR | O_CREAT, 0644);
697 	ATF_REQUIRE(fd >= 0);
698 	write_all(fd, contents, strlen(contents) + 1);
699 	close(fd);
700 
701 	SEND_RRQ("small.txt", "octet");
702 	recv_data(1, contents, strlen(contents) + 1);
703 	send_ack(1);
704 }
705 
706 /*
707  * Read a file following the example in RFC 7440.
708  */
709 TFTPD_TC_DEFINE(rrq_window_rfc7440,)
710 {
711 	int fd;
712 	size_t i;
713 	char options[] = OPTION_STR("windowsize", "4");
714 	alignas(uint32_t) char contents[13 * 512 - 4];
715 	uint32_t *u32p;
716 
717 	u32p = (uint32_t *)contents;
718 	for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++)
719 		u32p[i] = i;
720 
721 	fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0644);
722 	ATF_REQUIRE(fd >= 0);
723 	write_all(fd, contents, sizeof(contents));
724 	close(fd);
725 
726 	SEND_RRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4"));
727 	recv_oack(options, sizeof(options) - 1);
728 	send_ack(0);
729 	recv_data(1, &contents[0 * 512], 512);
730 	recv_data(2, &contents[1 * 512], 512);
731 	recv_data(3, &contents[2 * 512], 512);
732 	recv_data(4, &contents[3 * 512], 512);
733 	send_ack(4);
734 	recv_data(5, &contents[4 * 512], 512);
735 	recv_data(6, &contents[5 * 512], 512);
736 	recv_data(7, &contents[6 * 512], 512);
737 	recv_data(8, &contents[7 * 512], 512);
738 
739 	/* ACK 5 as if 6-8 were dropped. */
740 	send_ack(5);
741 	recv_data(6, &contents[5 * 512], 512);
742 	recv_data(7, &contents[6 * 512], 512);
743 	recv_data(8, &contents[7 * 512], 512);
744 	recv_data(9, &contents[8 * 512], 512);
745 	send_ack(9);
746 	recv_data(10, &contents[9 * 512], 512);
747 	recv_data(11, &contents[10 * 512], 512);
748 	recv_data(12, &contents[11 * 512], 512);
749 	recv_data(13, &contents[12 * 512], 508);
750 
751 	/* Drop ACK and after timeout receive 10-13. */
752 	recv_data(10, &contents[9 * 512], 512);
753 	recv_data(11, &contents[10 * 512], 512);
754 	recv_data(12, &contents[11 * 512], 512);
755 	recv_data(13, &contents[12 * 512], 508);
756 	send_ack(13);
757 }
758 
759 /*
760  * Try to transfer a file with an unknown mode.
761  */
762 TFTPD_TC_DEFINE(unknown_modes,)
763 {
764 	SEND_RRQ("foo.txt", "ascii");	/* Misspelling of "ascii" */
765 	RECV_ERROR(4, "Illegal TFTP operation");
766 	s = setup(&addr, __COUNTER__); \
767 	SEND_RRQ("foo.txt", "binary");	/* Obsolete.  Use "octet" instead */
768 	RECV_ERROR(4, "Illegal TFTP operation");
769 	s = setup(&addr, __COUNTER__); \
770 	SEND_RRQ("foo.txt", "en_US.UTF-8");
771 	RECV_ERROR(4, "Illegal TFTP operation");
772 	s = setup(&addr, __COUNTER__); \
773 	SEND_RRQ("foo.txt", "mail");	/* Obsolete in RFC-1350 */
774 	RECV_ERROR(4, "Illegal TFTP operation");
775 }
776 
777 /*
778  * Send an unknown opcode.  tftpd should respond with the appropriate error
779  */
780 TFTPD_TC_DEFINE(unknown_opcode,)
781 {
782 	/* Looks like an RRQ or WRQ request, but with a bad opcode */
783 	SEND_STR("\0\007foo.txt\0octet\0");
784 	RECV_ERROR(4, "Illegal TFTP operation");
785 }
786 
787 /*
788  * Invoke tftpd with "-w" and write to a nonexistent file.
789  */
790 TFTPD_TC_DEFINE(w_flag,, w_flag = 1;)
791 {
792 	int fd;
793 	ssize_t r;
794 	char contents[] = "small";
795 	char buffer[1024];
796 	size_t contents_len;
797 
798 	contents_len = strlen(contents) + 1;
799 	SEND_WRQ("small.txt", "octet");
800 	recv_ack(0);
801 	send_data(1, contents, contents_len);
802 	recv_ack(1);
803 
804 	fd = open("small.txt", O_RDONLY);
805 	ATF_REQUIRE(fd >= 0);
806 	r = read(fd, buffer, sizeof(buffer));
807 	close(fd);
808 	require_bufeq(contents, contents_len, buffer, r);
809 }
810 
811 /*
812  * Write a medium file, and simulate a dropped ACK packet
813  */
814 TFTPD_TC_DEFINE(wrq_dropped_ack,)
815 {
816 	int fd;
817 	size_t i;
818 	ssize_t r;
819 	uint32_t contents[192];
820 	char buffer[1024];
821 
822 	for (i = 0; i < nitems(contents); i++)
823 		contents[i] = i;
824 
825 	fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
826 	ATF_REQUIRE(fd >= 0);
827 	close(fd);
828 
829 	SEND_WRQ("medium.txt", "octet");
830 	recv_ack(0);
831 	send_data(1, (const char*)&contents[0], 512);
832 	/*
833 	 * Servers "sends" an ACK packet, but network drops it.
834 	 * Eventually, server should resend the last ACK
835 	 */
836 	(void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL);
837 	recv_ack(1);
838 	send_data(2, (const char*)&contents[128], 256);
839 	recv_ack(2);
840 
841 	fd = open("medium.txt", O_RDONLY);
842 	ATF_REQUIRE(fd >= 0);
843 	r = read(fd, buffer, sizeof(buffer));
844 	close(fd);
845 	require_bufeq((const char*)contents, 768, buffer, r);
846 }
847 
848 /*
849  * Write a small file, and simulate a dropped DATA packet
850  */
851 TFTPD_TC_DEFINE(wrq_dropped_data,)
852 {
853 	int fd;
854 	ssize_t r;
855 	char contents[] = "small";
856 	size_t contents_len;
857 	char buffer[1024];
858 
859 	fd = open("small.txt", O_RDWR | O_CREAT, 0666);
860 	ATF_REQUIRE(fd >= 0);
861 	close(fd);
862 	contents_len = strlen(contents) + 1;
863 
864 	SEND_WRQ("small.txt", "octet");
865 	recv_ack(0);
866 	/*
867 	 * Client "sends" a DATA packet, but network drops it.
868 	 * Eventually, server should resend the last ACK
869 	 */
870 	recv_ack(0);
871 	send_data(1, contents, contents_len);
872 	recv_ack(1);
873 
874 	fd = open("small.txt", O_RDONLY);
875 	ATF_REQUIRE(fd >= 0);
876 	r = read(fd, buffer, sizeof(buffer));
877 	close(fd);
878 	require_bufeq(contents, contents_len, buffer, r);
879 }
880 
881 /*
882  * Write a medium file, and simulate a duplicated DATA packet
883  */
884 TFTPD_TC_DEFINE(wrq_duped_data,)
885 {
886 	int fd;
887 	size_t i;
888 	ssize_t r;
889 	uint32_t contents[192];
890 	char buffer[1024];
891 
892 	for (i = 0; i < nitems(contents); i++)
893 		contents[i] = i;
894 
895 	fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
896 	ATF_REQUIRE(fd >= 0);
897 	close(fd);
898 
899 	SEND_WRQ("medium.txt", "octet");
900 	recv_ack(0);
901 	send_data(1, (const char*)&contents[0], 512);
902 	send_data(1, (const char*)&contents[0], 512);
903 	recv_ack(1);
904 	recv_ack(1);
905 	send_data(2, (const char*)&contents[128], 256);
906 	recv_ack(2);
907 
908 	fd = open("medium.txt", O_RDONLY);
909 	ATF_REQUIRE(fd >= 0);
910 	r = read(fd, buffer, sizeof(buffer));
911 	close(fd);
912 	require_bufeq((const char*)contents, 768, buffer, r);
913 }
914 
915 /*
916  * Attempt to write a file without write permissions
917  */
918 TFTPD_TC_DEFINE(wrq_eaccess,)
919 {
920 	int fd;
921 
922 	fd = open("empty.txt", O_CREAT | O_RDONLY, 0440);
923 	ATF_REQUIRE(fd >= 0);
924 	close(fd);
925 
926 	SEND_WRQ("empty.txt", "octet");
927 	RECV_ERROR(2, "Access violation");
928 }
929 
930 /*
931  * Attempt to write a file without world write permissions, but with world
932  * read permissions
933  */
934 TFTPD_TC_DEFINE(wrq_eaccess_world_readable,)
935 {
936 	int fd;
937 
938 	fd = open("empty.txt", O_CREAT | O_RDONLY, 0444);
939 	ATF_REQUIRE(fd >= 0);
940 	close(fd);
941 
942 	SEND_WRQ("empty.txt", "octet");
943 	RECV_ERROR(2, "Access violation");
944 }
945 
946 
947 /*
948  * Write a medium file of more than one block
949  */
950 TFTPD_TC_DEFINE(wrq_medium,)
951 {
952 	int fd;
953 	size_t i;
954 	ssize_t r;
955 	uint32_t contents[192];
956 	char buffer[1024];
957 
958 	for (i = 0; i < nitems(contents); i++)
959 		contents[i] = i;
960 
961 	fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
962 	ATF_REQUIRE(fd >= 0);
963 	close(fd);
964 
965 	SEND_WRQ("medium.txt", "octet");
966 	recv_ack(0);
967 	send_data(1, (const char*)&contents[0], 512);
968 	recv_ack(1);
969 	send_data(2, (const char*)&contents[128], 256);
970 	recv_ack(2);
971 
972 	fd = open("medium.txt", O_RDONLY);
973 	ATF_REQUIRE(fd >= 0);
974 	r = read(fd, buffer, sizeof(buffer));
975 	close(fd);
976 	require_bufeq((const char*)contents, 768, buffer, r);
977 }
978 
979 /*
980  * Write a medium file with a window size of 2.
981  */
982 TFTPD_TC_DEFINE(wrq_medium_window,)
983 {
984 	int fd;
985 	size_t i;
986 	ssize_t r;
987 	uint32_t contents[192];
988 	char buffer[1024];
989 	char options[] = OPTION_STR("windowsize", "2");
990 
991 	for (i = 0; i < nitems(contents); i++)
992 		contents[i] = i;
993 
994 	fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
995 	ATF_REQUIRE(fd >= 0);
996 	close(fd);
997 
998 	SEND_WRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2"));
999 	recv_oack(options, sizeof(options) - 1);
1000 	send_data(1, (const char*)&contents[0], 512);
1001 	send_data(2, (const char*)&contents[128], 256);
1002 	recv_ack(2);
1003 
1004 	fd = open("medium.txt", O_RDONLY);
1005 	ATF_REQUIRE(fd >= 0);
1006 	r = read(fd, buffer, sizeof(buffer));
1007 	close(fd);
1008 	require_bufeq((const char*)contents, 768, buffer, r);
1009 }
1010 
1011 /*
1012  * Write a file in netascii format
1013  */
1014 TFTPD_TC_DEFINE(wrq_netascii,)
1015 {
1016 	int fd;
1017 	ssize_t r;
1018 	/*
1019 	 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed
1020 	 * is not intended
1021 	 */
1022 	char contents[] = "foo\r\nbar\r\0baz\r\n";
1023 	char expected[] = "foo\nbar\rbaz\n";
1024 	size_t contents_len;
1025 	char buffer[1024];
1026 
1027 	fd = open("unix.txt", O_RDWR | O_CREAT, 0666);
1028 	ATF_REQUIRE(fd >= 0);
1029 	close(fd);
1030 	contents_len = sizeof(contents);
1031 
1032 	SEND_WRQ("unix.txt", "netascii");
1033 	recv_ack(0);
1034 	send_data(1, contents, contents_len);
1035 	recv_ack(1);
1036 
1037 	fd = open("unix.txt", O_RDONLY);
1038 	ATF_REQUIRE(fd >= 0);
1039 	r = read(fd, buffer, sizeof(buffer));
1040 	close(fd);
1041 	require_bufeq(expected, sizeof(expected), buffer, r);
1042 }
1043 
1044 /*
1045  * Attempt to write to a nonexistent file.  With the default options, this
1046  * isn't allowed.
1047  */
1048 TFTPD_TC_DEFINE(wrq_nonexistent,)
1049 {
1050 	SEND_WRQ("nonexistent.txt", "octet");
1051 	RECV_ERROR(1, "File not found");
1052 }
1053 
1054 /*
1055  * Write a small file of less than one block
1056  */
1057 TFTPD_TC_DEFINE(wrq_small,)
1058 {
1059 	int fd;
1060 	ssize_t r;
1061 	char contents[] = "small";
1062 	size_t contents_len;
1063 	char buffer[1024];
1064 
1065 	fd = open("small.txt", O_RDWR | O_CREAT, 0666);
1066 	ATF_REQUIRE(fd >= 0);
1067 	close(fd);
1068 	contents_len = strlen(contents) + 1;
1069 
1070 	SEND_WRQ("small.txt", "octet");
1071 	recv_ack(0);
1072 	send_data(1, contents, contents_len);
1073 	recv_ack(1);
1074 
1075 	fd = open("small.txt", O_RDONLY);
1076 	ATF_REQUIRE(fd >= 0);
1077 	r = read(fd, buffer, sizeof(buffer));
1078 	close(fd);
1079 	require_bufeq(contents, contents_len, buffer, r);
1080 }
1081 
1082 /*
1083  * Write an empty file over a non-empty one
1084  */
1085 TFTPD_TC_DEFINE(wrq_truncate,)
1086 {
1087 	int fd;
1088 	char contents[] = "small";
1089 	struct stat sb;
1090 
1091 	fd = open("small.txt", O_RDWR | O_CREAT, 0666);
1092 	ATF_REQUIRE(fd >= 0);
1093 	write_all(fd, contents, strlen(contents) + 1);
1094 	close(fd);
1095 
1096 	SEND_WRQ("small.txt", "octet");
1097 	recv_ack(0);
1098 	send_data(1, NULL, 0);
1099 	recv_ack(1);
1100 
1101 	ATF_REQUIRE_EQ(stat("small.txt", &sb), 0);
1102 	ATF_REQUIRE_EQ(sb.st_size, 0);
1103 }
1104 
1105 /*
1106  * Write a file following the example in RFC 7440.
1107  */
1108 TFTPD_TC_DEFINE(wrq_window_rfc7440,)
1109 {
1110 	int fd;
1111 	size_t i;
1112 	ssize_t r;
1113 	char options[] = OPTION_STR("windowsize", "4");
1114 	alignas(uint32_t) char contents[13 * 512 - 4];
1115 	char buffer[sizeof(contents)];
1116 	uint32_t *u32p;
1117 
1118 	u32p = (uint32_t *)contents;
1119 	for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++)
1120 		u32p[i] = i;
1121 
1122 	fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0666);
1123 	ATF_REQUIRE(fd >= 0);
1124 	close(fd);
1125 
1126 	SEND_WRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4"));
1127 	recv_oack(options, sizeof(options) - 1);
1128 	send_data(1, &contents[0 * 512], 512);
1129 	send_data(2, &contents[1 * 512], 512);
1130 	send_data(3, &contents[2 * 512], 512);
1131 	send_data(4, &contents[3 * 512], 512);
1132 	recv_ack(4);
1133 	send_data(5, &contents[4 * 512], 512);
1134 
1135 	/* Drop 6-8. */
1136 	recv_ack(5);
1137 	send_data(6, &contents[5 * 512], 512);
1138 	send_data(7, &contents[6 * 512], 512);
1139 	send_data(8, &contents[7 * 512], 512);
1140 	send_data(9, &contents[8 * 512], 512);
1141 	recv_ack(9);
1142 
1143 	/* Drop 11. */
1144 	send_data(10, &contents[9 * 512], 512);
1145 	send_data(12, &contents[11 * 512], 512);
1146 
1147 	/*
1148 	 * We can't send 13 here as tftpd has probably already seen 12
1149 	 * and sent the ACK of 10 if running locally.  While it would
1150 	 * recover by sending another ACK of 10, our state machine
1151 	 * would be out of sync.
1152 	 */
1153 
1154 	/* Ignore ACK for 10 and resend 10-13. */
1155 	recv_ack(10);
1156 	send_data(10, &contents[9 * 512], 512);
1157 	send_data(11, &contents[10 * 512], 512);
1158 	send_data(12, &contents[11 * 512], 512);
1159 	send_data(13, &contents[12 * 512], 508);
1160 	recv_ack(13);
1161 
1162 	fd = open("rfc7440.txt", O_RDONLY);
1163 	ATF_REQUIRE(fd >= 0);
1164 	r = read(fd, buffer, sizeof(buffer));
1165 	close(fd);
1166 	require_bufeq(contents, sizeof(contents), buffer, r);
1167 }
1168 
1169 
1170 /*
1171  * Main
1172  */
1173 
1174 ATF_TP_ADD_TCS(tp)
1175 {
1176 	TFTPD_TC_ADD(tp, abspath);
1177 	TFTPD_TC_ADD(tp, dotdot);
1178 	TFTPD_TC_ADD(tp, s_flag);
1179 	TFTPD_TC_ADD(tp, rrq_dropped_ack);
1180 	TFTPD_TC_ADD(tp, rrq_dropped_data);
1181 	TFTPD_TC_ADD(tp, rrq_duped_ack);
1182 	TFTPD_TC_ADD(tp, rrq_eaccess);
1183 	TFTPD_TC_ADD(tp, rrq_empty);
1184 	TFTPD_TC_ADD(tp, rrq_medium);
1185 	TFTPD_TC_ADD(tp, rrq_medium_window);
1186 	TFTPD_TC_ADD(tp, rrq_netascii);
1187 	TFTPD_TC_ADD(tp, rrq_nonexistent);
1188 	TFTPD_TC_ADD(tp, rrq_path_max);
1189 	TFTPD_TC_ADD(tp, rrq_small);
1190 	TFTPD_TC_ADD(tp, rrq_window_rfc7440);
1191 	TFTPD_TC_ADD(tp, unknown_modes);
1192 	TFTPD_TC_ADD(tp, unknown_opcode);
1193 	TFTPD_TC_ADD(tp, w_flag);
1194 	TFTPD_TC_ADD(tp, wrq_dropped_ack);
1195 	TFTPD_TC_ADD(tp, wrq_dropped_data);
1196 	TFTPD_TC_ADD(tp, wrq_duped_data);
1197 	TFTPD_TC_ADD(tp, wrq_eaccess);
1198 	TFTPD_TC_ADD(tp, wrq_eaccess_world_readable);
1199 	TFTPD_TC_ADD(tp, wrq_medium);
1200 	TFTPD_TC_ADD(tp, wrq_medium_window);
1201 	TFTPD_TC_ADD(tp, wrq_netascii);
1202 	TFTPD_TC_ADD(tp, wrq_nonexistent);
1203 	TFTPD_TC_ADD(tp, wrq_small);
1204 	TFTPD_TC_ADD(tp, wrq_truncate);
1205 	TFTPD_TC_ADD(tp, wrq_window_rfc7440);
1206 
1207 	return (atf_no_error());
1208 }
1209