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 int fd = -1; 228 char buffer[80] = {0}; 229 pid_t pid; 230 231 fd = open(pidfile, O_RDONLY); 232 if (fd < 0) 233 return; 234 if (read(fd, buffer, sizeof(buffer)) > 0) { 235 sscanf(buffer, "%d", &pid); 236 kill(pid, SIGTERM); 237 waitpid(pid, NULL, 0); 238 } 239 close(fd); 240 unlink(pidfile); 241 } 242 243 /* Assert that two binary buffers are identical */ 244 static void 245 require_bufeq(const char *expected, ssize_t expected_len, const char *actual, 246 ssize_t len) 247 { 248 ssize_t i; 249 250 ATF_REQUIRE_EQ_MSG(expected_len, len, 251 "Expected %zd bytes but got %zd", expected_len, len); 252 for (i = 0; i < len; i++) { 253 ATF_REQUIRE_EQ_MSG(actual[i], expected[i], 254 "Expected %#hhx at position %zd; got %hhx instead", 255 expected[i], i, actual[i]); 256 } 257 } 258 259 /* 260 * Start tftpd and return its communicating socket 261 * @param to Will be filled in for use with sendto 262 * @param idx Unique identifier of the test case 263 * @return Socket ready to use 264 */ 265 static int 266 setup(struct sockaddr_storage *to, uint16_t idx) 267 { 268 int client_s, server_s, pid, argv_idx; 269 char execname[] = "/usr/libexec/tftpd"; 270 char s_flag_str[] = "-s"; 271 char w_flag_str[] = "-w"; 272 char pwd[MAXPATHLEN]; 273 char *argv[10]; 274 struct sockaddr_in addr4; 275 struct sockaddr_in6 addr6; 276 struct sockaddr *server_addr; 277 struct pidfh *pfh; 278 uint16_t port = BASEPORT + idx; 279 socklen_t len; 280 281 if (protocol == PF_INET) { 282 len = sizeof(addr4); 283 bzero(&addr4, len); 284 addr4.sin_len = len; 285 addr4.sin_family = PF_INET; 286 addr4.sin_port = htons(port); 287 server_addr = (struct sockaddr*)&addr4; 288 } else { 289 len = sizeof(addr6); 290 bzero(&addr6, len); 291 addr6.sin6_len = len; 292 addr6.sin6_family = PF_INET6; 293 addr6.sin6_port = htons(port); 294 server_addr = (struct sockaddr*)&addr6; 295 } 296 297 ATF_REQUIRE_EQ(getcwd(pwd, sizeof(pwd)), pwd); 298 299 /* Must bind(2) pre-fork so it happens before the client's send(2) */ 300 ATF_REQUIRE((server_s = socket(protocol, SOCK_DGRAM, 0)) > 0); 301 ATF_REQUIRE_EQ_MSG(bind(server_s, server_addr, len), 0, 302 "bind failed with error %s", strerror(errno)); 303 304 pid = fork(); 305 switch (pid) { 306 case -1: 307 atf_tc_fail("fork failed"); 308 break; 309 case 0: 310 /* In child */ 311 pfh = pidfile_open(pidfile, 0644, NULL); 312 ATF_REQUIRE_MSG(pfh != NULL, 313 "pidfile_open: %s", strerror(errno)); 314 ATF_REQUIRE_EQ(pidfile_write(pfh), 0); 315 ATF_REQUIRE_EQ(pidfile_close(pfh), 0); 316 317 bzero(argv, sizeof(argv)); 318 argv[0] = execname; 319 argv_idx = 1; 320 if (w_flag) 321 argv[argv_idx++] = w_flag_str; 322 if (s_flag) 323 argv[argv_idx++] = s_flag_str; 324 argv[argv_idx++] = pwd; 325 ATF_REQUIRE_EQ(dup2(server_s, STDOUT_FILENO), STDOUT_FILENO); 326 ATF_REQUIRE_EQ(dup2(server_s, STDIN_FILENO), STDIN_FILENO); 327 ATF_REQUIRE_EQ(dup2(server_s, STDERR_FILENO), STDERR_FILENO); 328 execv(execname, argv); 329 atf_tc_fail("exec failed"); 330 break; 331 default: 332 /* In parent */ 333 bzero(to, sizeof(*to)); 334 if (protocol == PF_INET) { 335 struct sockaddr_in *to4 = (struct sockaddr_in*)to; 336 to4->sin_len = sizeof(*to4); 337 to4->sin_family = PF_INET; 338 to4->sin_port = htons(port); 339 to4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); 340 } else { 341 struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; 342 struct sockaddr_in6 *to6 = (struct sockaddr_in6*)to; 343 to6->sin6_len = sizeof(*to6); 344 to6->sin6_family = PF_INET6; 345 to6->sin6_port = htons(port); 346 to6->sin6_addr = loopback; 347 } 348 349 close(server_s); 350 ATF_REQUIRE((client_s = socket(protocol, SOCK_DGRAM, 0)) > 0); 351 break; 352 } 353 354 /* Clear the client's umask. Test cases will specify exact modes */ 355 umask(0000); 356 357 return (client_s); 358 } 359 360 /* Like write(2), but never returns less than the requested length */ 361 static void 362 write_all(int fd, const void *buf, size_t nbytes) 363 { 364 ssize_t r; 365 366 while (nbytes > 0) { 367 r = write(fd, buf, nbytes); 368 ATF_REQUIRE(r > 0); 369 nbytes -= r; 370 buf = (const char*)buf + r; 371 } 372 } 373 374 375 /* 376 * Test Cases 377 */ 378 379 /* 380 * Read a file, specified by absolute pathname. 381 */ 382 TFTPD_TC_DEFINE(abspath,) 383 { 384 int fd; 385 char command[1024]; 386 size_t pathlen; 387 char suffix[] = {'\0', 'o', 'c', 't', 'e', 't', '\0'}; 388 389 command[0] = 0; /* RRQ high byte */ 390 command[1] = 1; /* RRQ low byte */ 391 ATF_REQUIRE(getcwd(&command[2], sizeof(command) - 2) != NULL); 392 pathlen = strlcat(&command[2], "/abspath.txt", sizeof(command) - 2); 393 ATF_REQUIRE(pathlen + sizeof(suffix) < sizeof(command) - 2); 394 memmove(&command[2 + pathlen], suffix, sizeof(suffix)); 395 396 fd = open("abspath.txt", O_CREAT | O_RDONLY, 0644); 397 ATF_REQUIRE(fd >= 0); 398 close(fd); 399 400 send_bytes(command, 2 + pathlen + sizeof(suffix)); 401 recv_data(1, NULL, 0); 402 send_ack(1); 403 } 404 405 /* 406 * Attempt to read a file outside of the allowed directory(ies) 407 */ 408 TFTPD_TC_DEFINE(dotdot,) 409 { 410 ATF_REQUIRE_EQ(mkdir("subdir", 0777), 0); 411 SEND_RRQ("../disallowed.txt", "octet"); 412 RECV_ERROR(2, "Access violation"); 413 s = setup(&addr, __COUNTER__); \ 414 SEND_RRQ("subdir/../../disallowed.txt", "octet"); 415 RECV_ERROR(2, "Access violation"); 416 s = setup(&addr, __COUNTER__); \ 417 SEND_RRQ("/etc/passwd", "octet"); 418 RECV_ERROR(2, "Access violation"); 419 } 420 421 /* 422 * With "-s", tftpd should chroot to the specified directory 423 */ 424 TFTPD_TC_DEFINE(s_flag, atf_tc_set_md_var(tc, "require.user", "root");, 425 s_flag = true) 426 { 427 int fd; 428 char contents[] = "small"; 429 430 fd = open("small.txt", O_RDWR | O_CREAT, 0644); 431 ATF_REQUIRE(fd >= 0); 432 write_all(fd, contents, strlen(contents) + 1); 433 close(fd); 434 435 SEND_RRQ("/small.txt", "octet"); 436 recv_data(1, contents, strlen(contents) + 1); 437 send_ack(1); 438 } 439 440 /* 441 * Read a file, and simulate a dropped ACK packet 442 */ 443 TFTPD_TC_DEFINE(rrq_dropped_ack,) 444 { 445 int fd; 446 char contents[] = "small"; 447 448 fd = open("small.txt", O_RDWR | O_CREAT, 0644); 449 ATF_REQUIRE(fd >= 0); 450 write_all(fd, contents, strlen(contents) + 1); 451 close(fd); 452 453 SEND_RRQ("small.txt", "octet"); 454 recv_data(1, contents, strlen(contents) + 1); 455 /* 456 * client "sends" the ack, but network drops it 457 * Eventually, tftpd should resend the data packet 458 */ 459 recv_data(1, contents, strlen(contents) + 1); 460 send_ack(1); 461 } 462 463 /* 464 * Read a file, and simulate a dropped DATA packet 465 */ 466 TFTPD_TC_DEFINE(rrq_dropped_data,) 467 { 468 int fd; 469 size_t i; 470 uint32_t contents[192]; 471 char buffer[1024]; 472 473 for (i = 0; i < nitems(contents); i++) 474 contents[i] = i; 475 476 fd = open("medium.txt", O_RDWR | O_CREAT, 0644); 477 ATF_REQUIRE(fd >= 0); 478 write_all(fd, contents, sizeof(contents)); 479 close(fd); 480 481 SEND_RRQ("medium.txt", "octet"); 482 recv_data(1, (const char*)&contents[0], 512); 483 send_ack(1); 484 (void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL); 485 /* 486 * server "sends" the data, but network drops it 487 * Eventually, client should resend the last ACK 488 */ 489 send_ack(1); 490 recv_data(2, (const char*)&contents[128], 256); 491 send_ack(2); 492 } 493 494 /* 495 * Read a medium file, and simulate a duplicated ACK packet 496 */ 497 TFTPD_TC_DEFINE(rrq_duped_ack,) 498 { 499 int fd; 500 size_t i; 501 uint32_t contents[192]; 502 503 for (i = 0; i < nitems(contents); i++) 504 contents[i] = i; 505 506 fd = open("medium.txt", O_RDWR | O_CREAT, 0644); 507 ATF_REQUIRE(fd >= 0); 508 write_all(fd, contents, sizeof(contents)); 509 close(fd); 510 511 SEND_RRQ("medium.txt", "octet"); 512 recv_data(1, (const char*)&contents[0], 512); 513 send_ack(1); 514 send_ack(1); /* Dupe an ACK packet */ 515 recv_data(2, (const char*)&contents[128], 256); 516 recv_data(2, (const char*)&contents[128], 256); 517 send_ack(2); 518 } 519 520 521 /* 522 * Attempt to read a file without read permissions 523 */ 524 TFTPD_TC_DEFINE(rrq_eaccess,) 525 { 526 int fd; 527 528 fd = open("empty.txt", O_CREAT | O_RDONLY, 0000); 529 ATF_REQUIRE(fd >= 0); 530 close(fd); 531 532 SEND_RRQ("empty.txt", "octet"); 533 RECV_ERROR(2, "Access violation"); 534 } 535 536 /* 537 * Read an empty file 538 */ 539 TFTPD_TC_DEFINE(rrq_empty,) 540 { 541 int fd; 542 543 fd = open("empty.txt", O_CREAT | O_RDONLY, 0644); 544 ATF_REQUIRE(fd >= 0); 545 close(fd); 546 547 SEND_RRQ("empty.txt", "octet"); 548 recv_data(1, NULL, 0); 549 send_ack(1); 550 } 551 552 /* 553 * Read a medium file of more than one block 554 */ 555 TFTPD_TC_DEFINE(rrq_medium,) 556 { 557 int fd; 558 size_t i; 559 uint32_t contents[192]; 560 561 for (i = 0; i < nitems(contents); i++) 562 contents[i] = i; 563 564 fd = open("medium.txt", O_RDWR | O_CREAT, 0644); 565 ATF_REQUIRE(fd >= 0); 566 write_all(fd, contents, sizeof(contents)); 567 close(fd); 568 569 SEND_RRQ("medium.txt", "octet"); 570 recv_data(1, (const char*)&contents[0], 512); 571 send_ack(1); 572 recv_data(2, (const char*)&contents[128], 256); 573 send_ack(2); 574 } 575 576 /* 577 * Read a file in netascii format 578 */ 579 TFTPD_TC_DEFINE(rrq_netascii,) 580 { 581 int fd; 582 char contents[] = "foo\nbar\rbaz\n"; 583 /* 584 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed 585 * is not intended 586 */ 587 char expected[] = "foo\r\nbar\r\0baz\r\n"; 588 589 fd = open("unix.txt", O_RDWR | O_CREAT, 0644); 590 ATF_REQUIRE(fd >= 0); 591 write_all(fd, contents, strlen(contents) + 1); 592 close(fd); 593 594 SEND_RRQ("unix.txt", "netascii"); 595 recv_data(1, expected, sizeof(expected)); 596 send_ack(1); 597 } 598 599 /* 600 * Read a file that doesn't exist 601 */ 602 TFTPD_TC_DEFINE(rrq_nonexistent,) 603 { 604 SEND_RRQ("nonexistent.txt", "octet"); 605 RECV_ERROR(1, "File not found"); 606 } 607 608 /* 609 * Attempt to read a file whose name exceeds PATH_MAX 610 */ 611 TFTPD_TC_DEFINE(rrq_path_max,) 612 { 613 #define AReallyBigFileName \ 614 "AReallyBigFileNameXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 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 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 630 ".txt" 631 ATF_REQUIRE_MSG(strlen(AReallyBigFileName) > PATH_MAX, 632 "Somebody increased PATH_MAX. Update the test"); 633 SEND_RRQ(AReallyBigFileName, "octet"); 634 RECV_ERROR(4, "Illegal TFTP operation"); 635 } 636 637 /* 638 * Read a small file of less than one block 639 */ 640 TFTPD_TC_DEFINE(rrq_small,) 641 { 642 int fd; 643 char contents[] = "small"; 644 645 fd = open("small.txt", O_RDWR | O_CREAT, 0644); 646 ATF_REQUIRE(fd >= 0); 647 write_all(fd, contents, strlen(contents) + 1); 648 close(fd); 649 650 SEND_RRQ("small.txt", "octet"); 651 recv_data(1, contents, strlen(contents) + 1); 652 send_ack(1); 653 } 654 655 /* 656 * Try to transfer a file with an unknown mode. 657 */ 658 TFTPD_TC_DEFINE(unknown_modes,) 659 { 660 SEND_RRQ("foo.txt", "ascii"); /* Misspelling of "ascii" */ 661 RECV_ERROR(4, "Illegal TFTP operation"); 662 s = setup(&addr, __COUNTER__); \ 663 SEND_RRQ("foo.txt", "binary"); /* Obsolete. Use "octet" instead */ 664 RECV_ERROR(4, "Illegal TFTP operation"); 665 s = setup(&addr, __COUNTER__); \ 666 SEND_RRQ("foo.txt", "en_US.UTF-8"); 667 RECV_ERROR(4, "Illegal TFTP operation"); 668 s = setup(&addr, __COUNTER__); \ 669 SEND_RRQ("foo.txt", "mail"); /* Obsolete in RFC-1350 */ 670 RECV_ERROR(4, "Illegal TFTP operation"); 671 } 672 673 /* 674 * Send an unknown opcode. tftpd should respond with the appropriate error 675 */ 676 TFTPD_TC_DEFINE(unknown_opcode,) 677 { 678 /* Looks like an RRQ or WRQ request, but with a bad opcode */ 679 SEND_STR("\0\007foo.txt\0octet\0"); 680 RECV_ERROR(4, "Illegal TFTP operation"); 681 } 682 683 /* 684 * Invoke tftpd with "-w" and write to a nonexistent file. 685 */ 686 TFTPD_TC_DEFINE(w_flag,, w_flag = 1;) 687 { 688 int fd; 689 ssize_t r; 690 char contents[] = "small"; 691 char buffer[1024]; 692 size_t contents_len; 693 694 contents_len = strlen(contents) + 1; 695 SEND_WRQ("small.txt", "octet"); 696 recv_ack(0); 697 send_data(1, contents, contents_len); 698 recv_ack(1); 699 700 fd = open("small.txt", O_RDONLY); 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 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 r = read(fd, buffer, sizeof(buffer)); 770 close(fd); 771 require_bufeq(contents, contents_len, buffer, r); 772 } 773 774 /* 775 * Write a medium file, and simulate a duplicated DATA packet 776 */ 777 TFTPD_TC_DEFINE(wrq_duped_data,) 778 { 779 int fd; 780 size_t i; 781 ssize_t r; 782 uint32_t contents[192]; 783 char buffer[1024]; 784 785 for (i = 0; i < nitems(contents); i++) 786 contents[i] = i; 787 788 fd = open("medium.txt", O_RDWR | O_CREAT, 0666); 789 ATF_REQUIRE(fd >= 0); 790 close(fd); 791 792 SEND_WRQ("medium.txt", "octet"); 793 recv_ack(0); 794 send_data(1, (const char*)&contents[0], 512); 795 send_data(1, (const char*)&contents[0], 512); 796 recv_ack(1); 797 recv_ack(1); 798 send_data(2, (const char*)&contents[128], 256); 799 recv_ack(2); 800 801 fd = open("medium.txt", O_RDONLY); 802 r = read(fd, buffer, sizeof(buffer)); 803 close(fd); 804 require_bufeq((const char*)contents, 768, buffer, r); 805 } 806 807 /* 808 * Attempt to write a file without write permissions 809 */ 810 TFTPD_TC_DEFINE(wrq_eaccess,) 811 { 812 int fd; 813 814 fd = open("empty.txt", O_CREAT | O_RDONLY, 0440); 815 ATF_REQUIRE(fd >= 0); 816 close(fd); 817 818 SEND_WRQ("empty.txt", "octet"); 819 RECV_ERROR(2, "Access violation"); 820 } 821 822 /* 823 * Attempt to write a file without world write permissions, but with world 824 * read permissions 825 */ 826 TFTPD_TC_DEFINE(wrq_eaccess_world_readable,) 827 { 828 int fd; 829 830 fd = open("empty.txt", O_CREAT | O_RDONLY, 0444); 831 ATF_REQUIRE(fd >= 0); 832 close(fd); 833 834 SEND_WRQ("empty.txt", "octet"); 835 RECV_ERROR(2, "Access violation"); 836 } 837 838 839 /* 840 * Write a medium file of more than one block 841 */ 842 TFTPD_TC_DEFINE(wrq_medium,) 843 { 844 int fd; 845 size_t i; 846 ssize_t r; 847 uint32_t contents[192]; 848 char buffer[1024]; 849 850 for (i = 0; i < nitems(contents); i++) 851 contents[i] = i; 852 853 fd = open("medium.txt", O_RDWR | O_CREAT, 0666); 854 ATF_REQUIRE(fd >= 0); 855 close(fd); 856 857 SEND_WRQ("medium.txt", "octet"); 858 recv_ack(0); 859 send_data(1, (const char*)&contents[0], 512); 860 recv_ack(1); 861 send_data(2, (const char*)&contents[128], 256); 862 recv_ack(2); 863 864 fd = open("medium.txt", O_RDONLY); 865 r = read(fd, buffer, sizeof(buffer)); 866 close(fd); 867 require_bufeq((const char*)contents, 768, buffer, r); 868 } 869 870 /* 871 * Write a file in netascii format 872 */ 873 TFTPD_TC_DEFINE(wrq_netascii,) 874 { 875 int fd; 876 ssize_t r; 877 /* 878 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed 879 * is not intended 880 */ 881 char contents[] = "foo\r\nbar\r\0baz\r\n"; 882 char expected[] = "foo\nbar\rbaz\n"; 883 size_t contents_len; 884 char buffer[1024]; 885 886 fd = open("unix.txt", O_RDWR | O_CREAT, 0666); 887 ATF_REQUIRE(fd >= 0); 888 close(fd); 889 contents_len = sizeof(contents); 890 891 SEND_WRQ("unix.txt", "netascii"); 892 recv_ack(0); 893 send_data(1, contents, contents_len); 894 recv_ack(1); 895 896 fd = open("unix.txt", O_RDONLY); 897 r = read(fd, buffer, sizeof(buffer)); 898 close(fd); 899 require_bufeq(expected, sizeof(expected), buffer, r); 900 } 901 902 /* 903 * Attempt to write to a nonexistent file. With the default options, this 904 * isn't allowed. 905 */ 906 TFTPD_TC_DEFINE(wrq_nonexistent,) 907 { 908 SEND_WRQ("nonexistent.txt", "octet"); 909 RECV_ERROR(1, "File not found"); 910 } 911 912 /* 913 * Write a small file of less than one block 914 */ 915 TFTPD_TC_DEFINE(wrq_small,) 916 { 917 int fd; 918 ssize_t r; 919 char contents[] = "small"; 920 size_t contents_len; 921 char buffer[1024]; 922 923 fd = open("small.txt", O_RDWR | O_CREAT, 0666); 924 ATF_REQUIRE(fd >= 0); 925 close(fd); 926 contents_len = strlen(contents) + 1; 927 928 SEND_WRQ("small.txt", "octet"); 929 recv_ack(0); 930 send_data(1, contents, contents_len); 931 recv_ack(1); 932 933 fd = open("small.txt", O_RDONLY); 934 r = read(fd, buffer, sizeof(buffer)); 935 close(fd); 936 require_bufeq(contents, contents_len, buffer, r); 937 } 938 939 /* 940 * Write an empty file over a non-empty one 941 */ 942 TFTPD_TC_DEFINE(wrq_truncate,) 943 { 944 int fd; 945 char contents[] = "small"; 946 struct stat sb; 947 948 fd = open("small.txt", O_RDWR | O_CREAT, 0666); 949 ATF_REQUIRE(fd >= 0); 950 write_all(fd, contents, strlen(contents) + 1); 951 close(fd); 952 953 SEND_WRQ("small.txt", "octet"); 954 recv_ack(0); 955 send_data(1, NULL, 0); 956 recv_ack(1); 957 958 ATF_REQUIRE_EQ(stat("small.txt", &sb), 0); 959 ATF_REQUIRE_EQ(sb.st_size, 0); 960 } 961 962 963 /* 964 * Main 965 */ 966 967 ATF_TP_ADD_TCS(tp) 968 { 969 TFTPD_TC_ADD(tp, abspath); 970 TFTPD_TC_ADD(tp, dotdot); 971 TFTPD_TC_ADD(tp, s_flag); 972 TFTPD_TC_ADD(tp, rrq_dropped_ack); 973 TFTPD_TC_ADD(tp, rrq_dropped_data); 974 TFTPD_TC_ADD(tp, rrq_duped_ack); 975 TFTPD_TC_ADD(tp, rrq_eaccess); 976 TFTPD_TC_ADD(tp, rrq_empty); 977 TFTPD_TC_ADD(tp, rrq_medium); 978 TFTPD_TC_ADD(tp, rrq_netascii); 979 TFTPD_TC_ADD(tp, rrq_nonexistent); 980 TFTPD_TC_ADD(tp, rrq_path_max); 981 TFTPD_TC_ADD(tp, rrq_small); 982 TFTPD_TC_ADD(tp, unknown_modes); 983 TFTPD_TC_ADD(tp, unknown_opcode); 984 TFTPD_TC_ADD(tp, w_flag); 985 TFTPD_TC_ADD(tp, wrq_dropped_ack); 986 TFTPD_TC_ADD(tp, wrq_dropped_data); 987 TFTPD_TC_ADD(tp, wrq_duped_data); 988 TFTPD_TC_ADD(tp, wrq_eaccess); 989 TFTPD_TC_ADD(tp, wrq_eaccess_world_readable); 990 TFTPD_TC_ADD(tp, wrq_medium); 991 TFTPD_TC_ADD(tp, wrq_netascii); 992 TFTPD_TC_ADD(tp, wrq_nonexistent); 993 TFTPD_TC_ADD(tp, wrq_small); 994 TFTPD_TC_ADD(tp, wrq_truncate); 995 996 return (atf_no_error()); 997 } 998