1package ProFTPD::Tests::Config::Limit::LOGIN;
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  login_limit_ip_glob_range_bug3484 => {
19    order => ++$order,
20    test_class => [qw(bug forking inprogress)],
21  },
22
23  login_limit_allowgroup_backslash => {
24    order => ++$order,
25    test_class => [qw(bug forking)],
26  },
27
28  login_limit_multiple_sections => {
29    order => ++$order,
30    test_class => [qw(bug forking)],
31  },
32
33};
34
35sub new {
36  return shift()->SUPER::new(@_);
37}
38
39sub list_tests {
40  return testsuite_get_runnable_tests($TESTS);
41}
42
43sub login_limit_ip_glob_range_bug3484 {
44  my $self = shift;
45  my $tmpdir = $self->{tmpdir};
46
47  my $config_file = "$tmpdir/limit.conf";
48  my $pid_file = File::Spec->rel2abs("$tmpdir/limit.pid");
49  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/limit.scoreboard");
50
51  my $log_file = test_get_logfile();
52
53  my $auth_user_file = File::Spec->rel2abs("$tmpdir/limit.passwd");
54  my $auth_group_file = File::Spec->rel2abs("$tmpdir/limit.group");
55
56  my $user = 'proftpd';
57  my $passwd = 'test';
58  my $group ='ftpd';
59  my $home_dir = File::Spec->rel2abs($tmpdir);
60  my $uid = 500;
61  my $gid = 500;
62
63  # Make sure that, if we're running as root, that the home directory has
64  # permissions/privs set for the account we create
65  if ($< == 0) {
66    unless (chmod(0755, $home_dir)) {
67      die("Can't set perms on $home_dir to 0755: $!");
68    }
69
70    unless (chown($uid, $gid, $home_dir)) {
71      die("Can't set owner of $home_dir to $uid/$gid: $!");
72    }
73  }
74
75  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
76    '/bin/bash');
77  auth_group_write($auth_group_file, $group, $gid, $user);
78
79  # See http://forums.proftpd.org/smf/index.php/topic,4774.0.html
80
81  my $config = {
82    PidFile => $pid_file,
83    ScoreboardFile => $scoreboard_file,
84    SystemLog => $log_file,
85    TraceLog => $log_file,
86    Trace => 'netacl:20 dns:20',
87
88    AuthUserFile => $auth_user_file,
89    AuthGroupFile => $auth_group_file,
90    UseIPv6 => 'off',
91
92    IfModules => {
93      'mod_delay.c' => {
94        DelayEngine => 'off',
95      },
96    },
97
98    Limit => {
99      LOGIN => {
100        Deny => '127.0.0.[0-9]',
101      },
102    },
103
104  };
105
106  my ($port, $config_user, $config_group) = config_write($config_file, $config);
107
108  # Open pipes, for use between the parent and child processes.  Specifically,
109  # the child will indicate when it's done with its test by writing a message
110  # to the parent.
111  my ($rfh, $wfh);
112  unless (pipe($rfh, $wfh)) {
113    die("Can't open pipe: $!");
114  }
115
116  my $ex;
117
118  # Fork child
119  $self->handle_sigchld();
120  defined(my $pid = fork()) or die("Can't fork: $!");
121  if ($pid) {
122    eval {
123      my $client;
124
125      eval { $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port,
126        undef, 0) };
127      unless ($@) {
128        die("Connect succeeded unexpectedly");
129      }
130
131      my $conn_ex = ProFTPD::TestSuite::FTP::get_connect_exception();
132
133      my $expected = 'Connection refused';
134      $self->assert(qr/$expected/, $conn_ex,
135        test_msg("Expected '$expected', got '$conn_ex'"));
136    };
137
138    if ($@) {
139      $ex = $@;
140    }
141
142    $wfh->print("done\n");
143    $wfh->flush();
144
145  } else {
146    eval { server_wait($config_file, $rfh) };
147    if ($@) {
148      warn($@);
149      exit 1;
150    }
151
152    exit 0;
153  }
154
155  # Stop server
156  server_stop($pid_file);
157
158  $self->assert_child_ok($pid);
159
160  if ($ex) {
161    test_append_logfile($log_file, $ex);
162    unlink($log_file);
163
164    die($ex);
165  }
166
167  unlink($log_file);
168}
169
170sub login_limit_allowgroup_backslash {
171  my $self = shift;
172  my $tmpdir = $self->{tmpdir};
173
174  my $config_file = "$tmpdir/limit.conf";
175  my $pid_file = File::Spec->rel2abs("$tmpdir/limit.pid");
176  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/limit.scoreboard");
177
178  my $log_file = test_get_logfile();
179
180  my $auth_user_file = File::Spec->rel2abs("$tmpdir/limit.passwd");
181  my $auth_group_file = File::Spec->rel2abs("$tmpdir/limit.group");
182
183  # See:
184  #   https://forums.proftpd.org/smf/index.php/topic,11304.0.html
185
186  my $user = 'proftpd';
187  my $passwd = 'test';
188  my $group = 'ftpd\group';
189  my $home_dir = File::Spec->rel2abs($tmpdir);
190  my $uid = 500;
191  my $gid = 500;
192
193  # Make sure that, if we're running as root, that the home directory has
194  # permissions/privs set for the account we create
195  if ($< == 0) {
196    unless (chmod(0755, $home_dir)) {
197      die("Can't set perms on $home_dir to 0755: $!");
198    }
199
200    unless (chown($uid, $gid, $home_dir)) {
201      die("Can't set owner of $home_dir to $uid/$gid: $!");
202    }
203  }
204
205  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
206    '/bin/bash');
207  auth_group_write($auth_group_file, $group, $gid, $user);
208
209  # See http://forums.proftpd.org/smf/index.php/topic,4774.0.html
210
211  my $config = {
212    PidFile => $pid_file,
213    ScoreboardFile => $scoreboard_file,
214    SystemLog => $log_file,
215    TraceLog => $log_file,
216    Trace => 'netacl:20 dns:20',
217
218    AuthUserFile => $auth_user_file,
219    AuthGroupFile => $auth_group_file,
220    UseIPv6 => 'off',
221
222    IfModules => {
223      'mod_delay.c' => {
224        DelayEngine => 'off',
225      },
226    },
227
228    Limit => {
229      LOGIN => {
230        AllowGroup => $group,
231        DenyAll => '',
232      },
233    },
234
235  };
236
237  my ($port, $config_user, $config_group) = config_write($config_file, $config);
238
239  # Open pipes, for use between the parent and child processes.  Specifically,
240  # the child will indicate when it's done with its test by writing a message
241  # to the parent.
242  my ($rfh, $wfh);
243  unless (pipe($rfh, $wfh)) {
244    die("Can't open pipe: $!");
245  }
246
247  my $ex;
248
249  # Fork child
250  $self->handle_sigchld();
251  defined(my $pid = fork()) or die("Can't fork: $!");
252  if ($pid) {
253    eval {
254      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
255      $client->login($user, $passwd);
256      $client->quit();
257    };
258
259    if ($@) {
260      $ex = $@;
261    }
262
263    $wfh->print("done\n");
264    $wfh->flush();
265
266  } else {
267    eval { server_wait($config_file, $rfh) };
268    if ($@) {
269      warn($@);
270      exit 1;
271    }
272
273    exit 0;
274  }
275
276  # Stop server
277  server_stop($pid_file);
278
279  $self->assert_child_ok($pid);
280
281  if ($ex) {
282    test_append_logfile($log_file, $ex);
283    unlink($log_file);
284
285    die($ex);
286  }
287
288  unlink($log_file);
289}
290
291sub login_limit_multiple_sections {
292  my $self = shift;
293  my $tmpdir = $self->{tmpdir};
294
295  my $config_file = "$tmpdir/limit.conf";
296  my $pid_file = File::Spec->rel2abs("$tmpdir/limit.pid");
297  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/limit.scoreboard");
298
299  my $log_file = test_get_logfile();
300
301  my $auth_user_file = File::Spec->rel2abs("$tmpdir/limit.passwd");
302  my $auth_group_file = File::Spec->rel2abs("$tmpdir/limit.group");
303
304  my $user = 'proftpd';
305  my $passwd = 'test';
306  my $group ='ftpd';
307  my $home_dir = File::Spec->rel2abs($tmpdir);
308  my $uid = 500;
309  my $gid = 500;
310
311  # Make sure that, if we're running as root, that the home directory has
312  # permissions/privs set for the account we create
313  if ($< == 0) {
314    unless (chmod(0755, $home_dir)) {
315      die("Can't set perms on $home_dir to 0755: $!");
316    }
317
318    unless (chown($uid, $gid, $home_dir)) {
319      die("Can't set owner of $home_dir to $uid/$gid: $!");
320    }
321  }
322
323  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
324    '/bin/bash');
325  auth_group_write($auth_group_file, $group, $gid, $user);
326
327  # See http://forums.proftpd.org/smf/index.php/topic,4774.0.html
328
329  my $config = {
330    PidFile => $pid_file,
331    ScoreboardFile => $scoreboard_file,
332    SystemLog => $log_file,
333    TraceLog => $log_file,
334    Trace => 'netacl:20 dns:20',
335
336    AuthUserFile => $auth_user_file,
337    AuthGroupFile => $auth_group_file,
338    UseIPv6 => 'off',
339
340    IfModules => {
341      'mod_delay.c' => {
342        DelayEngine => 'off',
343      },
344    },
345  };
346
347  my ($port, $config_user, $config_group) = config_write($config_file, $config);
348
349  if (open(my $fh, ">> $config_file")) {
350    # When multiple <Limit LOGIN> sections are present, which one is used:
351    # first, or last?
352    print $fh <<EOC;
353<Limit LOGIN>
354  Order allow,deny
355  Allow 127.0.0.1
356  DenyAll
357</Limit>
358
359<Limit LOGIN>
360  Order deny,allow
361  Deny 127.0.0.1
362</Limit>
363
364EOC
365    unless (close($fh)) {
366      die("Can't write $config_file: $!");
367    }
368  } else {
369    die("Can't open $config_file: $!");
370  }
371
372  # Open pipes, for use between the parent and child processes.  Specifically,
373  # the child will indicate when it's done with its test by writing a message
374  # to the parent.
375  my ($rfh, $wfh);
376  unless (pipe($rfh, $wfh)) {
377    die("Can't open pipe: $!");
378  }
379
380  my $ex;
381
382  # Fork child
383  $self->handle_sigchld();
384  defined(my $pid = fork()) or die("Can't fork: $!");
385  if ($pid) {
386    eval {
387      my $client;
388
389      eval { $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port,
390        undef, 0) };
391      unless ($@) {
392        die("Connect succeeded unexpectedly");
393      }
394
395      my $conn_ex = ProFTPD::TestSuite::FTP::get_connect_exception();
396
397      # Looks like the last <Limit LOGIN> section wins.
398      my $expected = 'Connection refused';
399      $self->assert(qr/$expected/, $conn_ex,
400        test_msg("Expected '$expected', got '$conn_ex'"));
401    };
402
403    if ($@) {
404      $ex = $@;
405    }
406
407    $wfh->print("done\n");
408    $wfh->flush();
409
410  } else {
411    eval { server_wait($config_file, $rfh) };
412    if ($@) {
413      warn($@);
414      exit 1;
415    }
416
417    exit 0;
418  }
419
420  # Stop server
421  server_stop($pid_file);
422
423  $self->assert_child_ok($pid);
424
425  if ($ex) {
426    test_append_logfile($log_file, $ex);
427    unlink($log_file);
428
429    die($ex);
430  }
431
432  unlink($log_file);
433}
434
4351;
436