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