1package ProFTPD::Tests::Config::MaxLoginAttempts; 2 3use lib qw(t/lib); 4use base qw(ProFTPD::TestSuite::Child); 5use strict; 6 7use File::Spec; 8use IO::Handle; 9 10use ProFTPD::TestSuite::FTP; 11use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); 12 13$| = 1; 14 15my $order = 0; 16 17my $TESTS = { 18 maxloginattempts_one => { 19 order => ++$order, 20 test_class => [qw(forking)], 21 }, 22 23 maxloginattempts_absent => { 24 order => ++$order, 25 test_class => [qw(forking)], 26 }, 27 28}; 29 30sub new { 31 return shift()->SUPER::new(@_); 32} 33 34sub list_tests { 35 return testsuite_get_runnable_tests($TESTS); 36} 37 38sub maxloginattempts_one { 39 my $self = shift; 40 my $tmpdir = $self->{tmpdir}; 41 my $setup = test_setup($tmpdir, 'config'); 42 43 my $max_logins = 1; 44 45 my $config = { 46 PidFile => $setup->{pid_file}, 47 ScoreboardFile => $setup->{scoreboard_file}, 48 SystemLog => $setup->{log_file}, 49 50 AuthUserFile => $setup->{auth_user_file}, 51 AuthGroupFile => $setup->{auth_group_file}, 52 53 MaxLoginAttempts => $max_logins, 54 55 IfModules => { 56 'mod_delay.c' => { 57 DelayEngine => 'off', 58 }, 59 }, 60 }; 61 62 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 63 $config); 64 65 # Open pipes, for use between the parent and child processes. Specifically, 66 # the child will indicate when it's done with its test by writing a message 67 # to the parent. 68 my ($rfh, $wfh); 69 unless (pipe($rfh, $wfh)) { 70 die("Can't open pipe: $!"); 71 } 72 73 my $ex; 74 75 # Fork child 76 $self->handle_sigchld(); 77 defined(my $pid = fork()) or die("Can't fork: $!"); 78 if ($pid) { 79 eval { 80 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 81 82 eval { $client->login($setup->{user}, 'foo') }; 83 unless ($@) { 84 die("Logged in unexpectedly"); 85 } 86 87 my $resp_code = $client->response_code(); 88 my $resp_msg = $client->response_msg(0); 89 90 my $expected = 530; 91 $self->assert($expected == $resp_code, 92 test_msg("Expected response code $expected, got $resp_code")); 93 94 $expected = "Login incorrect."; 95 $self->assert($expected eq $resp_msg, 96 test_msg("Expected response message '$expected', got '$resp_msg'")); 97 98 # A MaxLoginAttempts of one should have caused our connection to be 99 # closed above. 100 101 eval { ($resp_code, $resp_msg) = $client->login($setup->{user}, 'foo') }; 102 unless ($@) { 103 die("Logged in unexpectedly ($resp_code $resp_msg)"); 104 } 105 106 $resp_code = $client->response_code(); 107 $resp_msg = $client->response_msg(0); 108 109 $expected = 599; 110 $self->assert($expected == $resp_code, 111 test_msg("Expected response code $expected, got $resp_code")); 112 }; 113 if ($@) { 114 $ex = $@; 115 } 116 117 $wfh->print("done\n"); 118 $wfh->flush(); 119 120 } else { 121 eval { server_wait($setup->{config_file}, $rfh) }; 122 if ($@) { 123 warn($@); 124 exit 1; 125 } 126 127 exit 0; 128 } 129 130 # Stop server 131 server_stop($setup->{pid_file}); 132 $self->assert_child_ok($pid); 133 134 # We can peruse the generated debug log messages for what we want, but 135 # only if the TEST_VERBOSE environment variable is true. 136 unless ($ENV{TEST_VERBOSE}) { 137 test_cleanup($setup->{log_file}, $ex); 138 return; 139 } 140 141 eval { 142 if (open(my $fh, "< $setup->{log_file}")) { 143 my $expected_post_cmd_err = 0; 144 my $expected_log_cmd_err = 0; 145 146 while (my $line = <$fh>) { 147 chomp($line); 148 149 if ($ENV{TEST_VERBOSE}) { 150 print STDERR "# $line\n"; 151 } 152 153 if ($line =~ /POST_CMD_ERR command 'PASS/) { 154 $expected_post_cmd_err = 1; 155 next; 156 } 157 158 if ($line =~ /LOG_CMD_ERR command 'PASS/) { 159 $expected_log_cmd_err = 1; 160 next; 161 } 162 } 163 164 close($fh); 165 166 $self->assert($expected_post_cmd_err && $expected_log_cmd_err, 167 test_msg("Did not see expected PASS POST_CMD_ERR and LOG_CMD_ERR log messages")); 168 169 } else { 170 die("Can't read $setup->{log_file}: $!"); 171 } 172 }; 173 if ($@) { 174 $ex = $@; 175 } 176 177 test_cleanup($setup->{log_file}, $ex); 178} 179 180sub maxloginattempts_absent { 181 my $self = shift; 182 my $tmpdir = $self->{tmpdir}; 183 my $setup = test_setup($tmpdir, 'config'); 184 185 my $config = { 186 PidFile => $setup->{pid_file}, 187 ScoreboardFile => $setup->{scoreboard_file}, 188 SystemLog => $setup->{log_file}, 189 190 AuthUserFile => $setup->{auth_user_file}, 191 AuthGroupFile => $setup->{auth_group_file}, 192 193 IfModules => { 194 'mod_delay.c' => { 195 DelayEngine => 'off', 196 }, 197 }, 198 }; 199 200 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 201 $config); 202 203 # Open pipes, for use between the parent and child processes. Specifically, 204 # the child will indicate when it's done with its test by writing a message 205 # to the parent. 206 my ($rfh, $wfh); 207 unless (pipe($rfh, $wfh)) { 208 die("Can't open pipe: $!"); 209 } 210 211 my $ex; 212 213 # Fork child 214 $self->handle_sigchld(); 215 defined(my $pid = fork()) or die("Can't fork: $!"); 216 if ($pid) { 217 eval { 218 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 219 220 for (my $i = 0; $i < 3; $i++) { 221 eval { $client->login($setup->{user}, 'foo') }; 222 unless ($@) { 223 die("Logged in unexpectedly"); 224 } 225 226 my $resp_code = $client->response_code(); 227 my $resp_msg = $client->response_msg(0); 228 229 my $expected = 530; 230 $self->assert($expected == $resp_code, 231 test_msg("Expected response code $expected, got $resp_code")); 232 233 $expected = "Login incorrect."; 234 $self->assert($expected eq $resp_msg, 235 test_msg("Expected response message '$expected', got '$resp_msg'")); 236 } 237 238 eval { $client->login($setup->{user}, 'foo') }; 239 unless ($@) { 240 die("Logged in unexpectedly"); 241 } 242 243 my $resp_code = $client->response_code(); 244 my $resp_msg = $client->response_msg(0); 245 246 my $expected = 599; 247 $self->assert($expected == $resp_code, 248 test_msg("Expected response code $expected, got $resp_code")); 249 }; 250 if ($@) { 251 $ex = $@; 252 } 253 254 $wfh->print("done\n"); 255 $wfh->flush(); 256 257 } else { 258 eval { server_wait($setup->{config_file}, $rfh) }; 259 if ($@) { 260 warn($@); 261 exit 1; 262 } 263 264 exit 0; 265 } 266 267 # Stop server 268 server_stop($setup->{pid_file}); 269 $self->assert_child_ok($pid); 270 271 test_cleanup($setup->{log_file}, $ex); 272} 273 2741; 275