functional.c revision 337248
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 337248 2018-08-03 14:18:02Z 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 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 */ 359static void 360write_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 */ 380TFTPD_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 */ 406TFTPD_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 */ 422TFTPD_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 */ 441TFTPD_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 */ 464TFTPD_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 */ 495TFTPD_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 */ 522TFTPD_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 */ 537TFTPD_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 */ 553TFTPD_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 */ 577TFTPD_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 */ 600TFTPD_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 */ 609TFTPD_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 */ 638TFTPD_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 */ 656TFTPD_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 */ 674TFTPD_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 atf_tc_expect_timeout("PR 226005 tftpd ignores bad opcodes but doesn't reject them"); 679 RECV_ERROR(4, "Illegal TFTP operation"); 680} 681 682/* 683 * Invoke tftpd with "-w" and write to a nonexistent file. 684 */ 685TFTPD_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 */ 709TFTPD_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 */ 746TFTPD_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 */ 779TFTPD_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 */ 813TFTPD_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 atf_tc_expect_fail("PR 225996 tftpd doesn't abort on a WRQ access " 823 "violation"); 824 RECV_ERROR(2, "Access violation"); 825} 826 827/* 828 * Attempt to write a file without world write permissions, but with world 829 * read permissions 830 */ 831TFTPD_TC_DEFINE(wrq_eaccess_world_readable,) 832{ 833 int fd; 834 835 fd = open("empty.txt", O_CREAT | O_RDONLY, 0444); 836 ATF_REQUIRE(fd >= 0); 837 close(fd); 838 839 SEND_WRQ("empty.txt", "octet"); 840 atf_tc_expect_fail("PR 225996 tftpd doesn't abort on a WRQ access " 841 "violation"); 842 RECV_ERROR(2, "Access violation"); 843} 844 845 846/* 847 * Write a medium file of more than one block 848 */ 849TFTPD_TC_DEFINE(wrq_medium,) 850{ 851 int fd; 852 size_t i; 853 ssize_t r; 854 uint32_t contents[192]; 855 char buffer[1024]; 856 857 for (i = 0; i < nitems(contents); i++) 858 contents[i] = i; 859 860 fd = open("medium.txt", O_RDWR | O_CREAT, 0666); 861 ATF_REQUIRE(fd >= 0); 862 close(fd); 863 864 SEND_WRQ("medium.txt", "octet"); 865 recv_ack(0); 866 send_data(1, (const char*)&contents[0], 512); 867 recv_ack(1); 868 send_data(2, (const char*)&contents[128], 256); 869 recv_ack(2); 870 871 fd = open("medium.txt", O_RDONLY); 872 ATF_REQUIRE(fd >= 0); 873 r = read(fd, buffer, sizeof(buffer)); 874 close(fd); 875 require_bufeq((const char*)contents, 768, buffer, r); 876} 877 878/* 879 * Write a file in netascii format 880 */ 881TFTPD_TC_DEFINE(wrq_netascii,) 882{ 883 int fd; 884 ssize_t r; 885 /* 886 * Weirdly, RFC-764 says that CR must be followed by NUL if a line feed 887 * is not intended 888 */ 889 char contents[] = "foo\r\nbar\r\0baz\r\n"; 890 char expected[] = "foo\nbar\rbaz\n"; 891 size_t contents_len; 892 char buffer[1024]; 893 894 fd = open("unix.txt", O_RDWR | O_CREAT, 0666); 895 ATF_REQUIRE(fd >= 0); 896 close(fd); 897 contents_len = sizeof(contents); 898 899 SEND_WRQ("unix.txt", "netascii"); 900 recv_ack(0); 901 send_data(1, contents, contents_len); 902 recv_ack(1); 903 904 fd = open("unix.txt", O_RDONLY); 905 ATF_REQUIRE(fd >= 0); 906 r = read(fd, buffer, sizeof(buffer)); 907 close(fd); 908 require_bufeq(expected, sizeof(expected), buffer, r); 909} 910 911/* 912 * Attempt to write to a nonexistent file. With the default options, this 913 * isn't allowed. 914 */ 915TFTPD_TC_DEFINE(wrq_nonexistent,) 916{ 917 SEND_WRQ("nonexistent.txt", "octet"); 918 atf_tc_expect_fail("PR 225996 tftpd doesn't abort on a WRQ access " 919 "violation"); 920 RECV_ERROR(1, "File not found"); 921} 922 923/* 924 * Write a small file of less than one block 925 */ 926TFTPD_TC_DEFINE(wrq_small,) 927{ 928 int fd; 929 ssize_t r; 930 char contents[] = "small"; 931 size_t contents_len; 932 char buffer[1024]; 933 934 fd = open("small.txt", O_RDWR | O_CREAT, 0666); 935 ATF_REQUIRE(fd >= 0); 936 close(fd); 937 contents_len = strlen(contents) + 1; 938 939 SEND_WRQ("small.txt", "octet"); 940 recv_ack(0); 941 send_data(1, contents, contents_len); 942 recv_ack(1); 943 944 fd = open("small.txt", O_RDONLY); 945 ATF_REQUIRE(fd >= 0); 946 r = read(fd, buffer, sizeof(buffer)); 947 close(fd); 948 require_bufeq(contents, contents_len, buffer, r); 949} 950 951/* 952 * Write an empty file over a non-empty one 953 */ 954TFTPD_TC_DEFINE(wrq_truncate,) 955{ 956 int fd; 957 char contents[] = "small"; 958 struct stat sb; 959 960 fd = open("small.txt", O_RDWR | O_CREAT, 0666); 961 ATF_REQUIRE(fd >= 0); 962 write_all(fd, contents, strlen(contents) + 1); 963 close(fd); 964 965 SEND_WRQ("small.txt", "octet"); 966 recv_ack(0); 967 send_data(1, NULL, 0); 968 recv_ack(1); 969 970 ATF_REQUIRE_EQ(stat("small.txt", &sb), 0); 971 ATF_REQUIRE_EQ(sb.st_size, 0); 972} 973 974 975/* 976 * Main 977 */ 978 979ATF_TP_ADD_TCS(tp) 980{ 981 TFTPD_TC_ADD(tp, abspath); 982 TFTPD_TC_ADD(tp, dotdot); 983 TFTPD_TC_ADD(tp, s_flag); 984 TFTPD_TC_ADD(tp, rrq_dropped_ack); 985 TFTPD_TC_ADD(tp, rrq_dropped_data); 986 TFTPD_TC_ADD(tp, rrq_duped_ack); 987 TFTPD_TC_ADD(tp, rrq_eaccess); 988 TFTPD_TC_ADD(tp, rrq_empty); 989 TFTPD_TC_ADD(tp, rrq_medium); 990 TFTPD_TC_ADD(tp, rrq_netascii); 991 TFTPD_TC_ADD(tp, rrq_nonexistent); 992 TFTPD_TC_ADD(tp, rrq_path_max); 993 TFTPD_TC_ADD(tp, rrq_small); 994 TFTPD_TC_ADD(tp, unknown_modes); 995 TFTPD_TC_ADD(tp, unknown_opcode); 996 TFTPD_TC_ADD(tp, w_flag); 997 TFTPD_TC_ADD(tp, wrq_dropped_ack); 998 TFTPD_TC_ADD(tp, wrq_dropped_data); 999 TFTPD_TC_ADD(tp, wrq_duped_data); 1000 TFTPD_TC_ADD(tp, wrq_eaccess); 1001 TFTPD_TC_ADD(tp, wrq_eaccess_world_readable); 1002 TFTPD_TC_ADD(tp, wrq_medium); 1003 TFTPD_TC_ADD(tp, wrq_netascii); 1004 TFTPD_TC_ADD(tp, wrq_nonexistent); 1005 TFTPD_TC_ADD(tp, wrq_small); 1006 TFTPD_TC_ADD(tp, wrq_truncate); 1007 1008 return (atf_no_error()); 1009} 1010