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