1package ProFTPD::Tests::Modules::mod_ldap; 2 3use lib qw(t/lib); 4use base qw(ProFTPD::TestSuite::Child); 5use strict; 6 7use File::Path qw(mkpath rmtree); 8use File::Spec; 9use IO::Handle; 10 11use Net::LDAP qw(LDAP_NO_SUCH_OBJECT); 12use Net::LDAP::Entry; 13 14use ProFTPD::TestSuite::FTP; 15use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); 16 17$| = 1; 18 19my $order = 0; 20 21# FIXME: we probably can't test these directives without depending 22# on OpenLDAP's slapd being installed, and starting our own directory 23# instance that we can configure and start/stop as we wish. 24# 25# LDAPAttr 26# LDAPAuthBinds 27# LDAPProtocolVersion 28# LDAPUseTLS 29 30my $TESTS = { 31 ldap_users_authallowed => { 32 order => ++$order, 33 test_class => [qw(forking)], 34 }, 35 36 ldap_users_authdenied => { 37 order => ++$order, 38 test_class => [qw(forking)], 39 }, 40 41 ldap_genhomedir_with_username => { 42 order => ++$order, 43 test_class => [qw(forking)], 44 }, 45 46 ldap_genhomedir_without_username => { 47 order => ++$order, 48 test_class => [qw(forking)], 49 }, 50 51 ldap_genhomedir_forcegenhdir => { 52 order => ++$order, 53 test_class => [qw(forking)], 54 }, 55 56 ldap_groups_authdenied => { 57 order => ++$order, 58 test_class => [qw(forking)], 59 }, 60 61 ldap_groups_authallowed => { 62 order => ++$order, 63 test_class => [qw(forking)], 64 }, 65 66 ldap_quota_on_user => { 67 order => ++$order, 68 test_class => [qw(forking)], 69 }, 70 71 ldap_quota_default => { 72 order => ++$order, 73 test_class => [qw(forking)], 74 }, 75 76 ldap_default_uid => { 77 order => ++$order, 78 test_class => [qw(forking)], 79 }, 80 81 ldap_default_gid => { 82 order => ++$order, 83 test_class => [qw(forking)], 84 }, 85 86 ldap_default_force_uid => { 87 order => ++$order, 88 test_class => [qw(forking)], 89 }, 90 91 ldap_default_force_gid => { 92 order => ++$order, 93 test_class => [qw(forking)], 94 }, 95 96 ldap_alias_dereference_off => { 97 order => ++$order, 98 test_class => [qw(forking)], 99 }, 100 101 ldap_alias_dereference_on => { 102 order => ++$order, 103 test_class => [qw(forking)], 104 }, 105 106 ldap_scope_base => { 107 order => ++$order, 108 test_class => [qw(forking)], 109 }, 110 111 ldap_scope_sub => { 112 order => ++$order, 113 test_class => [qw(forking)], 114 }, 115 116 ldap_default_auth_scheme => { 117 order => ++$order, 118 test_class => [qw(forking)], 119 }, 120 121}; 122 123sub new { 124 return shift()->SUPER::new(@_); 125} 126 127sub list_tests { 128 return testsuite_get_runnable_tests($TESTS); 129} 130 131sub ldap_auth { 132 my $self = shift; 133 my $allow_auth = shift; 134 my $tmpdir = $self->{tmpdir}; 135 136 my $config_file = "$tmpdir/ldap.conf"; 137 my $pid_file = File::Spec->rel2abs("$tmpdir/ldap.pid"); 138 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/ldap.scoreboard"); 139 140 my $log_file = File::Spec->rel2abs('tests.log'); 141 142 my $server = $ENV{LDAP_SERVER} ? $ENV{LDAP_SERVER} : 'localhost'; 143 my $bind_dn = $ENV{LDAP_BIND_DN}; 144 my $bind_pass = $ENV{LDAP_BIND_PASS}; 145 my $ldap_base = $ENV{LDAP_USER_BASE}; 146 my $user = 'proftpdtest' . int(rand(4294967296)); 147 my $passwd = 'foobar'; 148 my $uid = 1000; 149 my $gid = 1000; 150 my $home_dir = File::Spec->rel2abs($tmpdir); 151 152 my $ld = Net::LDAP->new([$server]); 153 $self->assert($ld); 154 $self->assert($ld->bind($bind_dn, password => $bind_pass)); 155 156 my $entry = Net::LDAP::Entry->new("uid=$user,$ldap_base"); 157 $entry->delete(); 158 my $msg = $entry->update($ld); 159 if ($msg->is_error()) { 160 $self->annotate($msg->error()); 161 } 162 $self->assert(!$msg->is_error() || $msg->code() == LDAP_NO_SUCH_OBJECT); 163 164 $entry = Net::LDAP::Entry->new( 165 "uid=$user,$ldap_base", 166 objectClass => ['posixAccount', 'account'], 167 uid => $user, 168 userPassword => $passwd, 169 uidNumber => $uid, 170 gidNumber => $gid, 171 homeDirectory => $home_dir, 172 cn => 'ProFTPD Test', 173 ); 174 $msg = $entry->update($ld); 175 $self->assert(!$msg->is_error()); 176 177 # Make sure that, if we're running as root, that the home directory has 178 # permissions/privs set for the account we create 179 if ($< == 0) { 180 unless (chmod(0755, $home_dir)) { 181 die("Can't set perms on $home_dir to 0755: $!"); 182 } 183 184 unless (chown($uid, $gid, $home_dir)) { 185 die("Can't set owner of $home_dir to $uid/$gid: $!"); 186 } 187 } 188 189 my $config = { 190 PidFile => $pid_file, 191 ScoreboardFile => $scoreboard_file, 192 SystemLog => $log_file, 193 TraceLog => $log_file, 194 Trace => 'auth:10', 195 196 IfModules => { 197 'mod_delay.c' => { 198 DelayEngine => 'off', 199 }, 200 201 'mod_ldap.c' => { 202 LDAPServer => $server, 203 LDAPBindDN => "$bind_dn $bind_pass", 204 LDAPUsers => "$ldap_base (uid=%u)", 205 }, 206 }, 207 }; 208 209 my ($port, $config_user, $config_group) = config_write($config_file, $config); 210 211 # Open pipes, for use between the parent and child processes. Specifically, 212 # the child will indicate when it's done with its test by writing a message 213 # to the parent. 214 my ($rfh, $wfh); 215 unless (pipe($rfh, $wfh)) { 216 die("Can't open pipe: $!"); 217 } 218 219 my $ex; 220 221 # Fork child 222 $self->handle_sigchld(); 223 defined(my $pid = fork()) or die("Can't fork: $!"); 224 if ($pid) { 225 eval { 226 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 227 if ($allow_auth) { 228 $client->login($user, $passwd); 229 } else { 230 eval { $client->login($user, $passwd . '-') }; 231 unless ($@) { 232 die("Login succeeded unexpectedly"); 233 } 234 } 235 236 my ($resp_code, $resp_msg); 237 $resp_code = $client->response_code(); 238 $resp_msg = $client->response_msg(); 239 240 my $expected; 241 242 if ($allow_auth) { 243 $expected = 230; 244 $self->assert($expected == $resp_code, 245 test_msg("Expected $expected, got $resp_code")); 246 247 # FIXME: assert() -> assert_str_equals() 248 $expected = "User $user logged in"; 249 $self->assert($expected eq $resp_msg, 250 test_msg("Expected '$expected', got '$resp_msg'")); 251 } else { 252 $expected = 530; 253 $self->assert($expected == $resp_code, 254 test_msg("Expected $expected, got $resp_code")); 255 256 $expected = 'Login incorrect.'; 257 $self->assert($expected eq $resp_msg, 258 test_msg("Expected '$expected', got '$resp_msg'")); 259 } 260 }; 261 262 if ($@) { 263 $ex = $@; 264 } 265 266 $wfh->print("done\n"); 267 $wfh->flush(); 268 269 } else { 270 eval { server_wait($config_file, $rfh) }; 271 if ($@) { 272 warn($@); 273 exit 1; 274 } 275 276 exit 0; 277 } 278 279 server_stop($pid_file); 280 281 $self->assert_child_ok($pid); 282 283 if ($ex) { 284 die($ex); 285 } 286 287 unlink($log_file); 288} 289 290sub ldap_users_authallowed { 291 my $self = shift; 292 293 ldap_auth($self, 1); 294} 295 296sub ldap_users_authdenied { 297 my $self = shift; 298 299 ldap_auth($self, 0); 300} 301 302sub ldap_genhomedir { 303 my $self = shift; 304 my $with_username = shift; 305 my $force_genhdir = shift; 306 my $tmpdir = $self->{tmpdir}; 307 308 my $config_file = "$tmpdir/ldap.conf"; 309 my $pid_file = File::Spec->rel2abs("$tmpdir/ldap.pid"); 310 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/ldap.scoreboard"); 311 312 my $log_file = File::Spec->rel2abs('tests.log'); 313 314 my $server = $ENV{LDAP_SERVER} ? $ENV{LDAP_SERVER} : 'localhost'; 315 my $bind_dn = $ENV{LDAP_BIND_DN}; 316 my $bind_pass = $ENV{LDAP_BIND_PASS}; 317 my $ldap_base = $ENV{LDAP_USER_BASE}; 318 my $user = 'proftpdtest' . int(rand(4294967296)); 319 my $passwd = 'foobar'; 320 my $uid = 1000; 321 my $gid = 1000; 322 323 my $home_dir; 324 if ($with_username) { 325 $home_dir = File::Spec->rel2abs($tmpdir) . "/$user"; 326 } else { 327 $home_dir = File::Spec->rel2abs($tmpdir); 328 } 329 330 if ($with_username) { 331 mkdir($home_dir); 332 } 333 334 my $ld = Net::LDAP->new([$server]); 335 $self->assert($ld); 336 $self->assert($ld->bind($bind_dn, password => $bind_pass)); 337 338 my $entry = Net::LDAP::Entry->new("uid=$user,$ldap_base"); 339 $entry->delete(); 340 my $msg = $entry->update($ld); 341 if ($msg->is_error()) { 342 $self->annotate($msg->error()); 343 } 344 $self->assert(!$msg->is_error() || $msg->code() == LDAP_NO_SUCH_OBJECT); 345 346 if ($force_genhdir) { 347 $entry = Net::LDAP::Entry->new( 348 "uid=$user,$ldap_base", 349 objectClass => ['posixAccount', 'account'], 350 uid => $user, 351 userPassword => $passwd, 352 uidNumber => $uid, 353 gidNumber => $gid, 354 homeDirectory => '/nonexistent', 355 cn => 'ProFTPD Test', 356 ); 357 } else { 358 $entry = Net::LDAP::Entry->new( 359 "uid=$user,$ldap_base", 360 objectClass => ['posixAccount', 'account'], 361 uid => $user, 362 userPassword => $passwd, 363 uidNumber => $uid, 364 gidNumber => $gid, 365 homeDirectory => $home_dir, 366 cn => 'ProFTPD Test', 367 ); 368 } 369 $msg = $entry->update($ld); 370 $self->assert(!$msg->is_error()); 371 372 # Make sure that, if we're running as root, that the home directory has 373 # permissions/privs set for the account we create 374 if ($< == 0) { 375 unless (chmod(0755, $home_dir)) { 376 die("Can't set perms on $home_dir to 0755: $!"); 377 } 378 379 unless (chown($uid, $gid, $home_dir)) { 380 die("Can't set owner of $home_dir to $uid/$gid: $!"); 381 } 382 } 383 384 my $config = { 385 PidFile => $pid_file, 386 ScoreboardFile => $scoreboard_file, 387 SystemLog => $log_file, 388 TraceLog => $log_file, 389 Trace => 'auth:10', 390 391 IfModules => { 392 'mod_delay.c' => { 393 DelayEngine => 'off', 394 }, 395 396 'mod_ldap.c' => { 397 LDAPServer => $server, 398 LDAPBindDN => "$bind_dn $bind_pass", 399 LDAPUsers => "$ldap_base (uid=%u)", 400 LDAPGenerateHomedir => 'on', 401 LDAPGenerateHomedirPrefix => $tmpdir, 402 LDAPGenerateHomedirPrefixNoUsername => 403 $with_username ? 'off' : 'on', 404 LDAPForceGeneratedHomedir => 405 $force_genhdir ? 'on' : 'off', 406 }, 407 }, 408 }; 409 410 open(HDIR_FILE, ">$home_dir/testfile") || 411 die("Unable to open $home_dir/testfile for writing: $!"); 412 print HDIR_FILE "test file in generated homedir\n"; 413 close(HDIR_FILE); 414 415 my ($port, $config_user, $config_group) = config_write($config_file, $config); 416 417 # Open pipes, for use between the parent and child processes. Specifically, 418 # the child will indicate when it's done with its test by writing a message 419 # to the parent. 420 my ($rfh, $wfh); 421 unless (pipe($rfh, $wfh)) { 422 die("Can't open pipe: $!"); 423 } 424 425 my $ex; 426 427 # Fork child 428 $self->handle_sigchld(); 429 defined(my $pid = fork()) or die("Can't fork: $!"); 430 if ($pid) { 431 eval { 432 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 433 $client->login($user, $passwd); 434 435 my ($resp_code, $resp_msg); 436 $resp_code = $client->response_code(); 437 $resp_msg = $client->response_msg(); 438 439 my $expected; 440 441 $expected = 230; 442 $self->assert($expected == $resp_code, 443 test_msg("Expected $expected, got $resp_code")); 444 445 $expected = "User $user logged in"; 446 $self->assert($expected eq $resp_msg, 447 test_msg("Expected '$expected', got '$resp_msg'")); 448 449 $client->retr('testfile'); 450 }; 451 452 if ($@) { 453 $ex = $@; 454 } 455 456 $wfh->print("done\n"); 457 $wfh->flush(); 458 459 } else { 460 eval { server_wait($config_file, $rfh) }; 461 if ($@) { 462 warn($@); 463 exit 1; 464 } 465 466 exit 0; 467 } 468 469 server_stop($pid_file); 470 471 $self->assert_child_ok($pid); 472 473 if ($ex) { 474 die($ex); 475 } 476 477 unlink($log_file); 478} 479 480sub ldap_genhomedir_with_username { 481 my $self = shift; 482 483 ldap_genhomedir($self, 1, 0); 484} 485 486sub ldap_genhomedir_without_username { 487 my $self = shift; 488 489 ldap_genhomedir($self, 0, 0); 490} 491 492sub ldap_genhomedir_forcegenhdir { 493 my $self = shift; 494 495 ldap_genhomedir($self, 0, 1); 496} 497 498sub ldap_groups_auth { 499 my $self = shift; 500 my $allow_auth = shift; 501 my $tmpdir = $self->{tmpdir}; 502 503 my $config_file = "$tmpdir/ldap.conf"; 504 my $pid_file = File::Spec->rel2abs("$tmpdir/ldap.pid"); 505 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/ldap.scoreboard"); 506 507 my $log_file = File::Spec->rel2abs('tests.log'); 508 509 my $server = $ENV{LDAP_SERVER} ? $ENV{LDAP_SERVER} : 'localhost'; 510 my $bind_dn = $ENV{LDAP_BIND_DN}; 511 my $bind_pass = $ENV{LDAP_BIND_PASS}; 512 my $ldap_base = $ENV{LDAP_USER_BASE}; 513 my $user = 'proftpdtest' . int(rand(4294967296)); 514 my $passwd = 'foobar'; 515 my $uid = 1000; 516 my $gid = 1000; 517 my $home_dir = File::Spec->rel2abs($tmpdir); 518 519 my $group = 'proftpdtestgroup' . int(rand(4294967296)); 520 my $groupid = 10000; 521 522 my $ld = Net::LDAP->new([$server]); 523 $self->assert($ld); 524 $self->assert($ld->bind($bind_dn, password => $bind_pass)); 525 526 my $entry = Net::LDAP::Entry->new("uid=$user,$ldap_base"); 527 $entry->delete(); 528 my $msg = $entry->update($ld); 529 $self->assert(!$msg->is_error() || $msg->code() == LDAP_NO_SUCH_OBJECT); 530 531 $entry = Net::LDAP::Entry->new("cn=$group,$ldap_base"); 532 $entry->delete(); 533 $msg = $entry->update($ld); 534 $self->assert(!$msg->is_error() || $msg->code() == LDAP_NO_SUCH_OBJECT); 535 536 $entry = Net::LDAP::Entry->new( 537 "uid=$user,$ldap_base", 538 objectClass => ['posixAccount', 'account'], 539 uid => $user, 540 userPassword => $passwd, 541 uidNumber => $uid, 542 gidNumber => $gid, 543 homeDirectory => $home_dir, 544 cn => 'ProFTPD Test', 545 ); 546 $msg = $entry->update($ld); 547 $self->assert(!$msg->is_error()); 548 549 if ($allow_auth) { 550 $entry = Net::LDAP::Entry->new( 551 "cn=$group,$ldap_base", 552 objectClass => 'posixGroup', 553 cn => $group, 554 gidNumber => $groupid, 555 memberUid => $user, 556 ); 557 } else { 558 $entry = Net::LDAP::Entry->new( 559 "cn=$group,$ldap_base", 560 objectClass => 'posixGroup', 561 cn => $group, 562 gidNumber => $groupid, 563 ); 564 } 565 $msg = $entry->update($ld); 566 $self->assert(!$msg->is_error()); 567 568 # Make sure that, if we're running as root, that the home directory has 569 # permissions/privs set for the account we create 570 if ($< == 0) { 571 unless (chmod(0755, $home_dir)) { 572 die("Can't set perms on $home_dir to 0755: $!"); 573 } 574 575 unless (chown($uid, $gid, $home_dir)) { 576 die("Can't set owner of $home_dir to $uid/$gid: $!"); 577 } 578 } 579 580 my $config = { 581 PidFile => $pid_file, 582 ScoreboardFile => $scoreboard_file, 583 SystemLog => $log_file, 584 TraceLog => $log_file, 585 Trace => 'auth:10', 586 587 Limit => { 588 LOGIN => { 589 AllowGroup => $group, 590 DenyAll => '', 591 } 592 }, 593 594 IfModules => { 595 'mod_delay.c' => { 596 DelayEngine => 'off', 597 }, 598 599 'mod_ldap.c' => { 600 LDAPServer => $server, 601 LDAPBindDN => "$bind_dn $bind_pass", 602 LDAPUsers => "$ldap_base (uid=%u)", 603 LDAPGroups => "$ldap_base (uid=%u)", 604 }, 605 }, 606 }; 607 608 my ($port, $config_user, $config_group) = config_write($config_file, $config); 609 610 # Open pipes, for use between the parent and child processes. Specifically, 611 # the child will indicate when it's done with its test by writing a message 612 # to the parent. 613 my ($rfh, $wfh); 614 unless (pipe($rfh, $wfh)) { 615 die("Can't open pipe: $!"); 616 } 617 618 my $ex; 619 620 # Fork child 621 $self->handle_sigchld(); 622 defined(my $pid = fork()) or die("Can't fork: $!"); 623 if ($pid) { 624 eval { 625 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 626 if ($allow_auth) { 627 $client->login($user, $passwd); 628 } else { 629 eval { $client->login($user, $passwd) }; 630 unless ($@) { 631 die("Login succeeded unexpectedly"); 632 } 633 } 634 635 my ($resp_code, $resp_msg); 636 $resp_code = $client->response_code(); 637 $resp_msg = $client->response_msg(); 638 639 my $expected; 640 641 if ($allow_auth) { 642 $expected = 230; 643 $self->assert($expected == $resp_code, 644 test_msg("Expected $expected, got $resp_code")); 645 646 $expected = "User $user logged in"; 647 $self->assert($expected eq $resp_msg, 648 test_msg("Expected '$expected', got '$resp_msg'")); 649 } else { 650 $expected = 530; 651 $self->assert($expected == $resp_code, 652 test_msg("Expected $expected, got $resp_code")); 653 654 $expected = 'Login incorrect.'; 655 $self->assert($expected eq $resp_msg, 656 test_msg("Expected '$expected', got '$resp_msg'")); 657 } 658 }; 659 660 if ($@) { 661 $ex = $@; 662 } 663 664 $wfh->print("done\n"); 665 $wfh->flush(); 666 667 } else { 668 eval { server_wait($config_file, $rfh) }; 669 if ($@) { 670 warn($@); 671 exit 1; 672 } 673 674 exit 0; 675 } 676 677 server_stop($pid_file); 678 679 $self->assert_child_ok($pid); 680 681 if ($ex) { 682 die($ex); 683 } 684 685 unlink($log_file); 686} 687 688sub ldap_groups_authdenied { 689 my $self = shift; 690 691 ldap_groups_auth($self, 0); 692} 693 694sub ldap_groups_authallowed { 695 my $self = shift; 696 697 ldap_groups_auth($self, 1); 698} 699 700sub ldap_quota { 701 my $self = shift; 702 my $default_quota = shift; 703 my $tmpdir = $self->{tmpdir}; 704 my $abs_tmpdir = File::Spec->rel2abs($self->{tmpdir}); 705 706 my $config_file = "$tmpdir/ldap.conf"; 707 my $pid_file = File::Spec->rel2abs("$tmpdir/ldap.pid"); 708 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/ldap.scoreboard"); 709 710 my $log_file = File::Spec->rel2abs('tests.log'); 711 712 my $server = $ENV{LDAP_SERVER} ? $ENV{LDAP_SERVER} : 'localhost'; 713 my $bind_dn = $ENV{LDAP_BIND_DN}; 714 my $bind_pass = $ENV{LDAP_BIND_PASS}; 715 my $ldap_base = $ENV{LDAP_USER_BASE}; 716 my $user = 'proftpdtest' . int(rand(4294967296)); 717 my $passwd = 'foobar'; 718 my $uid = 1000; 719 my $gid = 1000; 720 my $home_dir = File::Spec->rel2abs($tmpdir); 721 722 my $ftpquota_path; 723 if ($ENV{PROFTPD_TEST_PATH}) { 724 $ftpquota_path = "$ENV{PROFTPD_TEST_PATH}/../contrib/ftpquota"; 725 } else { 726 $ftpquota_path = '../contrib/ftpquota'; 727 } 728 729 my $ld = Net::LDAP->new([$server]); 730 $self->assert($ld); 731 $self->assert($ld->bind($bind_dn, password => $bind_pass)); 732 733 my $entry = Net::LDAP::Entry->new("uid=$user,$ldap_base"); 734 $entry->delete(); 735 my $msg = $entry->update($ld); 736 if ($msg->is_error()) { 737 $self->annotate($msg->error()); 738 } 739 $self->assert(!$msg->is_error() || $msg->code() == LDAP_NO_SUCH_OBJECT); 740 741 if ($default_quota) { 742 $entry = Net::LDAP::Entry->new( 743 "uid=$user,$ldap_base", 744 objectClass => ['posixAccount', 'account'], 745 uid => $user, 746 userPassword => $passwd, 747 uidNumber => $uid, 748 gidNumber => $gid, 749 homeDirectory => $home_dir, 750 cn => 'ProFTPD Test', 751 ); 752 } else { 753 $entry = Net::LDAP::Entry->new( 754 "uid=$user,$ldap_base", 755 objectClass => ['posixAccount', 'account', 'proftpdQuota'], 756 uid => $user, 757 userPassword => $passwd, 758 uidNumber => $uid, 759 gidNumber => $gid, 760 homeDirectory => $home_dir, 761 cn => 'ProFTPD Test', 762 ftpQuota => 'false,hard,1048576,0,0,0,0,0', 763 ); 764 } 765 $msg = $entry->update($ld); 766 $self->assert(!$msg->is_error()); 767 768 # Make sure that, if we're running as root, that the home directory has 769 # permissions/privs set for the account we create 770 if ($< == 0) { 771 unless (chmod(0755, $home_dir)) { 772 die("Can't set perms on $home_dir to 0755: $!"); 773 } 774 775 unless (chown($uid, $gid, $home_dir)) { 776 die("Can't set owner of $home_dir to $uid/$gid: $!"); 777 } 778 } 779 780 my $config = { 781 PidFile => $pid_file, 782 ScoreboardFile => $scoreboard_file, 783 SystemLog => $log_file, 784 TraceLog => $log_file, 785 Trace => 'auth:10', 786 787 IfModules => { 788 'mod_delay.c' => { 789 DelayEngine => 'off', 790 }, 791 792 'mod_ldap.c' => { 793 LDAPServer => $server, 794 LDAPBindDN => "$bind_dn $bind_pass", 795 LDAPUsers => "$ldap_base (uid=%u)", 796 LDAPDefaultQuota => 'false,hard,10485760,0,0,0,0,0', 797 }, 798 799 'mod_quotatab.c' => { 800 QuotaEngine => 'on', 801 QuotaShowQuotas => 'on', 802 QuotaDisplayUnits => 'Mb', 803 QuotaLimitTable => 'ldap:', 804 QuotaTallyTable => "file:$abs_tmpdir/quota-tally", 805 QuotaLog => '/var/log/quota', 806 }, 807 }, 808 }; 809 810 # FIXME: error checking? 811 system("$ftpquota_path --type tally --create-table --table-path \"$tmpdir/quota-tally\""); 812 813 my ($port, $config_user, $config_group) = config_write($config_file, $config); 814 815 # Open pipes, for use between the parent and child processes. Specifically, 816 # the child will indicate when it's done with its test by writing a message 817 # to the parent. 818 my ($rfh, $wfh); 819 unless (pipe($rfh, $wfh)) { 820 die("Can't open pipe: $!"); 821 } 822 823 my $ex; 824 825 # Fork child 826 $self->handle_sigchld(); 827 defined(my $pid = fork()) or die("Can't fork: $!"); 828 if ($pid) { 829 eval { 830 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 831 $client->login($user, $passwd); 832 833 my ($resp_code, $resp_msg); 834 $resp_code = $client->response_code(); 835 $resp_msg = $client->response_msg(); 836 837 my $expected; 838 839 $expected = 230; 840 $self->assert($expected == $resp_code, 841 test_msg("Expected $expected, got $resp_code")); 842 843 $expected = "User $user logged in"; 844 $self->assert($expected eq $resp_msg, 845 test_msg("Expected '$expected', got '$resp_msg'")); 846 847 848 $client->quote('SITE QUOTA'); 849 $resp_code = $client->response_code(); 850 $resp_msg = join("\n", @{$client->response_msgs()}); 851 852 $self->assert_matches(qr/Quota Type:\s*User/mi, $resp_msg, 853 test_msg("Expected User quota type, got '$resp_msg'")); 854 if ($default_quota) { 855 $self->assert_matches(qr/Uploaded Mb:\s*0\.00\/10\.00/mi, $resp_msg, 856 test_msg("Expected 0.0/1.0 Mb uploaded, got '$resp_msg'")); 857 } else { 858 $self->assert_matches(qr/Uploaded Mb:\s*0\.00\/1\.00/mi, $resp_msg, 859 test_msg("Expected 0.0/1.0 Mb uploaded, got '$resp_msg'")); 860 } 861 862 $client->quit(); 863 }; 864 865 if ($@) { 866 $ex = $@; 867 } 868 869 $wfh->print("done\n"); 870 $wfh->flush(); 871 872 } else { 873 eval { server_wait($config_file, $rfh) }; 874 if ($@) { 875 warn($@); 876 exit 1; 877 } 878 879 exit 0; 880 } 881 882 server_stop($pid_file); 883 884 $self->assert_child_ok($pid); 885 886 if ($ex) { 887 die($ex); 888 } 889 890 unlink($log_file); 891} 892 893sub ldap_quota_on_user { 894 my $self = shift; 895 896 ldap_quota($self, 0); 897} 898 899sub ldap_quota_default { 900 my $self = shift; 901 902 ldap_quota($self, 1); 903} 904 905sub ldap_default_uid_gid { 906 my $self = shift; 907 my $uid_or_gid = shift; 908 my $force_default = shift; 909 my $tmpdir = $self->{tmpdir}; 910 911 my $config_file = "$tmpdir/ldap.conf"; 912 my $pid_file = File::Spec->rel2abs("$tmpdir/ldap.pid"); 913 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/ldap.scoreboard"); 914 915 my $log_file = File::Spec->rel2abs('tests.log'); 916 917 my $server = $ENV{LDAP_SERVER} ? $ENV{LDAP_SERVER} : 'localhost'; 918 my $bind_dn = $ENV{LDAP_BIND_DN}; 919 my $bind_pass = $ENV{LDAP_BIND_PASS}; 920 my $ldap_base = $ENV{LDAP_USER_BASE}; 921 my $user = 'proftpdtest' . int(rand(4294967296)); 922 my $passwd = 'foobar'; 923 my $ldap_uid = 1000; 924 my $ldap_gid = 1000; 925 my $home_dir = File::Spec->rel2abs($tmpdir) . "/$user"; 926 927 mkdir($home_dir); 928 929 my $ld = Net::LDAP->new([$server]); 930 $self->assert($ld); 931 $self->assert($ld->bind($bind_dn, password => $bind_pass)); 932 933 my $entry = Net::LDAP::Entry->new("uid=$user,$ldap_base"); 934 $entry->delete(); 935 my $msg = $entry->update($ld); 936 if ($msg->is_error()) { 937 $self->annotate($msg->error()); 938 } 939 $self->assert(!$msg->is_error() || $msg->code() == LDAP_NO_SUCH_OBJECT); 940 941 # FIXME: posixAccount requires [ug]idNumber, so we can't check 942 # LDAPDefault[UG]ID behavior when uidNumber/gidNumber are absent. 943 # We'll have to declare a dependency on a different (local) 944 # objectClass, or have the mod_ldap test suite depend on OpenLDAP's 945 # slapd being installed, and start our own directory instance. 946 $entry = Net::LDAP::Entry->new( 947 "uid=$user,$ldap_base", 948 objectClass => ['posixAccount', 'account'], 949 uid => $user, 950 userPassword => $passwd, 951 uidNumber => $ldap_uid, 952 gidNumber => $ldap_gid, 953 homeDirectory => $home_dir, 954 cn => 'ProFTPD Test', 955 ); 956 $msg = $entry->update($ld); 957 $self->assert(!$msg->is_error()); 958 959 my $config = { 960 PidFile => $pid_file, 961 ScoreboardFile => $scoreboard_file, 962 SystemLog => $log_file, 963 TraceLog => $log_file, 964 Trace => 'auth:10', 965 966 IfModules => { 967 'mod_delay.c' => { 968 DelayEngine => 'off', 969 }, 970 971 'mod_ldap.c' => { 972 LDAPServer => $server, 973 LDAPBindDN => "$bind_dn $bind_pass", 974 LDAPUsers => "$ldap_base (uid=%u)", 975 LDAPGenerateHomedir => 'on', 976 LDAPGenerateHomedirPrefix => $tmpdir, 977 }, 978 }, 979 }; 980 # If we're forcing the UID/GID, force a *different* UID/GID so 981 # we can determine whether the forcing is in effect. 982 my $proftpd_uid = $ldap_uid; 983 my $proftpd_gid = $ldap_gid; 984 if ($uid_or_gid eq 'uid') { 985 if ($force_default) { 986 $proftpd_uid = $ldap_uid + 1; 987 $config->{IfModules}->{'mod_ldap.c'}->{LDAPForceDefaultUID} = 'on'; 988 } 989 $config->{IfModules}->{'mod_ldap.c'}->{LDAPDefaultUID} = $proftpd_uid; 990 } elsif ($uid_or_gid eq 'gid') { 991 if ($force_default) { 992 $proftpd_gid = $ldap_gid + 1; 993 $config->{IfModules}->{'mod_ldap.c'}->{LDAPForceDefaultGID} = 'on'; 994 } 995 $config->{IfModules}->{'mod_ldap.c'}->{LDAPDefaultGID} = $proftpd_gid; 996 } 997 998 # Make sure that, if we're running as root, that the home directory has 999 # permissions/privs set for the account we create 1000 if ($< == 0) { 1001 unless (chmod(0755, $home_dir)) { 1002 die("Can't set perms on $home_dir to 0755: $!"); 1003 } 1004 1005 unless (chown($proftpd_uid, $proftpd_gid, $home_dir)) { 1006 die("Can't set owner of $home_dir to $proftpd_uid/$proftpd_gid: $!"); 1007 } 1008 } 1009 1010 open(HDIR_FILE, ">$home_dir/testfile") || 1011 die("Unable to open $home_dir/testfile for writing: $!"); 1012 print HDIR_FILE "test file\n"; 1013 close(HDIR_FILE); 1014 unless (chown($proftpd_uid, $proftpd_gid, "$home_dir/testfile")) { 1015 die("Can't set owner of $home_dir/testfile to $ldap_uid/$ldap_gid: $!"); 1016 } 1017 unless (chmod(0700, "$home_dir/testfile")) { 1018 die("Can't set file permission bits on $home_dir/testfile to 0700: $!"); 1019 } 1020 1021 my ($port, $config_user, $config_group) = config_write($config_file, $config); 1022 1023 # Open pipes, for use between the parent and child processes. Specifically, 1024 # the child will indicate when it's done with its test by writing a message 1025 # to the parent. 1026 my ($rfh, $wfh); 1027 unless (pipe($rfh, $wfh)) { 1028 die("Can't open pipe: $!"); 1029 } 1030 1031 my $ex; 1032 1033 # Fork child 1034 $self->handle_sigchld(); 1035 defined(my $pid = fork()) or die("Can't fork: $!"); 1036 if ($pid) { 1037 eval { 1038 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1039 $client->login($user, $passwd); 1040 1041 my ($resp_code, $resp_msg); 1042 $resp_code = $client->response_code(); 1043 $resp_msg = $client->response_msg(); 1044 1045 my $expected; 1046 1047 $expected = 230; 1048 $self->assert($expected == $resp_code, 1049 test_msg("Expected $expected, got $resp_code")); 1050 1051 $expected = "User $user logged in"; 1052 $self->assert($expected eq $resp_msg, 1053 test_msg("Expected '$expected', got '$resp_msg'")); 1054 1055 $client->retr('testfile'); 1056 }; 1057 1058 if ($@) { 1059 $ex = $@; 1060 } 1061 1062 $wfh->print("done\n"); 1063 $wfh->flush(); 1064 1065 } else { 1066 eval { server_wait($config_file, $rfh) }; 1067 if ($@) { 1068 warn($@); 1069 exit 1; 1070 } 1071 1072 exit 0; 1073 } 1074 1075 server_stop($pid_file); 1076 1077 $self->assert_child_ok($pid); 1078 1079 if ($ex) { 1080 die($ex); 1081 } 1082 1083 unlink($log_file); 1084} 1085 1086sub ldap_default_uid { 1087 my $self = shift; 1088 1089 ldap_default_uid_gid($self, 'uid', 0); 1090} 1091 1092sub ldap_default_gid { 1093 my $self = shift; 1094 1095 ldap_default_uid_gid($self, 'gid', 0); 1096} 1097 1098sub ldap_default_force_uid { 1099 my $self = shift; 1100 1101 ldap_default_uid_gid($self, 'uid', 1); 1102} 1103 1104sub ldap_default_force_gid { 1105 my $self = shift; 1106 1107 ldap_default_uid_gid($self, 'gid', 1); 1108} 1109 1110sub ldap_alias_dereference { 1111 my $self = shift; 1112 my $dereference_enabled = shift; 1113 my $tmpdir = $self->{tmpdir}; 1114 my $abs_tmpdir = File::Spec->rel2abs($self->{tmpdir}); 1115 1116 my $config_file = "$tmpdir/ldap.conf"; 1117 my $pid_file = File::Spec->rel2abs("$tmpdir/ldap.pid"); 1118 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/ldap.scoreboard"); 1119 1120 my $log_file = File::Spec->rel2abs('tests.log'); 1121 1122 my $server = $ENV{LDAP_SERVER} ? $ENV{LDAP_SERVER} : 'localhost'; 1123 my $bind_dn = $ENV{LDAP_BIND_DN}; 1124 my $bind_pass = $ENV{LDAP_BIND_PASS}; 1125 my $ldap_base = $ENV{LDAP_USER_BASE}; 1126 my $ldap_base_alias = 'ou=proftpdtest' . int(rand(4294967296)) . ',' . 1127 $ldap_base; 1128 my $user = 'proftpdtest' . int(rand(4294967296)); 1129 my $passwd = 'foobar'; 1130 my $uid = 1000; 1131 my $gid = 1000; 1132 my $home_dir = File::Spec->rel2abs($tmpdir); 1133 1134 my $ld = Net::LDAP->new([$server]); 1135 $self->assert($ld); 1136 $self->assert($ld->bind($bind_dn, password => $bind_pass)); 1137 1138 my $entry = Net::LDAP::Entry->new("uid=$user,$ldap_base"); 1139 $entry->delete(); 1140 my $msg = $entry->update($ld); 1141 if ($msg->is_error()) { 1142 $self->annotate($msg->error()); 1143 } 1144 $self->assert(!$msg->is_error() || $msg->code() == LDAP_NO_SUCH_OBJECT); 1145 1146 $entry = Net::LDAP::Entry->new( 1147 $ldap_base_alias, 1148 objectClass => ['alias', 'extensibleObject'], 1149 aliasedObjectName => $ldap_base, 1150 ); 1151 $msg = $entry->update($ld); 1152 $self->assert(!$msg->is_error()); 1153 1154 $entry = Net::LDAP::Entry->new( 1155 "uid=$user,$ldap_base", 1156 objectClass => ['posixAccount', 'account'], 1157 uid => $user, 1158 userPassword => $passwd, 1159 uidNumber => $uid, 1160 gidNumber => $gid, 1161 homeDirectory => $home_dir, 1162 cn => 'ProFTPD Test', 1163 ); 1164 $msg = $entry->update($ld); 1165 $self->assert(!$msg->is_error()); 1166 1167 # Make sure that, if we're running as root, that the home directory has 1168 # permissions/privs set for the account we create 1169 if ($< == 0) { 1170 unless (chmod(0755, $home_dir)) { 1171 die("Can't set perms on $home_dir to 0755: $!"); 1172 } 1173 1174 unless (chown($uid, $gid, $home_dir)) { 1175 die("Can't set owner of $home_dir to $uid/$gid: $!"); 1176 } 1177 } 1178 1179 my $config = { 1180 PidFile => $pid_file, 1181 ScoreboardFile => $scoreboard_file, 1182 SystemLog => $log_file, 1183 TraceLog => $log_file, 1184 Trace => 'auth:10', 1185 1186 IfModules => { 1187 'mod_delay.c' => { 1188 DelayEngine => 'off', 1189 }, 1190 1191 'mod_ldap.c' => { 1192 LDAPServer => $server, 1193 LDAPBindDN => "$bind_dn $bind_pass", 1194 LDAPUsers => "$ldap_base_alias (uid=%u)", 1195 }, 1196 1197 'mod_quotatab.c' => { 1198 QuotaEngine => 'on', 1199 QuotaShowQuotas => 'on', 1200 QuotaDisplayUnits => 'Mb', 1201 QuotaLimitTable => 'ldap:', 1202 QuotaTallyTable => "file:$abs_tmpdir/quota-tally", 1203 QuotaLog => '/var/log/quota', 1204 }, 1205 }, 1206 }; 1207 if ($dereference_enabled) { 1208 $config->{IfModules}->{'mod_ldap.c'}->{LDAPAliasDereference} = 'always'; 1209 } 1210 1211 my ($port, $config_user, $config_group) = config_write($config_file, $config); 1212 1213 # Open pipes, for use between the parent and child processes. Specifically, 1214 # the child will indicate when it's done with its test by writing a message 1215 # to the parent. 1216 my ($rfh, $wfh); 1217 unless (pipe($rfh, $wfh)) { 1218 die("Can't open pipe: $!"); 1219 } 1220 1221 my $ex; 1222 1223 # Fork child 1224 $self->handle_sigchld(); 1225 defined(my $pid = fork()) or die("Can't fork: $!"); 1226 if ($pid) { 1227 eval { 1228 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1229 if ($dereference_enabled) { 1230 $client->login($user, $passwd); 1231 } else { 1232 eval { $client->login($user, $passwd) }; 1233 unless ($@) { 1234 die("Login succeeded unexpectedly"); 1235 } 1236 } 1237 1238 my ($resp_code, $resp_msg); 1239 $resp_code = $client->response_code(); 1240 $resp_msg = $client->response_msg(); 1241 1242 my $expected; 1243 1244 if ($dereference_enabled) { 1245 $expected = 230; 1246 $self->assert($expected == $resp_code, 1247 test_msg("Expected $expected, got $resp_code")); 1248 1249 $expected = "User $user logged in"; 1250 $self->assert($expected eq $resp_msg, 1251 test_msg("Expected '$expected', got '$resp_msg'")); 1252 } else { 1253 $expected = 530; 1254 $self->assert($expected == $resp_code, 1255 test_msg("Expected $expected, got $resp_code")); 1256 1257 $expected = 'Login incorrect.'; 1258 $self->assert($expected eq $resp_msg, 1259 test_msg("Expected '$expected', got '$resp_msg'")); 1260 } 1261 1262 $client->quit(); 1263 }; 1264 1265 if ($@) { 1266 $ex = $@; 1267 } 1268 1269 $wfh->print("done\n"); 1270 $wfh->flush(); 1271 1272 } else { 1273 eval { server_wait($config_file, $rfh) }; 1274 if ($@) { 1275 warn($@); 1276 exit 1; 1277 } 1278 1279 exit 0; 1280 } 1281 1282 server_stop($pid_file); 1283 1284 $self->assert_child_ok($pid); 1285 1286 if ($ex) { 1287 die($ex); 1288 } 1289 1290 unlink($log_file); 1291} 1292 1293sub ldap_alias_dereference_off { 1294 my $self = shift; 1295 1296 ldap_alias_dereference($self, 0); 1297} 1298 1299sub ldap_alias_dereference_on { 1300 my $self = shift; 1301 1302 ldap_alias_dereference($self, 1); 1303} 1304 1305sub ldap_scope { 1306 my $self = shift; 1307 my $scope = shift; 1308 my $tmpdir = $self->{tmpdir}; 1309 my $abs_tmpdir = File::Spec->rel2abs($self->{tmpdir}); 1310 1311 my $config_file = "$tmpdir/ldap.conf"; 1312 my $pid_file = File::Spec->rel2abs("$tmpdir/ldap.pid"); 1313 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/ldap.scoreboard"); 1314 1315 my $log_file = File::Spec->rel2abs('tests.log'); 1316 1317 my $server = $ENV{LDAP_SERVER} ? $ENV{LDAP_SERVER} : 'localhost'; 1318 my $bind_dn = $ENV{LDAP_BIND_DN}; 1319 my $bind_pass = $ENV{LDAP_BIND_PASS}; 1320 my $ldap_base = $ENV{LDAP_USER_BASE}; 1321 my $user = 'proftpdtest' . int(rand(4294967296)); 1322 my $passwd = 'foobar'; 1323 my $uid = 1000; 1324 my $gid = 1000; 1325 my $home_dir = File::Spec->rel2abs($tmpdir); 1326 1327 my $ld = Net::LDAP->new([$server]); 1328 $self->assert($ld); 1329 $self->assert($ld->bind($bind_dn, password => $bind_pass)); 1330 1331 my $entry = Net::LDAP::Entry->new("uid=$user,$ldap_base"); 1332 $entry->delete(); 1333 my $msg = $entry->update($ld); 1334 if ($msg->is_error()) { 1335 $self->annotate($msg->error()); 1336 } 1337 $self->assert(!$msg->is_error() || $msg->code() == LDAP_NO_SUCH_OBJECT); 1338 1339 $entry = Net::LDAP::Entry->new( 1340 "uid=$user,$ldap_base", 1341 objectClass => ['posixAccount', 'account'], 1342 uid => $user, 1343 userPassword => $passwd, 1344 uidNumber => $uid, 1345 gidNumber => $gid, 1346 homeDirectory => $home_dir, 1347 cn => 'ProFTPD Test', 1348 ); 1349 $msg = $entry->update($ld); 1350 $self->assert(!$msg->is_error()); 1351 1352 # Make sure that, if we're running as root, that the home directory has 1353 # permissions/privs set for the account we create 1354 if ($< == 0) { 1355 unless (chmod(0755, $home_dir)) { 1356 die("Can't set perms on $home_dir to 0755: $!"); 1357 } 1358 1359 unless (chown($uid, $gid, $home_dir)) { 1360 die("Can't set owner of $home_dir to $uid/$gid: $!"); 1361 } 1362 } 1363 1364 my $config = { 1365 PidFile => $pid_file, 1366 ScoreboardFile => $scoreboard_file, 1367 SystemLog => $log_file, 1368 TraceLog => $log_file, 1369 Trace => 'auth:10', 1370 1371 IfModules => { 1372 'mod_delay.c' => { 1373 DelayEngine => 'off', 1374 }, 1375 1376 'mod_ldap.c' => { 1377 LDAPServer => $server, 1378 LDAPBindDN => "$bind_dn $bind_pass", 1379 LDAPUsers => "$ldap_base (uid=%u)", 1380 LDAPSearchScope => $scope, 1381 }, 1382 1383 'mod_quotatab.c' => { 1384 QuotaEngine => 'on', 1385 QuotaShowQuotas => 'on', 1386 QuotaDisplayUnits => 'Mb', 1387 QuotaLimitTable => 'ldap:', 1388 QuotaTallyTable => "file:$abs_tmpdir/quota-tally", 1389 QuotaLog => '/var/log/quota', 1390 }, 1391 }, 1392 }; 1393 1394 my ($port, $config_user, $config_group) = config_write($config_file, $config); 1395 1396 # Open pipes, for use between the parent and child processes. Specifically, 1397 # the child will indicate when it's done with its test by writing a message 1398 # to the parent. 1399 my ($rfh, $wfh); 1400 unless (pipe($rfh, $wfh)) { 1401 die("Can't open pipe: $!"); 1402 } 1403 1404 my $ex; 1405 1406 # Fork child 1407 $self->handle_sigchld(); 1408 defined(my $pid = fork()) or die("Can't fork: $!"); 1409 if ($pid) { 1410 eval { 1411 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1412 if ($scope eq 'subtree') { 1413 $client->login($user, $passwd); 1414 } else { 1415 eval { $client->login($user, $passwd) }; 1416 unless ($@) { 1417 die("Login succeeded unexpectedly"); 1418 } 1419 } 1420 1421 my ($resp_code, $resp_msg); 1422 $resp_code = $client->response_code(); 1423 $resp_msg = $client->response_msg(); 1424 1425 my $expected; 1426 1427 if ($scope eq 'subtree') { 1428 $expected = 230; 1429 $self->assert($expected == $resp_code, 1430 test_msg("Expected $expected, got $resp_code")); 1431 1432 $expected = "User $user logged in"; 1433 $self->assert($expected eq $resp_msg, 1434 test_msg("Expected '$expected', got '$resp_msg'")); 1435 } else { 1436 $expected = 530; 1437 $self->assert($expected == $resp_code, 1438 test_msg("Expected $expected, got $resp_code")); 1439 1440 $expected = 'Login incorrect.'; 1441 $self->assert($expected eq $resp_msg, 1442 test_msg("Expected '$expected', got '$resp_msg'")); 1443 } 1444 1445 $client->quit(); 1446 }; 1447 1448 if ($@) { 1449 $ex = $@; 1450 } 1451 1452 $wfh->print("done\n"); 1453 $wfh->flush(); 1454 1455 } else { 1456 eval { server_wait($config_file, $rfh) }; 1457 if ($@) { 1458 warn($@); 1459 exit 1; 1460 } 1461 1462 exit 0; 1463 } 1464 1465 server_stop($pid_file); 1466 1467 $self->assert_child_ok($pid); 1468 1469 if ($ex) { 1470 die($ex); 1471 } 1472 1473 unlink($log_file); 1474} 1475 1476sub ldap_scope_base { 1477 my $self = shift; 1478 1479 ldap_scope($self, 'base'); 1480} 1481 1482sub ldap_scope_sub { 1483 my $self = shift; 1484 1485 ldap_scope($self, 'subtree'); 1486} 1487 1488sub ldap_default_auth_scheme { 1489 my $self = shift; 1490 my $tmpdir = $self->{tmpdir}; 1491 my $abs_tmpdir = File::Spec->rel2abs($self->{tmpdir}); 1492 1493 my $config_file = "$tmpdir/ldap.conf"; 1494 my $pid_file = File::Spec->rel2abs("$tmpdir/ldap.pid"); 1495 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/ldap.scoreboard"); 1496 1497 my $log_file = File::Spec->rel2abs('tests.log'); 1498 1499 my $server = $ENV{LDAP_SERVER} ? $ENV{LDAP_SERVER} : 'localhost'; 1500 my $bind_dn = $ENV{LDAP_BIND_DN}; 1501 my $bind_pass = $ENV{LDAP_BIND_PASS}; 1502 my $ldap_base = $ENV{LDAP_USER_BASE}; 1503 my $user = 'proftpdtest' . int(rand(4294967296)); 1504 my $passwd = 'foobar'; 1505 my $uid = 1000; 1506 my $gid = 1000; 1507 my $home_dir = File::Spec->rel2abs($tmpdir); 1508 1509 my $ld = Net::LDAP->new([$server]); 1510 $self->assert($ld); 1511 $self->assert($ld->bind($bind_dn, password => $bind_pass)); 1512 1513 my $entry = Net::LDAP::Entry->new("uid=$user,$ldap_base"); 1514 $entry->delete(); 1515 my $msg = $entry->update($ld); 1516 if ($msg->is_error()) { 1517 $self->annotate($msg->error()); 1518 } 1519 $self->assert(!$msg->is_error() || $msg->code() == LDAP_NO_SUCH_OBJECT); 1520 1521 $entry = Net::LDAP::Entry->new( 1522 "uid=$user,$ldap_base", 1523 objectClass => ['posixAccount', 'account'], 1524 uid => $user, 1525 userPassword => crypt($passwd, 1526 join '', ('.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z')[rand 64, rand 64]), 1527 uidNumber => $uid, 1528 gidNumber => $gid, 1529 homeDirectory => $home_dir, 1530 cn => 'ProFTPD Test', 1531 ); 1532 $msg = $entry->update($ld); 1533 $self->assert(!$msg->is_error()); 1534 1535 # Make sure that, if we're running as root, that the home directory has 1536 # permissions/privs set for the account we create 1537 if ($< == 0) { 1538 unless (chmod(0755, $home_dir)) { 1539 die("Can't set perms on $home_dir to 0755: $!"); 1540 } 1541 1542 unless (chown($uid, $gid, $home_dir)) { 1543 die("Can't set owner of $home_dir to $uid/$gid: $!"); 1544 } 1545 } 1546 1547 my $config = { 1548 PidFile => $pid_file, 1549 ScoreboardFile => $scoreboard_file, 1550 SystemLog => $log_file, 1551 TraceLog => $log_file, 1552 Trace => 'auth:10', 1553 1554 IfModules => { 1555 'mod_delay.c' => { 1556 DelayEngine => 'off', 1557 }, 1558 1559 'mod_ldap.c' => { 1560 LDAPServer => $server, 1561 LDAPBindDN => "$bind_dn $bind_pass", 1562 LDAPUsers => "$ldap_base (uid=%u)", 1563 LDAPAuthBinds => 'off', 1564 LDAPDefaultAuthScheme => 'crypt', 1565 }, 1566 1567 'mod_quotatab.c' => { 1568 QuotaEngine => 'on', 1569 QuotaShowQuotas => 'on', 1570 QuotaDisplayUnits => 'Mb', 1571 QuotaLimitTable => 'ldap:', 1572 QuotaTallyTable => "file:$abs_tmpdir/quota-tally", 1573 QuotaLog => '/var/log/quota', 1574 }, 1575 }, 1576 }; 1577 1578 my ($port, $config_user, $config_group) = config_write($config_file, $config); 1579 1580 # Open pipes, for use between the parent and child processes. Specifically, 1581 # the child will indicate when it's done with its test by writing a message 1582 # to the parent. 1583 my ($rfh, $wfh); 1584 unless (pipe($rfh, $wfh)) { 1585 die("Can't open pipe: $!"); 1586 } 1587 1588 my $ex; 1589 1590 # Fork child 1591 $self->handle_sigchld(); 1592 defined(my $pid = fork()) or die("Can't fork: $!"); 1593 if ($pid) { 1594 eval { 1595 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1596 $client->login($user, $passwd); 1597 1598 my ($resp_code, $resp_msg); 1599 $resp_code = $client->response_code(); 1600 $resp_msg = $client->response_msg(); 1601 1602 my $expected; 1603 1604 $expected = 230; 1605 $self->assert($expected == $resp_code, 1606 test_msg("Expected $expected, got $resp_code")); 1607 1608 $expected = "User $user logged in"; 1609 $self->assert($expected eq $resp_msg, 1610 test_msg("Expected '$expected', got '$resp_msg'")); 1611 1612 $client->quit(); 1613 }; 1614 1615 if ($@) { 1616 $ex = $@; 1617 } 1618 1619 $wfh->print("done\n"); 1620 $wfh->flush(); 1621 1622 } else { 1623 eval { server_wait($config_file, $rfh) }; 1624 if ($@) { 1625 warn($@); 1626 exit 1; 1627 } 1628 1629 exit 0; 1630 } 1631 1632 server_stop($pid_file); 1633 1634 $self->assert_child_ok($pid); 1635 1636 if ($ex) { 1637 die($ex); 1638 } 1639 1640 unlink($log_file); 1641} 1642 16431; 1644