1package ProFTPD::Tests::Config::Directory::Limits; 2 3use lib qw(t/lib); 4use base qw(ProFTPD::TestSuite::Child); 5use strict; 6 7use Cwd; 8use File::Path qw(mkpath); 9use File::Spec; 10use IO::Handle; 11 12use ProFTPD::TestSuite::FTP; 13use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); 14 15$| = 1; 16 17my $order = 0; 18 19# A fun collection of various <Directory> and <Limit> configurations 20 21my $TESTS = { 22 limits_with_glob_then_nonglob_dirs_for_same_path => { 23 order => ++$order, 24 test_class => [qw(forking)], 25 }, 26 27 limits_with_nonglob_then_glob_dirs_for_same_path => { 28 order => ++$order, 29 test_class => [qw(forking)], 30 }, 31 32 limits_with_glob_denied_delete_bug3146 => { 33 order => ++$order, 34 test_class => [qw(bug forking)], 35 }, 36 37 limits_without_glob_denied_delete_bug3146 => { 38 order => ++$order, 39 test_class => [qw(bug forking)], 40 }, 41 42 limits_commands_comma_space_delimited_deferred_paths_bug3147 => { 43 order => ++$order, 44 test_class => [qw(bug forking)], 45 }, 46 47 limits_commands_comma_delimited_deferred_paths_bug3147 => { 48 order => ++$order, 49 test_class => [qw(bug forking)], 50 }, 51 52 limits_commands_no_commas_deferred_paths_bug3147 => { 53 order => ++$order, 54 test_class => [qw(bug forking)], 55 }, 56 57 limits_rename_dir_ok_write_denied => { 58 order => ++$order, 59 test_class => [qw(forking)], 60 }, 61 62 limits_rename_dir_failed_rnfr_denied => { 63 order => ++$order, 64 test_class => [qw(forking)], 65 }, 66 67 limits_one_char_dir_bug3337 => { 68 order => ++$order, 69 test_class => [qw(bug forking)], 70 }, 71 72 limits_symlink_dir_bug3166 => { 73 order => ++$order, 74 test_class => [qw(bug forking rootprivs)], 75 }, 76 77 limits_anon_dir_abs_path_bug3283 => { 78 order => ++$order, 79 test_class => [qw(bug forking rootprivs)], 80 }, 81 82 limits_with_multi_globs_denied_delete => { 83 order => ++$order, 84 test_class => [qw(forking)], 85 }, 86 87 limits_retr_bug3915 => { 88 order => ++$order, 89 test_class => [qw(bug forking)], 90 }, 91 92 limits_retr_bug3915_chrooted => { 93 order => ++$order, 94 test_class => [qw(bug forking rootprivs)], 95 }, 96 97 limits_stor_with_multiple_groups_chrooted => { 98 order => ++$order, 99 test_class => [qw(bug forking rootprivs)], 100 }, 101 102}; 103 104sub new { 105 return shift()->SUPER::new(@_); 106} 107 108sub list_tests { 109 return testsuite_get_runnable_tests($TESTS); 110} 111 112sub limits_with_glob_then_nonglob_dirs_for_same_path { 113 my $self = shift; 114 my $tmpdir = $self->{tmpdir}; 115 116 my $config_file = "$tmpdir/dir.conf"; 117 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 118 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 119 120 my $log_file = test_get_logfile(); 121 122 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 123 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 124 125 my $user = 'proftpd'; 126 my $passwd = 'test'; 127 my $group = 'ftpd'; 128 my $home_dir = File::Spec->rel2abs("$tmpdir/home/users/$user"); 129 my $uid = 500; 130 my $gid = 500; 131 132 mkpath($home_dir); 133 134 # Make sure that, if we're running as root, that the home directory has 135 # permissions/privs set for the account we create 136 if ($< == 0) { 137 unless (chmod(0755, $home_dir)) { 138 die("Can't set perms on $home_dir to 0755: $!"); 139 } 140 141 unless (chown($uid, $gid, $home_dir)) { 142 die("Can't set owner of $home_dir to $uid/$gid: $!"); 143 } 144 } 145 146 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 147 '/bin/bash'); 148 auth_group_write($auth_group_file, $group, $gid, $user); 149 150 my $abs_tmp_dir = File::Spec->rel2abs($tmpdir); 151 152 my $config = { 153 PidFile => $pid_file, 154 ScoreboardFile => $scoreboard_file, 155 SystemLog => $log_file, 156 TraceLog => $log_file, 157 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 158 159 AuthUserFile => $auth_user_file, 160 AuthGroupFile => $auth_group_file, 161 162 DefaultChdir => '~', 163 164 IfModules => { 165 'mod_delay.c' => { 166 DelayEngine => 'off', 167 }, 168 }, 169 }; 170 171 my ($port, $config_user, $config_group) = config_write($config_file, $config); 172 173 # In order to ensure that the <Directory> sections appear in the desired 174 # order in the config file, we write them out here. The config_write() 175 # function, whilst useful, uses hashrefs, which means the ordering of the 176 # keys is not necessarily predictable. 177 if (open(my $fh, ">> $config_file")) { 178 print $fh <<EOD; 179<Directory $abs_tmp_dir/home/*/proftpd> 180 <Limit ALL> 181 DenyAll 182 </Limit> 183</Directory> 184<Directory $abs_tmp_dir/home/users/proftpd> 185 <Limit ALL> 186 AllowAll 187 </Limit> 188</Directory> 189EOD 190 close($fh); 191 192 } else { 193 die("Can't read $config_file: $!"); 194 } 195 196 # Open pipes, for use between the parent and child processes. Specifically, 197 # the child will indicate when it's done with its test by writing a message 198 # to the parent. 199 my ($rfh, $wfh); 200 unless (pipe($rfh, $wfh)) { 201 die("Can't open pipe: $!"); 202 } 203 204 my $ex; 205 206 # Fork child 207 $self->handle_sigchld(); 208 defined(my $pid = fork()) or die("Can't fork: $!"); 209 if ($pid) { 210 eval { 211 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 212 $client->login($user, $passwd); 213 214 my ($resp_code, $resp_msg); 215 216 my $conn = $client->stor_raw('test.txt'); 217 unless ($conn) { 218 die("Failed to STOR test.txt: " . $client->response_code() . " " . 219 $client->response_msg()); 220 }; 221 222 my $buf = "Hello, World\n"; 223 $conn->write($buf, length($buf)); 224 $conn->close(); 225 226 $resp_code = $client->response_code(); 227 $resp_msg = $client->response_msg(); 228 229 my $expected; 230 231 $expected = 226; 232 $self->assert($expected == $resp_code, 233 test_msg("Expected $expected, got $resp_code")); 234 235 $expected = "Transfer complete"; 236 $self->assert($expected eq $resp_msg, 237 test_msg("Expected '$expected', got '$resp_msg'")); 238 }; 239 240 if ($@) { 241 $ex = $@; 242 } 243 244 $wfh->print("done\n"); 245 $wfh->flush(); 246 247 } else { 248 eval { server_wait($config_file, $rfh) }; 249 if ($@) { 250 warn($@); 251 exit 1; 252 } 253 254 exit 0; 255 } 256 257 # Stop server 258 server_stop($pid_file); 259 260 $self->assert_child_ok($pid); 261 262 if ($ex) { 263 test_append_logfile($log_file, $ex); 264 unlink($log_file); 265 266 die($ex); 267 } 268 269 unlink($log_file); 270} 271 272sub limits_with_nonglob_then_glob_dirs_for_same_path { 273 my $self = shift; 274 my $tmpdir = $self->{tmpdir}; 275 276 my $config_file = "$tmpdir/dir.conf"; 277 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 278 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 279 280 my $log_file = test_get_logfile(); 281 282 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 283 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 284 285 my $user = 'proftpd'; 286 my $passwd = 'test'; 287 my $group = 'ftpd'; 288 my $home_dir = File::Spec->rel2abs("$tmpdir/home/users/$user"); 289 my $uid = 500; 290 my $gid = 500; 291 292 mkpath($home_dir); 293 294 # Make sure that, if we're running as root, that the home directory has 295 # permissions/privs set for the account we create 296 if ($< == 0) { 297 unless (chmod(0755, $home_dir)) { 298 die("Can't set perms on $home_dir to 0755: $!"); 299 } 300 301 unless (chown($uid, $gid, $home_dir)) { 302 die("Can't set owner of $home_dir to $uid/$gid: $!"); 303 } 304 } 305 306 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 307 '/bin/bash'); 308 auth_group_write($auth_group_file, $group, $gid, $user); 309 310 my $abs_tmp_dir = File::Spec->rel2abs($tmpdir); 311 312 my $config = { 313 PidFile => $pid_file, 314 ScoreboardFile => $scoreboard_file, 315 SystemLog => $log_file, 316 TraceLog => $log_file, 317 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 318 319 AuthUserFile => $auth_user_file, 320 AuthGroupFile => $auth_group_file, 321 322 DefaultChdir => '~', 323 324 IfModules => { 325 'mod_delay.c' => { 326 DelayEngine => 'off', 327 }, 328 }, 329 }; 330 331 my ($port, $config_user, $config_group) = config_write($config_file, $config); 332 333 # In order to ensure that the <Directory> sections appear in the desired 334 # order in the config file, we write them out here. The config_write() 335 # function, whilst useful, uses hashrefs, which means the ordering of the 336 # keys is not necessarily predictable. 337 if (open(my $fh, ">> $config_file")) { 338 print $fh <<EOD; 339<Directory $abs_tmp_dir/home/users/proftpd> 340 <Limit ALL> 341 AllowAll 342 </Limit> 343</Directory> 344<Directory $abs_tmp_dir/home/*/proftpd> 345 <Limit ALL> 346 DenyAll 347 </Limit> 348</Directory> 349EOD 350 close($fh); 351 352 } else { 353 die("Can't read $config_file: $!"); 354 } 355 356 # Open pipes, for use between the parent and child processes. Specifically, 357 # the child will indicate when it's done with its test by writing a message 358 # to the parent. 359 my ($rfh, $wfh); 360 unless (pipe($rfh, $wfh)) { 361 die("Can't open pipe: $!"); 362 } 363 364 my $ex; 365 366 # Fork child 367 $self->handle_sigchld(); 368 defined(my $pid = fork()) or die("Can't fork: $!"); 369 if ($pid) { 370 eval { 371 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 372 $client->login($user, $passwd); 373 374 my ($resp_code, $resp_msg); 375 376 my $conn = $client->stor_raw('test.txt'); 377 if ($conn) { 378 die("STOR test.txt succeeded unexpectedly"); 379 }; 380 381 $resp_code = $client->response_code(); 382 $resp_msg = $client->response_msg(); 383 384 my $expected; 385 386 $expected = 550; 387 $self->assert($expected == $resp_code, 388 test_msg("Expected $expected, got $resp_code")); 389 390 $expected = "test.txt: Operation not permitted"; 391 $self->assert($expected eq $resp_msg, 392 test_msg("Expected '$expected', got '$resp_msg'")); 393 }; 394 395 if ($@) { 396 $ex = $@; 397 } 398 399 $wfh->print("done\n"); 400 $wfh->flush(); 401 402 } else { 403 eval { server_wait($config_file, $rfh) }; 404 if ($@) { 405 warn($@); 406 exit 1; 407 } 408 409 exit 0; 410 } 411 412 # Stop server 413 server_stop($pid_file); 414 415 $self->assert_child_ok($pid); 416 417 if ($ex) { 418 test_append_logfile($log_file, $ex); 419 unlink($log_file); 420 421 die($ex); 422 } 423 424 unlink($log_file); 425} 426 427sub limits_with_glob_denied_delete_bug3146 { 428 my $self = shift; 429 my $tmpdir = $self->{tmpdir}; 430 431 # This config is from: 432 # 433 # http://forums.proftpd.org/smf/index.php/topic,3491.0 434 # http://bugs.proftpd.org/show_bug.cgi?id=3146 435 436 my $config_file = "$tmpdir/dir.conf"; 437 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 438 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 439 440 my $log_file = test_get_logfile(); 441 442 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 443 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 444 445 my $user = 'proftpd'; 446 my $passwd = 'test'; 447 my $group = 'ftpd'; 448 my $home_dir = File::Spec->rel2abs("$tmpdir/home/users/$user"); 449 my $uid = 500; 450 my $gid = 500; 451 452 mkpath($home_dir); 453 454 my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); 455 if (open(my $fh, "> $test_file")) { 456 print $fh "Hello, World!\n"; 457 458 unless (close($fh)) { 459 die("Can't write $test_file: $!"); 460 } 461 462 } else { 463 die("Can't open $test_file: $!"); 464 } 465 466 # Make sure that, if we're running as root, that the home directory has 467 # permissions/privs set for the account we create 468 if ($< == 0) { 469 unless (chmod(0755, $home_dir)) { 470 die("Can't set perms on $home_dir to 0755: $!"); 471 } 472 473 unless (chown($uid, $gid, $home_dir)) { 474 die("Can't set owner of $home_dir to $uid/$gid: $!"); 475 } 476 } 477 478 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 479 '/bin/bash'); 480 auth_group_write($auth_group_file, $group, $gid, $user); 481 482 my $abs_tmp_dir = File::Spec->rel2abs($tmpdir); 483 484 my $config = { 485 PidFile => $pid_file, 486 ScoreboardFile => $scoreboard_file, 487 SystemLog => $log_file, 488 TraceLog => $log_file, 489 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 490 491 AuthUserFile => $auth_user_file, 492 AuthGroupFile => $auth_group_file, 493 494 DefaultChdir => '~', 495 496 Directory => { 497 "$abs_tmp_dir/home/*/proftpd" => { 498 Limit => { 499 'ALL' => { 500 DenyAll => '', 501 }, 502 503 'CDUP CWD XCWD XCUP LIST NLST' => { 504 AllowAll => '', 505 }, 506 507 'STOR STOU' => { 508 AllowAll => '', 509 }, 510 }, 511 }, 512 }, 513 514 IfModules => { 515 'mod_delay.c' => { 516 DelayEngine => 'off', 517 }, 518 }, 519 }; 520 521 my ($port, $config_user, $config_group) = config_write($config_file, $config); 522 523 # Open pipes, for use between the parent and child processes. Specifically, 524 # the child will indicate when it's done with its test by writing a message 525 # to the parent. 526 my ($rfh, $wfh); 527 unless (pipe($rfh, $wfh)) { 528 die("Can't open pipe: $!"); 529 } 530 531 my $ex; 532 533 # Fork child 534 $self->handle_sigchld(); 535 defined(my $pid = fork()) or die("Can't fork: $!"); 536 if ($pid) { 537 eval { 538 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 539 $client->login($user, $passwd); 540 541 my ($resp_code, $resp_msg); 542 eval { $client->dele('test.txt') }; 543 unless ($@) { 544 die("DELE succeeded unexpectedly"); 545 546 } else { 547 $resp_code = $client->response_code(); 548 $resp_msg = $client->response_msg(); 549 } 550 551 my $expected; 552 553 $expected = 550; 554 $self->assert($expected == $resp_code, 555 test_msg("Expected $expected, got $resp_code")); 556 557 $expected = "test.txt: Operation not permitted"; 558 $self->assert($expected eq $resp_msg, 559 test_msg("Expected '$expected', got '$resp_msg'")); 560 }; 561 562 if ($@) { 563 $ex = $@; 564 } 565 566 $wfh->print("done\n"); 567 $wfh->flush(); 568 569 } else { 570 eval { server_wait($config_file, $rfh) }; 571 if ($@) { 572 warn($@); 573 exit 1; 574 } 575 576 exit 0; 577 } 578 579 # Stop server 580 server_stop($pid_file); 581 582 $self->assert_child_ok($pid); 583 584 if ($ex) { 585 test_append_logfile($log_file, $ex); 586 unlink($log_file); 587 588 die($ex); 589 } 590 591 unlink($log_file); 592} 593 594sub limits_without_glob_denied_delete_bug3146 { 595 my $self = shift; 596 my $tmpdir = $self->{tmpdir}; 597 598 # This config is from: 599 # 600 # http://forums.proftpd.org/smf/index.php/topic,3491.0 601 # 602 # only without the glob pattern, to check for regressions. 603 604 my $config_file = "$tmpdir/dir.conf"; 605 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 606 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 607 608 my $log_file = test_get_logfile(); 609 610 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 611 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 612 613 my $user = 'proftpd'; 614 my $passwd = 'test'; 615 my $group = 'ftpd'; 616 my $home_dir = File::Spec->rel2abs("$tmpdir/home/users/$user"); 617 my $uid = 500; 618 my $gid = 500; 619 620 mkpath($home_dir); 621 622 my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); 623 if (open(my $fh, "> $test_file")) { 624 print $fh "Hello, World!\n"; 625 626 unless (close($fh)) { 627 die("Can't write $test_file: $!"); 628 } 629 630 } else { 631 die("Can't open $test_file: $!"); 632 } 633 634 # Make sure that, if we're running as root, that the home directory has 635 # permissions/privs set for the account we create 636 if ($< == 0) { 637 unless (chmod(0755, $home_dir)) { 638 die("Can't set perms on $home_dir to 0755: $!"); 639 } 640 641 unless (chown($uid, $gid, $home_dir)) { 642 die("Can't set owner of $home_dir to $uid/$gid: $!"); 643 } 644 } 645 646 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 647 '/bin/bash'); 648 auth_group_write($auth_group_file, $group, $gid, $user); 649 650 my $abs_tmp_dir = File::Spec->rel2abs($tmpdir); 651 652 my $config = { 653 PidFile => $pid_file, 654 ScoreboardFile => $scoreboard_file, 655 SystemLog => $log_file, 656 TraceLog => $log_file, 657 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 658 659 AuthUserFile => $auth_user_file, 660 AuthGroupFile => $auth_group_file, 661 662 DefaultChdir => '~', 663 664 Directory => { 665 "$abs_tmp_dir/home/users/proftpd" => { 666 Limit => { 667 'ALL' => { 668 DenyAll => '', 669 }, 670 671 'CDUP CWD XCWD XCUP LIST NLST' => { 672 AllowAll => '', 673 }, 674 675 'STOR STOU' => { 676 AllowAll => '', 677 }, 678 }, 679 }, 680 }, 681 682 IfModules => { 683 'mod_delay.c' => { 684 DelayEngine => 'off', 685 }, 686 }, 687 }; 688 689 my ($port, $config_user, $config_group) = config_write($config_file, $config); 690 691 # Open pipes, for use between the parent and child processes. Specifically, 692 # the child will indicate when it's done with its test by writing a message 693 # to the parent. 694 my ($rfh, $wfh); 695 unless (pipe($rfh, $wfh)) { 696 die("Can't open pipe: $!"); 697 } 698 699 my $ex; 700 701 # Fork child 702 $self->handle_sigchld(); 703 defined(my $pid = fork()) or die("Can't fork: $!"); 704 if ($pid) { 705 eval { 706 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 707 $client->login($user, $passwd); 708 709 my ($resp_code, $resp_msg); 710 eval { $client->dele('test.txt') }; 711 unless ($@) { 712 die("DELE succeeded unexpectedly"); 713 714 } else { 715 $resp_code = $client->response_code(); 716 $resp_msg = $client->response_msg(); 717 } 718 719 my $expected; 720 721 $expected = 550; 722 $self->assert($expected == $resp_code, 723 test_msg("Expected $expected, got $resp_code")); 724 725 $expected = "test.txt: Operation not permitted"; 726 $self->assert($expected eq $resp_msg, 727 test_msg("Expected '$expected', got '$resp_msg'")); 728 }; 729 730 if ($@) { 731 $ex = $@; 732 } 733 734 $wfh->print("done\n"); 735 $wfh->flush(); 736 737 } else { 738 eval { server_wait($config_file, $rfh) }; 739 if ($@) { 740 warn($@); 741 exit 1; 742 } 743 744 exit 0; 745 } 746 747 # Stop server 748 server_stop($pid_file); 749 750 $self->assert_child_ok($pid); 751 752 if ($ex) { 753 test_append_logfile($log_file, $ex); 754 unlink($log_file); 755 756 die($ex); 757 } 758 759 unlink($log_file); 760} 761 762sub limits_commands_comma_space_delimited_deferred_paths_bug3147 { 763 my $self = shift; 764 my $tmpdir = $self->{tmpdir}; 765 766 # This config is from: 767 # 768 # http://forums.proftpd.org/smf/index.php/topic,3648.0 769 # http://bugs.proftpd.org/show_bug.cgi?id=3147 770 771 my $config_file = "$tmpdir/dir.conf"; 772 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 773 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 774 775 my $log_file = test_get_logfile(); 776 777 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 778 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 779 780 my $user = 'proftpd'; 781 my $passwd = 'test'; 782 my $group = 'ftpd'; 783 my $home_dir = File::Spec->rel2abs("$tmpdir/home/$user"); 784 my $uid = 500; 785 my $gid = 500; 786 787 mkpath($home_dir); 788 789 my $sub_dir = File::Spec->rel2abs("$tmpdir/home/$user/upload"); 790 mkpath($sub_dir); 791 792 # Make sure that, if we're running as root, that the home directory has 793 # permissions/privs set for the account we create 794 if ($< == 0) { 795 unless (chmod(0755, $home_dir, $sub_dir)) { 796 die("Can't set perms on $home_dir, $sub_dir to 0755: $!"); 797 } 798 799 unless (chown($uid, $gid, $home_dir, $sub_dir)) { 800 die("Can't set owner of $home_dir, $sub_dir to $uid/$gid: $!"); 801 } 802 } 803 804 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 805 '/bin/bash'); 806 auth_group_write($auth_group_file, $group, $gid, $user); 807 808 my $config = { 809 PidFile => $pid_file, 810 ScoreboardFile => $scoreboard_file, 811 SystemLog => $log_file, 812 TraceLog => $log_file, 813 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 814 815 AuthUserFile => $auth_user_file, 816 AuthGroupFile => $auth_group_file, 817 818 DefaultChdir => '~', 819 820 Directory => { 821 "~$user" => { 822 Limit => { 823 'WRITE' => { 824 DenyAll => '', 825 }, 826 }, 827 }, 828 829 # Bug#3147 happened because proftpd's parsing of the list of commands 830 # in the <Limit> section was not properly handling commas after the 831 # command names. 832 833 "~$user/upload" => { 834 Limit => { 835 'STOR, APPE, MKD, RMD, RNTO, STOU, XMKD, XRMD' => { 836 AllowAll => '', 837 }, 838 }, 839 }, 840 }, 841 842 IfModules => { 843 'mod_delay.c' => { 844 DelayEngine => 'off', 845 }, 846 }, 847 }; 848 849 my ($port, $config_user, $config_group) = config_write($config_file, $config); 850 851 # Open pipes, for use between the parent and child processes. Specifically, 852 # the child will indicate when it's done with its test by writing a message 853 # to the parent. 854 my ($rfh, $wfh); 855 unless (pipe($rfh, $wfh)) { 856 die("Can't open pipe: $!"); 857 } 858 859 my $ex; 860 861 # Fork child 862 $self->handle_sigchld(); 863 defined(my $pid = fork()) or die("Can't fork: $!"); 864 if ($pid) { 865 eval { 866 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 867 $client->login($user, $passwd); 868 869 my ($resp_code, $resp_msg); 870 871 my $conn = $client->stor_raw('upload/test.txt'); 872 unless ($conn) { 873 die("Failed to STOR upload/test.txt: " . $client->response_code() . 874 " " . $client->response_msg()); 875 } 876 877 my $buf = "Hello, World!\n"; 878 $conn->write($buf, length($buf)); 879 $conn->close(); 880 881 $resp_code = $client->response_code(); 882 $resp_msg = $client->response_msg(); 883 884 my $expected; 885 886 $expected = 226; 887 $self->assert($expected == $resp_code, 888 test_msg("Expected $expected, got $resp_code")); 889 890 $expected = "Transfer complete"; 891 $self->assert($expected eq $resp_msg, 892 test_msg("Expected '$expected', got '$resp_msg'")); 893 }; 894 895 if ($@) { 896 $ex = $@; 897 } 898 899 $wfh->print("done\n"); 900 $wfh->flush(); 901 902 } else { 903 eval { server_wait($config_file, $rfh) }; 904 if ($@) { 905 warn($@); 906 exit 1; 907 } 908 909 exit 0; 910 } 911 912 # Stop server 913 server_stop($pid_file); 914 915 $self->assert_child_ok($pid); 916 917 if ($ex) { 918 test_append_logfile($log_file, $ex); 919 unlink($log_file); 920 921 die($ex); 922 } 923 924 unlink($log_file); 925} 926 927sub limits_commands_comma_delimited_deferred_paths_bug3147 { 928 my $self = shift; 929 my $tmpdir = $self->{tmpdir}; 930 931 # This config is from: 932 # 933 # http://forums.proftpd.org/smf/index.php/topic,3648.0 934 # http://bugs.proftpd.org/show_bug.cgi?id=3147 935 936 my $config_file = "$tmpdir/dir.conf"; 937 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 938 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 939 940 my $log_file = test_get_logfile(); 941 942 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 943 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 944 945 my $user = 'proftpd'; 946 my $passwd = 'test'; 947 my $group = 'ftpd'; 948 my $home_dir = File::Spec->rel2abs("$tmpdir/home/$user"); 949 my $uid = 500; 950 my $gid = 500; 951 952 mkpath($home_dir); 953 954 my $sub_dir = File::Spec->rel2abs("$tmpdir/home/$user/upload"); 955 mkpath($sub_dir); 956 957 # Make sure that, if we're running as root, that the home directory has 958 # permissions/privs set for the account we create 959 if ($< == 0) { 960 unless (chmod(0755, $home_dir, $sub_dir)) { 961 die("Can't set perms on $home_dir, $sub_dir to 0755: $!"); 962 } 963 964 unless (chown($uid, $gid, $home_dir, $sub_dir)) { 965 die("Can't set owner of $home_dir, $sub_dir to $uid/$gid: $!"); 966 } 967 } 968 969 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 970 '/bin/bash'); 971 auth_group_write($auth_group_file, $group, $gid, $user); 972 973 my $config = { 974 PidFile => $pid_file, 975 ScoreboardFile => $scoreboard_file, 976 SystemLog => $log_file, 977 TraceLog => $log_file, 978 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 979 980 AuthUserFile => $auth_user_file, 981 AuthGroupFile => $auth_group_file, 982 983 DefaultChdir => '~', 984 985 Directory => { 986 "~$user" => { 987 Limit => { 988 'WRITE' => { 989 DenyAll => '', 990 }, 991 }, 992 }, 993 994 # Bug#3147 happened because proftpd's parsing of the list of commands 995 # in the <Limit> section was not properly handling commas after the 996 # command names. 997 998 "~$user/upload" => { 999 Limit => { 1000 'STOR,APPE,MKD,RMD,RNTO,STOU,XMKD,XRMD' => { 1001 AllowAll => '', 1002 }, 1003 }, 1004 }, 1005 }, 1006 1007 IfModules => { 1008 'mod_delay.c' => { 1009 DelayEngine => 'off', 1010 }, 1011 }, 1012 }; 1013 1014 my ($port, $config_user, $config_group) = config_write($config_file, $config); 1015 1016 # Open pipes, for use between the parent and child processes. Specifically, 1017 # the child will indicate when it's done with its test by writing a message 1018 # to the parent. 1019 my ($rfh, $wfh); 1020 unless (pipe($rfh, $wfh)) { 1021 die("Can't open pipe: $!"); 1022 } 1023 1024 my $ex; 1025 1026 # Fork child 1027 $self->handle_sigchld(); 1028 defined(my $pid = fork()) or die("Can't fork: $!"); 1029 if ($pid) { 1030 eval { 1031 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1032 $client->login($user, $passwd); 1033 1034 my ($resp_code, $resp_msg); 1035 1036 my $conn = $client->stor_raw('upload/test.txt'); 1037 unless ($conn) { 1038 die("Failed to STOR upload/test.txt: " . $client->response_code() . 1039 " " . $client->response_msg()); 1040 } 1041 1042 my $buf = "Hello, World!\n"; 1043 $conn->write($buf, length($buf)); 1044 $conn->close(); 1045 1046 $resp_code = $client->response_code(); 1047 $resp_msg = $client->response_msg(); 1048 1049 my $expected; 1050 1051 $expected = 226; 1052 $self->assert($expected == $resp_code, 1053 test_msg("Expected $expected, got $resp_code")); 1054 1055 $expected = "Transfer complete"; 1056 $self->assert($expected eq $resp_msg, 1057 test_msg("Expected '$expected', got '$resp_msg'")); 1058 }; 1059 1060 if ($@) { 1061 $ex = $@; 1062 } 1063 1064 $wfh->print("done\n"); 1065 $wfh->flush(); 1066 1067 } else { 1068 eval { server_wait($config_file, $rfh) }; 1069 if ($@) { 1070 warn($@); 1071 exit 1; 1072 } 1073 1074 exit 0; 1075 } 1076 1077 # Stop server 1078 server_stop($pid_file); 1079 1080 $self->assert_child_ok($pid); 1081 1082 if ($ex) { 1083 test_append_logfile($log_file, $ex); 1084 unlink($log_file); 1085 1086 die($ex); 1087 } 1088 1089 unlink($log_file); 1090} 1091 1092sub limits_commands_no_commas_deferred_paths_bug3147 { 1093 my $self = shift; 1094 my $tmpdir = $self->{tmpdir}; 1095 1096 # This config is from: 1097 # 1098 # http://forums.proftpd.org/smf/index.php/topic,3648.0 1099 # http://bugs.proftpd.org/show_bug.cgi?id=3147 1100 1101 my $config_file = "$tmpdir/dir.conf"; 1102 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 1103 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 1104 1105 my $log_file = test_get_logfile(); 1106 1107 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 1108 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 1109 1110 my $user = 'proftpd'; 1111 my $passwd = 'test'; 1112 my $group = 'ftpd'; 1113 my $home_dir = File::Spec->rel2abs("$tmpdir/home/$user"); 1114 my $uid = 500; 1115 my $gid = 500; 1116 1117 mkpath($home_dir); 1118 1119 my $sub_dir = File::Spec->rel2abs("$tmpdir/home/$user/upload"); 1120 mkpath($sub_dir); 1121 1122 # Make sure that, if we're running as root, that the home directory has 1123 # permissions/privs set for the account we create 1124 if ($< == 0) { 1125 unless (chmod(0755, $home_dir, $sub_dir)) { 1126 die("Can't set perms on $home_dir, $sub_dir to 0755: $!"); 1127 } 1128 1129 unless (chown($uid, $gid, $home_dir, $sub_dir)) { 1130 die("Can't set owner of $home_dir, $sub_dir to $uid/$gid: $!"); 1131 } 1132 } 1133 1134 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 1135 '/bin/bash'); 1136 auth_group_write($auth_group_file, $group, $gid, $user); 1137 1138 my $config = { 1139 PidFile => $pid_file, 1140 ScoreboardFile => $scoreboard_file, 1141 SystemLog => $log_file, 1142 TraceLog => $log_file, 1143 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 1144 1145 AuthUserFile => $auth_user_file, 1146 AuthGroupFile => $auth_group_file, 1147 1148 DefaultChdir => '~', 1149 1150 Directory => { 1151 "~$user" => { 1152 Limit => { 1153 'WRITE' => { 1154 DenyAll => '', 1155 }, 1156 }, 1157 }, 1158 1159 # Bug#3147 happened because proftpd's parsing of the list of commands 1160 # in the <Limit> section was not properly handling commas after the 1161 # command names. Make sure the fix for Bug#3147 doesn't break command 1162 # lists which DO NOT use commas. 1163 1164 "~$user/upload" => { 1165 Limit => { 1166 'STOR APPE MKD RMD RNTO STOU XMKD XRMD' => { 1167 AllowAll => '', 1168 }, 1169 }, 1170 }, 1171 }, 1172 1173 IfModules => { 1174 'mod_delay.c' => { 1175 DelayEngine => 'off', 1176 }, 1177 }, 1178 }; 1179 1180 my ($port, $config_user, $config_group) = config_write($config_file, $config); 1181 1182 # Open pipes, for use between the parent and child processes. Specifically, 1183 # the child will indicate when it's done with its test by writing a message 1184 # to the parent. 1185 my ($rfh, $wfh); 1186 unless (pipe($rfh, $wfh)) { 1187 die("Can't open pipe: $!"); 1188 } 1189 1190 my $ex; 1191 1192 # Fork child 1193 $self->handle_sigchld(); 1194 defined(my $pid = fork()) or die("Can't fork: $!"); 1195 if ($pid) { 1196 eval { 1197 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1198 $client->login($user, $passwd); 1199 1200 my ($resp_code, $resp_msg); 1201 1202 my $conn = $client->stor_raw('upload/test.txt'); 1203 unless ($conn) { 1204 die("Failed to STOR upload/test.txt: " . $client->response_code() . 1205 " " . $client->response_msg()); 1206 } 1207 1208 my $buf = "Hello, World!\n"; 1209 $conn->write($buf, length($buf)); 1210 $conn->close(); 1211 1212 $resp_code = $client->response_code(); 1213 $resp_msg = $client->response_msg(); 1214 1215 my $expected; 1216 1217 $expected = 226; 1218 $self->assert($expected == $resp_code, 1219 test_msg("Expected $expected, got $resp_code")); 1220 1221 $expected = "Transfer complete"; 1222 $self->assert($expected eq $resp_msg, 1223 test_msg("Expected '$expected', got '$resp_msg'")); 1224 }; 1225 1226 if ($@) { 1227 $ex = $@; 1228 } 1229 1230 $wfh->print("done\n"); 1231 $wfh->flush(); 1232 1233 } else { 1234 eval { server_wait($config_file, $rfh) }; 1235 if ($@) { 1236 warn($@); 1237 exit 1; 1238 } 1239 1240 exit 0; 1241 } 1242 1243 # Stop server 1244 server_stop($pid_file); 1245 1246 $self->assert_child_ok($pid); 1247 1248 if ($ex) { 1249 test_append_logfile($log_file, $ex); 1250 unlink($log_file); 1251 1252 die($ex); 1253 } 1254 1255 unlink($log_file); 1256} 1257 1258sub limits_rename_dir_ok_write_denied { 1259 my $self = shift; 1260 my $tmpdir = $self->{tmpdir}; 1261 1262 my $config_file = "$tmpdir/dir.conf"; 1263 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 1264 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 1265 1266 my $log_file = test_get_logfile(); 1267 1268 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 1269 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 1270 1271 my $user = 'proftpd'; 1272 my $passwd = 'test'; 1273 my $group = 'ftpd'; 1274 my $home_dir = File::Spec->rel2abs("$tmpdir/home/$user"); 1275 my $uid = 500; 1276 my $gid = 500; 1277 1278 mkpath($home_dir); 1279 1280 my $sub_dir = File::Spec->rel2abs("$tmpdir/home/$user/upload"); 1281 mkpath($sub_dir); 1282 1283 # Make sure that, if we're running as root, that the home directory has 1284 # permissions/privs set for the account we create 1285 if ($< == 0) { 1286 unless (chmod(0755, $home_dir, $sub_dir)) { 1287 die("Can't set perms on $home_dir, $sub_dir to 0755: $!"); 1288 } 1289 1290 unless (chown($uid, $gid, $home_dir, $sub_dir)) { 1291 die("Can't set owner of $home_dir, $sub_dir to $uid/$gid: $!"); 1292 } 1293 } 1294 1295 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 1296 '/bin/bash'); 1297 auth_group_write($auth_group_file, $group, $gid, $user); 1298 1299 my $config = { 1300 PidFile => $pid_file, 1301 ScoreboardFile => $scoreboard_file, 1302 SystemLog => $log_file, 1303 TraceLog => $log_file, 1304 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 1305 1306 AuthUserFile => $auth_user_file, 1307 AuthGroupFile => $auth_group_file, 1308 1309 DefaultChdir => '~', 1310 1311 Directory => { 1312 "~$user/*" => { 1313 Limit => { 1314 'CWD XCWD RNFR RNTO' => { 1315 AllowAll => '', 1316 }, 1317 1318 'ALL' => { 1319 DenyAll => '', 1320 }, 1321 }, 1322 }, 1323 1324 "~$user/upload" => { 1325 Limit => { 1326 'WRITE' => { 1327 DenyAll => '', 1328 }, 1329 }, 1330 }, 1331 }, 1332 1333 IfModules => { 1334 'mod_delay.c' => { 1335 DelayEngine => 'off', 1336 }, 1337 }, 1338 }; 1339 1340 my ($port, $config_user, $config_group) = config_write($config_file, $config); 1341 1342 # Open pipes, for use between the parent and child processes. Specifically, 1343 # the child will indicate when it's done with its test by writing a message 1344 # to the parent. 1345 my ($rfh, $wfh); 1346 unless (pipe($rfh, $wfh)) { 1347 die("Can't open pipe: $!"); 1348 } 1349 1350 my $ex; 1351 1352 # Fork child 1353 $self->handle_sigchld(); 1354 defined(my $pid = fork()) or die("Can't fork: $!"); 1355 if ($pid) { 1356 eval { 1357 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1358 $client->login($user, $passwd); 1359 1360 my ($resp_code, $resp_msg); 1361 1362 ($resp_code, $resp_msg) = $client->rnfr('upload'); 1363 1364 my $expected; 1365 1366 $expected = 350; 1367 $self->assert($expected == $resp_code, 1368 test_msg("Expected $expected, got $resp_code")); 1369 1370 $expected = "File or directory exists, ready for destination name"; 1371 $self->assert($expected eq $resp_msg, 1372 test_msg("Expected '$expected', got '$resp_msg'")); 1373 1374 ($resp_code, $resp_msg) = $client->rnto('upload2'); 1375 1376 $expected = 250; 1377 $self->assert($expected == $resp_code, 1378 test_msg("Expected $expected, got $resp_code")); 1379 1380 $expected = "Rename successful"; 1381 $self->assert($expected eq $resp_msg, 1382 test_msg("Expected '$expected', got '$resp_msg'")); 1383 }; 1384 1385 if ($@) { 1386 $ex = $@; 1387 } 1388 1389 $wfh->print("done\n"); 1390 $wfh->flush(); 1391 1392 } else { 1393 eval { server_wait($config_file, $rfh) }; 1394 if ($@) { 1395 warn($@); 1396 exit 1; 1397 } 1398 1399 exit 0; 1400 } 1401 1402 # Stop server 1403 server_stop($pid_file); 1404 1405 $self->assert_child_ok($pid); 1406 1407 if ($ex) { 1408 test_append_logfile($log_file, $ex); 1409 unlink($log_file); 1410 1411 die($ex); 1412 } 1413 1414 unlink($log_file); 1415} 1416 1417sub limits_rename_dir_failed_rnfr_denied { 1418 my $self = shift; 1419 my $tmpdir = $self->{tmpdir}; 1420 1421 my $config_file = "$tmpdir/dir.conf"; 1422 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 1423 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 1424 1425 my $log_file = test_get_logfile(); 1426 1427 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 1428 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 1429 1430 my $user = 'proftpd'; 1431 my $passwd = 'test'; 1432 my $group = 'ftpd'; 1433 my $home_dir = File::Spec->rel2abs("$tmpdir/home/$user"); 1434 my $uid = 500; 1435 my $gid = 500; 1436 1437 mkpath($home_dir); 1438 1439 my $sub_dir = File::Spec->rel2abs("$tmpdir/home/$user/upload"); 1440 mkpath($sub_dir); 1441 1442 # Make sure that, if we're running as root, that the home directory has 1443 # permissions/privs set for the account we create 1444 if ($< == 0) { 1445 unless (chmod(0755, $home_dir, $sub_dir)) { 1446 die("Can't set perms on $home_dir, $sub_dir to 0755: $!"); 1447 } 1448 1449 unless (chown($uid, $gid, $home_dir, $sub_dir)) { 1450 die("Can't set owner of $home_dir, $sub_dir to $uid/$gid: $!"); 1451 } 1452 } 1453 1454 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 1455 '/bin/bash'); 1456 auth_group_write($auth_group_file, $group, $gid, $user); 1457 1458 my $config = { 1459 PidFile => $pid_file, 1460 ScoreboardFile => $scoreboard_file, 1461 SystemLog => $log_file, 1462 TraceLog => $log_file, 1463 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 1464 1465 AuthUserFile => $auth_user_file, 1466 AuthGroupFile => $auth_group_file, 1467 1468 DefaultChdir => '~', 1469 1470 Directory => { 1471 "~$user/*" => { 1472 Limit => { 1473 'CWD XCWD RNFR RNTO' => { 1474 AllowAll => '', 1475 }, 1476 1477 'ALL' => { 1478 DenyAll => '', 1479 }, 1480 }, 1481 }, 1482 1483 "~$user/upload" => { 1484 Limit => { 1485 'RNFR' => { 1486 DenyAll => '', 1487 }, 1488 }, 1489 }, 1490 }, 1491 1492 IfModules => { 1493 'mod_delay.c' => { 1494 DelayEngine => 'off', 1495 }, 1496 }, 1497 }; 1498 1499 my ($port, $config_user, $config_group) = config_write($config_file, $config); 1500 1501 # Open pipes, for use between the parent and child processes. Specifically, 1502 # the child will indicate when it's done with its test by writing a message 1503 # to the parent. 1504 my ($rfh, $wfh); 1505 unless (pipe($rfh, $wfh)) { 1506 die("Can't open pipe: $!"); 1507 } 1508 1509 my $ex; 1510 1511 # Fork child 1512 $self->handle_sigchld(); 1513 defined(my $pid = fork()) or die("Can't fork: $!"); 1514 if ($pid) { 1515 eval { 1516 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1517 $client->login($user, $passwd); 1518 1519 my ($resp_code, $resp_msg); 1520 1521 eval { $client->rnfr('upload') }; 1522 unless ($@) { 1523 die("RNFR upload succeeded unexpectedly"); 1524 1525 } else { 1526 $resp_code = $client->response_code(); 1527 $resp_msg = $client->response_msg(); 1528 } 1529 1530 my $expected; 1531 1532 $expected = 550; 1533 $self->assert($expected == $resp_code, 1534 test_msg("Expected $expected, got $resp_code")); 1535 1536 $expected = "upload: Operation not permitted"; 1537 $self->assert($expected eq $resp_msg, 1538 test_msg("Expected '$expected', got '$resp_msg'")); 1539 }; 1540 1541 if ($@) { 1542 $ex = $@; 1543 } 1544 1545 $wfh->print("done\n"); 1546 $wfh->flush(); 1547 1548 } else { 1549 eval { server_wait($config_file, $rfh) }; 1550 if ($@) { 1551 warn($@); 1552 exit 1; 1553 } 1554 1555 exit 0; 1556 } 1557 1558 # Stop server 1559 server_stop($pid_file); 1560 1561 $self->assert_child_ok($pid); 1562 1563 if ($ex) { 1564 test_append_logfile($log_file, $ex); 1565 unlink($log_file); 1566 1567 die($ex); 1568 } 1569 1570 unlink($log_file); 1571} 1572 1573sub limits_one_char_dir_bug3337 { 1574 my $self = shift; 1575 my $tmpdir = $self->{tmpdir}; 1576 1577 my $config_file = "$tmpdir/dir.conf"; 1578 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 1579 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 1580 1581 my $log_file = test_get_logfile(); 1582 1583 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 1584 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 1585 1586 my $user = 'proftpd'; 1587 my $passwd = 'test'; 1588 my $group = 'ftpd'; 1589 my $home_dir = File::Spec->rel2abs($tmpdir); 1590 my $uid = 500; 1591 my $gid = 500; 1592 1593 my $a_dir = File::Spec->rel2abs("$home_dir/a"); 1594 mkpath($a_dir); 1595 1596 my $ab_dir = File::Spec->rel2abs("$home_dir/ab"); 1597 mkpath($ab_dir); 1598 1599 # Make sure that, if we're running as root, that the home directory has 1600 # permissions/privs set for the account we create 1601 if ($< == 0) { 1602 unless (chmod(0755, $home_dir, $a_dir, $ab_dir)) { 1603 die("Can't set perms on $home_dir to 0755: $!"); 1604 } 1605 1606 unless (chown($uid, $gid, $home_dir, $a_dir, $ab_dir)) { 1607 die("Can't set owner of $home_dir to $uid/$gid: $!"); 1608 } 1609 } 1610 1611 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 1612 '/bin/bash'); 1613 auth_group_write($auth_group_file, $group, $gid, $user); 1614 1615 # Reported in 1616 # 1617 # http://forums.proftpd.org/smf/index.php/topic,4170.0.html 1618 1619 my $config = { 1620 PidFile => $pid_file, 1621 ScoreboardFile => $scoreboard_file, 1622 SystemLog => $log_file, 1623 TraceLog => $log_file, 1624 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 1625 1626 AuthUserFile => $auth_user_file, 1627 AuthGroupFile => $auth_group_file, 1628 DefaultChdir => '~', 1629 1630 Directory => { 1631 "$home_dir" => { 1632 Limit => { 1633 WRITE => { 1634 DenyUser => $user, 1635 }, 1636 }, 1637 }, 1638 1639 "$a_dir" => { 1640 Limit => { 1641 WRITE => { 1642 AllowUser => $user, 1643 }, 1644 }, 1645 }, 1646 1647 "$ab_dir" => { 1648 Limit => { 1649 WRITE => { 1650 AllowUser => $user, 1651 }, 1652 }, 1653 }, 1654 }, 1655 1656 IfModules => { 1657 'mod_delay.c' => { 1658 DelayEngine => 'off', 1659 }, 1660 }, 1661 }; 1662 1663 my ($port, $config_user, $config_group) = config_write($config_file, $config); 1664 1665 # Open pipes, for use between the parent and child processes. Specifically, 1666 # the child will indicate when it's done with its test by writing a message 1667 # to the parent. 1668 my ($rfh, $wfh); 1669 unless (pipe($rfh, $wfh)) { 1670 die("Can't open pipe: $!"); 1671 } 1672 1673 my $ex; 1674 1675 # Fork child 1676 $self->handle_sigchld(); 1677 defined(my $pid = fork()) or die("Can't fork: $!"); 1678 if ($pid) { 1679 eval { 1680 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1681 $client->login($user, $passwd); 1682 1683 $client->cwd('ab'); 1684 my $conn = $client->stor_raw('test.txt'); 1685 unless ($conn) { 1686 die("STOR test.txt failed: " . $client->response_code() . " " . 1687 $client->response_msg()); 1688 } 1689 1690 $conn->close(); 1691 1692 my $resp_code = $client->response_code(); 1693 my $resp_msg = $client->response_msg(); 1694 1695 my $expected; 1696 1697 $expected = 226; 1698 $self->assert($expected == $resp_code, 1699 test_msg("Expected $expected, got $resp_code")); 1700 1701 $expected = "Transfer complete"; 1702 $self->assert($expected eq $resp_msg, 1703 test_msg("Expected '$expected', got '$resp_msg'")); 1704 1705 $client->cwd('../a'); 1706 $conn = $client->stor_raw('test.txt'); 1707 unless ($conn) { 1708 die("STOR test.txt failed: " . $client->response_code() . " " . 1709 $client->response_msg()); 1710 } 1711 1712 $conn->close(); 1713 1714 $resp_code = $client->response_code(); 1715 $resp_msg = $client->response_msg(); 1716 1717 $expected = 226; 1718 $self->assert($expected == $resp_code, 1719 test_msg("Expected $expected, got $resp_code")); 1720 1721 $expected = "Transfer complete"; 1722 $self->assert($expected eq $resp_msg, 1723 test_msg("Expected '$expected', got '$resp_msg'")); 1724 }; 1725 1726 if ($@) { 1727 $ex = $@; 1728 } 1729 1730 $wfh->print("done\n"); 1731 $wfh->flush(); 1732 1733 } else { 1734 eval { server_wait($config_file, $rfh) }; 1735 if ($@) { 1736 warn($@); 1737 exit 1; 1738 } 1739 1740 exit 0; 1741 } 1742 1743 # Stop server 1744 server_stop($pid_file); 1745 1746 $self->assert_child_ok($pid); 1747 1748 if ($ex) { 1749 test_append_logfile($log_file, $ex); 1750 unlink($log_file); 1751 1752 die($ex); 1753 } 1754 1755 unlink($log_file); 1756} 1757 1758sub limits_symlink_dir_bug3166 { 1759 my $self = shift; 1760 my $tmpdir = $self->{tmpdir}; 1761 1762 my $config_file = "$tmpdir/dir.conf"; 1763 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 1764 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 1765 1766 my $log_file = test_get_logfile(); 1767 1768 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 1769 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 1770 1771 my $user = 'proftpd'; 1772 my $passwd = 'test'; 1773 my $group = 'ftpd'; 1774 my $home_dir = File::Spec->rel2abs($tmpdir); 1775 my $uid = 500; 1776 my $gid = 500; 1777 1778 my $src_dir = File::Spec->rel2abs("$tmpdir/dispatch/$user"); 1779 mkpath($src_dir); 1780 1781 my $dst_dir = File::Spec->rel2abs("$tmpdir/writable/dir1"); 1782 mkpath($dst_dir); 1783 1784 # Change to the $src_dir in order to create a symlink with a relative 1785 # path (so that it will work in the chroot). 1786 1787 my $cwd = getcwd(); 1788 unless (chdir($src_dir)) { 1789 die("Can't chdir to $src_dir: $!"); 1790 } 1791 1792 unless (symlink("../../writable/dir1", "dir1")) { 1793 die("Can't symlink '../../writable/dir1' to 'dir1': $!"); 1794 } 1795 1796 unless (chdir($cwd)) { 1797 die("Can't chdir to $cwd: $!"); 1798 } 1799 1800 # Make sure that, if we're running as root, that the home directory has 1801 # permissions/privs set for the account we create 1802 if ($< == 0) { 1803 unless (chmod(0755, $home_dir, $src_dir, $dst_dir)) { 1804 die("Can't set perms on $home_dir to 0755: $!"); 1805 } 1806 1807 unless (chown($uid, $gid, $home_dir, $src_dir, $dst_dir)) { 1808 die("Can't set owner of $home_dir to $uid/$gid: $!"); 1809 } 1810 } 1811 1812 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 1813 '/bin/bash'); 1814 auth_group_write($auth_group_file, $group, $gid, $user); 1815 1816 my $config = { 1817 PidFile => $pid_file, 1818 ScoreboardFile => $scoreboard_file, 1819 SystemLog => $log_file, 1820 TraceLog => $log_file, 1821 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 1822 1823 AuthUserFile => $auth_user_file, 1824 AuthGroupFile => $auth_group_file, 1825 DefaultRoot => '~', 1826 1827 IfModules => { 1828 'mod_delay.c' => { 1829 DelayEngine => 'off', 1830 }, 1831 }, 1832 }; 1833 1834 my ($port, $config_user, $config_group) = config_write($config_file, $config); 1835 1836 if (open(my $fh, ">> $config_file")) { 1837 print $fh <<EOD; 1838<Directory $src_dir> 1839 <Limit ALL> 1840 Order allow, deny 1841 AllowUser $user 1842 DenyAll 1843 </Limit> 1844</Directory> 1845 1846<Directory $home_dir/writable> 1847 <Limit ALL> 1848 Order allow, deny 1849 AllowUser admin 1850 DenyAll 1851 </Limit> 1852 1853 <Limit DIRS PORT PASV EPRT EPSV> 1854 Order allow, deny 1855 AllowUser admin 1856 AllowUser test 1857 DenyAll 1858 </Limit> 1859</Directory> 1860 1861<Directory $dst_dir> 1862 1863 # Bug#3166 was caused by using just ALL in this limit; the parent 1864 # directory's limit included the DIRS command group, which took precedence 1865 # over ALL. By adding DIRS to the limit (which includes the test user), 1866 # the desired behavior is achieved. 1867 1868 <Limit ALL DIRS> 1869 Order allow, deny 1870 AllowUser admin 1871 AllowUser $user 1872 DenyAll 1873 </Limit> 1874</Directory> 1875EOD 1876 close($fh); 1877 1878 } else { 1879 die("Can't read $config_file: $!"); 1880 } 1881 1882 # Open pipes, for use between the parent and child processes. Specifically, 1883 # the child will indicate when it's done with its test by writing a message 1884 # to the parent. 1885 my ($rfh, $wfh); 1886 unless (pipe($rfh, $wfh)) { 1887 die("Can't open pipe: $!"); 1888 } 1889 1890 my $ex; 1891 1892 # Fork child 1893 $self->handle_sigchld(); 1894 defined(my $pid = fork()) or die("Can't fork: $!"); 1895 if ($pid) { 1896 eval { 1897 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1898 $client->login($user, $passwd); 1899 1900 $client->cwd('dispatch'); 1901 $client->nlst(); 1902 1903 $client->cwd($user); 1904 $client->nlst(); 1905 1906 $client->cwd('dir1'); 1907 1908 my ($resp_code, $resp_msg) = $client->pwd(); 1909 1910 my $expected; 1911 1912 $expected = 257; 1913 $self->assert($expected == $resp_code, 1914 test_msg("Expected $expected, got $resp_code")); 1915 1916 $expected = '"/writable/dir1" is the current directory'; 1917 $self->assert($expected eq $resp_msg, 1918 test_msg("Expected '$expected', got '$resp_msg'")); 1919 }; 1920 1921 if ($@) { 1922 $ex = $@; 1923 } 1924 1925 $wfh->print("done\n"); 1926 $wfh->flush(); 1927 1928 } else { 1929 eval { server_wait($config_file, $rfh) }; 1930 if ($@) { 1931 warn($@); 1932 exit 1; 1933 } 1934 1935 exit 0; 1936 } 1937 1938 # Stop server 1939 server_stop($pid_file); 1940 1941 $self->assert_child_ok($pid); 1942 1943 if ($ex) { 1944 test_append_logfile($log_file, $ex); 1945 unlink($log_file); 1946 1947 die($ex); 1948 } 1949 1950 unlink($log_file); 1951} 1952 1953sub limits_anon_dir_abs_path_bug3283 { 1954 my $self = shift; 1955 my $tmpdir = $self->{tmpdir}; 1956 1957 my $config_file = "$tmpdir/dir.conf"; 1958 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 1959 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 1960 1961 my $log_file = test_get_logfile(); 1962 1963 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 1964 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 1965 1966 my ($user, $group) = config_get_identity(); 1967 1968 my $passwd = 'test'; 1969 my $anon_dir = File::Spec->rel2abs($tmpdir); 1970 my $uid = 500; 1971 my $gid = 500; 1972 1973 my $hide_dir = File::Spec->rel2abs("$tmpdir/hide"); 1974 mkpath($hide_dir); 1975 1976 # Make sure that, if we're running as root, that the home directory has 1977 # permissions/privs set for the account we create 1978 if ($< == 0) { 1979 unless (chmod(0755, $anon_dir, $hide_dir)) { 1980 die("Can't set perms on $anon_dir to 0755: $!"); 1981 } 1982 1983 unless (chown($uid, $gid, $anon_dir, $hide_dir)) { 1984 die("Can't set owner of $anon_dir to $uid/$gid: $!"); 1985 } 1986 } 1987 1988 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, '/tmp', 1989 '/bin/bash'); 1990 auth_group_write($auth_group_file, $group, $gid, $user); 1991 1992 my $config = { 1993 PidFile => $pid_file, 1994 ScoreboardFile => $scoreboard_file, 1995 SystemLog => $log_file, 1996 TraceLog => $log_file, 1997 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 1998 1999 AuthUserFile => $auth_user_file, 2000 AuthGroupFile => $auth_group_file, 2001 2002 IfModules => { 2003 'mod_delay.c' => { 2004 DelayEngine => 'off', 2005 }, 2006 }, 2007 }; 2008 2009 my ($port, $config_user, $config_group) = config_write($config_file, $config); 2010 2011 if (open(my $fh, ">> $config_file")) { 2012 print $fh <<EOD; 2013<Anonymous $anon_dir> 2014 User $user 2015 Group $group 2016 UserAlias anonymous $user 2017 RequireValidShell off 2018 2019 <Directory $hide_dir> 2020 <Limit ALL> 2021 DenyAll 2022 </Limit> 2023 </Directory> 2024</Anonymous> 2025EOD 2026 close($fh); 2027 2028 } else { 2029 die("Can't read $config_file: $!"); 2030 } 2031 2032 # Open pipes, for use between the parent and child processes. Specifically, 2033 # the child will indicate when it's done with its test by writing a message 2034 # to the parent. 2035 my ($rfh, $wfh); 2036 unless (pipe($rfh, $wfh)) { 2037 die("Can't open pipe: $!"); 2038 } 2039 2040 my $ex; 2041 2042 # Fork child 2043 $self->handle_sigchld(); 2044 defined(my $pid = fork()) or die("Can't fork: $!"); 2045 if ($pid) { 2046 eval { 2047 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 2048 $client->login($user, $passwd); 2049 2050 my $conn = $client->list_raw(); 2051 unless ($conn) { 2052 die("LIST failed: " . $client->response_code() . " " . 2053 $client->response_msg()); 2054 } 2055 2056 my $buf; 2057 $conn->read($buf, 32768, 30); 2058 $conn->close(); 2059 2060 my $lines = [split(/\n/, $buf)]; 2061 2062 foreach my $line (@$lines) { 2063 if ($line =~ /\s+(\S+)$/) { 2064 my $dir_name = $1; 2065 2066 # The <Directory> with <Limit ALL>DenyAll should not appear in 2067 # the returned list 2068 2069 my $unexpected = 'hide'; 2070 $self->assert($unexpected ne $dir_name, 2071 test_msg("Unexpectedly saw '$dir_name' in LIST")); 2072 } 2073 } 2074 2075 $client->quit(); 2076 }; 2077 2078 if ($@) { 2079 $ex = $@; 2080 } 2081 2082 $wfh->print("done\n"); 2083 $wfh->flush(); 2084 2085 } else { 2086 eval { server_wait($config_file, $rfh) }; 2087 if ($@) { 2088 warn($@); 2089 exit 1; 2090 } 2091 2092 exit 0; 2093 } 2094 2095 # Stop server 2096 server_stop($pid_file); 2097 2098 $self->assert_child_ok($pid); 2099 2100 if ($ex) { 2101 test_append_logfile($log_file, $ex); 2102 unlink($log_file); 2103 2104 die($ex); 2105 } 2106 2107 unlink($log_file); 2108} 2109 2110sub limits_with_multi_globs_denied_delete { 2111 my $self = shift; 2112 my $tmpdir = $self->{tmpdir}; 2113 2114 # This config is from: 2115 # 2116 # http://forums.proftpd.org/smf/index.php/topic,6807.0.html 2117 2118 my $config_file = "$tmpdir/dir.conf"; 2119 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 2120 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 2121 2122 my $log_file = test_get_logfile(); 2123 2124 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 2125 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 2126 2127 my $user = 'proftpd'; 2128 my $passwd = 'test'; 2129 my $group = 'ftpd'; 2130 my $users_dir = File::Spec->rel2abs("$tmpdir/home/users"); 2131 my $home_dir = File::Spec->rel2abs("$users_dir/$user"); 2132 my $uid = 500; 2133 my $gid = 500; 2134 2135 mkpath($home_dir); 2136 2137 my $test_file = File::Spec->rel2abs("$home_dir/hiddenfile_foo.txt"); 2138 if (open(my $fh, "> $test_file")) { 2139 print $fh "Hello, World!\n"; 2140 2141 unless (close($fh)) { 2142 die("Can't write $test_file: $!"); 2143 } 2144 2145 } else { 2146 die("Can't open $test_file: $!"); 2147 } 2148 2149 # Make sure that, if we're running as root, that the home directory has 2150 # permissions/privs set for the account we create 2151 if ($< == 0) { 2152 unless (chmod(0755, $home_dir)) { 2153 die("Can't set perms on $home_dir to 0755: $!"); 2154 } 2155 2156 unless (chown($uid, $gid, $home_dir)) { 2157 die("Can't set owner of $home_dir to $uid/$gid: $!"); 2158 } 2159 } 2160 2161 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 2162 '/bin/bash'); 2163 auth_group_write($auth_group_file, $group, $gid, $user); 2164 2165 my $abs_tmp_dir = File::Spec->rel2abs($tmpdir); 2166 2167 my $glob_expr = "$users_dir/*/hiddenfile_*"; 2168 if ($^O eq 'darwin') { 2169 # MacOSX-specific hack/tweak 2170 $glob_expr = '/private' . $glob_expr; 2171 } 2172 2173 my $config = { 2174 PidFile => $pid_file, 2175 ScoreboardFile => $scoreboard_file, 2176 SystemLog => $log_file, 2177 TraceLog => $log_file, 2178 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 2179 2180 AuthUserFile => $auth_user_file, 2181 AuthGroupFile => $auth_group_file, 2182 2183 DefaultChdir => '~', 2184 2185 Directory => { 2186 $glob_expr => { 2187 Limit => { 2188 'ALL' => { 2189 DenyAll => '', 2190 }, 2191 }, 2192 }, 2193 }, 2194 2195 IfModules => { 2196 'mod_delay.c' => { 2197 DelayEngine => 'off', 2198 }, 2199 }, 2200 }; 2201 2202 my ($port, $config_user, $config_group) = config_write($config_file, $config); 2203 2204 # Open pipes, for use between the parent and child processes. Specifically, 2205 # the child will indicate when it's done with its test by writing a message 2206 # to the parent. 2207 my ($rfh, $wfh); 2208 unless (pipe($rfh, $wfh)) { 2209 die("Can't open pipe: $!"); 2210 } 2211 2212 my $ex; 2213 2214 # Fork child 2215 $self->handle_sigchld(); 2216 defined(my $pid = fork()) or die("Can't fork: $!"); 2217 if ($pid) { 2218 eval { 2219 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 2220 $client->login($user, $passwd); 2221 2222 my $filename = 'hiddenfile_foo.txt'; 2223 eval { $client->dele($filename) }; 2224 unless ($@) { 2225 die("DELE succeeded unexpectedly"); 2226 } 2227 2228 my $resp_code = $client->response_code(); 2229 my $resp_msg = $client->response_msg(); 2230 2231 my $expected; 2232 2233 $expected = 550; 2234 $self->assert($expected == $resp_code, 2235 test_msg("Expected $expected, got $resp_code")); 2236 2237 $expected = "$filename: Operation not permitted"; 2238 $self->assert($expected eq $resp_msg, 2239 test_msg("Expected '$expected', got '$resp_msg'")); 2240 }; 2241 2242 if ($@) { 2243 $ex = $@; 2244 } 2245 2246 $wfh->print("done\n"); 2247 $wfh->flush(); 2248 2249 } else { 2250 eval { server_wait($config_file, $rfh) }; 2251 if ($@) { 2252 warn($@); 2253 exit 1; 2254 } 2255 2256 exit 0; 2257 } 2258 2259 # Stop server 2260 server_stop($pid_file); 2261 2262 $self->assert_child_ok($pid); 2263 2264 if ($ex) { 2265 test_append_logfile($log_file, $ex); 2266 unlink($log_file); 2267 2268 die($ex); 2269 } 2270 2271 unlink($log_file); 2272} 2273 2274sub limits_retr_bug3915 { 2275 my $self = shift; 2276 my $tmpdir = $self->{tmpdir}; 2277 2278 my $config_file = "$tmpdir/dir.conf"; 2279 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 2280 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 2281 2282 my $log_file = test_get_logfile(); 2283 2284 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 2285 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 2286 2287 my $user = 'proftpd'; 2288 my $passwd = 'test'; 2289 my $group = 'ftpd'; 2290 my $home_dir = File::Spec->rel2abs("$tmpdir/home/users/$user"); 2291 my $uid = 500; 2292 my $gid = 500; 2293 2294 mkpath($home_dir); 2295 2296 # Make sure that, if we're running as root, that the home directory has 2297 # permissions/privs set for the account we create 2298 if ($< == 0) { 2299 unless (chmod(0755, $home_dir)) { 2300 die("Can't set perms on $home_dir to 0755: $!"); 2301 } 2302 2303 unless (chown($uid, $gid, $home_dir)) { 2304 die("Can't set owner of $home_dir to $uid/$gid: $!"); 2305 } 2306 } 2307 2308 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 2309 '/bin/bash'); 2310 auth_group_write($auth_group_file, $group, $gid, $user); 2311 2312 my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); 2313 if (open(my $fh, "> $test_file")) { 2314 print $fh "Hello, World!\n"; 2315 2316 unless (close($fh)) { 2317 die("Can't write $test_file: $!"); 2318 } 2319 2320 } else { 2321 die("Can't open $test_file: $!"); 2322 } 2323 2324 my $config = { 2325 PidFile => $pid_file, 2326 ScoreboardFile => $scoreboard_file, 2327 SystemLog => $log_file, 2328 TraceLog => $log_file, 2329 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 2330 2331 AuthUserFile => $auth_user_file, 2332 AuthGroupFile => $auth_group_file, 2333 2334 DefaultChdir => '~', 2335 2336 IfModules => { 2337 'mod_delay.c' => { 2338 DelayEngine => 'off', 2339 }, 2340 }, 2341 }; 2342 2343 my ($port, $config_user, $config_group) = config_write($config_file, $config); 2344 2345 if (open(my $fh, ">> $config_file")) { 2346 print $fh <<EOC; 2347<Directory ~$user> 2348 <Limit RETR> 2349 DenyAll 2350 </Limit> 2351</Directory> 2352EOC 2353 close($fh); 2354 2355 } else { 2356 die("Can't read $config_file: $!"); 2357 } 2358 2359 # Open pipes, for use between the parent and child processes. Specifically, 2360 # the child will indicate when it's done with its test by writing a message 2361 # to the parent. 2362 my ($rfh, $wfh); 2363 unless (pipe($rfh, $wfh)) { 2364 die("Can't open pipe: $!"); 2365 } 2366 2367 my $ex; 2368 2369 # Fork child 2370 $self->handle_sigchld(); 2371 defined(my $pid = fork()) or die("Can't fork: $!"); 2372 if ($pid) { 2373 eval { 2374 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 2375 $client->login($user, $passwd); 2376 2377 my ($resp_code, $resp_msg); 2378 2379 my $conn = $client->retr_raw('test.txt'); 2380 if ($conn) { 2381 die("RETR test.txt succeeded unexpectedly"); 2382 } 2383 2384 $resp_code = $client->response_code(); 2385 $resp_msg = $client->response_msg(); 2386 2387 my $expected; 2388 2389 $expected = 550; 2390 $self->assert($expected == $resp_code, 2391 test_msg("Expected response code $expected, got $resp_code")); 2392 2393 $expected = "test.txt: Operation not permitted"; 2394 $self->assert($expected eq $resp_msg, 2395 test_msg("Expected response message '$expected', got '$resp_msg'")); 2396 }; 2397 2398 if ($@) { 2399 $ex = $@; 2400 } 2401 2402 $wfh->print("done\n"); 2403 $wfh->flush(); 2404 2405 } else { 2406 eval { server_wait($config_file, $rfh) }; 2407 if ($@) { 2408 warn($@); 2409 exit 1; 2410 } 2411 2412 exit 0; 2413 } 2414 2415 # Stop server 2416 server_stop($pid_file); 2417 2418 $self->assert_child_ok($pid); 2419 2420 if ($ex) { 2421 test_append_logfile($log_file, $ex); 2422 unlink($log_file); 2423 2424 die($ex); 2425 } 2426 2427 unlink($log_file); 2428} 2429 2430sub limits_retr_bug3915_chrooted { 2431 my $self = shift; 2432 my $tmpdir = $self->{tmpdir}; 2433 2434 my $config_file = "$tmpdir/dir.conf"; 2435 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 2436 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 2437 2438 my $log_file = test_get_logfile(); 2439 2440 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 2441 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 2442 2443 my $user = 'proftpd'; 2444 my $passwd = 'test'; 2445 my $group = 'ftpd'; 2446 my $home_dir = File::Spec->rel2abs("$tmpdir/home/users/$user"); 2447 my $uid = 500; 2448 my $gid = 500; 2449 2450 mkpath($home_dir); 2451 2452 # Make sure that, if we're running as root, that the home directory has 2453 # permissions/privs set for the account we create 2454 if ($< == 0) { 2455 unless (chmod(0755, $home_dir)) { 2456 die("Can't set perms on $home_dir to 0755: $!"); 2457 } 2458 2459 unless (chown($uid, $gid, $home_dir)) { 2460 die("Can't set owner of $home_dir to $uid/$gid: $!"); 2461 } 2462 } 2463 2464 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 2465 '/bin/bash'); 2466 auth_group_write($auth_group_file, $group, $gid, $user); 2467 2468 my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); 2469 if (open(my $fh, "> $test_file")) { 2470 print $fh "Hello, World!\n"; 2471 2472 unless (close($fh)) { 2473 die("Can't write $test_file: $!"); 2474 } 2475 2476 } else { 2477 die("Can't open $test_file: $!"); 2478 } 2479 2480 my $config = { 2481 PidFile => $pid_file, 2482 ScoreboardFile => $scoreboard_file, 2483 SystemLog => $log_file, 2484 TraceLog => $log_file, 2485 Trace => 'DEFAULT:10 fsio:0 directory:10 lock:0', 2486 2487 AuthUserFile => $auth_user_file, 2488 AuthGroupFile => $auth_group_file, 2489 2490 DefaultRoot => '~', 2491 2492 IfModules => { 2493 'mod_delay.c' => { 2494 DelayEngine => 'off', 2495 }, 2496 }, 2497 }; 2498 2499 my ($port, $config_user, $config_group) = config_write($config_file, $config); 2500 2501 if (open(my $fh, ">> $config_file")) { 2502 print $fh <<EOC; 2503<Directory ~$user> 2504 <Limit RETR> 2505 DenyAll 2506 </Limit> 2507</Directory> 2508EOC 2509 close($fh); 2510 2511 } else { 2512 die("Can't read $config_file: $!"); 2513 } 2514 2515 # Open pipes, for use between the parent and child processes. Specifically, 2516 # the child will indicate when it's done with its test by writing a message 2517 # to the parent. 2518 my ($rfh, $wfh); 2519 unless (pipe($rfh, $wfh)) { 2520 die("Can't open pipe: $!"); 2521 } 2522 2523 my $ex; 2524 2525 # Fork child 2526 $self->handle_sigchld(); 2527 defined(my $pid = fork()) or die("Can't fork: $!"); 2528 if ($pid) { 2529 eval { 2530 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 2531 $client->login($user, $passwd); 2532 2533 my ($resp_code, $resp_msg); 2534 2535 my $conn = $client->retr_raw('test.txt'); 2536 if ($conn) { 2537 die("RETR test.txt succeeded unexpectedly"); 2538 } 2539 2540 $resp_code = $client->response_code(); 2541 $resp_msg = $client->response_msg(); 2542 2543 my $expected; 2544 2545 $expected = 550; 2546 $self->assert($expected == $resp_code, 2547 test_msg("Expected response code $expected, got $resp_code")); 2548 2549 $expected = "test.txt: Operation not permitted"; 2550 $self->assert($expected eq $resp_msg, 2551 test_msg("Expected response message '$expected', got '$resp_msg'")); 2552 }; 2553 2554 if ($@) { 2555 $ex = $@; 2556 } 2557 2558 $wfh->print("done\n"); 2559 $wfh->flush(); 2560 2561 } else { 2562 eval { server_wait($config_file, $rfh) }; 2563 if ($@) { 2564 warn($@); 2565 exit 1; 2566 } 2567 2568 exit 0; 2569 } 2570 2571 # Stop server 2572 server_stop($pid_file); 2573 2574 $self->assert_child_ok($pid); 2575 2576 if ($ex) { 2577 test_append_logfile($log_file, $ex); 2578 unlink($log_file); 2579 2580 die($ex); 2581 } 2582 2583 unlink($log_file); 2584} 2585 2586sub limits_stor_with_multiple_groups_chrooted { 2587 my $self = shift; 2588 my $tmpdir = $self->{tmpdir}; 2589 2590 my $config_file = "$tmpdir/dir.conf"; 2591 my $pid_file = File::Spec->rel2abs("$tmpdir/dir.pid"); 2592 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/dir.scoreboard"); 2593 2594 my $log_file = test_get_logfile(); 2595 2596 my $auth_user_file = File::Spec->rel2abs("$tmpdir/dir.passwd"); 2597 my $auth_group_file = File::Spec->rel2abs("$tmpdir/dir.group"); 2598 2599 my $user = 'proftpd'; 2600 my $passwd = 'test'; 2601 my $group = 'ftpd'; 2602 my $home_dir = File::Spec->rel2abs($tmpdir); 2603 my $uid = 500; 2604 my $gid = 500; 2605 2606 my $read_group = 'ftpd_read'; 2607 my $read_gid = 501; 2608 my $write_group = 'ftpd_write'; 2609 my $write_gid = 502; 2610 2611 my $test_dir = File::Spec->rel2abs("$tmpdir/test.d"); 2612 mkpath($test_dir); 2613 2614 my $sub_dir = File::Spec->rel2abs("$test_dir/sub.d"); 2615 mkpath($sub_dir); 2616 2617 # Make sure that, if we're running as root, that the home directory has 2618 # permissions/privs set for the account we create 2619 if ($< == 0) { 2620 unless (chmod(0755, $home_dir)) { 2621 die("Can't set perms on $home_dir to 0755: $!"); 2622 } 2623 2624 unless (chown($uid, $gid, $home_dir)) { 2625 die("Can't set owner of $home_dir to $uid/$gid: $!"); 2626 } 2627 2628 unless(chmod(0775, $test_dir, $sub_dir)) { 2629 die("Can't set perms on $test_dir to 0775: $!"); 2630 } 2631 2632 unless (chown($uid, $write_gid, $test_dir, $sub_dir)) { 2633 die("Can't set owner of $test_dir to $uid/$write_gid: $!"); 2634 } 2635 2636 } 2637 2638 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 2639 '/bin/bash'); 2640 auth_group_write($auth_group_file, $group, $gid, $user); 2641 auth_group_write($auth_group_file, $read_group, $read_gid, $user); 2642 auth_group_write($auth_group_file, $write_group, $write_gid, $user); 2643 2644 # See: 2645 # https://forums.proftpd.org/smf/index.php/topic,11304.0.html 2646 2647 my $config = { 2648 PidFile => $pid_file, 2649 ScoreboardFile => $scoreboard_file, 2650 SystemLog => $log_file, 2651 TraceLog => $log_file, 2652 Trace => 'DEFAULT:10 fileperms:10 fsio:0 directory:10 lock:0', 2653 2654 AuthUserFile => $auth_user_file, 2655 AuthGroupFile => $auth_group_file, 2656 2657 DefaultRoot => '~', 2658 2659 IfModules => { 2660 'mod_delay.c' => { 2661 DelayEngine => 'off', 2662 }, 2663 }, 2664 }; 2665 2666 my ($port, $config_user, $config_group) = config_write($config_file, $config); 2667 2668 if (open(my $fh, ">> $config_file")) { 2669 print $fh <<EOD; 2670<Directory $test_dir> 2671 <Limit DIRS READ> 2672 AllowGroup OR $read_group,$write_group 2673 </Limit> 2674 2675 <Limit ALL> 2676 DenyAll 2677 </Limit> 2678</Directory> 2679 2680<Directory $sub_dir> 2681 <Limit DIRS READ> 2682 AllowGroup OR $read_group,$write_group 2683 </Limit> 2684 2685 <Limit ALL> 2686 AllowGroup $write_group 2687 DenyAll 2688 </Limit> 2689</Directory> 2690EOD 2691 close($fh); 2692 2693 } else { 2694 die("Can't read $config_file: $!"); 2695 } 2696 2697 # Open pipes, for use between the parent and child processes. Specifically, 2698 # the child will indicate when it's done with its test by writing a message 2699 # to the parent. 2700 my ($rfh, $wfh); 2701 unless (pipe($rfh, $wfh)) { 2702 die("Can't open pipe: $!"); 2703 } 2704 2705 my $ex; 2706 2707 # Fork child 2708 $self->handle_sigchld(); 2709 defined(my $pid = fork()) or die("Can't fork: $!"); 2710 if ($pid) { 2711 eval { 2712 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 2713 $client->login($user, $passwd); 2714 $client->cwd('test.d/sub.d'); 2715 2716 my $conn = $client->stor_raw('test.txt'); 2717 unless ($conn) { 2718 die("Failed to STOR test.txt: " . $client->response_code() . " " . 2719 $client->response_msg()); 2720 }; 2721 2722 my $buf = "Hello, World\n"; 2723 $conn->write($buf, length($buf), 25); 2724 eval { $conn->close() }; 2725 2726 my $resp_code = $client->response_code(); 2727 my $resp_msg = $client->response_msg(); 2728 $self->assert_transfer_ok($resp_code, $resp_msg); 2729 2730 $client->quit(); 2731 }; 2732 2733 if ($@) { 2734 $ex = $@; 2735 } 2736 2737 $wfh->print("done\n"); 2738 $wfh->flush(); 2739 2740 } else { 2741 eval { server_wait($config_file, $rfh) }; 2742 if ($@) { 2743 warn($@); 2744 exit 1; 2745 } 2746 2747 exit 0; 2748 } 2749 2750 # Stop server 2751 server_stop($pid_file); 2752 2753 $self->assert_child_ok($pid); 2754 2755 if ($ex) { 2756 test_append_logfile($log_file, $ex); 2757 unlink($log_file); 2758 2759 die($ex); 2760 } 2761 2762 unlink($log_file); 2763} 2764 27651; 2766