functional.c revision 337247
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: stable/10/libexec/tftpd/tests/functional.c 337247 2018-08-03 14:17:11Z asomers $"); 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 46static const uint16_t BASEPORT = 6969; 47static const char pidfile[] = "tftpd.pid"; 48static int protocol = PF_UNSPEC; 49static int s = -1; /* tftp client socket */ 50static struct sockaddr_storage addr; /* Destination address for the client */ 51static bool s_flag = false; /* Pass -s to tftpd */ 52static bool w_flag = false; /* Pass -w to tftpd */ 53 54/* Helper functions*/ 55static 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 84static void 85recv_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 */ 97static void 98recv_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 */ 113static void 114send_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 122static void 123send_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 */ 148static void 149send_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, ...) \ 177static void \ 178name ## _body(void); \ 179ATF_TC_WITH_CLEANUP(name ## _v4); \ 180ATF_TC_HEAD(name ## _v4, tc) \ 181{ \ 182 head \ 183} \ 184ATF_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} \ 192ATF_TC_CLEANUP(name ## _v4, tc) \ 193{ \ 194 cleanup(); \ 195} \ 196ATF_TC_WITH_CLEANUP(name ## _v6); \ 197ATF_TC_HEAD(name ## _v6, tc) \ 198{ \ 199 head \ 200} \ 201ATF_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} \ 209ATF_TC_CLEANUP(name ## _v6, tc) \ 210{ \ 211 cleanup(); \ 212} \ 213static void \ 214name ## _body(void) 215 216/* Add the IPv4 and IPv6 versions of a test case */ 217#define TFTPD_TC_ADD(tp, name ) \ 218do { \ 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 */ 224static void 225cleanup(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 */ 242static void 243require_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 */ 263static int 264setup(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 return (client_s); 352} 353 354/* Like write(2), but never returns less than the requested length */ 355static void 356write_all(int fd, const void *buf, size_t nbytes) 357{ 358 ssize_t r; 359 360 while (nbytes > 0) { 361 r = write(fd, buf, nbytes); 362 ATF_REQUIRE(r > 0); 363 nbytes -= r; 364 buf = (const char*)buf + r; 365 } 366} 367 368 369/* 370 * Test Cases 371 */ 372 373/* 374 * Read a file, specified by absolute pathname. 375 */ 376TFTPD_TC_DEFINE(abspath,) 377{ 378 int fd; 379 char command[1024]; 380 size_t pathlen; 381 char suffix[] = {'\0', 'o', 'c', 't', 'e', 't', '\0'}; 382 383 command[0] = 0; /* RRQ high byte */ 384 command[1] = 1; /* RRQ low byte */ 385 ATF_REQUIRE(getcwd(&command[2], sizeof(command) - 2) != NULL); 386 pathlen = strlcat(&command[2], "/abspath.txt", sizeof(command) - 2); 387 ATF_REQUIRE(pathlen + sizeof(suffix) < sizeof(command) - 2); 388 memmove(&command[2 + pathlen], suffix, sizeof(suffix)); 389 390 fd = open("abspath.txt", O_CREAT | O_RDONLY, 0644); 391 ATF_REQUIRE(fd >= 0); 392 close(fd); 393 394 send_bytes(command, 2 + pathlen + sizeof(suffix)); 395 recv_data(1, NULL, 0); 396 send_ack(1); 397} 398 399/* 400 * Attempt to read a file outside of the allowed directory(ies) 401 */ 402TFTPD_TC_DEFINE(dotdot,) 403{ 404 ATF_REQUIRE_EQ(mkdir("subdir", 0777), 0); 405 SEND_RRQ("../disallowed.txt", "octet"); 406 RECV_ERROR(2, "Access violation"); 407 s = setup(&addr, __COUNTER__); \ 408 SEND_RRQ("subdir/../../disallowed.txt", "octet"); 409 RECV_ERROR(2, "Access violation"); 410 s = setup(&addr, __COUNTER__); \ 411 SEND_RRQ("/etc/passwd", "octet"); 412 RECV_ERROR(2, "Access violation"); 413} 414 415/* 416 * With "-s", tftpd should chroot to the specified directory 417 */ 418TFTPD_TC_DEFINE(s_flag, atf_tc_set_md_var(tc, "require.user", "root");, 419 s_flag = true) 420{ 421 int fd; 422 char contents[] = "small"; 423 424 fd = open("small.txt", O_RDWR | O_CREAT, 0644); 425 ATF_REQUIRE(fd >= 0); 426 write_all(fd, contents, strlen(contents) + 1); 427 close(fd); 428 429 SEND_RRQ("/small.txt", "octet"); 430 recv_data(1, contents, strlen(contents) + 1); 431 send_ack(1); 432} 433 434/* 435 * Read a file, and simulate a dropped ACK packet 436 */ 437TFTPD_TC_DEFINE(rrq_dropped_ack,) 438{ 439 int fd; 440 char contents[] = "small"; 441 442 fd = open("small.txt", O_RDWR | O_CREAT, 0644); 443 ATF_REQUIRE(fd >= 0); 444 write_all(fd, contents, strlen(contents) + 1); 445 close(fd); 446 447 SEND_RRQ("small.txt", "octet"); 448 recv_data(1, contents, strlen(contents) + 1); 449 /* 450 * client "sends" the ack, but network drops it 451 * Eventually, tftpd should resend the data packet 452 */ 453 recv_data(1, contents, strlen(contents) + 1); 454 send_ack(1); 455} 456 457/* 458 * Read a file, and simulate a dropped DATA packet 459 */ 460TFTPD_TC_DEFINE(rrq_dropped_data,) 461{ 462 int fd; 463 size_t i; 464 uint32_t contents[192]; 465 char buffer[1024]; 466 467 for (i = 0; i < nitems(contents); i++) 468 contents[i] = i; 469 470 fd = open("medium.txt", O_RDWR | O_CREAT, 0644); 471 ATF_REQUIRE(fd >= 0); 472 write_all(fd, contents, sizeof(contents)); 473 close(fd); 474 475 SEND_RRQ("medium.txt", "octet"); 476 recv_data(1, (const char*)&contents[0], 512); 477 send_ack(1); 478 (void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL); 479 /* 480 * server "sends" the data, but network drops it 481 * Eventually, client should resend the last ACK 482 */ 483 send_ack(1); 484 recv_data(2, (const char*)&contents[128], 256); 485 send_ack(2); 486} 487 488/* 489 * Read a medium file, and simulate a duplicated ACK packet 490 */ 491TFTPD_TC_DEFINE(rrq_duped_ack,) 492{ 493 int fd; 494 size_t i; 495 uint32_t contents[192]; 496 497 for (i = 0; i < nitems(contents); i++) 498 contents[i] = i; 499 500 fd = open("medium.txt", O_RDWR | O_CREAT, 0644); 501 ATF_REQUIRE(fd >= 0); 502 write_all(fd, contents, sizeof(contents)); 503 close(fd); 504 505 SEND_RRQ("medium.txt", "octet"); 506 recv_data(1, (const char*)&contents[0], 512); 507 send_ack(1); 508 send_ack(1); /* Dupe an ACK packet */ 509 recv_data(2, (const char*)&contents[128], 256); 510 recv_data(2, (const char*)&contents[128], 256); 511 send_ack(2); 512} 513 514 515/* 516 * Attempt to read a file without read permissions 517 */ 518TFTPD_TC_DEFINE(rrq_eaccess,) 519{ 520 int fd; 521 522 fd = open("empty.txt", O_CREAT | O_RDONLY, 0000); 523 ATF_REQUIRE(fd >= 0); 524 close(fd); 525 526 SEND_RRQ("empty.txt", "octet"); 527 RECV_ERROR(2, "Access violation"); 528} 529 530/* 531 * Read an empty file 532 */ 533TFTPD_TC_DEFINE(rrq_empty,) 534{ 535 int fd; 536 537 fd = open("empty.txt", O_CREAT | O_RDONLY, 0644); 538 ATF_REQUIRE(fd >= 0); 539 close(fd); 540 541 SEND_RRQ("empty.txt", "octet"); 542 recv_data(1, NULL, 0); 543 send_ack(1); 544} 545 546/* 547 * Read a medium file of more than one block 548 */ 549TFTPD_TC_DEFINE(rrq_medium,) 550{ 551 int fd; 552 size_t i; 553 uint32_t contents[192]; 554 555 for (i = 0; i < nitems(contents); i++) 556 contents[i] = i; 557 558 fd = open("medium.txt", O_RDWR | O_CREAT, 0644); 559 ATF_REQUIRE(fd >= 0); 560 write_all(fd, contents, sizeof(contents)); 561 close(fd); 562 563 SEND_RRQ("medium.txt", "octet"); 564 recv_data(1, (const char*)&contents[0], 512); 565 send_ack(1); 566 recv_data(2, (const char*)&contents[128], 256); 567 send_ack(2); 568} 569 570/* 571 * Read a file in netascii format 572 */ 573TFTPD_TC_DEFINE(rrq_netascii,) 574{ 575 int fd; 576 char contents[] = "foo\nbar\rbaz\n"; 577 /* 578 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed 579 * is not intended 580 */ 581 char expected[] = "foo\r\nbar\r\0baz\r\n"; 582 583 fd = open("unix.txt", O_RDWR | O_CREAT, 0644); 584 ATF_REQUIRE(fd >= 0); 585 write_all(fd, contents, strlen(contents) + 1); 586 close(fd); 587 588 SEND_RRQ("unix.txt", "netascii"); 589 recv_data(1, expected, sizeof(expected)); 590 send_ack(1); 591} 592 593/* 594 * Read a file that doesn't exist 595 */ 596TFTPD_TC_DEFINE(rrq_nonexistent,) 597{ 598 SEND_RRQ("nonexistent.txt", "octet"); 599 RECV_ERROR(1, "File not found"); 600} 601 602/* 603 * Attempt to read a file whose name exceeds PATH_MAX 604 */ 605TFTPD_TC_DEFINE(rrq_path_max,) 606{ 607#define AReallyBigFileName \ 608 "AReallyBigFileNameXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 609 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 610 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 611 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 612 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 613 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 614 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 615 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 616 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 617 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 618 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 619 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 620 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 621 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 622 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 623 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"\ 624 ".txt" 625 ATF_REQUIRE_MSG(strlen(AReallyBigFileName) > PATH_MAX, 626 "Somebody increased PATH_MAX. Update the test"); 627 SEND_RRQ(AReallyBigFileName, "octet"); 628 RECV_ERROR(4, "Illegal TFTP operation"); 629} 630 631/* 632 * Read a small file of less than one block 633 */ 634TFTPD_TC_DEFINE(rrq_small,) 635{ 636 int fd; 637 char contents[] = "small"; 638 639 fd = open("small.txt", O_RDWR | O_CREAT, 0644); 640 ATF_REQUIRE(fd >= 0); 641 write_all(fd, contents, strlen(contents) + 1); 642 close(fd); 643 644 SEND_RRQ("small.txt", "octet"); 645 recv_data(1, contents, strlen(contents) + 1); 646 send_ack(1); 647} 648 649/* 650 * Try to transfer a file with an unknown mode. 651 */ 652TFTPD_TC_DEFINE(unknown_modes,) 653{ 654 SEND_RRQ("foo.txt", "ascii"); /* Misspelling of "ascii" */ 655 RECV_ERROR(4, "Illegal TFTP operation"); 656 s = setup(&addr, __COUNTER__); \ 657 SEND_RRQ("foo.txt", "binary"); /* Obsolete. Use "octet" instead */ 658 RECV_ERROR(4, "Illegal TFTP operation"); 659 s = setup(&addr, __COUNTER__); \ 660 SEND_RRQ("foo.txt", "en_US.UTF-8"); 661 RECV_ERROR(4, "Illegal TFTP operation"); 662 s = setup(&addr, __COUNTER__); \ 663 SEND_RRQ("foo.txt", "mail"); /* Obsolete in RFC-1350 */ 664 RECV_ERROR(4, "Illegal TFTP operation"); 665} 666 667/* 668 * Send an unknown opcode. tftpd should respond with the appropriate error 669 */ 670TFTPD_TC_DEFINE(unknown_opcode,) 671{ 672 /* Looks like an RRQ or WRQ request, but with a bad opcode */ 673 SEND_STR("\0\007foo.txt\0octet\0"); 674 atf_tc_expect_timeout("PR 226005 tftpd ignores bad opcodes but doesn't reject them"); 675 RECV_ERROR(4, "Illegal TFTP operation"); 676} 677 678/* 679 * Invoke tftpd with "-w" and write to a nonexistent file. 680 */ 681TFTPD_TC_DEFINE(w_flag,, w_flag = 1;) 682{ 683 int fd; 684 ssize_t r; 685 char contents[] = "small"; 686 char buffer[1024]; 687 size_t contents_len; 688 689 contents_len = strlen(contents) + 1; 690 SEND_WRQ("small.txt", "octet"); 691 recv_ack(0); 692 send_data(1, contents, contents_len); 693 recv_ack(1); 694 695 fd = open("small.txt", O_RDONLY); 696 ATF_REQUIRE(fd >= 0); 697 r = read(fd, buffer, sizeof(buffer)); 698 close(fd); 699 require_bufeq(contents, contents_len, buffer, r); 700} 701 702/* 703 * Write a medium file, and simulate a dropped ACK packet 704 */ 705TFTPD_TC_DEFINE(wrq_dropped_ack,) 706{ 707 int fd; 708 size_t i; 709 ssize_t r; 710 uint32_t contents[192]; 711 char buffer[1024]; 712 713 for (i = 0; i < nitems(contents); i++) 714 contents[i] = i; 715 716 fd = open("medium.txt", O_RDWR | O_CREAT, 0644); 717 ATF_REQUIRE(fd >= 0); 718 close(fd); 719 720 SEND_WRQ("medium.txt", "octet"); 721 recv_ack(0); 722 send_data(1, (const char*)&contents[0], 512); 723 /* 724 * Servers "sends" an ACK packet, but network drops it. 725 * Eventually, server should resend the last ACK 726 */ 727 (void) recvfrom(s, buffer, sizeof(buffer), 0, NULL, NULL); 728 recv_ack(1); 729 send_data(2, (const char*)&contents[128], 256); 730 recv_ack(2); 731 732 fd = open("medium.txt", O_RDONLY); 733 ATF_REQUIRE(fd >= 0); 734 r = read(fd, buffer, sizeof(buffer)); 735 close(fd); 736 require_bufeq((const char*)contents, 768, buffer, r); 737} 738 739/* 740 * Write a small file, and simulate a dropped DATA packet 741 */ 742TFTPD_TC_DEFINE(wrq_dropped_data,) 743{ 744 int fd; 745 ssize_t r; 746 char contents[] = "small"; 747 size_t contents_len; 748 char buffer[1024]; 749 750 fd = open("small.txt", O_RDWR | O_CREAT, 0644); 751 ATF_REQUIRE(fd >= 0); 752 close(fd); 753 contents_len = strlen(contents) + 1; 754 755 SEND_WRQ("small.txt", "octet"); 756 recv_ack(0); 757 /* 758 * Client "sends" a DATA packet, but network drops it. 759 * Eventually, server should resend the last ACK 760 */ 761 recv_ack(0); 762 send_data(1, contents, contents_len); 763 recv_ack(1); 764 765 fd = open("small.txt", O_RDONLY); 766 ATF_REQUIRE(fd >= 0); 767 r = read(fd, buffer, sizeof(buffer)); 768 close(fd); 769 require_bufeq(contents, contents_len, buffer, r); 770} 771 772/* 773 * Write a medium file, and simulate a duplicated DATA packet 774 */ 775TFTPD_TC_DEFINE(wrq_duped_data,) 776{ 777 int fd; 778 size_t i; 779 ssize_t r; 780 uint32_t contents[192]; 781 char buffer[1024]; 782 783 for (i = 0; i < nitems(contents); i++) 784 contents[i] = i; 785 786 fd = open("medium.txt", O_RDWR | O_CREAT, 0644); 787 ATF_REQUIRE(fd >= 0); 788 close(fd); 789 790 SEND_WRQ("medium.txt", "octet"); 791 recv_ack(0); 792 send_data(1, (const char*)&contents[0], 512); 793 send_data(1, (const char*)&contents[0], 512); 794 recv_ack(1); 795 recv_ack(1); 796 send_data(2, (const char*)&contents[128], 256); 797 recv_ack(2); 798 799 fd = open("medium.txt", O_RDONLY); 800 ATF_REQUIRE(fd >= 0); 801 r = read(fd, buffer, sizeof(buffer)); 802 close(fd); 803 require_bufeq((const char*)contents, 768, buffer, r); 804} 805 806/* 807 * Attempt to write a file without write permissions 808 */ 809TFTPD_TC_DEFINE(wrq_eaccess,) 810{ 811 int fd; 812 813 fd = open("empty.txt", O_CREAT | O_RDONLY, 0440); 814 ATF_REQUIRE(fd >= 0); 815 close(fd); 816 817 SEND_WRQ("empty.txt", "octet"); 818 atf_tc_expect_fail("PR 225996 tftpd doesn't abort on a WRQ access " 819 "violation"); 820 RECV_ERROR(2, "Access violation"); 821} 822 823/* 824 * Attempt to write a file without world write permissions, but with world 825 * read permissions 826 */ 827TFTPD_TC_DEFINE(wrq_eaccess_world_readable,) 828{ 829 int fd; 830 831 fd = open("empty.txt", O_CREAT | O_RDONLY, 0444); 832 ATF_REQUIRE(fd >= 0); 833 close(fd); 834 835 SEND_WRQ("empty.txt", "octet"); 836 atf_tc_expect_fail("PR 226004 with relative pathnames, tftpd doesn't validate world writability"); 837 RECV_ERROR(2, "Access violation"); 838} 839 840 841/* 842 * Write a medium file of more than one block 843 */ 844TFTPD_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 */ 876TFTPD_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 */ 910TFTPD_TC_DEFINE(wrq_nonexistent,) 911{ 912 SEND_WRQ("nonexistent.txt", "octet"); 913 atf_tc_expect_fail("PR 225996 tftpd doesn't abort on a WRQ access " 914 "violation"); 915 RECV_ERROR(1, "File not found"); 916} 917 918/* 919 * Write a small file of less than one block 920 */ 921TFTPD_TC_DEFINE(wrq_small,) 922{ 923 int fd; 924 ssize_t r; 925 char contents[] = "small"; 926 size_t contents_len; 927 char buffer[1024]; 928 929 fd = open("small.txt", O_RDWR | O_CREAT, 0666); 930 ATF_REQUIRE(fd >= 0); 931 close(fd); 932 contents_len = strlen(contents) + 1; 933 934 SEND_WRQ("small.txt", "octet"); 935 recv_ack(0); 936 send_data(1, contents, contents_len); 937 recv_ack(1); 938 939 fd = open("small.txt", O_RDONLY); 940 ATF_REQUIRE(fd >= 0); 941 r = read(fd, buffer, sizeof(buffer)); 942 close(fd); 943 require_bufeq(contents, contents_len, buffer, r); 944} 945 946/* 947 * Write an empty file over a non-empty one 948 */ 949TFTPD_TC_DEFINE(wrq_truncate,) 950{ 951 int fd; 952 char contents[] = "small"; 953 struct stat sb; 954 955 fd = open("small.txt", O_RDWR | O_CREAT, 0666); 956 ATF_REQUIRE(fd >= 0); 957 write_all(fd, contents, strlen(contents) + 1); 958 close(fd); 959 960 SEND_WRQ("small.txt", "octet"); 961 recv_ack(0); 962 send_data(1, NULL, 0); 963 recv_ack(1); 964 965 ATF_REQUIRE_EQ(stat("small.txt", &sb), 0); 966 ATF_REQUIRE_EQ(sb.st_size, 0); 967} 968 969 970/* 971 * Main 972 */ 973 974ATF_TP_ADD_TCS(tp) 975{ 976 TFTPD_TC_ADD(tp, abspath); 977 TFTPD_TC_ADD(tp, dotdot); 978 TFTPD_TC_ADD(tp, s_flag); 979 TFTPD_TC_ADD(tp, rrq_dropped_ack); 980 TFTPD_TC_ADD(tp, rrq_dropped_data); 981 TFTPD_TC_ADD(tp, rrq_duped_ack); 982 TFTPD_TC_ADD(tp, rrq_eaccess); 983 TFTPD_TC_ADD(tp, rrq_empty); 984 TFTPD_TC_ADD(tp, rrq_medium); 985 TFTPD_TC_ADD(tp, rrq_netascii); 986 TFTPD_TC_ADD(tp, rrq_nonexistent); 987 TFTPD_TC_ADD(tp, rrq_path_max); 988 TFTPD_TC_ADD(tp, rrq_small); 989 TFTPD_TC_ADD(tp, unknown_modes); 990 TFTPD_TC_ADD(tp, unknown_opcode); 991 TFTPD_TC_ADD(tp, w_flag); 992 TFTPD_TC_ADD(tp, wrq_dropped_ack); 993 TFTPD_TC_ADD(tp, wrq_dropped_data); 994 TFTPD_TC_ADD(tp, wrq_duped_data); 995 TFTPD_TC_ADD(tp, wrq_eaccess); 996 TFTPD_TC_ADD(tp, wrq_eaccess_world_readable); 997 TFTPD_TC_ADD(tp, wrq_medium); 998 TFTPD_TC_ADD(tp, wrq_netascii); 999 TFTPD_TC_ADD(tp, wrq_nonexistent); 1000 TFTPD_TC_ADD(tp, wrq_small); 1001 TFTPD_TC_ADD(tp, wrq_truncate); 1002 1003 return (atf_no_error()); 1004} 1005