1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2018 Alan Somers. All rights reserved. 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 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 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/param.h> 31 #include <sys/socket.h> 32 #include <sys/stat.h> 33 #include <sys/wait.h> 34 35 #include <netinet/in.h> 36 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <signal.h> 40 #include <stdio.h> 41 #include <unistd.h> 42 43 #include <atf-c.h> 44 #include <libutil.h> 45 46 static const uint16_t BASEPORT = 6969; 47 static const char pidfile[] = "tftpd.pid"; 48 static int protocol = PF_UNSPEC; 49 static int s = -1; /* tftp client socket */ 50 static struct sockaddr_storage addr; /* Destination address for the client */ 51 static bool s_flag = false; /* Pass -s to tftpd */ 52 static bool w_flag = false; /* Pass -w to tftpd */ 53 54 /* Helper functions*/ 55 static void require_bufeq(const char *expected, ssize_t expected_len, 56 const char *actual, ssize_t len); 57 58 /* 59 * Receive a response from tftpd 60 * @param hdr The reply's expected header, as a char array 61 * @param contents The reply's expected contents, as a char array 62 * @param contents_len Length of contents 63 */ 64 #define RECV(hdr, contents, contents_len) do { \ 65 char buffer[1024]; \ 66 struct sockaddr_storage from; \ 67 socklen_t fromlen = sizeof(from); \ 68 ssize_t r = recvfrom(s, buffer, sizeof(buffer), 0, \ 69 (struct sockaddr*)&from, &fromlen); \ 70 ATF_REQUIRE(r > 0); \ 71 require_bufeq((hdr), sizeof(hdr), buffer, \ 72 MIN(r, (ssize_t)sizeof(hdr))); \ 73 require_bufeq((const char*) (contents), (contents_len), \ 74 &buffer[sizeof(hdr)], r - sizeof(hdr)); \ 75 if (protocol == PF_INET) { \ 76 ((struct sockaddr_in*)&addr)->sin_port = \ 77 ((struct sockaddr_in*)&from)->sin_port; \ 78 } else { \ 79 ((struct sockaddr_in6*)&addr)->sin6_port = \ 80 ((struct sockaddr_in6*)&from)->sin6_port; \ 81 } \ 82 } while(0) 83 84 static void 85 recv_ack(uint16_t blocknum) 86 { 87 char hdr[] = {0, 4, blocknum >> 8, blocknum & 0xFF}; 88 RECV(hdr, NULL, 0); 89 } 90 91 /* 92 * Receive a data packet from tftpd 93 * @param blocknum Expected block number to be received 94 * @param contents Pointer to expected contents 95 * @param contents_len Length of contents expected to receive 96 */ 97 static void 98 recv_data(uint16_t blocknum, const char* contents, size_t contents_len) 99 { 100 char hdr[] = {0, 3, blocknum >> 8, blocknum & 0xFF}; 101 RECV(hdr, contents, contents_len); 102 } 103 104 #define RECV_ERROR(code, msg) do { \ 105 char hdr[] = {0, 5, code >> 8, code & 0xFF}; \ 106 RECV(hdr, msg, sizeof(msg)); \ 107 } while (0) 108 109 /* 110 * send a command to tftpd. 111 * @param cmd Command to send, as a char array 112 */ 113 static void 114 send_bytes(const void* cmd, ssize_t len) 115 { 116 ssize_t r; 117 118 r = sendto(s, cmd, len, 0, (struct sockaddr*)(&addr), addr.ss_len); 119 ATF_REQUIRE_EQ(r, len); 120 } 121 122 static void 123 send_data(uint16_t blocknum, const char* contents, size_t contents_len) 124 { 125 char buffer[1024]; 126 127 buffer[0] = 0; /* DATA opcode high byte */ 128 buffer[1] = 3; /* DATA opcode low byte */ 129 buffer[2] = blocknum >> 8; 130 buffer[3] = blocknum & 0xFF; 131 memmove(&buffer[4], contents, contents_len); 132 send_bytes(buffer, 4 + contents_len); 133 } 134 135 /* 136 * send a command to tftpd. 137 * @param cmd Command to send, as a const string 138 * (terminating NUL will be ignored) 139 */ 140 #define SEND_STR(cmd) ATF_REQUIRE_EQ( \ 141 sendto(s, (cmd), sizeof(cmd) - 1, 0, (struct sockaddr*)(&addr), \ 142 addr.ss_len), \ 143 sizeof(cmd) - 1) 144 145 /* 146 * Acknowledge block blocknum 147 */ 148 static void 149 send_ack(uint16_t blocknum) 150 { 151 char packet[] = { 152 0, 4, /* ACK opcode in BE */ 153 blocknum >> 8, 154 blocknum & 0xFF 155 }; 156 157 send_bytes(packet, sizeof(packet)); 158 159 } 160 161 /* 162 * send a read request to tftpd. 163 * @param filename filename as a string, absolute or relative 164 * @param mode either "octet" or "netascii" 165 */ 166 #define SEND_RRQ(filename, mode) SEND_STR("\0\001" filename "\0" mode "\0") 167 168 /* 169 * send a write request to tftpd. 170 * @param filename filename as a string, absolute or relative 171 * @param mode either "octet" or "netascii" 172 */ 173 #define SEND_WRQ(filename, mode) SEND_STR("\0\002" filename "\0" mode "\0") 174 175 /* Define a test case, for both IPv4 and IPv6 */ 176 #define TFTPD_TC_DEFINE(name, head, ...) \ 177 static void \ 178 name ## _body(void); \ 179 ATF_TC_WITH_CLEANUP(name ## _v4); \ 180 ATF_TC_HEAD(name ## _v4, tc) \ 181 { \ 182 head \ 183 } \ 184 ATF_TC_BODY(name ## _v4, tc) \ 185 { \ 186 __VA_ARGS__; \ 187 protocol = AF_INET; \ 188 s = setup(&addr, __COUNTER__); \ 189 name ## _body(); \ 190 close(s); \ 191 } \ 192 ATF_TC_CLEANUP(name ## _v4, tc) \ 193 { \ 194 cleanup(); \ 195 } \ 196 ATF_TC_WITH_CLEANUP(name ## _v6); \ 197 ATF_TC_HEAD(name ## _v6, tc) \ 198 { \ 199 head \ 200 } \ 201 ATF_TC_BODY(name ## _v6, tc) \ 202 { \ 203 __VA_ARGS__; \ 204 protocol = AF_INET6; \ 205 s = setup(&addr, __COUNTER__); \ 206 name ## _body(); \ 207 close(s); \ 208 } \ 209 ATF_TC_CLEANUP(name ## _v6, tc) \ 210 { \ 211 cleanup(); \ 212 } \ 213 static void \ 214 name ## _body(void) 215 216 /* Add the IPv4 and IPv6 versions of a test case */ 217 #define TFTPD_TC_ADD(tp, name ) \ 218 do { \ 219 ATF_TP_ADD_TC(tp, name ## _v4); \ 220 ATF_TP_ADD_TC(tp, name ## _v6); \ 221 } while (0) 222 223 /* Standard cleanup used by all testcases */ 224 static void 225 cleanup(void) 226 { 227 FILE *f; 228 pid_t pid; 229 230 f = fopen(pidfile, "r"); 231 if (f == NULL) 232 return; 233 if (fscanf(f, "%d", &pid) == 1) { 234 kill(pid, SIGTERM); 235 waitpid(pid, NULL, 0); 236 } 237 fclose(f); 238 unlink(pidfile); 239 } 240 241 /* Assert that two binary buffers are identical */ 242 static void 243 require_bufeq(const char *expected, ssize_t expected_len, const char *actual, 244 ssize_t len) 245 { 246 ssize_t i; 247 248 ATF_REQUIRE_EQ_MSG(expected_len, len, 249 "Expected %zd bytes but got %zd", expected_len, len); 250 for (i = 0; i < len; i++) { 251 ATF_REQUIRE_EQ_MSG(actual[i], expected[i], 252 "Expected %#hhx at position %zd; got %hhx instead", 253 expected[i], i, actual[i]); 254 } 255 } 256 257 /* 258 * Start tftpd and return its communicating socket 259 * @param to Will be filled in for use with sendto 260 * @param idx Unique identifier of the test case 261 * @return Socket ready to use 262 */ 263 static int 264 setup(struct sockaddr_storage *to, uint16_t idx) 265 { 266 int client_s, server_s, pid, argv_idx; 267 char execname[] = "/usr/libexec/tftpd"; 268 char s_flag_str[] = "-s"; 269 char w_flag_str[] = "-w"; 270 char pwd[MAXPATHLEN]; 271 char *argv[10]; 272 struct sockaddr_in addr4; 273 struct sockaddr_in6 addr6; 274 struct sockaddr *server_addr; 275 struct pidfh *pfh; 276 uint16_t port = BASEPORT + idx; 277 socklen_t len; 278 279 if (protocol == PF_INET) { 280 len = sizeof(addr4); 281 bzero(&addr4, len); 282 addr4.sin_len = len; 283 addr4.sin_family = PF_INET; 284 addr4.sin_port = htons(port); 285 server_addr = (struct sockaddr*)&addr4; 286 } else { 287 len = sizeof(addr6); 288 bzero(&addr6, len); 289 addr6.sin6_len = len; 290 addr6.sin6_family = PF_INET6; 291 addr6.sin6_port = htons(port); 292 server_addr = (struct sockaddr*)&addr6; 293 } 294 295 ATF_REQUIRE_EQ(getcwd(pwd, sizeof(pwd)), pwd); 296 297 /* Must bind(2) pre-fork so it happens before the client's send(2) */ 298 ATF_REQUIRE((server_s = socket(protocol, SOCK_DGRAM, 0)) > 0); 299 ATF_REQUIRE_EQ_MSG(bind(server_s, server_addr, len), 0, 300 "bind failed with error %s", strerror(errno)); 301 302 pid = fork(); 303 switch (pid) { 304 case -1: 305 atf_tc_fail("fork failed"); 306 break; 307 case 0: 308 /* In child */ 309 pfh = pidfile_open(pidfile, 0644, NULL); 310 ATF_REQUIRE_MSG(pfh != NULL, 311 "pidfile_open: %s", strerror(errno)); 312 ATF_REQUIRE_EQ(pidfile_write(pfh), 0); 313 ATF_REQUIRE_EQ(pidfile_close(pfh), 0); 314 315 bzero(argv, sizeof(argv)); 316 argv[0] = execname; 317 argv_idx = 1; 318 if (w_flag) 319 argv[argv_idx++] = w_flag_str; 320 if (s_flag) 321 argv[argv_idx++] = s_flag_str; 322 argv[argv_idx++] = pwd; 323 ATF_REQUIRE_EQ(dup2(server_s, STDOUT_FILENO), STDOUT_FILENO); 324 ATF_REQUIRE_EQ(dup2(server_s, STDIN_FILENO), STDIN_FILENO); 325 ATF_REQUIRE_EQ(dup2(server_s, STDERR_FILENO), STDERR_FILENO); 326 execv(execname, argv); 327 atf_tc_fail("exec failed"); 328 break; 329 default: 330 /* In parent */ 331 bzero(to, sizeof(*to)); 332 if (protocol == PF_INET) { 333 struct sockaddr_in *to4 = (struct sockaddr_in*)to; 334 to4->sin_len = sizeof(*to4); 335 to4->sin_family = PF_INET; 336 to4->sin_port = htons(port); 337 to4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); 338 } else { 339 struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; 340 struct sockaddr_in6 *to6 = (struct sockaddr_in6*)to; 341 to6->sin6_len = sizeof(*to6); 342 to6->sin6_family = PF_INET6; 343 to6->sin6_port = htons(port); 344 to6->sin6_addr = loopback; 345 } 346 347 close(server_s); 348 ATF_REQUIRE((client_s = socket(protocol, SOCK_DGRAM, 0)) > 0); 349 break; 350 } 351 352 /* Clear the client's umask. Test cases will specify exact modes */ 353 umask(0000); 354 355 return (client_s); 356 } 357 358 /* Like write(2), but never returns less than the requested length */ 359 static void 360 write_all(int fd, const void *buf, size_t nbytes) 361 { 362 ssize_t r; 363 364 while (nbytes > 0) { 365 r = write(fd, buf, nbytes); 366 ATF_REQUIRE(r > 0); 367 nbytes -= r; 368 buf = (const char*)buf + r; 369 } 370 } 371 372 373 /* 374 * Test Cases 375 */ 376 377 /* 378 * Read a file, specified by absolute pathname. 379 */ 380 TFTPD_TC_DEFINE(abspath,) 381 { 382 int fd; 383 char command[1024]; 384 size_t pathlen; 385 char suffix[] = {'\0', 'o', 'c', 't', 'e', 't', '\0'}; 386 387 command[0] = 0; /* RRQ high byte */ 388 command[1] = 1; /* RRQ low byte */ 389 ATF_REQUIRE(getcwd(&command[2], sizeof(command) - 2) != NULL); 390 pathlen = strlcat(&command[2], "/abspath.txt", sizeof(command) - 2); 391 ATF_REQUIRE(pathlen + sizeof(suffix) < sizeof(command) - 2); 392 memmove(&command[2 + pathlen], suffix, sizeof(suffix)); 393 394 fd = open("abspath.txt", O_CREAT | O_RDONLY, 0644); 395 ATF_REQUIRE(fd >= 0); 396 close(fd); 397 398 send_bytes(command, 2 + pathlen + sizeof(suffix)); 399 recv_data(1, NULL, 0); 400 send_ack(1); 401 } 402 403 /* 404 * Attempt to read a file outside of the allowed directory(ies) 405 */ 406 TFTPD_TC_DEFINE(dotdot,) 407 { 408 ATF_REQUIRE_EQ(mkdir("subdir", 0777), 0); 409 SEND_RRQ("../disallowed.txt", "octet"); 410 RECV_ERROR(2, "Access violation"); 411 s = setup(&addr, __COUNTER__); \ 412 SEND_RRQ("subdir/../../disallowed.txt", "octet"); 413 RECV_ERROR(2, "Access violation"); 414 s = setup(&addr, __COUNTER__); \ 415 SEND_RRQ("/etc/passwd", "octet"); 416 RECV_ERROR(2, "Access violation"); 417 } 418 419 /* 420 * With "-s", tftpd should chroot to the specified directory 421 */ 422 TFTPD_TC_DEFINE(s_flag, atf_tc_set_md_var(tc, "require.user", "root");, 423 s_flag = true) 424 { 425 int fd; 426 char contents[] = "small"; 427 428 fd = open("small.txt", O_RDWR | O_CREAT, 0644); 429 ATF_REQUIRE(fd >= 0); 430 write_all(fd, contents, strlen(contents) + 1); 431 close(fd); 432 433 SEND_RRQ("/small.txt", "octet"); 434 recv_data(1, contents, strlen(contents) + 1); 435 send_ack(1); 436 } 437 438 /* 439 * Read a file, and simulate a dropped ACK packet 440 */ 441 TFTPD_TC_DEFINE(rrq_dropped_ack,) 442 { 443 int fd; 444 char contents[] = "small"; 445 446 fd = open("small.txt", O_RDWR | O_CREAT, 0644); 447 ATF_REQUIRE(fd >= 0); 448 write_all(fd, contents, strlen(contents) + 1); 449 close(fd); 450 451 SEND_RRQ("small.txt", "octet"); 452 recv_data(1, contents, strlen(contents) + 1); 453 /* 454 * client "sends" the ack, but network drops it 455 * Eventually, tftpd should resend the data packet 456 */ 457 recv_data(1, contents, strlen(contents) + 1); 458 send_ack(1); 459 } 460 461 /* 462 * Read a file, and simulate a dropped DATA packet 463 */ 464 TFTPD_TC_DEFINE(rrq_dropped_data,) 465 { 466 int fd; 467 size_t i; 468 uint32_t contents[192]; 469 char buffer[1024]; 470 471 for (i = 0; i < nitems(contents); i++) 472 contents[i] = i; 473 474 fd = open("medium.txt", O_RDWR | O_CREAT, 0644); 475 ATF_REQUIRE(fd >= 0); 476 write_all(fd, contents, sizeof(contents)); 477 close(fd); 478 479 SEND_RRQ("medium.txt", "octet"); 480 recv_data(1, (const char*)&contents[0], 512); 481 send_ack(1); 482 (void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL); 483 /* 484 * server "sends" the data, but network drops it 485 * Eventually, client should resend the last ACK 486 */ 487 send_ack(1); 488 recv_data(2, (const char*)&contents[128], 256); 489 send_ack(2); 490 } 491 492 /* 493 * Read a medium file, and simulate a duplicated ACK packet 494 */ 495 TFTPD_TC_DEFINE(rrq_duped_ack,) 496 { 497 int fd; 498 size_t i; 499 uint32_t contents[192]; 500 501 for (i = 0; i < nitems(contents); i++) 502 contents[i] = i; 503 504 fd = open("medium.txt", O_RDWR | O_CREAT, 0644); 505 ATF_REQUIRE(fd >= 0); 506 write_all(fd, contents, sizeof(contents)); 507 close(fd); 508 509 SEND_RRQ("medium.txt", "octet"); 510 recv_data(1, (const char*)&contents[0], 512); 511 send_ack(1); 512 send_ack(1); /* Dupe an ACK packet */ 513 recv_data(2, (const char*)&contents[128], 256); 514 recv_data(2, (const char*)&contents[128], 256); 515 send_ack(2); 516 } 517 518 519 /* 520 * Attempt to read a file without read permissions 521 */ 522 TFTPD_TC_DEFINE(rrq_eaccess,) 523 { 524 int fd; 525 526 fd = open("empty.txt", O_CREAT | O_RDONLY, 0000); 527 ATF_REQUIRE(fd >= 0); 528 close(fd); 529 530 SEND_RRQ("empty.txt", "octet"); 531 RECV_ERROR(2, "Access violation"); 532 } 533 534 /* 535 * Read an empty file 536 */ 537 TFTPD_TC_DEFINE(rrq_empty,) 538 { 539 int fd; 540 541 fd = open("empty.txt", O_CREAT | O_RDONLY, 0644); 542 ATF_REQUIRE(fd >= 0); 543 close(fd); 544 545 SEND_RRQ("empty.txt", "octet"); 546 recv_data(1, NULL, 0); 547 send_ack(1); 548 } 549 550 /* 551 * Read a medium file of more than one block 552 */ 553 TFTPD_TC_DEFINE(rrq_medium,) 554 { 555 int fd; 556 size_t i; 557 uint32_t contents[192]; 558 559 for (i = 0; i < nitems(contents); i++) 560 contents[i] = i; 561 562 fd = open("medium.txt", O_RDWR | O_CREAT, 0644); 563 ATF_REQUIRE(fd >= 0); 564 write_all(fd, contents, sizeof(contents)); 565 close(fd); 566 567 SEND_RRQ("medium.txt", "octet"); 568 recv_data(1, (const char*)&contents[0], 512); 569 send_ack(1); 570 recv_data(2, (const char*)&contents[128], 256); 571 send_ack(2); 572 } 573 574 /* 575 * Read a file in netascii format 576 */ 577 TFTPD_TC_DEFINE(rrq_netascii,) 578 { 579 int fd; 580 char contents[] = "foo\nbar\rbaz\n"; 581 /* 582 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed 583 * is not intended 584 */ 585 char expected[] = "foo\r\nbar\r\0baz\r\n"; 586 587 fd = open("unix.txt", O_RDWR | O_CREAT, 0644); 588 ATF_REQUIRE(fd >= 0); 589 write_all(fd, contents, strlen(contents) + 1); 590 close(fd); 591 592 SEND_RRQ("unix.txt", "netascii"); 593 recv_data(1, expected, sizeof(expected)); 594 send_ack(1); 595 } 596 597 /* 598 * Read a file that doesn't exist 599 */ 600 TFTPD_TC_DEFINE(rrq_nonexistent,) 601 { 602 SEND_RRQ("nonexistent.txt", "octet"); 603 RECV_ERROR(1, "File not found"); 604 } 605 606 /* 607 * Attempt to read a file whose name exceeds PATH_MAX 608 */ 609 TFTPD_TC_DEFINE(rrq_path_max,) 610 { 611 #define AReallyBigFileName \ 612 "AReallyBigFileNameXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 613 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 614 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 615 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 616 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 617 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 618 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 619 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 620 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 621 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 622 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 623 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 624 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 625 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 626 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 627 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 628 ".txt" 629 ATF_REQUIRE_MSG(strlen(AReallyBigFileName) > PATH_MAX, 630 "Somebody increased PATH_MAX. Update the test"); 631 SEND_RRQ(AReallyBigFileName, "octet"); 632 RECV_ERROR(4, "Illegal TFTP operation"); 633 } 634 635 /* 636 * Read a small file of less than one block 637 */ 638 TFTPD_TC_DEFINE(rrq_small,) 639 { 640 int fd; 641 char contents[] = "small"; 642 643 fd = open("small.txt", O_RDWR | O_CREAT, 0644); 644 ATF_REQUIRE(fd >= 0); 645 write_all(fd, contents, strlen(contents) + 1); 646 close(fd); 647 648 SEND_RRQ("small.txt", "octet"); 649 recv_data(1, contents, strlen(contents) + 1); 650 send_ack(1); 651 } 652 653 /* 654 * Try to transfer a file with an unknown mode. 655 */ 656 TFTPD_TC_DEFINE(unknown_modes,) 657 { 658 SEND_RRQ("foo.txt", "ascii"); /* Misspelling of "ascii" */ 659 RECV_ERROR(4, "Illegal TFTP operation"); 660 s = setup(&addr, __COUNTER__); \ 661 SEND_RRQ("foo.txt", "binary"); /* Obsolete. Use "octet" instead */ 662 RECV_ERROR(4, "Illegal TFTP operation"); 663 s = setup(&addr, __COUNTER__); \ 664 SEND_RRQ("foo.txt", "en_US.UTF-8"); 665 RECV_ERROR(4, "Illegal TFTP operation"); 666 s = setup(&addr, __COUNTER__); \ 667 SEND_RRQ("foo.txt", "mail"); /* Obsolete in RFC-1350 */ 668 RECV_ERROR(4, "Illegal TFTP operation"); 669 } 670 671 /* 672 * Send an unknown opcode. tftpd should respond with the appropriate error 673 */ 674 TFTPD_TC_DEFINE(unknown_opcode,) 675 { 676 /* Looks like an RRQ or WRQ request, but with a bad opcode */ 677 SEND_STR("\0\007foo.txt\0octet\0"); 678 RECV_ERROR(4, "Illegal TFTP operation"); 679 } 680 681 /* 682 * Invoke tftpd with "-w" and write to a nonexistent file. 683 */ 684 TFTPD_TC_DEFINE(w_flag,, w_flag = 1;) 685 { 686 int fd; 687 ssize_t r; 688 char contents[] = "small"; 689 char buffer[1024]; 690 size_t contents_len; 691 692 contents_len = strlen(contents) + 1; 693 SEND_WRQ("small.txt", "octet"); 694 recv_ack(0); 695 send_data(1, contents, contents_len); 696 recv_ack(1); 697 698 fd = open("small.txt", O_RDONLY); 699 ATF_REQUIRE(fd >= 0); 700 r = read(fd, buffer, sizeof(buffer)); 701 close(fd); 702 require_bufeq(contents, contents_len, buffer, r); 703 } 704 705 /* 706 * Write a medium file, and simulate a dropped ACK packet 707 */ 708 TFTPD_TC_DEFINE(wrq_dropped_ack,) 709 { 710 int fd; 711 size_t i; 712 ssize_t r; 713 uint32_t contents[192]; 714 char buffer[1024]; 715 716 for (i = 0; i < nitems(contents); i++) 717 contents[i] = i; 718 719 fd = open("medium.txt", O_RDWR | O_CREAT, 0666); 720 ATF_REQUIRE(fd >= 0); 721 close(fd); 722 723 SEND_WRQ("medium.txt", "octet"); 724 recv_ack(0); 725 send_data(1, (const char*)&contents[0], 512); 726 /* 727 * Servers "sends" an ACK packet, but network drops it. 728 * Eventually, server should resend the last ACK 729 */ 730 (void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL); 731 recv_ack(1); 732 send_data(2, (const char*)&contents[128], 256); 733 recv_ack(2); 734 735 fd = open("medium.txt", O_RDONLY); 736 ATF_REQUIRE(fd >= 0); 737 r = read(fd, buffer, sizeof(buffer)); 738 close(fd); 739 require_bufeq((const char*)contents, 768, buffer, r); 740 } 741 742 /* 743 * Write a small file, and simulate a dropped DATA packet 744 */ 745 TFTPD_TC_DEFINE(wrq_dropped_data,) 746 { 747 int fd; 748 ssize_t r; 749 char contents[] = "small"; 750 size_t contents_len; 751 char buffer[1024]; 752 753 fd = open("small.txt", O_RDWR | O_CREAT, 0666); 754 ATF_REQUIRE(fd >= 0); 755 close(fd); 756 contents_len = strlen(contents) + 1; 757 758 SEND_WRQ("small.txt", "octet"); 759 recv_ack(0); 760 /* 761 * Client "sends" a DATA packet, but network drops it. 762 * Eventually, server should resend the last ACK 763 */ 764 recv_ack(0); 765 send_data(1, contents, contents_len); 766 recv_ack(1); 767 768 fd = open("small.txt", O_RDONLY); 769 ATF_REQUIRE(fd >= 0); 770 r = read(fd, buffer, sizeof(buffer)); 771 close(fd); 772 require_bufeq(contents, contents_len, buffer, r); 773 } 774 775 /* 776 * Write a medium file, and simulate a duplicated DATA packet 777 */ 778 TFTPD_TC_DEFINE(wrq_duped_data,) 779 { 780 int fd; 781 size_t i; 782 ssize_t r; 783 uint32_t contents[192]; 784 char buffer[1024]; 785 786 for (i = 0; i < nitems(contents); i++) 787 contents[i] = i; 788 789 fd = open("medium.txt", O_RDWR | O_CREAT, 0666); 790 ATF_REQUIRE(fd >= 0); 791 close(fd); 792 793 SEND_WRQ("medium.txt", "octet"); 794 recv_ack(0); 795 send_data(1, (const char*)&contents[0], 512); 796 send_data(1, (const char*)&contents[0], 512); 797 recv_ack(1); 798 recv_ack(1); 799 send_data(2, (const char*)&contents[128], 256); 800 recv_ack(2); 801 802 fd = open("medium.txt", O_RDONLY); 803 ATF_REQUIRE(fd >= 0); 804 r = read(fd, buffer, sizeof(buffer)); 805 close(fd); 806 require_bufeq((const char*)contents, 768, buffer, r); 807 } 808 809 /* 810 * Attempt to write a file without write permissions 811 */ 812 TFTPD_TC_DEFINE(wrq_eaccess,) 813 { 814 int fd; 815 816 fd = open("empty.txt", O_CREAT | O_RDONLY, 0440); 817 ATF_REQUIRE(fd >= 0); 818 close(fd); 819 820 SEND_WRQ("empty.txt", "octet"); 821 RECV_ERROR(2, "Access violation"); 822 } 823 824 /* 825 * Attempt to write a file without world write permissions, but with world 826 * read permissions 827 */ 828 TFTPD_TC_DEFINE(wrq_eaccess_world_readable,) 829 { 830 int fd; 831 832 fd = open("empty.txt", O_CREAT | O_RDONLY, 0444); 833 ATF_REQUIRE(fd >= 0); 834 close(fd); 835 836 SEND_WRQ("empty.txt", "octet"); 837 RECV_ERROR(2, "Access violation"); 838 } 839 840 841 /* 842 * Write a medium file of more than one block 843 */ 844 TFTPD_TC_DEFINE(wrq_medium,) 845 { 846 int fd; 847 size_t i; 848 ssize_t r; 849 uint32_t contents[192]; 850 char buffer[1024]; 851 852 for (i = 0; i < nitems(contents); i++) 853 contents[i] = i; 854 855 fd = open("medium.txt", O_RDWR | O_CREAT, 0666); 856 ATF_REQUIRE(fd >= 0); 857 close(fd); 858 859 SEND_WRQ("medium.txt", "octet"); 860 recv_ack(0); 861 send_data(1, (const char*)&contents[0], 512); 862 recv_ack(1); 863 send_data(2, (const char*)&contents[128], 256); 864 recv_ack(2); 865 866 fd = open("medium.txt", O_RDONLY); 867 ATF_REQUIRE(fd >= 0); 868 r = read(fd, buffer, sizeof(buffer)); 869 close(fd); 870 require_bufeq((const char*)contents, 768, buffer, r); 871 } 872 873 /* 874 * Write a file in netascii format 875 */ 876 TFTPD_TC_DEFINE(wrq_netascii,) 877 { 878 int fd; 879 ssize_t r; 880 /* 881 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed 882 * is not intended 883 */ 884 char contents[] = "foo\r\nbar\r\0baz\r\n"; 885 char expected[] = "foo\nbar\rbaz\n"; 886 size_t contents_len; 887 char buffer[1024]; 888 889 fd = open("unix.txt", O_RDWR | O_CREAT, 0666); 890 ATF_REQUIRE(fd >= 0); 891 close(fd); 892 contents_len = sizeof(contents); 893 894 SEND_WRQ("unix.txt", "netascii"); 895 recv_ack(0); 896 send_data(1, contents, contents_len); 897 recv_ack(1); 898 899 fd = open("unix.txt", O_RDONLY); 900 ATF_REQUIRE(fd >= 0); 901 r = read(fd, buffer, sizeof(buffer)); 902 close(fd); 903 require_bufeq(expected, sizeof(expected), buffer, r); 904 } 905 906 /* 907 * Attempt to write to a nonexistent file. With the default options, this 908 * isn't allowed. 909 */ 910 TFTPD_TC_DEFINE(wrq_nonexistent,) 911 { 912 SEND_WRQ("nonexistent.txt", "octet"); 913 RECV_ERROR(1, "File not found"); 914 } 915 916 /* 917 * Write a small file of less than one block 918 */ 919 TFTPD_TC_DEFINE(wrq_small,) 920 { 921 int fd; 922 ssize_t r; 923 char contents[] = "small"; 924 size_t contents_len; 925 char buffer[1024]; 926 927 fd = open("small.txt", O_RDWR | O_CREAT, 0666); 928 ATF_REQUIRE(fd >= 0); 929 close(fd); 930 contents_len = strlen(contents) + 1; 931 932 SEND_WRQ("small.txt", "octet"); 933 recv_ack(0); 934 send_data(1, contents, contents_len); 935 recv_ack(1); 936 937 fd = open("small.txt", O_RDONLY); 938 ATF_REQUIRE(fd >= 0); 939 r = read(fd, buffer, sizeof(buffer)); 940 close(fd); 941 require_bufeq(contents, contents_len, buffer, r); 942 } 943 944 /* 945 * Write an empty file over a non-empty one 946 */ 947 TFTPD_TC_DEFINE(wrq_truncate,) 948 { 949 int fd; 950 char contents[] = "small"; 951 struct stat sb; 952 953 fd = open("small.txt", O_RDWR | O_CREAT, 0666); 954 ATF_REQUIRE(fd >= 0); 955 write_all(fd, contents, strlen(contents) + 1); 956 close(fd); 957 958 SEND_WRQ("small.txt", "octet"); 959 recv_ack(0); 960 send_data(1, NULL, 0); 961 recv_ack(1); 962 963 ATF_REQUIRE_EQ(stat("small.txt", &sb), 0); 964 ATF_REQUIRE_EQ(sb.st_size, 0); 965 } 966 967 968 /* 969 * Main 970 */ 971 972 ATF_TP_ADD_TCS(tp) 973 { 974 TFTPD_TC_ADD(tp, abspath); 975 TFTPD_TC_ADD(tp, dotdot); 976 TFTPD_TC_ADD(tp, s_flag); 977 TFTPD_TC_ADD(tp, rrq_dropped_ack); 978 TFTPD_TC_ADD(tp, rrq_dropped_data); 979 TFTPD_TC_ADD(tp, rrq_duped_ack); 980 TFTPD_TC_ADD(tp, rrq_eaccess); 981 TFTPD_TC_ADD(tp, rrq_empty); 982 TFTPD_TC_ADD(tp, rrq_medium); 983 TFTPD_TC_ADD(tp, rrq_netascii); 984 TFTPD_TC_ADD(tp, rrq_nonexistent); 985 TFTPD_TC_ADD(tp, rrq_path_max); 986 TFTPD_TC_ADD(tp, rrq_small); 987 TFTPD_TC_ADD(tp, unknown_modes); 988 TFTPD_TC_ADD(tp, unknown_opcode); 989 TFTPD_TC_ADD(tp, w_flag); 990 TFTPD_TC_ADD(tp, wrq_dropped_ack); 991 TFTPD_TC_ADD(tp, wrq_dropped_data); 992 TFTPD_TC_ADD(tp, wrq_duped_data); 993 TFTPD_TC_ADD(tp, wrq_eaccess); 994 TFTPD_TC_ADD(tp, wrq_eaccess_world_readable); 995 TFTPD_TC_ADD(tp, wrq_medium); 996 TFTPD_TC_ADD(tp, wrq_netascii); 997 TFTPD_TC_ADD(tp, wrq_nonexistent); 998 TFTPD_TC_ADD(tp, wrq_small); 999 TFTPD_TC_ADD(tp, wrq_truncate); 1000 1001 return (atf_no_error()); 1002 } 1003