1package ProFTPD::Tests::Logins;
2
3use lib qw(t/lib);
4use base qw(ProFTPD::TestSuite::Child);
5use strict;
6
7use Cwd;
8use File::Path qw(mkpath);
9use File::Spec;
10use IO::Handle;
11
12use ProFTPD::TestSuite::FTP;
13use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite);
14
15$| = 1;
16
17my $order = 0;
18
19my $TESTS = {
20  login_plaintext_fails_bad_password => {
21    order => ++$order,
22    test_class => [qw(forking)],
23  },
24
25  login_plaintext_fails_empty_password_bug4139 => {
26    order => ++$order,
27    test_class => [qw(bug forking)],
28  },
29
30  login_anonymous_ok => {
31    order => ++$order,
32    test_class => [qw(forking rootprivs)],
33  },
34
35  login_anonymous_with_delay_ok => {
36    order => ++$order,
37    test_class => [qw(forking rootprivs)],
38  },
39
40  login_anonymous_user_alias_ok => {
41    order => ++$order,
42    test_class => [qw(forking rootprivs)],
43  },
44
45  login_anonymous_fails_bad_perms => {
46    order => ++$order,
47    test_class => [qw(forking)],
48  },
49
50  login_anonymous_fails_empty_password_bug4139 => {
51    order => ++$order,
52    test_class => [qw(bug forking)],
53  },
54
55  login_anonymous_symlink_dir_ok => {
56    order => ++$order,
57    test_class => [qw(forking rootprivs)],
58  },
59
60  login_anonymous_allowchrootsymlinks_bug3852 => {
61    order => ++$order,
62    test_class => [qw(bug forking rootprivs)],
63  },
64
65  login_multiple_attempts_per_conn => {
66    order => ++$order,
67    test_class => [qw(forking)],
68  },
69
70  login_regular_with_anon_defined_bug3307 => {
71    order => ++$order,
72    test_class => [qw(bug forking)],
73  },
74
75  login_fails_bad_sequence => {
76    order => ++$order,
77    test_class => [qw(forking)],
78  },
79
80  login_reauthenticate_fails_bug3736 => {
81    order => ++$order,
82    test_class => [qw(bug forking)],
83  },
84
85  login_reauthenticate_ok_same_user_bug4217 => {
86    order => ++$order,
87    test_class => [qw(bug forking)],
88  },
89
90  login_reauthenticate_fails_different_user_bug4217 => {
91    order => ++$order,
92    test_class => [qw(bug forking)],
93  },
94
95  login_reauthenticate_fails_extra_pass_bug4217 => {
96    order => ++$order,
97    test_class => [qw(bug forking)],
98  },
99
100};
101
102sub new {
103  return shift()->SUPER::new(@_);
104}
105
106sub list_tests {
107  return testsuite_get_runnable_tests($TESTS);
108}
109
110sub login_plaintext_fails_bad_password {
111  my $self = shift;
112  my $tmpdir = $self->{tmpdir};
113
114  my $config_file = "$tmpdir/login.conf";
115  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
116  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
117
118  my $log_file = test_get_logfile();
119
120  my $config = {
121    PidFile => $pid_file,
122    ScoreboardFile => $scoreboard_file,
123    SystemLog => $log_file,
124
125    IfModules => {
126      'mod_delay.c' => {
127        DelayEngine => 'off',
128      },
129    },
130  };
131
132  my ($port, $user, $group) = config_write($config_file, $config);
133
134  # Open pipes, for use between the parent and child processes.  Specifically,
135  # the child will indicate when it's done with its test by writing a message
136  # to the parent.
137  my ($rfh, $wfh);
138  unless (pipe($rfh, $wfh)) {
139    die("Can't open pipe: $!");
140  }
141
142  my $ex;
143
144  # Fork child
145  $self->handle_sigchld();
146  defined(my $pid = fork()) or die("Can't fork: $!");
147  if ($pid) {
148    eval {
149      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
150
151      # In parent process, login to server using a plaintext password which
152      # should NOT work.
153      eval { $client->login('daemon', '*') };
154      unless ($@) {
155        die("Logged in unexpectedly");
156      }
157    };
158
159    if ($@) {
160      $ex = $@;
161    }
162
163    $wfh->print("done\n");
164    $wfh->flush();
165
166  } else {
167    eval { server_wait($config_file, $rfh) };
168    if ($@) {
169      warn($@);
170      exit 1;
171    }
172
173    exit 0;
174  }
175
176  # Stop server
177  server_stop($pid_file);
178
179  $self->assert_child_ok($pid);
180
181  if ($ex) {
182    test_append_logfile($log_file, $ex);
183    unlink($log_file);
184
185    die($ex);
186  }
187
188  unlink($log_file);
189}
190
191sub login_plaintext_fails_empty_password_bug4139 {
192  my $self = shift;
193  my $tmpdir = $self->{tmpdir};
194
195  my $config_file = "$tmpdir/login.conf";
196  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
197  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
198
199  my $log_file = test_get_logfile();
200
201  my $auth_user_file = File::Spec->rel2abs("$tmpdir/login.passwd");
202  my $auth_group_file = File::Spec->rel2abs("$tmpdir/login.group");
203
204  my $user = 'proftpd';
205  my $passwd = '';
206  my $group = 'ftpd';
207  my $home_dir = File::Spec->rel2abs($tmpdir);
208  my $uid = 500;
209  my $gid = 500;
210
211  # Make sure that, if we're running as root, that the home directory has
212  # permissions/privs set for the account we create
213  if ($< == 0) {
214    unless (chmod(0755, $home_dir)) {
215      die("Can't set perms on $home_dir to 0755: $!");
216    }
217
218    unless (chown($uid, $gid, $home_dir)) {
219      die("Can't set owner of $home_dir to $uid/$gid: $!");
220    }
221  }
222
223  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
224    '/bin/bash');
225  auth_group_write($auth_group_file, $group, $gid, $user);
226
227  my $config = {
228    PidFile => $pid_file,
229    ScoreboardFile => $scoreboard_file,
230    SystemLog => $log_file,
231
232    AuthUserFile => $auth_user_file,
233    AuthGroupFile => $auth_group_file,
234
235    AllowEmptyPasswords => 'off',
236
237    IfModules => {
238      'mod_delay.c' => {
239        DelayEngine => 'off',
240      },
241    },
242  };
243
244  my ($port, $config_user, $config_group) = config_write($config_file, $config);
245
246  # Open pipes, for use between the parent and child processes.  Specifically,
247  # the child will indicate when it's done with its test by writing a message
248  # to the parent.
249  my ($rfh, $wfh);
250  unless (pipe($rfh, $wfh)) {
251    die("Can't open pipe: $!");
252  }
253
254  my $ex;
255
256  # Fork child
257  $self->handle_sigchld();
258  defined(my $pid = fork()) or die("Can't fork: $!");
259  if ($pid) {
260    eval {
261      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
262      eval { $client->login($user, $passwd) };
263      unless ($@) {
264        die("Logged in unexpectedly");
265      }
266
267      my $resp_code = $client->response_code();
268      my $resp_msg = $client->response_msg();
269
270      my $expected;
271
272      $expected = 501;
273      $self->assert($expected == $resp_code,
274        test_msg("Expected response code $expected, got $resp_code"));
275
276      $expected = 'Login incorrect.';
277      $self->assert($expected eq $resp_msg,
278        test_msg("Expected response message '$expected', got '$resp_msg'"));
279
280      $client->quit();
281    };
282
283    if ($@) {
284      $ex = $@;
285    }
286
287    $wfh->print("done\n");
288    $wfh->flush();
289
290  } else {
291    eval { server_wait($config_file, $rfh) };
292    if ($@) {
293      warn($@);
294      exit 1;
295    }
296
297    exit 0;
298  }
299
300  # Stop server
301  server_stop($pid_file);
302
303  $self->assert_child_ok($pid);
304
305  if ($ex) {
306    test_append_logfile($log_file, $ex);
307    unlink($log_file);
308
309    die($ex);
310  }
311
312  unlink($log_file);
313}
314
315sub login_anonymous_ok {
316  my $self = shift;
317  my $tmpdir = $self->{tmpdir};
318
319  my $config_file = "$tmpdir/login.conf";
320  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
321  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
322
323  my $log_file = test_get_logfile();
324
325  my $auth_user_file = File::Spec->rel2abs("$tmpdir/login.passwd");
326  my $auth_group_file = File::Spec->rel2abs("$tmpdir/login.group");
327
328  my ($config_user, $config_group) = config_get_identity();
329
330  my $anon_dir = File::Spec->rel2abs($tmpdir);
331  my $uid = 500;
332  my $gid = 500;
333
334  # Make sure that, if we're running as root, that the home directory has
335  # permissions/privs set for the account we create
336  if ($< == 0) {
337    unless (chmod(0755, $anon_dir)) {
338      die("Can't set perms on $anon_dir to 0755: $!");
339    }
340
341    unless (chown($uid, $gid, $anon_dir)) {
342      die("Can't set owner of $anon_dir to $uid/$gid: $!");
343    }
344  }
345
346  auth_user_write($auth_user_file, $config_user, 'foo', $uid, $gid,
347    '/tmp', '/bin/bash');
348  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
349
350  my $config = {
351    PidFile => $pid_file,
352    ScoreboardFile => $scoreboard_file,
353    SystemLog => $log_file,
354    TraceLog => $log_file,
355    Trace => 'DEFAULT:10 privs:10',
356
357    AuthUserFile => $auth_user_file,
358    AuthGroupFile => $auth_group_file,
359
360    IfModules => {
361      'mod_delay.c' => {
362        DelayEngine => 'off',
363      },
364    },
365
366    Anonymous => {
367      $anon_dir => {
368        User => $config_user,
369        Group => $config_group,
370        UserAlias => "anonymous $config_user",
371        RequireValidShell => 'off',
372      },
373    },
374  };
375
376  my ($port, $user, $group) = config_write($config_file, $config);
377
378  # Open pipes, for use between the parent and child processes.  Specifically,
379  # the child will indicate when it's done with its test by writing a message
380  # to the parent.
381  my ($rfh, $wfh);
382  unless (pipe($rfh, $wfh)) {
383    die("Can't open pipe: $!");
384  }
385
386  my $ex;
387
388  # Fork child
389  $self->handle_sigchld();
390  defined(my $pid = fork()) or die("Can't fork: $!");
391  if ($pid) {
392    eval {
393      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
394
395      # In parent process, login anonymously to server using a plaintext
396      # password which SHOULD work.
397      my ($resp_code, $resp_msg) = $client->login($config_user, 'ftp@nospam.org');
398
399      my $expected;
400
401      $expected = 230;
402      $self->assert($expected == $resp_code,
403        test_msg("Expected $expected, got $resp_code"));
404
405      $expected = 'Anonymous access granted, restrictions apply';
406      $self->assert($expected eq $resp_msg,
407        test_msg("Expected '$expected', got '$resp_msg'"));
408
409      $client->quit();
410    };
411
412    if ($@) {
413      $ex = $@;
414    }
415
416    $wfh->print("done\n");
417    $wfh->flush();
418
419  } else {
420    eval { server_wait($config_file, $rfh) };
421    if ($@) {
422      warn($@);
423      exit 1;
424    }
425
426    exit 0;
427  }
428
429  # Stop server
430  server_stop($pid_file);
431
432  $self->assert_child_ok($pid);
433
434  if ($ex) {
435    test_append_logfile($log_file, $ex);
436    unlink($log_file);
437
438    die($ex);
439  }
440
441  unlink($log_file);
442}
443
444sub login_anonymous_with_delay_ok {
445  my $self = shift;
446  my $tmpdir = $self->{tmpdir};
447
448  my $config_file = "$tmpdir/login.conf";
449  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
450  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
451
452  my $log_file = test_get_logfile();
453
454  my $auth_user_file = File::Spec->rel2abs("$tmpdir/login.passwd");
455  my $auth_group_file = File::Spec->rel2abs("$tmpdir/login.group");
456
457  my ($config_user, $config_group) = config_get_identity();
458
459  my $anon_dir = File::Spec->rel2abs($tmpdir);
460  my $uid = 500;
461  my $gid = 500;
462
463  # Make sure that, if we're running as root, that the home directory has
464  # permissions/privs set for the account we create
465  if ($< == 0) {
466    unless (chmod(0755, $anon_dir)) {
467      die("Can't set perms on $anon_dir to 0755: $!");
468    }
469
470    unless (chown($uid, $gid, $anon_dir)) {
471      die("Can't set owner of $anon_dir to $uid/$gid: $!");
472    }
473  }
474
475  auth_user_write($auth_user_file, $config_user, 'foo', $uid, $gid,
476    '/tmp', '/bin/bash');
477  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
478
479  my $delay_tab = File::Spec->rel2abs("$tmpdir/delay.tab");
480
481  my $config = {
482    PidFile => $pid_file,
483    ScoreboardFile => $scoreboard_file,
484    SystemLog => $log_file,
485    TraceLog => $log_file,
486    Trace => 'DEFAULT:10 privs:10',
487
488    AuthUserFile => $auth_user_file,
489    AuthGroupFile => $auth_group_file,
490
491    IfModules => {
492      'mod_delay.c' => {
493        DelayTable => $delay_tab,
494      },
495    },
496
497    Anonymous => {
498      $anon_dir => {
499        User => $config_user,
500        Group => $config_group,
501        UserAlias => "anonymous $config_user",
502        RequireValidShell => 'off',
503      },
504    },
505  };
506
507  my ($port, $user, $group) = config_write($config_file, $config);
508
509  # Open pipes, for use between the parent and child processes.  Specifically,
510  # the child will indicate when it's done with its test by writing a message
511  # to the parent.
512  my ($rfh, $wfh);
513  unless (pipe($rfh, $wfh)) {
514    die("Can't open pipe: $!");
515  }
516
517  my $ex;
518
519  # Fork child
520  $self->handle_sigchld();
521  defined(my $pid = fork()) or die("Can't fork: $!");
522  if ($pid) {
523    eval {
524      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
525
526      # In parent process, login anonymously to server using a plaintext
527      # password which SHOULD work.
528      my ($resp_code, $resp_msg) = $client->login($config_user, 'ftp@nospam.org');
529
530      my $expected;
531
532      $expected = 230;
533      $self->assert($expected == $resp_code,
534        test_msg("Expected $expected, got $resp_code"));
535
536      $expected = 'Anonymous access granted, restrictions apply';
537      $self->assert($expected eq $resp_msg,
538        test_msg("Expected '$expected', got '$resp_msg'"));
539
540      $client->quit();
541    };
542
543    if ($@) {
544      $ex = $@;
545    }
546
547    $wfh->print("done\n");
548    $wfh->flush();
549
550  } else {
551    eval { server_wait($config_file, $rfh) };
552    if ($@) {
553      warn($@);
554      exit 1;
555    }
556
557    exit 0;
558  }
559
560  # Stop server
561  server_stop($pid_file);
562
563  $self->assert_child_ok($pid);
564
565  if ($ex) {
566    test_append_logfile($log_file, $ex);
567    unlink($log_file);
568
569    die($ex);
570  }
571
572  unlink($log_file);
573}
574
575sub login_anonymous_user_alias_ok {
576  my $self = shift;
577  my $tmpdir = $self->{tmpdir};
578
579  my $config_file = "$tmpdir/login.conf";
580  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
581  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
582
583  my $log_file = test_get_logfile();
584
585  my $auth_user_file = File::Spec->rel2abs("$tmpdir/login.passwd");
586  my $auth_group_file = File::Spec->rel2abs("$tmpdir/login.group");
587
588  my ($config_user, $config_group) = config_get_identity();
589
590  my $anon_dir = File::Spec->rel2abs($tmpdir);
591  my $uid = 500;
592  my $gid = 500;
593
594  # Make sure that, if we're running as root, that the home directory has
595  # permissions/privs set for the account we create
596  if ($< == 0) {
597    unless (chmod(0755, $anon_dir)) {
598      die("Can't set perms on $anon_dir to 0755: $!");
599    }
600
601    unless (chown($uid, $gid, $anon_dir)) {
602      die("Can't set owner of $anon_dir to $uid/$gid: $!");
603    }
604  }
605
606  auth_user_write($auth_user_file, $config_user, 'foo', $uid, $gid,
607    '/tmp', '/bin/bash');
608  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
609
610  my $config = {
611    PidFile => $pid_file,
612    ScoreboardFile => $scoreboard_file,
613    SystemLog => $log_file,
614    TraceLog => $log_file,
615    Trace => 'DEFAULT:10 privs:10',
616
617    AuthUserFile => $auth_user_file,
618    AuthGroupFile => $auth_group_file,
619
620    IfModules => {
621      'mod_delay.c' => {
622        DelayEngine => 'off',
623      },
624    },
625
626    Anonymous => {
627      $anon_dir => {
628        User => $config_user,
629        Group => $config_group,
630        UserAlias => "anonymous $config_user",
631        RequireValidShell => 'off',
632      },
633    },
634  };
635
636  my ($port, $user, $group) = config_write($config_file, $config);
637
638  # Open pipes, for use between the parent and child processes.  Specifically,
639  # the child will indicate when it's done with its test by writing a message
640  # to the parent.
641  my ($rfh, $wfh);
642  unless (pipe($rfh, $wfh)) {
643    die("Can't open pipe: $!");
644  }
645
646  my $ex;
647
648  # Fork child
649  $self->handle_sigchld();
650  defined(my $pid = fork()) or die("Can't fork: $!");
651  if ($pid) {
652    eval {
653      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
654
655      # In parent process, login anonymously to server using a plaintext
656      # password which SHOULD work.
657      my ($resp_code, $resp_msg) = $client->login('anonymous', 'ftp@nospam.org');
658
659      my $expected;
660
661      $expected = 230;
662      $self->assert($expected == $resp_code,
663        test_msg("Expected $expected, got $resp_code"));
664
665      $expected = 'Anonymous access granted, restrictions apply';
666      $self->assert($expected eq $resp_msg,
667        test_msg("Expected '$expected', got '$resp_msg'"));
668
669      $client->quit();
670    };
671
672    if ($@) {
673      $ex = $@;
674    }
675
676    $wfh->print("done\n");
677    $wfh->flush();
678
679  } else {
680    eval { server_wait($config_file, $rfh) };
681    if ($@) {
682      warn($@);
683      exit 1;
684    }
685
686    exit 0;
687  }
688
689  # Stop server
690  server_stop($pid_file);
691
692  $self->assert_child_ok($pid);
693
694  if ($ex) {
695    test_append_logfile($log_file, $ex);
696    unlink($log_file);
697
698    die($ex);
699  }
700
701  unlink($log_file);
702}
703
704sub login_anonymous_fails_bad_perms {
705  my $self = shift;
706  my $tmpdir = $self->{tmpdir};
707
708  my $config_file = "$tmpdir/login.conf";
709  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
710  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
711
712  my $log_file = test_get_logfile();
713
714  my $anon_dir = File::Spec->rel2abs($tmpdir);
715
716  my ($user, $group) = config_get_identity();
717
718  my $config = {
719    PidFile => $pid_file,
720    ScoreboardFile => $scoreboard_file,
721    SystemLog => $log_file,
722
723    IfModules => {
724      'mod_delay.c' => {
725        DelayEngine => 'off',
726      },
727    },
728
729    Anonymous => {
730      $anon_dir => {
731        User => $user,
732        Group => $group,
733        UserAlias => "anonymous $user",
734        RequireValidShell => 'off',
735      },
736    },
737  };
738
739  my ($port, $config_user, $config_group) = config_write($config_file, $config);
740
741  # Open pipes, for use between the parent and child processes.  Specifically,
742  # the child will indicate when it's done with its test by writing a message
743  # to the parent.
744  my ($rfh, $wfh);
745  unless (pipe($rfh, $wfh)) {
746    die("Can't open pipe: $!");
747  }
748
749  my $ex;
750
751  # Fork child
752  $self->handle_sigchld();
753  defined(my $pid = fork()) or die("Can't fork: $!");
754  if ($pid) {
755    eval {
756      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
757
758      my $perms = (stat($anon_dir))[2];
759      chmod(0660, $anon_dir);
760
761      eval { $client->login('anonymous', 'ftp@nospam.org') };
762      unless ($@) {
763        die("Unexpectedly logged in anonymously");
764      }
765
766      chmod($perms, $anon_dir);
767    };
768
769    if ($@) {
770      $ex = $@;
771    }
772
773    $wfh->print("done\n");
774    $wfh->flush();
775
776  } else {
777    eval { server_wait($config_file, $rfh) };
778    if ($@) {
779      warn($@);
780      exit 1;
781    }
782
783    exit 0;
784  }
785
786  # Stop server
787  server_stop($pid_file);
788
789  $self->assert_child_ok($pid);
790
791  if ($ex) {
792    test_append_logfile($log_file, $ex);
793    unlink($log_file);
794
795    die($ex);
796  }
797
798  unlink($log_file);
799}
800
801sub login_anonymous_fails_empty_password_bug4139 {
802  my $self = shift;
803  my $tmpdir = $self->{tmpdir};
804
805  my $config_file = "$tmpdir/login.conf";
806  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
807  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
808
809  my $log_file = test_get_logfile();
810
811  my $auth_user_file = File::Spec->rel2abs("$tmpdir/login.passwd");
812  my $auth_group_file = File::Spec->rel2abs("$tmpdir/login.group");
813
814  my ($config_user, $config_group) = config_get_identity();
815
816  my $anon_dir = File::Spec->rel2abs($tmpdir);
817  my $uid = 500;
818  my $gid = 500;
819
820  # Make sure that, if we're running as root, that the home directory has
821  # permissions/privs set for the account we create
822  if ($< == 0) {
823    unless (chmod(0755, $anon_dir)) {
824      die("Can't set perms on $anon_dir to 0755: $!");
825    }
826
827    unless (chown($uid, $gid, $anon_dir)) {
828      die("Can't set owner of $anon_dir to $uid/$gid: $!");
829    }
830  }
831
832  auth_user_write($auth_user_file, $config_user, 'foo', $uid, $gid,
833    '/tmp', '/bin/bash');
834  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
835
836  my $config = {
837    PidFile => $pid_file,
838    ScoreboardFile => $scoreboard_file,
839    SystemLog => $log_file,
840    TraceLog => $log_file,
841    Trace => 'DEFAULT:10 privs:10',
842
843    AuthUserFile => $auth_user_file,
844    AuthGroupFile => $auth_group_file,
845
846    IfModules => {
847      'mod_delay.c' => {
848        DelayEngine => 'off',
849      },
850    },
851
852    Anonymous => {
853      $anon_dir => {
854        User => $config_user,
855        Group => $config_group,
856        UserAlias => "anonymous $config_user",
857        RequireValidShell => 'off',
858        AllowEmptyPasswords => 'off',
859      },
860    },
861  };
862
863  my ($port, $user, $group) = config_write($config_file, $config);
864
865  # Open pipes, for use between the parent and child processes.  Specifically,
866  # the child will indicate when it's done with its test by writing a message
867  # to the parent.
868  my ($rfh, $wfh);
869  unless (pipe($rfh, $wfh)) {
870    die("Can't open pipe: $!");
871  }
872
873  my $ex;
874
875  # Fork child
876  $self->handle_sigchld();
877  defined(my $pid = fork()) or die("Can't fork: $!");
878  if ($pid) {
879    eval {
880      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
881      eval { $client->login($config_user, '') };
882      unless ($@) {
883        die("Anonymous login succeeded unexpectedly");
884      }
885
886      my $resp_code = $client->response_code();
887      my $resp_msg = $client->response_msg();
888
889      my $expected;
890
891      $expected = 501;
892      $self->assert($expected == $resp_code,
893        test_msg("Expected response code $expected, got $resp_code"));
894
895      $expected = 'Login incorrect.';
896      $self->assert($expected eq $resp_msg,
897        test_msg("Expected response message '$expected', got '$resp_msg'"));
898
899      $client->quit();
900    };
901
902    if ($@) {
903      $ex = $@;
904    }
905
906    $wfh->print("done\n");
907    $wfh->flush();
908
909  } else {
910    eval { server_wait($config_file, $rfh) };
911    if ($@) {
912      warn($@);
913      exit 1;
914    }
915
916    exit 0;
917  }
918
919  # Stop server
920  server_stop($pid_file);
921
922  $self->assert_child_ok($pid);
923
924  if ($ex) {
925    test_append_logfile($log_file, $ex);
926    unlink($log_file);
927
928    die($ex);
929  }
930
931  unlink($log_file);
932}
933
934sub login_anonymous_symlink_dir_ok {
935  my $self = shift;
936  my $tmpdir = $self->{tmpdir};
937
938  my $config_file = "$tmpdir/login.conf";
939  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
940  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
941
942  my $log_file = test_get_logfile();
943
944  my $auth_user_file = File::Spec->rel2abs("$tmpdir/login.passwd");
945  my $auth_group_file = File::Spec->rel2abs("$tmpdir/login.group");
946
947  my ($config_user, $config_group) = config_get_identity();
948
949  my $anon_dir = File::Spec->rel2abs("$tmpdir/users/anonymous");
950  my $uid = 500;
951  my $gid = 500;
952
953  my $intermed_dir = File::Spec->rel2abs("$tmpdir/users");
954  mkpath($intermed_dir);
955
956  my $symlink_dst = File::Spec->rel2abs("$tmpdir/public_ftp");
957  mkpath($symlink_dst);
958
959  my $cwd = getcwd();
960
961  unless (chdir($intermed_dir)) {
962    die("Can't chdir to $intermed_dir: $!");
963  }
964
965  unless (symlink("../public_ftp", "./anonymous")) {
966    die("Can't symlink '../public_ftp' to './anonymous': $!");
967  }
968
969  unless (chdir($cwd)) {
970    die("Can't chdir to $cwd: $!");
971  }
972
973  # Make sure that, if we're running as root, that the home directory has
974  # permissions/privs set for the account we create
975  if ($< == 0) {
976    unless (chmod(0755, $symlink_dst)) {
977      die("Can't set perms on $symlink_dst to 0755: $!");
978    }
979
980    unless (chown($uid, $gid, $symlink_dst)) {
981      die("Can't set owner of $symlink_dst to $uid/$gid: $!");
982    }
983  }
984
985  auth_user_write($auth_user_file, $config_user, 'foo', $uid, $gid,
986    '/tmp', '/bin/bash');
987  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
988
989  my $config = {
990    PidFile => $pid_file,
991    ScoreboardFile => $scoreboard_file,
992    SystemLog => $log_file,
993    TraceLog => $log_file,
994    Trace => 'DEFAULT:10 privs:10',
995
996    AuthUserFile => $auth_user_file,
997    AuthGroupFile => $auth_group_file,
998
999    IfModules => {
1000      'mod_delay.c' => {
1001        DelayEngine => 'off',
1002      },
1003    },
1004
1005    Anonymous => {
1006      $anon_dir => {
1007        User => $config_user,
1008        Group => $config_group,
1009        UserAlias => "anonymous $config_user",
1010        RequireValidShell => 'off',
1011      },
1012    },
1013  };
1014
1015  my ($port, $user, $group) = config_write($config_file, $config);
1016
1017  # Open pipes, for use between the parent and child processes.  Specifically,
1018  # the child will indicate when it's done with its test by writing a message
1019  # to the parent.
1020  my ($rfh, $wfh);
1021  unless (pipe($rfh, $wfh)) {
1022    die("Can't open pipe: $!");
1023  }
1024
1025  my $ex;
1026
1027  # Fork child
1028  $self->handle_sigchld();
1029  defined(my $pid = fork()) or die("Can't fork: $!");
1030  if ($pid) {
1031    eval {
1032      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1033
1034      # In parent process, login anonymously to server using a plaintext
1035      # password which SHOULD work.
1036      my ($resp_code, $resp_msg) = $client->login($config_user, 'ftp@nospam.org');
1037
1038      my $expected;
1039
1040      $expected = 230;
1041      $self->assert($expected == $resp_code,
1042        test_msg("Expected $expected, got $resp_code"));
1043
1044      $expected = 'Anonymous access granted, restrictions apply';
1045      $self->assert($expected eq $resp_msg,
1046        test_msg("Expected '$expected', got '$resp_msg'"));
1047
1048      $client->quit();
1049    };
1050
1051    if ($@) {
1052      $ex = $@;
1053    }
1054
1055    $wfh->print("done\n");
1056    $wfh->flush();
1057
1058  } else {
1059    eval { server_wait($config_file, $rfh) };
1060    if ($@) {
1061      warn($@);
1062      exit 1;
1063    }
1064
1065    exit 0;
1066  }
1067
1068  # Stop server
1069  server_stop($pid_file);
1070
1071  $self->assert_child_ok($pid);
1072
1073  if ($ex) {
1074    test_append_logfile($log_file, $ex);
1075    unlink($log_file);
1076
1077    die($ex);
1078  }
1079
1080  unlink($log_file);
1081}
1082
1083sub login_anonymous_allowchrootsymlinks_bug3852 {
1084  my $self = shift;
1085  my $tmpdir = $self->{tmpdir};
1086
1087  my $config_file = "$tmpdir/login.conf";
1088  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
1089  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
1090
1091  my $log_file = test_get_logfile();
1092
1093  my $auth_user_file = File::Spec->rel2abs("$tmpdir/login.passwd");
1094  my $auth_group_file = File::Spec->rel2abs("$tmpdir/login.group");
1095
1096  my ($config_user, $config_group) = config_get_identity();
1097
1098  my $anon_dir = File::Spec->rel2abs("$tmpdir/users/anonymous");
1099  my $uid = 500;
1100  my $gid = 500;
1101
1102  my $intermed_dir = File::Spec->rel2abs("$tmpdir/users");
1103  mkpath($intermed_dir);
1104
1105  my $symlink_dst = File::Spec->rel2abs("$tmpdir/public_ftp");
1106  mkpath($symlink_dst);
1107
1108  my $cwd = getcwd();
1109
1110  unless (chdir($intermed_dir)) {
1111    die("Can't chdir to $intermed_dir: $!");
1112  }
1113
1114  unless (symlink("../public_ftp", "./anonymous")) {
1115    die("Can't symlink '../public_ftp' to './anonymous': $!");
1116  }
1117
1118  unless (chdir($cwd)) {
1119    die("Can't chdir to $cwd: $!");
1120  }
1121
1122  # Make sure that, if we're running as root, that the home directory has
1123  # permissions/privs set for the account we create
1124  if ($< == 0) {
1125    unless (chmod(0755, $symlink_dst)) {
1126      die("Can't set perms on $symlink_dst to 0755: $!");
1127    }
1128
1129    unless (chown($uid, $gid, $symlink_dst)) {
1130      die("Can't set owner of $symlink_dst to $uid/$gid: $!");
1131    }
1132  }
1133
1134  auth_user_write($auth_user_file, $config_user, 'foo', $uid, $gid,
1135    '/tmp', '/bin/bash');
1136  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
1137
1138  my $config = {
1139    PidFile => $pid_file,
1140    ScoreboardFile => $scoreboard_file,
1141    SystemLog => $log_file,
1142    TraceLog => $log_file,
1143    Trace => 'DEFAULT:10',
1144
1145    AuthUserFile => $auth_user_file,
1146    AuthGroupFile => $auth_group_file,
1147
1148    AllowChrootSymlinks => 'off',
1149
1150    IfModules => {
1151      'mod_delay.c' => {
1152        DelayEngine => 'off',
1153      },
1154    },
1155
1156    Anonymous => {
1157      $anon_dir => {
1158        User => $config_user,
1159        Group => $config_group,
1160        UserAlias => "anonymous $config_user",
1161        RequireValidShell => 'off',
1162      },
1163    },
1164  };
1165
1166  my ($port, $user, $group) = config_write($config_file, $config);
1167
1168  # Open pipes, for use between the parent and child processes.  Specifically,
1169  # the child will indicate when it's done with its test by writing a message
1170  # to the parent.
1171  my ($rfh, $wfh);
1172  unless (pipe($rfh, $wfh)) {
1173    die("Can't open pipe: $!");
1174  }
1175
1176  my $ex;
1177
1178  # Fork child
1179  $self->handle_sigchld();
1180  defined(my $pid = fork()) or die("Can't fork: $!");
1181  if ($pid) {
1182    eval {
1183      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1184      eval { $client->login($config_user, 'ftp@nospam.org') };
1185      unless ($@) {
1186        die("Anonymous login succeeded unexpectedly");
1187      }
1188
1189      my $resp_code = $client->response_code();
1190      my $resp_msg = $client->response_msg();
1191
1192      my $expected;
1193
1194      $expected = 530;
1195      $self->assert($expected == $resp_code,
1196        test_msg("Expected response code $expected, got $resp_code"));
1197
1198      $expected = 'Login incorrect.';
1199      $self->assert($expected eq $resp_msg,
1200        test_msg("Expected response message '$expected', got '$resp_msg'"));
1201
1202      $client->quit();
1203    };
1204
1205    if ($@) {
1206      $ex = $@;
1207    }
1208
1209    $wfh->print("done\n");
1210    $wfh->flush();
1211
1212  } else {
1213    eval { server_wait($config_file, $rfh) };
1214    if ($@) {
1215      warn($@);
1216      exit 1;
1217    }
1218
1219    exit 0;
1220  }
1221
1222  # Stop server
1223  server_stop($pid_file);
1224
1225  $self->assert_child_ok($pid);
1226
1227  if ($ex) {
1228    test_append_logfile($log_file, $ex);
1229    unlink($log_file);
1230
1231    die($ex);
1232  }
1233
1234  unlink($log_file);
1235}
1236
1237sub login_multiple_attempts_per_conn {
1238  my $self = shift;
1239  my $tmpdir = $self->{tmpdir};
1240
1241  my $config_file = "$tmpdir/login.conf";
1242  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
1243  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
1244
1245  my $log_file = test_get_logfile();
1246
1247  my $auth_user_file = File::Spec->rel2abs("$tmpdir/login.passwd");
1248  my $auth_group_file = File::Spec->rel2abs("$tmpdir/login.group");
1249
1250  my $test_file = File::Spec->rel2abs($config_file);
1251
1252  my $user = 'proftpd';
1253  my $passwd = 'test';
1254  my $group = 'ftpd';
1255  my $home_dir = File::Spec->rel2abs($tmpdir);
1256  my $uid = 500;
1257  my $gid = 500;
1258
1259  # Make sure that, if we're running as root, that the home directory has
1260  # permissions/privs set for the account we create
1261  if ($< == 0) {
1262    unless (chmod(0755, $home_dir)) {
1263      die("Can't set perms on $home_dir to 0755: $!");
1264    }
1265
1266    unless (chown($uid, $gid, $home_dir)) {
1267      die("Can't set owner of $home_dir to $uid/$gid: $!");
1268    }
1269  }
1270
1271  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
1272    '/bin/bash');
1273  auth_group_write($auth_group_file, $group, $gid, $user);
1274
1275  my $config = {
1276    PidFile => $pid_file,
1277    ScoreboardFile => $scoreboard_file,
1278    SystemLog => $log_file,
1279
1280    AuthUserFile => $auth_user_file,
1281    AuthGroupFile => $auth_group_file,
1282
1283    IfModules => {
1284      'mod_delay.c' => {
1285        DelayEngine => 'off',
1286      },
1287    },
1288  };
1289
1290  my ($port, $config_user, $config_group) = config_write($config_file, $config);
1291
1292  # Open pipes, for use between the parent and child processes.  Specifically,
1293  # the child will indicate when it's done with its test by writing a message
1294  # to the parent.
1295  my ($rfh, $wfh);
1296  unless (pipe($rfh, $wfh)) {
1297    die("Can't open pipe: $!");
1298  }
1299
1300  my $ex;
1301
1302  # Fork child
1303  $self->handle_sigchld();
1304  defined(my $pid = fork()) or die("Can't fork: $!");
1305  if ($pid) {
1306    eval {
1307      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1308      eval { $client->login($user, 'foo') };
1309      unless ($@) {
1310        die("Login succeeded unexpectedly");
1311      }
1312
1313      $client->login($user, $passwd);
1314    };
1315
1316    if ($@) {
1317      $ex = $@;
1318    }
1319
1320    $wfh->print("done\n");
1321    $wfh->flush();
1322
1323  } else {
1324    eval { server_wait($config_file, $rfh) };
1325    if ($@) {
1326      warn($@);
1327      exit 1;
1328    }
1329
1330    exit 0;
1331  }
1332
1333  # Stop server
1334  server_stop($pid_file);
1335
1336  $self->assert_child_ok($pid);
1337
1338  if ($ex) {
1339    test_append_logfile($log_file, $ex);
1340    unlink($log_file);
1341
1342    die($ex);
1343  }
1344
1345  unlink($log_file);
1346}
1347
1348sub login_regular_with_anon_defined_bug3307 {
1349  my $self = shift;
1350  my $tmpdir = $self->{tmpdir};
1351
1352  my $config_file = "$tmpdir/login.conf";
1353  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
1354  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
1355
1356  my $log_file = test_get_logfile();
1357
1358  my $auth_user_file = File::Spec->rel2abs("$tmpdir/login.passwd");
1359  my $auth_group_file = File::Spec->rel2abs("$tmpdir/login.group");
1360
1361  my $test_file = File::Spec->rel2abs($config_file);
1362
1363  my $user = 'proftpd';
1364  my $passwd = 'test';
1365  my $home_dir = File::Spec->rel2abs($tmpdir);
1366  my $uid = 500;
1367  my $gid = 500;
1368
1369  # Make sure that, if we're running as root, that the home directory has
1370  # permissions/privs set for the account we create
1371  if ($< == 0) {
1372    unless (chmod(0755, $home_dir)) {
1373      die("Can't set perms on $home_dir to 0755: $!");
1374    }
1375
1376    unless (chown($uid, $gid, $home_dir)) {
1377      die("Can't set owner of $home_dir to $uid/$gid: $!");
1378    }
1379  }
1380
1381  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
1382    '/bin/bash');
1383  auth_group_write($auth_group_file, 'ftpd', $gid, $user);
1384
1385  my ($config_user, $config_group) = config_get_identity();
1386
1387  my $config = {
1388    PidFile => $pid_file,
1389    ScoreboardFile => $scoreboard_file,
1390    SystemLog => $log_file,
1391
1392    AuthUserFile => $auth_user_file,
1393    AuthGroupFile => $auth_group_file,
1394
1395    Anonymous => {
1396      $home_dir => {
1397        User => $config_user,
1398        Group => $config_group,
1399        RequireValidShell => 'off',
1400        UserAlias => "anonymous $config_user",
1401      },
1402    },
1403
1404    IfModules => {
1405      'mod_delay.c' => {
1406        DelayEngine => 'off',
1407      },
1408    },
1409  };
1410
1411  my $port;
1412  ($port, $config_user, $config_group) = config_write($config_file, $config);
1413
1414  # Open pipes, for use between the parent and child processes.  Specifically,
1415  # the child will indicate when it's done with its test by writing a message
1416  # to the parent.
1417  my ($rfh, $wfh);
1418  unless (pipe($rfh, $wfh)) {
1419    die("Can't open pipe: $!");
1420  }
1421
1422  my $ex;
1423
1424  # Fork child
1425  $self->handle_sigchld();
1426  defined(my $pid = fork()) or die("Can't fork: $!");
1427  if ($pid) {
1428    eval {
1429      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1430
1431      my ($resp_code, $resp_msg) = $client->user($user);
1432
1433      my $expected;
1434
1435      $expected = 331;
1436      $self->assert($expected == $resp_code,
1437        test_msg("Expected $expected, got $resp_code"));
1438
1439      $expected = "Password required for $user";
1440      $self->assert($expected eq $resp_msg,
1441        test_msg("Expected '$expected', got '$resp_msg'"));
1442
1443      $client->quit();
1444    };
1445
1446    if ($@) {
1447      $ex = $@;
1448    }
1449
1450    $wfh->print("done\n");
1451    $wfh->flush();
1452
1453  } else {
1454    eval { server_wait($config_file, $rfh) };
1455    if ($@) {
1456      warn($@);
1457      exit 1;
1458    }
1459
1460    exit 0;
1461  }
1462
1463  # Stop server
1464  server_stop($pid_file);
1465
1466  $self->assert_child_ok($pid);
1467
1468  if ($ex) {
1469    test_append_logfile($log_file, $ex);
1470    unlink($log_file);
1471
1472    die($ex);
1473  }
1474
1475  unlink($log_file);
1476}
1477
1478sub login_fails_bad_sequence {
1479  my $self = shift;
1480  my $tmpdir = $self->{tmpdir};
1481
1482  my $config_file = "$tmpdir/login.conf";
1483  my $pid_file = File::Spec->rel2abs("$tmpdir/login.pid");
1484  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/login.scoreboard");
1485
1486  my $log_file = test_get_logfile();
1487
1488  my $auth_user_file = File::Spec->rel2abs("$tmpdir/login.passwd");
1489  my $auth_group_file = File::Spec->rel2abs("$tmpdir/login.group");
1490
1491  my $user = 'proftpd';
1492  my $passwd = 'test';
1493  my $group = 'ftpd';
1494  my $home_dir = File::Spec->rel2abs($tmpdir);
1495  my $uid = 500;
1496  my $gid = 500;
1497
1498  # Make sure that, if we're running as root, that the home directory has
1499  # permissions/privs set for the account we create
1500  if ($< == 0) {
1501    unless (chmod(0755, $home_dir)) {
1502      die("Can't set perms on $home_dir to 0755: $!");
1503    }
1504
1505    unless (chown($uid, $gid, $home_dir)) {
1506      die("Can't set owner of $home_dir to $uid/$gid: $!");
1507    }
1508  }
1509
1510  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
1511    '/bin/bash');
1512  auth_group_write($auth_group_file, $group, $gid, $user);
1513
1514  my $config = {
1515    PidFile => $pid_file,
1516    ScoreboardFile => $scoreboard_file,
1517    SystemLog => $log_file,
1518
1519    AuthUserFile => $auth_user_file,
1520    AuthGroupFile => $auth_group_file,
1521
1522    IfModules => {
1523      'mod_delay.c' => {
1524        DelayEngine => 'off',
1525      },
1526    },
1527  };
1528
1529  my ($port, $config_user, $config_group) = config_write($config_file, $config);
1530
1531  # Open pipes, for use between the parent and child processes.  Specifically,
1532  # the child will indicate when it's done with its test by writing a message
1533  # to the parent.
1534  my ($rfh, $wfh);
1535  unless (pipe($rfh, $wfh)) {
1536    die("Can't open pipe: $!");
1537  }
1538
1539  my $ex;
1540
1541  # Fork child
1542  $self->handle_sigchld();
1543  defined(my $pid = fork()) or die("Can't fork: $!");
1544  if ($pid) {
1545    eval {
1546      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1547      $client->user($user);
1548
1549      eval { $client->pwd() };
1550      unless ($@) {
1551        die("PWD after USER succeeded unexpectedly");
1552      }
1553
1554      my $resp_code = $client->response_code();
1555      my $resp_msg = $client->response_msg();
1556
1557      my $expected = 530;
1558      $self->assert($expected == $resp_code,
1559        test_msg("Expected response code $expected, got $resp_code"));
1560
1561      $expected = "Please login with USER and PASS";
1562      $self->assert($expected eq $resp_msg,
1563        test_msg("Expected response message '$expected', got '$resp_msg'"));
1564    };
1565
1566    if ($@) {
1567      $ex = $@;
1568    }
1569
1570    $wfh->print("done\n");
1571    $wfh->flush();
1572
1573  } else {
1574    eval { server_wait($config_file, $rfh) };
1575    if ($@) {
1576      warn($@);
1577      exit 1;
1578    }
1579
1580    exit 0;
1581  }
1582
1583  # Stop server
1584  server_stop($pid_file);
1585
1586  $self->assert_child_ok($pid);
1587
1588  if ($ex) {
1589    test_append_logfile($log_file, $ex);
1590    unlink($log_file);
1591
1592    die($ex);
1593  }
1594
1595  unlink($log_file);
1596}
1597
1598sub login_reauthenticate_fails_bug3736 {
1599  my $self = shift;
1600  my $tmpdir = $self->{tmpdir};
1601  my $setup = test_setup($tmpdir, 'login');
1602
1603  my $config = {
1604    PidFile => $setup->{pid_file},
1605    ScoreboardFile => $setup->{scoreboard_file},
1606    SystemLog => $setup->{log_file},
1607
1608    AuthUserFile => $setup->{auth_user_file},
1609    AuthGroupFile => $setup->{auth_group_file},
1610
1611    IfModules => {
1612      'mod_delay.c' => {
1613        DelayEngine => 'off',
1614      },
1615    },
1616  };
1617
1618  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1619    $config);
1620
1621  # Open pipes, for use between the parent and child processes.  Specifically,
1622  # the child will indicate when it's done with its test by writing a message
1623  # to the parent.
1624  my ($rfh, $wfh);
1625  unless (pipe($rfh, $wfh)) {
1626    die("Can't open pipe: $!");
1627  }
1628
1629  my $ex;
1630
1631  # Fork child
1632  $self->handle_sigchld();
1633  defined(my $pid = fork()) or die("Can't fork: $!");
1634  if ($pid) {
1635    eval {
1636      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1637      $client->login($setup->{user}, $setup->{passwd});
1638
1639      my $other_user = 'foobar';
1640      eval { $client->user($other_user) };
1641      unless ($@) {
1642        die("Subsequent USER command succeeded unexpectedly");
1643      }
1644
1645      my $resp_code = $client->response_code();
1646      my $resp_msg = $client->response_msg();
1647
1648      my $expected = 501;
1649      $self->assert($expected == $resp_code,
1650        test_msg("Expected response code $expected, got $resp_code"));
1651
1652      $expected = "Reauthentication not supported";
1653      $self->assert($expected eq $resp_msg,
1654        test_msg("Expected response message '$expected', got '$resp_msg'"));
1655    };
1656
1657    if ($@) {
1658      $ex = $@;
1659    }
1660
1661    $wfh->print("done\n");
1662    $wfh->flush();
1663
1664  } else {
1665    eval { server_wait($setup->{config_file}, $rfh) };
1666    if ($@) {
1667      warn($@);
1668      exit 1;
1669    }
1670
1671    exit 0;
1672  }
1673
1674  # Stop server
1675  server_stop($setup->{pid_file});
1676  $self->assert_child_ok($pid);
1677
1678  test_cleanup($setup->{log_file}, $ex);
1679}
1680
1681sub login_reauthenticate_ok_same_user_bug4217 {
1682  my $self = shift;
1683  my $tmpdir = $self->{tmpdir};
1684  my $setup = test_setup($tmpdir, 'login');
1685
1686  my $config = {
1687    PidFile => $setup->{pid_file},
1688    ScoreboardFile => $setup->{scoreboard_file},
1689    SystemLog => $setup->{log_file},
1690
1691    AuthUserFile => $setup->{auth_user_file},
1692    AuthGroupFile => $setup->{auth_group_file},
1693
1694    IfModules => {
1695      'mod_delay.c' => {
1696        DelayEngine => 'off',
1697      },
1698    },
1699  };
1700
1701  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1702    $config);
1703
1704  # Open pipes, for use between the parent and child processes.  Specifically,
1705  # the child will indicate when it's done with its test by writing a message
1706  # to the parent.
1707  my ($rfh, $wfh);
1708  unless (pipe($rfh, $wfh)) {
1709    die("Can't open pipe: $!");
1710  }
1711
1712  my $ex;
1713
1714  # Fork child
1715  $self->handle_sigchld();
1716  defined(my $pid = fork()) or die("Can't fork: $!");
1717  if ($pid) {
1718    eval {
1719      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1720      $client->login($setup->{user}, $setup->{passwd});
1721
1722      my ($resp_code, $resp_msg) = $client->user($setup->{user});
1723      my $expected = 230;
1724      $self->assert($expected == $resp_code,
1725        test_msg("Expected response code $expected, got $resp_code"));
1726
1727      $expected = "User $setup->{user} logged in";
1728      $self->assert($expected eq $resp_msg,
1729        test_msg("Expected response message '$expected', got '$resp_msg'"));
1730    };
1731
1732    if ($@) {
1733      $ex = $@;
1734    }
1735
1736    $wfh->print("done\n");
1737    $wfh->flush();
1738
1739  } else {
1740    eval { server_wait($setup->{config_file}, $rfh) };
1741    if ($@) {
1742      warn($@);
1743      exit 1;
1744    }
1745
1746    exit 0;
1747  }
1748
1749  # Stop server
1750  server_stop($setup->{pid_file});
1751  $self->assert_child_ok($pid);
1752
1753  test_cleanup($setup->{log_file}, $ex);
1754}
1755
1756sub login_reauthenticate_fails_different_user_bug4217 {
1757  my $self = shift;
1758  my $tmpdir = $self->{tmpdir};
1759  my $setup = test_setup($tmpdir, 'login');
1760
1761  my $config = {
1762    PidFile => $setup->{pid_file},
1763    ScoreboardFile => $setup->{scoreboard_file},
1764    SystemLog => $setup->{log_file},
1765
1766    AuthUserFile => $setup->{auth_user_file},
1767    AuthGroupFile => $setup->{auth_group_file},
1768
1769    IfModules => {
1770      'mod_delay.c' => {
1771        DelayEngine => 'off',
1772      },
1773    },
1774  };
1775
1776  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1777    $config);
1778
1779  # Open pipes, for use between the parent and child processes.  Specifically,
1780  # the child will indicate when it's done with its test by writing a message
1781  # to the parent.
1782  my ($rfh, $wfh);
1783  unless (pipe($rfh, $wfh)) {
1784    die("Can't open pipe: $!");
1785  }
1786
1787  my $ex;
1788
1789  # Fork child
1790  $self->handle_sigchld();
1791  defined(my $pid = fork()) or die("Can't fork: $!");
1792  if ($pid) {
1793    eval {
1794      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1795      $client->login($setup->{user}, $setup->{passwd});
1796
1797      my $bad_user = "foobar";
1798      eval { $client->user($bad_user) };
1799      unless ($@) {
1800        die("Subsequent USER with different name succeeded unexpectedly");
1801      }
1802
1803      my $resp_code = $client->response_code();
1804      my $resp_msg = $client->response_msg();
1805
1806      my $expected = 501;
1807      $self->assert($expected == $resp_code,
1808        test_msg("Expected response code $expected, got $resp_code"));
1809
1810      $expected = "Reauthentication not supported";
1811      $self->assert($expected eq $resp_msg,
1812        test_msg("Expected response message '$expected', got '$resp_msg'"));
1813    };
1814
1815    if ($@) {
1816      $ex = $@;
1817    }
1818
1819    $wfh->print("done\n");
1820    $wfh->flush();
1821
1822  } else {
1823    eval { server_wait($setup->{config_file}, $rfh) };
1824    if ($@) {
1825      warn($@);
1826      exit 1;
1827    }
1828
1829    exit 0;
1830  }
1831
1832  # Stop server
1833  server_stop($setup->{pid_file});
1834  $self->assert_child_ok($pid);
1835
1836  test_cleanup($setup->{log_file}, $ex);
1837}
1838
1839sub login_reauthenticate_fails_extra_pass_bug4217 {
1840  my $self = shift;
1841  my $tmpdir = $self->{tmpdir};
1842  my $setup = test_setup($tmpdir, 'login');
1843
1844  my $config = {
1845    PidFile => $setup->{pid_file},
1846    ScoreboardFile => $setup->{scoreboard_file},
1847    SystemLog => $setup->{log_file},
1848
1849    AuthUserFile => $setup->{auth_user_file},
1850    AuthGroupFile => $setup->{auth_group_file},
1851
1852    IfModules => {
1853      'mod_delay.c' => {
1854        DelayEngine => 'off',
1855      },
1856    },
1857  };
1858
1859  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1860    $config);
1861
1862  # Open pipes, for use between the parent and child processes.  Specifically,
1863  # the child will indicate when it's done with its test by writing a message
1864  # to the parent.
1865  my ($rfh, $wfh);
1866  unless (pipe($rfh, $wfh)) {
1867    die("Can't open pipe: $!");
1868  }
1869
1870  my $ex;
1871
1872  # Fork child
1873  $self->handle_sigchld();
1874  defined(my $pid = fork()) or die("Can't fork: $!");
1875  if ($pid) {
1876    eval {
1877      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1878      $client->login($setup->{user}, $setup->{passwd});
1879
1880      # We can't use login() here, since that will properly handle the 230
1881      # response from the USER command.
1882      $client->user($setup->{user});
1883      eval { $client->pass($setup->{passwd}) };
1884      unless ($@) {
1885        die("Subsequent PASS succeeded unexpectedly");
1886      }
1887
1888      my $resp_code = $client->response_code();
1889      my $resp_msg = $client->response_msg();
1890
1891      my $expected = 503;
1892      $self->assert($expected == $resp_code,
1893        test_msg("Expected response code $expected, got $resp_code"));
1894
1895      $expected = "You are already logged in";
1896      $self->assert($expected eq $resp_msg,
1897        test_msg("Expected response message '$expected', got '$resp_msg'"));
1898    };
1899
1900    if ($@) {
1901      $ex = $@;
1902    }
1903
1904    $wfh->print("done\n");
1905    $wfh->flush();
1906
1907  } else {
1908    eval { server_wait($setup->{config_file}, $rfh) };
1909    if ($@) {
1910      warn($@);
1911      exit 1;
1912    }
1913
1914    exit 0;
1915  }
1916
1917  # Stop server
1918  server_stop($setup->{pid_file});
1919  $self->assert_child_ok($pid);
1920
1921  test_cleanup($setup->{log_file}, $ex);
1922}
1923
19241;
1925