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