1package ProFTPD::Tests::Config::RequireValidShell; 2 3use lib qw(t/lib); 4use base qw(ProFTPD::TestSuite::Child); 5use strict; 6 7use File::Spec; 8use IO::Handle; 9 10use ProFTPD::TestSuite::FTP; 11use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); 12 13$| = 1; 14 15my $order = 0; 16 17my $TESTS = { 18 requirevalidshell_off_valid_shell => { 19 order => ++$order, 20 test_class => [qw(forking)], 21 }, 22 23 requirevalidshell_off_invalid_shell => { 24 order => ++$order, 25 test_class => [qw(forking)], 26 }, 27 28 requirevalidshell_on_valid_shell => { 29 order => ++$order, 30 test_class => [qw(forking)], 31 }, 32 33 requirevalidshell_on_invalid_shell => { 34 order => ++$order, 35 test_class => [qw(forking)], 36 }, 37}; 38 39sub new { 40 return shift()->SUPER::new(@_); 41} 42 43sub list_tests { 44 return testsuite_get_runnable_tests($TESTS); 45} 46 47sub get_shell { 48 my $use_valid_shell = shift; 49 my $shells = {}; 50 51 if (open(my $fh, "< /etc/shells")) { 52 while (my $line = <$fh>) { 53 chomp($line); 54 55 next if $line =~ /^#/; 56 next if $line =~ /^\s+$/; 57 next if length($line) == 0; 58 59 $shells->{$line} = 1; 60 } 61 62 close($fh); 63 64 } else { 65 die("Can't read /etc/shells: $!"); 66 } 67 68 if ($use_valid_shell) { 69 return (keys(%$shells))[0]; 70 71 } else { 72 my $invalid_shells = [qw(/bin/foo /bin/false /bin/notavalidshell /bin/foobarbazquxxquzz)]; 73 74 foreach my $invalid_shell (@$invalid_shells) { 75 unless (defined($shells->{$invalid_shell})) { 76 return $invalid_shell; 77 } 78 } 79 80 return "unknown"; 81 } 82} 83 84sub requirevalidshell_off_valid_shell { 85 my $self = shift; 86 my $tmpdir = $self->{tmpdir}; 87 88 my $config_file = "$tmpdir/config.conf"; 89 my $pid_file = File::Spec->rel2abs("$tmpdir/config.pid"); 90 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/config.scoreboard"); 91 92 my $log_file = File::Spec->rel2abs('tests.log'); 93 94 my $auth_user_file = File::Spec->rel2abs("$tmpdir/config.passwd"); 95 my $auth_group_file = File::Spec->rel2abs("$tmpdir/config.group"); 96 97 my $user = 'proftpd'; 98 my $passwd = 'test'; 99 my $home_dir = File::Spec->rel2abs($tmpdir); 100 my $uid = 500; 101 my $gid = 500; 102 103 # Make sure that, if we're running as root, that the home directory has 104 # permissions/privs set for the account we create 105 if ($< == 0) { 106 unless (chmod(0755, $home_dir)) { 107 die("Can't set perms on $home_dir to 0755: $!"); 108 } 109 110 unless (chown($uid, $gid, $home_dir)) { 111 die("Can't set owner of $home_dir to $uid/$gid: $!"); 112 } 113 } 114 115 my $shell = get_shell(1); 116 117 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 118 $shell); 119 auth_group_write($auth_group_file, 'ftpd', $gid, $user); 120 121 my $config = { 122 PidFile => $pid_file, 123 ScoreboardFile => $scoreboard_file, 124 SystemLog => $log_file, 125 126 AuthUserFile => $auth_user_file, 127 AuthGroupFile => $auth_group_file, 128 129 RequireValidShell => 'off', 130 131 IfModules => { 132 'mod_delay.c' => { 133 DelayEngine => 'off', 134 }, 135 }, 136 }; 137 138 my ($port, $config_user, $config_group) = config_write($config_file, $config); 139 140 # Open pipes, for use between the parent and child processes. Specifically, 141 # the child will indicate when it's done with its test by writing a message 142 # to the parent. 143 my ($rfh, $wfh); 144 unless (pipe($rfh, $wfh)) { 145 die("Can't open pipe: $!"); 146 } 147 148 my $ex; 149 150 # Fork child 151 $self->handle_sigchld(); 152 defined(my $pid = fork()) or die("Can't fork: $!"); 153 if ($pid) { 154 eval { 155 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 156 157 $client->login($user, $passwd); 158 }; 159 160 if ($@) { 161 $ex = $@; 162 } 163 164 $wfh->print("done\n"); 165 $wfh->flush(); 166 167 } else { 168 eval { server_wait($config_file, $rfh) }; 169 if ($@) { 170 warn($@); 171 exit 1; 172 } 173 174 exit 0; 175 } 176 177 # Stop server 178 server_stop($pid_file); 179 180 $self->assert_child_ok($pid); 181 182 if ($ex) { 183 die($ex); 184 } 185 186 unlink($log_file); 187} 188 189sub requirevalidshell_off_invalid_shell { 190 my $self = shift; 191 my $tmpdir = $self->{tmpdir}; 192 193 my $config_file = "$tmpdir/config.conf"; 194 my $pid_file = File::Spec->rel2abs("$tmpdir/config.pid"); 195 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/config.scoreboard"); 196 197 my $log_file = File::Spec->rel2abs('tests.log'); 198 199 my $auth_user_file = File::Spec->rel2abs("$tmpdir/config.passwd"); 200 my $auth_group_file = File::Spec->rel2abs("$tmpdir/config.group"); 201 202 my $user = 'proftpd'; 203 my $passwd = 'test'; 204 my $home_dir = File::Spec->rel2abs($tmpdir); 205 my $uid = 500; 206 my $gid = 500; 207 208 # Make sure that, if we're running as root, that the home directory has 209 # permissions/privs set for the account we create 210 if ($< == 0) { 211 unless (chmod(0755, $home_dir)) { 212 die("Can't set perms on $home_dir to 0755: $!"); 213 } 214 215 unless (chown($uid, $gid, $home_dir)) { 216 die("Can't set owner of $home_dir to $uid/$gid: $!"); 217 } 218 } 219 220 my $shell = get_shell(0); 221 222 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 223 $shell); 224 auth_group_write($auth_group_file, 'ftpd', $gid, $user); 225 226 my $config = { 227 PidFile => $pid_file, 228 ScoreboardFile => $scoreboard_file, 229 SystemLog => $log_file, 230 231 AuthUserFile => $auth_user_file, 232 AuthGroupFile => $auth_group_file, 233 234 RequireValidShell => 'off', 235 236 IfModules => { 237 'mod_delay.c' => { 238 DelayEngine => 'off', 239 }, 240 }, 241 }; 242 243 my ($port, $config_user, $config_group) = config_write($config_file, $config); 244 245 # Open pipes, for use between the parent and child processes. Specifically, 246 # the child will indicate when it's done with its test by writing a message 247 # to the parent. 248 my ($rfh, $wfh); 249 unless (pipe($rfh, $wfh)) { 250 die("Can't open pipe: $!"); 251 } 252 253 my $ex; 254 255 # Fork child 256 $self->handle_sigchld(); 257 defined(my $pid = fork()) or die("Can't fork: $!"); 258 if ($pid) { 259 eval { 260 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 261 262 $client->login($user, $passwd); 263 }; 264 265 if ($@) { 266 $ex = $@; 267 } 268 269 $wfh->print("done\n"); 270 $wfh->flush(); 271 272 } else { 273 eval { server_wait($config_file, $rfh) }; 274 if ($@) { 275 warn($@); 276 exit 1; 277 } 278 279 exit 0; 280 } 281 282 # Stop server 283 server_stop($pid_file); 284 285 $self->assert_child_ok($pid); 286 287 if ($ex) { 288 die($ex); 289 } 290 291 unlink($log_file); 292} 293 294sub requirevalidshell_on_valid_shell { 295 my $self = shift; 296 my $tmpdir = $self->{tmpdir}; 297 298 my $config_file = "$tmpdir/config.conf"; 299 my $pid_file = File::Spec->rel2abs("$tmpdir/config.pid"); 300 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/config.scoreboard"); 301 302 my $log_file = File::Spec->rel2abs('tests.log'); 303 304 my $auth_user_file = File::Spec->rel2abs("$tmpdir/config.passwd"); 305 my $auth_group_file = File::Spec->rel2abs("$tmpdir/config.group"); 306 307 my $user = 'proftpd'; 308 my $passwd = 'test'; 309 my $home_dir = File::Spec->rel2abs($tmpdir); 310 my $uid = 500; 311 my $gid = 500; 312 313 # Make sure that, if we're running as root, that the home directory has 314 # permissions/privs set for the account we create 315 if ($< == 0) { 316 unless (chmod(0755, $home_dir)) { 317 die("Can't set perms on $home_dir to 0755: $!"); 318 } 319 320 unless (chown($uid, $gid, $home_dir)) { 321 die("Can't set owner of $home_dir to $uid/$gid: $!"); 322 } 323 } 324 325 my $shell = get_shell(1); 326 327 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 328 $shell); 329 auth_group_write($auth_group_file, 'ftpd', $gid, $user); 330 331 my $config = { 332 PidFile => $pid_file, 333 ScoreboardFile => $scoreboard_file, 334 SystemLog => $log_file, 335 336 AuthUserFile => $auth_user_file, 337 AuthGroupFile => $auth_group_file, 338 339 RequireValidShell => 'on', 340 341 IfModules => { 342 'mod_delay.c' => { 343 DelayEngine => 'off', 344 }, 345 }, 346 }; 347 348 my ($port, $config_user, $config_group) = config_write($config_file, $config); 349 350 # Open pipes, for use between the parent and child processes. Specifically, 351 # the child will indicate when it's done with its test by writing a message 352 # to the parent. 353 my ($rfh, $wfh); 354 unless (pipe($rfh, $wfh)) { 355 die("Can't open pipe: $!"); 356 } 357 358 my $ex; 359 360 # Fork child 361 $self->handle_sigchld(); 362 defined(my $pid = fork()) or die("Can't fork: $!"); 363 if ($pid) { 364 eval { 365 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 366 367 $client->login($user, $passwd); 368 }; 369 370 if ($@) { 371 $ex = $@; 372 } 373 374 $wfh->print("done\n"); 375 $wfh->flush(); 376 377 } else { 378 eval { server_wait($config_file, $rfh) }; 379 if ($@) { 380 warn($@); 381 exit 1; 382 } 383 384 exit 0; 385 } 386 387 # Stop server 388 server_stop($pid_file); 389 390 $self->assert_child_ok($pid); 391 392 if ($ex) { 393 die($ex); 394 } 395 396 unlink($log_file); 397} 398 399sub requirevalidshell_on_invalid_shell { 400 my $self = shift; 401 my $tmpdir = $self->{tmpdir}; 402 403 my $config_file = "$tmpdir/config.conf"; 404 my $pid_file = File::Spec->rel2abs("$tmpdir/config.pid"); 405 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/config.scoreboard"); 406 407 my $log_file = File::Spec->rel2abs('tests.log'); 408 409 my $auth_user_file = File::Spec->rel2abs("$tmpdir/config.passwd"); 410 my $auth_group_file = File::Spec->rel2abs("$tmpdir/config.group"); 411 412 my $user = 'proftpd'; 413 my $passwd = 'test'; 414 my $home_dir = File::Spec->rel2abs($tmpdir); 415 my $uid = 500; 416 my $gid = 500; 417 418 # Make sure that, if we're running as root, that the home directory has 419 # permissions/privs set for the account we create 420 if ($< == 0) { 421 unless (chmod(0755, $home_dir)) { 422 die("Can't set perms on $home_dir to 0755: $!"); 423 } 424 425 unless (chown($uid, $gid, $home_dir)) { 426 die("Can't set owner of $home_dir to $uid/$gid: $!"); 427 } 428 } 429 430 my $shell = get_shell(0); 431 432 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 433 $shell); 434 auth_group_write($auth_group_file, 'ftpd', $gid, $user); 435 436 my $config = { 437 PidFile => $pid_file, 438 ScoreboardFile => $scoreboard_file, 439 SystemLog => $log_file, 440 441 AuthUserFile => $auth_user_file, 442 AuthGroupFile => $auth_group_file, 443 444 RequireValidShell => 'on', 445 446 IfModules => { 447 'mod_delay.c' => { 448 DelayEngine => 'off', 449 }, 450 }, 451 }; 452 453 my ($port, $config_user, $config_group) = config_write($config_file, $config); 454 455 # Open pipes, for use between the parent and child processes. Specifically, 456 # the child will indicate when it's done with its test by writing a message 457 # to the parent. 458 my ($rfh, $wfh); 459 unless (pipe($rfh, $wfh)) { 460 die("Can't open pipe: $!"); 461 } 462 463 my $ex; 464 465 # Fork child 466 $self->handle_sigchld(); 467 defined(my $pid = fork()) or die("Can't fork: $!"); 468 if ($pid) { 469 eval { 470 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 471 472 my ($resp_code, $resp_msg); 473 474 eval { $client->login($user, $passwd) }; 475 unless ($@) { 476 die("Login succeeded unexpectedly"); 477 478 } else { 479 $resp_code = $client->response_code(); 480 $resp_msg = $client->response_msg(); 481 } 482 483 my $expected; 484 485 $expected = 530; 486 $self->assert($expected == $resp_code, 487 test_msg("Expected $expected, got $resp_code")); 488 489 $expected = "Login incorrect."; 490 $self->assert($expected eq $resp_msg, 491 test_msg("Expected '$expected', got '$resp_msg'")); 492 }; 493 494 if ($@) { 495 $ex = $@; 496 } 497 498 $wfh->print("done\n"); 499 $wfh->flush(); 500 501 } else { 502 eval { server_wait($config_file, $rfh) }; 503 if ($@) { 504 warn($@); 505 exit 1; 506 } 507 508 exit 0; 509 } 510 511 # Stop server 512 server_stop($pid_file); 513 514 $self->assert_child_ok($pid); 515 516 if ($ex) { 517 die($ex); 518 } 519 520 unlink($log_file); 521} 522 5231; 524