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