1package ProFTPD::Tests::Config::Limit::Anonymous;
2
3use lib qw(t/lib);
4use base qw(ProFTPD::TestSuite::Child);
5use strict;
6
7use File::Path qw(mkpath);
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  anon_limit_write => {
20    order => ++$order,
21    test_class => [qw(forking rootprivs)],
22  },
23
24  anon_limit_write_changing_dirs => {
25    order => ++$order,
26    test_class => [qw(forking rootprivs)],
27  },
28
29  anon_limit_write_with_ftpaccess => {
30    order => ++$order,
31    test_class => [qw(forking rootprivs)],
32  },
33
34  anon_limit_login_dns_name => {
35    order => ++$order,
36    test_class => [qw(forking rootprivs)],
37  },
38
39  anon_limit_write_allow_stor_using_abs_path => {
40    order => ++$order,
41    test_class => [qw(forking rootprivs)],
42  },
43
44  anon_limit_write_allow_stor_using_rel_path => {
45    order => ++$order,
46    test_class => [qw(forking rootprivs)],
47  },
48
49};
50
51sub new {
52  return shift()->SUPER::new(@_);
53}
54
55sub list_tests {
56  return testsuite_get_runnable_tests($TESTS);
57}
58
59sub anon_limit_write {
60  my $self = shift;
61  my $tmpdir = $self->{tmpdir};
62
63  my $config_file = "$tmpdir/limit.conf";
64  my $pid_file = File::Spec->rel2abs("$tmpdir/limit.pid");
65  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/limit.scoreboard");
66
67  my $log_file = File::Spec->rel2abs('tests.log');
68
69  my $auth_user_file = File::Spec->rel2abs("$tmpdir/limit.passwd");
70  my $auth_group_file = File::Spec->rel2abs("$tmpdir/limit.group");
71
72  my ($config_user, $config_group) = config_get_identity();
73
74  my $anon_dir = File::Spec->rel2abs($tmpdir);
75  my $uid = 500;
76  my $gid = 500;
77
78  # Make sure that, if we're running as root, that the home directory has
79  # permissions/privs set for the account we create
80  if ($< == 0) {
81    unless (chmod(0755, $anon_dir)) {
82      die("Can't set perms on $anon_dir to 0755: $!");
83    }
84
85    unless (chown($uid, $gid, $anon_dir)) {
86      die("Can't set owner of $anon_dir to $uid/$gid: $!");
87    }
88  }
89
90  auth_user_write($auth_user_file, $config_user, 'test', $uid, $gid, '/tmp',
91    '/bin/bash');
92  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
93
94  # Test the config mentioned here:
95  #
96  #  http://forums.proftpd.org/smf/index.php/topic,4076.0.html
97  #
98  # Specifically this portion:
99  #
100  #  <Anonymous>
101  #    <Directory *>
102  #      <Limit WRITE>
103  #        DenyAll
104  #      </Limit>
105  #    </Directory>
106  #  </Anonymous>
107  #
108  # Does the use of '*' there allow creation of directories there?
109
110  my $config = {
111    PidFile => $pid_file,
112    ScoreboardFile => $scoreboard_file,
113    SystemLog => $log_file,
114
115    AuthUserFile => $auth_user_file,
116    AuthGroupFile => $auth_group_file,
117
118    IfModules => {
119      'mod_delay.c' => {
120        DelayEngine => 'off',
121      },
122    },
123  };
124
125  my ($port, $user, $group) = config_write($config_file, $config);
126
127  if (open(my $fh, ">> $config_file")) {
128    print $fh <<EOC;
129<Anonymous $anon_dir>
130  User $config_user
131  Group $config_group
132  UserAlias anonymous $config_user
133  RequireValidShell off
134
135  <Directory *>
136    <Limit WRITE>
137      DenyAll
138    </Limit>
139  </Directory>
140</Anonymous>
141EOC
142    unless (close($fh)) {
143      die("Can't write $config_file: $!");
144    }
145
146  } else {
147    die("Can't open $config_file: $!");
148  }
149
150  # Open pipes, for use between the parent and child processes.  Specifically,
151  # the child will indicate when it's done with its test by writing a message
152  # to the parent.
153  my ($rfh, $wfh);
154  unless (pipe($rfh, $wfh)) {
155    die("Can't open pipe: $!");
156  }
157
158  my $ex;
159
160  # Fork child
161  $self->handle_sigchld();
162  defined(my $pid = fork()) or die("Can't fork: $!");
163  if ($pid) {
164    eval {
165      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
166      $client->login('anonymous', 'ftp@nospam.org');
167
168      eval { $client->mkd('testdir') };
169      unless ($@) {
170        die("MKD testdir succeeded unexpectedly");
171      }
172
173      my $resp_code = $client->response_code();
174      my $resp_msg = $client->response_msg();
175
176      my $expected;
177
178      $expected = 550;
179      $self->assert($expected == $resp_code,
180        test_msg("Expected response code $expected, got $resp_code"));
181
182      $expected = "testdir: Permission denied";
183      $self->assert($expected eq $resp_msg,
184        test_msg("Expected response message '$expected', got '$resp_msg'"));
185    };
186
187    if ($@) {
188      $ex = $@;
189    }
190
191    $wfh->print("done\n");
192    $wfh->flush();
193
194  } else {
195    eval { server_wait($config_file, $rfh) };
196    if ($@) {
197      warn($@);
198      exit 1;
199    }
200
201    exit 0;
202  }
203
204  # Stop server
205  server_stop($pid_file);
206
207  $self->assert_child_ok($pid);
208
209  if ($ex) {
210    die($ex);
211  }
212
213  unlink($log_file);
214}
215
216sub anon_limit_write_changing_dirs {
217  my $self = shift;
218  my $tmpdir = $self->{tmpdir};
219
220  my $config_file = "$tmpdir/limit.conf";
221  my $pid_file = File::Spec->rel2abs("$tmpdir/limit.pid");
222  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/limit.scoreboard");
223
224  my $log_file = File::Spec->rel2abs('tests.log');
225
226  my $auth_user_file = File::Spec->rel2abs("$tmpdir/limit.passwd");
227  my $auth_group_file = File::Spec->rel2abs("$tmpdir/limit.group");
228
229  my ($config_user, $config_group) = config_get_identity();
230
231  my $anon_dir = File::Spec->rel2abs($tmpdir);
232  my $uid = 500;
233  my $gid = 500;
234
235  my $sub_dir = File::Spec->rel2abs("$tmpdir/subdir");
236  mkpath($sub_dir);
237
238  # Make sure that, if we're running as root, that the home directory has
239  # permissions/privs set for the account we create
240  if ($< == 0) {
241    unless (chmod(0755, $anon_dir, $sub_dir)) {
242      die("Can't set perms on $anon_dir, $sub_dir to 0755: $!");
243    }
244
245    unless (chown($uid, $gid, $anon_dir, $sub_dir)) {
246      die("Can't set owner of $anon_dir, $sub_dir to $uid/$gid: $!");
247    }
248  }
249
250  auth_user_write($auth_user_file, $config_user, 'test', $uid, $gid, '/tmp',
251    '/bin/bash');
252  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
253
254  # Test the config mentioned here:
255  #
256  #  http://forums.proftpd.org/smf/index.php/topic,4076.0.html
257  #
258  # Specifically this portion:
259  #
260  #  <Anonymous>
261  #    <Directory *>
262  #      <Limit WRITE>
263  #        DenyAll
264  #      </Limit>
265  #    </Directory>
266  #  </Anonymous>
267  #
268  # Does the use of '*' there allow creation of directories there?
269
270  my $config = {
271    PidFile => $pid_file,
272    ScoreboardFile => $scoreboard_file,
273    SystemLog => $log_file,
274
275    AuthUserFile => $auth_user_file,
276    AuthGroupFile => $auth_group_file,
277
278    IfModules => {
279      'mod_delay.c' => {
280        DelayEngine => 'off',
281      },
282    },
283  };
284
285  my ($port, $user, $group) = config_write($config_file, $config);
286
287  if (open(my $fh, ">> $config_file")) {
288    print $fh <<EOC;
289<Anonymous $anon_dir>
290  User $config_user
291  Group $config_group
292  UserAlias anonymous $config_user
293  RequireValidShell off
294
295  <Directory *>
296    <Limit WRITE>
297      DenyAll
298    </Limit>
299  </Directory>
300</Anonymous>
301EOC
302    unless (close($fh)) {
303      die("Can't write $config_file: $!");
304    }
305
306  } else {
307    die("Can't open $config_file: $!");
308  }
309
310  # Open pipes, for use between the parent and child processes.  Specifically,
311  # the child will indicate when it's done with its test by writing a message
312  # to the parent.
313  my ($rfh, $wfh);
314  unless (pipe($rfh, $wfh)) {
315    die("Can't open pipe: $!");
316  }
317
318  my $ex;
319
320  # Fork child
321  $self->handle_sigchld();
322  defined(my $pid = fork()) or die("Can't fork: $!");
323  if ($pid) {
324    eval {
325      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
326      $client->login('anonymous', 'ftp@nospam.org');
327      $client->cwd('subdir');
328
329      eval { $client->mkd('testdir') };
330      unless ($@) {
331        die("MKD testdir succeeded unexpectedly");
332      }
333
334      my $resp_code = $client->response_code();
335      my $resp_msg = $client->response_msg();
336
337      my $expected;
338
339      $expected = 550;
340      $self->assert($expected == $resp_code,
341        test_msg("Expected response code $expected, got $resp_code"));
342
343      $expected = "testdir: Permission denied";
344      $self->assert($expected eq $resp_msg,
345        test_msg("Expected response message '$expected', got '$resp_msg'"));
346
347      # Now try to create a directory one level up from where we are
348      eval { $client->mkd('../testdir') };
349      unless ($@) {
350        die("MKD ../testdir succeeded unexpectedly");
351      }
352
353      $resp_code = $client->response_code();
354      $resp_msg = $client->response_msg();
355
356      $expected = 550;
357      $self->assert($expected == $resp_code,
358        test_msg("Expected response code $expected, got $resp_code"));
359
360      $expected = "../testdir: Permission denied";
361      $self->assert($expected eq $resp_msg,
362        test_msg("Expected response message '$expected', got '$resp_msg'"));
363    };
364
365    if ($@) {
366      $ex = $@;
367    }
368
369    $wfh->print("done\n");
370    $wfh->flush();
371
372  } else {
373    eval { server_wait($config_file, $rfh) };
374    if ($@) {
375      warn($@);
376      exit 1;
377    }
378
379    exit 0;
380  }
381
382  # Stop server
383  server_stop($pid_file);
384
385  $self->assert_child_ok($pid);
386
387  if ($ex) {
388    die($ex);
389  }
390
391  unlink($log_file);
392}
393
394sub anon_limit_write_with_ftpaccess {
395  my $self = shift;
396  my $tmpdir = $self->{tmpdir};
397
398  my $config_file = "$tmpdir/limit.conf";
399  my $pid_file = File::Spec->rel2abs("$tmpdir/limit.pid");
400  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/limit.scoreboard");
401
402  my $log_file = File::Spec->rel2abs('tests.log');
403
404  my $auth_user_file = File::Spec->rel2abs("$tmpdir/limit.passwd");
405  my $auth_group_file = File::Spec->rel2abs("$tmpdir/limit.group");
406
407  my ($config_user, $config_group) = config_get_identity();
408
409  my $anon_dir = File::Spec->rel2abs($tmpdir);
410  my $uid = 500;
411  my $gid = 500;
412
413  my $sub_dir = File::Spec->rel2abs("$tmpdir/subdir");
414  mkpath($sub_dir);
415
416  # Make sure that, if we're running as root, that the home directory has
417  # permissions/privs set for the account we create
418  if ($< == 0) {
419    unless (chmod(0755, $anon_dir, $sub_dir)) {
420      die("Can't set perms on $anon_dir, $sub_dir to 0755: $!");
421    }
422
423    unless (chown($uid, $gid, $anon_dir, $sub_dir)) {
424      die("Can't set owner of $anon_dir, $sub_dir to $uid/$gid: $!");
425    }
426  }
427
428  # Create a .ftpaccess file in the sub-directory.
429  if (open(my $fh, "> $anon_dir/.ftpaccess")) {
430    print $fh <<EOC;
431DirFakeUser on foo
432EOC
433    unless (close($fh)) {
434      die("Can't write $anon_dir/.ftpaccess: $!");
435    }
436
437  } else {
438    die("Can't open $anon_dir/.ftpaccess: $!");
439  }
440
441  auth_user_write($auth_user_file, $config_user, 'test', $uid, $gid, '/tmp',
442    '/bin/bash');
443  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
444
445  # Test the config mentioned here:
446  #
447  #  http://forums.proftpd.org/smf/index.php/topic,4076.0.html
448  #
449  # Specifically this portion:
450  #
451  #  <Anonymous>
452  #    <Directory *>
453  #      <Limit WRITE>
454  #        DenyAll
455  #      </Limit>
456  #    </Directory>
457  #  </Anonymous>
458  #
459  # Does the use of '*' there allow creation of directories there?
460
461  my $config = {
462    PidFile => $pid_file,
463    ScoreboardFile => $scoreboard_file,
464    SystemLog => $log_file,
465
466    AuthUserFile => $auth_user_file,
467    AuthGroupFile => $auth_group_file,
468
469    AllowOverride => 'on',
470
471    IfModules => {
472      'mod_delay.c' => {
473        DelayEngine => 'off',
474      },
475    },
476  };
477
478  my ($port, $user, $group) = config_write($config_file, $config);
479
480  if (open(my $fh, ">> $config_file")) {
481    print $fh <<EOC;
482<Anonymous $anon_dir>
483  User $config_user
484  Group $config_group
485  UserAlias anonymous $config_user
486  RequireValidShell off
487
488  <Directory *>
489    <Limit WRITE>
490      DenyAll
491    </Limit>
492  </Directory>
493</Anonymous>
494EOC
495    unless (close($fh)) {
496      die("Can't write $config_file: $!");
497    }
498
499  } else {
500    die("Can't open $config_file: $!");
501  }
502
503  # Open pipes, for use between the parent and child processes.  Specifically,
504  # the child will indicate when it's done with its test by writing a message
505  # to the parent.
506  my ($rfh, $wfh);
507  unless (pipe($rfh, $wfh)) {
508    die("Can't open pipe: $!");
509  }
510
511  my $ex;
512
513  # Fork child
514  $self->handle_sigchld();
515  defined(my $pid = fork()) or die("Can't fork: $!");
516  if ($pid) {
517    eval {
518      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
519      $client->login('anonymous', 'ftp@nospam.org');
520      $client->cwd('subdir');
521
522      eval { $client->mkd('testdir') };
523      unless ($@) {
524        die("MKD testdir succeeded unexpectedly");
525      }
526
527      my $resp_code = $client->response_code();
528      my $resp_msg = $client->response_msg();
529
530      my $expected;
531
532      $expected = 550;
533      $self->assert($expected == $resp_code,
534        test_msg("Expected response code $expected, got $resp_code"));
535
536      $expected = "testdir: Permission denied";
537      $self->assert($expected eq $resp_msg,
538        test_msg("Expected response message '$expected', got '$resp_msg'"));
539
540      # Now try to create a directory one level up from where we are
541      eval { $client->mkd('../testdir') };
542      unless ($@) {
543        die("MKD ../testdir succeeded unexpectedly");
544      }
545
546      $resp_code = $client->response_code();
547      $resp_msg = $client->response_msg();
548
549      $expected = 550;
550      $self->assert($expected == $resp_code,
551        test_msg("Expected response code $expected, got $resp_code"));
552
553      $expected = "../testdir: Permission denied";
554      $self->assert($expected eq $resp_msg,
555        test_msg("Expected response message '$expected', got '$resp_msg'"));
556
557      $client->cwd('/');
558
559      eval { $client->mkd('testdir') };
560      unless ($@) {
561        die("MKD testdir succeeded unexpectedly");
562      }
563
564      $resp_code = $client->response_code();
565      $resp_msg = $client->response_msg();
566
567      $expected = 550;
568      $self->assert($expected == $resp_code,
569        test_msg("Expected response code $expected, got $resp_code"));
570
571      $expected = "testdir: Permission denied";
572      $self->assert($expected eq $resp_msg,
573        test_msg("Expected response message '$expected', got '$resp_msg'"));
574    };
575
576    if ($@) {
577      $ex = $@;
578    }
579
580    $wfh->print("done\n");
581    $wfh->flush();
582
583  } else {
584    eval { server_wait($config_file, $rfh) };
585    if ($@) {
586      warn($@);
587      exit 1;
588    }
589
590    exit 0;
591  }
592
593  # Stop server
594  server_stop($pid_file);
595
596  $self->assert_child_ok($pid);
597
598  if ($ex) {
599    die($ex);
600  }
601
602  unlink($log_file);
603}
604
605sub anon_limit_login_dns_name {
606  my $self = shift;
607  my $tmpdir = $self->{tmpdir};
608
609  my $config_file = "$tmpdir/limit.conf";
610  my $pid_file = File::Spec->rel2abs("$tmpdir/limit.pid");
611  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/limit.scoreboard");
612
613  my $log_file = File::Spec->rel2abs('tests.log');
614
615  my $auth_user_file = File::Spec->rel2abs("$tmpdir/limit.passwd");
616  my $auth_group_file = File::Spec->rel2abs("$tmpdir/limit.group");
617
618  my ($config_user, $config_group) = config_get_identity();
619
620  my $anon_dir = File::Spec->rel2abs($tmpdir);
621  my $uid = 500;
622  my $gid = 500;
623
624  # Make sure that, if we're running as root, that the home directory has
625  # permissions/privs set for the account we create
626  if ($< == 0) {
627    unless (chmod(0755, $anon_dir)) {
628      die("Can't set perms on $anon_dir to 0755: $!");
629    }
630
631    unless (chown($uid, $gid, $anon_dir)) {
632      die("Can't set owner of $anon_dir to $uid/$gid: $!");
633    }
634  }
635
636  auth_user_write($auth_user_file, $config_user, 'test', $uid, $gid, '/tmp',
637    '/bin/bash');
638  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
639
640  # Test the config mentioned here:
641  #
642  #  http://forums.proftpd.org/smf/index.php/topic,4436.0.html
643  #
644  # Specifically this portion:
645  #
646  #  <Anonymous>
647  #    <Limit LOGIN>
648  #      Allow from <dns-name>
649  #      DenyAll
650  #    </Limit>
651  #  </Anonymous>
652
653  my $config = {
654    PidFile => $pid_file,
655    ScoreboardFile => $scoreboard_file,
656    SystemLog => $log_file,
657    TraceLog => $log_file,
658    Trace => 'DEFAULT:10 netacl:10 netaddr:10 dns:10',
659
660    AuthUserFile => $auth_user_file,
661    AuthGroupFile => $auth_group_file,
662    UseReverseDNS => 'on',
663
664    IfModules => {
665      'mod_delay.c' => {
666        DelayEngine => 'off',
667      },
668    },
669  };
670
671  my ($port, $user, $group) = config_write($config_file, $config);
672
673  if (open(my $fh, ">> $config_file")) {
674    print $fh <<EOC;
675<Anonymous $anon_dir>
676  User $config_user
677  Group $config_group
678  UserAlias anonymous $config_user
679  RequireValidShell off
680
681  <Limit LOGIN>
682    Allow from localhost
683    DenyAll
684  </Limit>
685</Anonymous>
686EOC
687    unless (close($fh)) {
688      die("Can't write $config_file: $!");
689    }
690
691  } else {
692    die("Can't open $config_file: $!");
693  }
694
695  # Open pipes, for use between the parent and child processes.  Specifically,
696  # the child will indicate when it's done with its test by writing a message
697  # to the parent.
698  my ($rfh, $wfh);
699  unless (pipe($rfh, $wfh)) {
700    die("Can't open pipe: $!");
701  }
702
703  my $ex;
704
705  # Fork child
706  $self->handle_sigchld();
707  defined(my $pid = fork()) or die("Can't fork: $!");
708  if ($pid) {
709    eval {
710      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
711      $client->login('anonymous', 'ftp@nospam.org');
712    };
713
714    if ($@) {
715      $ex = $@;
716    }
717
718    $wfh->print("done\n");
719    $wfh->flush();
720
721  } else {
722    eval { server_wait($config_file, $rfh) };
723    if ($@) {
724      warn($@);
725      exit 1;
726    }
727
728    exit 0;
729  }
730
731  # Stop server
732  server_stop($pid_file);
733
734  $self->assert_child_ok($pid);
735
736  if ($ex) {
737    die($ex);
738  }
739
740  unlink($log_file);
741}
742
743sub anon_limit_write_allow_stor_using_abs_path {
744  my $self = shift;
745  my $tmpdir = $self->{tmpdir};
746
747  my $config_file = "$tmpdir/limit.conf";
748  my $pid_file = File::Spec->rel2abs("$tmpdir/limit.pid");
749  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/limit.scoreboard");
750
751  my $log_file = File::Spec->rel2abs('tests.log');
752
753  my $auth_user_file = File::Spec->rel2abs("$tmpdir/limit.passwd");
754  my $auth_group_file = File::Spec->rel2abs("$tmpdir/limit.group");
755
756  my ($config_user, $config_group) = config_get_identity();
757
758  my $anon_dir = File::Spec->rel2abs($tmpdir);
759  my $uid = 500;
760  my $gid = 500;
761
762  my $uploads_dir = File::Spec->rel2abs("$anon_dir/uploads");
763  mkpath($uploads_dir);
764
765  my $test_file = File::Spec->rel2abs("$uploads_dir/test.txt");
766
767  # Make sure that, if we're running as root, that the home directory has
768  # permissions/privs set for the account we create
769  if ($< == 0) {
770    unless (chmod(0755, $anon_dir, $uploads_dir)) {
771      die("Can't set perms on $anon_dir to 0755: $!");
772    }
773
774    unless (chown($uid, $gid, $anon_dir, $uploads_dir)) {
775      die("Can't set owner of $anon_dir to $uid/$gid: $!");
776    }
777  }
778
779  auth_user_write($auth_user_file, $config_user, 'test', $uid, $gid, '/tmp',
780    '/bin/bash');
781  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
782
783  # Test this config:
784  #
785  #  <Anonymous>
786  #    <Limit WRITE SITE_CHMOD>
787  #      DenyAll
788  #    </Limit>
789  #
790  #    <Directory uploads/*>
791  #      <Limit STOR>
792  #        AllowAll
793  #      </Limit>
794  #    </Directory>
795  #  </Anonymous>
796
797  my $config = {
798    PidFile => $pid_file,
799    ScoreboardFile => $scoreboard_file,
800    SystemLog => $log_file,
801
802    AuthUserFile => $auth_user_file,
803    AuthGroupFile => $auth_group_file,
804
805    IfModules => {
806      'mod_delay.c' => {
807        DelayEngine => 'off',
808      },
809    },
810  };
811
812  my ($port, $user, $group) = config_write($config_file, $config);
813
814  if (open(my $fh, ">> $config_file")) {
815    print $fh <<EOC;
816<Anonymous $anon_dir>
817  User $config_user
818  Group $config_group
819  UserAlias anonymous $config_user
820  RequireValidShell off
821
822  <Limit WRITE SITE_CHMOD>
823    DenyAll
824  </Limit>
825
826  <Directory uploads/*>
827    <Limit STOR>
828      AllowAll
829    </Limit>
830  </Directory>
831</Anonymous>
832EOC
833    unless (close($fh)) {
834      die("Can't write $config_file: $!");
835    }
836
837  } else {
838    die("Can't open $config_file: $!");
839  }
840
841  # Open pipes, for use between the parent and child processes.  Specifically,
842  # the child will indicate when it's done with its test by writing a message
843  # to the parent.
844  my ($rfh, $wfh);
845  unless (pipe($rfh, $wfh)) {
846    die("Can't open pipe: $!");
847  }
848
849  my $ex;
850
851  # Fork child
852  $self->handle_sigchld();
853  defined(my $pid = fork()) or die("Can't fork: $!");
854  if ($pid) {
855    eval {
856      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
857      $client->login('anonymous', 'ftp@nospam.org');
858
859      my $conn = $client->stor_raw('uploads/test.txt');
860      unless ($conn) {
861        die("STOR uploads/test.txt failed:" . $client->response_code() . " " .
862          $client->response_msg());
863      }
864
865      my $buf = "Hello, World!\n";
866      $conn->write($buf, length($buf), 15);
867      eval { $conn->close() };
868
869      my $resp_code = $client->response_code();
870      my $resp_msg = $client->response_msg();
871
872      $self->assert_transfer_ok($resp_code, $resp_msg);
873
874      $client->quit();
875
876      $self->assert(-f $test_file,
877        test_msg("File $test_file does not exist as expected"));
878    };
879
880    if ($@) {
881      $ex = $@;
882    }
883
884    $wfh->print("done\n");
885    $wfh->flush();
886
887  } else {
888    eval { server_wait($config_file, $rfh) };
889    if ($@) {
890      warn($@);
891      exit 1;
892    }
893
894    exit 0;
895  }
896
897  # Stop server
898  server_stop($pid_file);
899
900  $self->assert_child_ok($pid);
901
902  if ($ex) {
903    die($ex);
904  }
905
906  unlink($log_file);
907}
908
909sub anon_limit_write_allow_stor_using_rel_path {
910  my $self = shift;
911  my $tmpdir = $self->{tmpdir};
912
913  my $config_file = "$tmpdir/limit.conf";
914  my $pid_file = File::Spec->rel2abs("$tmpdir/limit.pid");
915  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/limit.scoreboard");
916
917  my $log_file = File::Spec->rel2abs('tests.log');
918
919  my $auth_user_file = File::Spec->rel2abs("$tmpdir/limit.passwd");
920  my $auth_group_file = File::Spec->rel2abs("$tmpdir/limit.group");
921
922  my ($config_user, $config_group) = config_get_identity();
923
924  my $anon_dir = File::Spec->rel2abs($tmpdir);
925  my $uid = 500;
926  my $gid = 500;
927
928  my $uploads_dir = File::Spec->rel2abs("$anon_dir/uploads");
929  mkpath($uploads_dir);
930
931  my $test_file = File::Spec->rel2abs("$uploads_dir/test.txt");
932
933  # Make sure that, if we're running as root, that the home directory has
934  # permissions/privs set for the account we create
935  if ($< == 0) {
936    unless (chmod(0755, $anon_dir, $uploads_dir)) {
937      die("Can't set perms on $anon_dir to 0755: $!");
938    }
939
940    unless (chown($uid, $gid, $anon_dir, $uploads_dir)) {
941      die("Can't set owner of $anon_dir to $uid/$gid: $!");
942    }
943  }
944
945  auth_user_write($auth_user_file, $config_user, 'test', $uid, $gid, '/tmp',
946    '/bin/bash');
947  auth_group_write($auth_group_file, $config_group, $gid, $config_user);
948
949  # Test this config:
950  #
951  #  <Anonymous>
952  #    <Limit WRITE SITE_CHMOD>
953  #      DenyAll
954  #    </Limit>
955  #
956  #    <Directory uploads/*>
957  #      <Limit STOR>
958  #        AllowAll
959  #      </Limit>
960  #    </Directory>
961  #  </Anonymous>
962
963  my $config = {
964    PidFile => $pid_file,
965    ScoreboardFile => $scoreboard_file,
966    SystemLog => $log_file,
967
968    AuthUserFile => $auth_user_file,
969    AuthGroupFile => $auth_group_file,
970
971    IfModules => {
972      'mod_delay.c' => {
973        DelayEngine => 'off',
974      },
975    },
976  };
977
978  my ($port, $user, $group) = config_write($config_file, $config);
979
980  if (open(my $fh, ">> $config_file")) {
981    print $fh <<EOC;
982<Anonymous $anon_dir>
983  User $config_user
984  Group $config_group
985  UserAlias anonymous $config_user
986  RequireValidShell off
987
988  <Limit WRITE SITE_CHMOD>
989    DenyAll
990  </Limit>
991
992  <Directory uploads/*>
993    <Limit STOR>
994      AllowAll
995    </Limit>
996  </Directory>
997</Anonymous>
998EOC
999    unless (close($fh)) {
1000      die("Can't write $config_file: $!");
1001    }
1002
1003  } else {
1004    die("Can't open $config_file: $!");
1005  }
1006
1007  # Open pipes, for use between the parent and child processes.  Specifically,
1008  # the child will indicate when it's done with its test by writing a message
1009  # to the parent.
1010  my ($rfh, $wfh);
1011  unless (pipe($rfh, $wfh)) {
1012    die("Can't open pipe: $!");
1013  }
1014
1015  my $ex;
1016
1017  # Fork child
1018  $self->handle_sigchld();
1019  defined(my $pid = fork()) or die("Can't fork: $!");
1020  if ($pid) {
1021    eval {
1022      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1023      $client->login('anonymous', 'ftp@nospam.org');
1024      $client->cwd('uploads');
1025
1026      my $conn = $client->stor_raw('test.txt');
1027      unless ($conn) {
1028        die("STOR test.txt failed:" . $client->response_code() . " " .
1029          $client->response_msg());
1030      }
1031
1032      my $buf = "Hello, World!\n";
1033      $conn->write($buf, length($buf), 15);
1034      eval { $conn->close() };
1035
1036      my $resp_code = $client->response_code();
1037      my $resp_msg = $client->response_msg();
1038
1039      $self->assert_transfer_ok($resp_code, $resp_msg);
1040
1041      $client->quit();
1042
1043      $self->assert(-f $test_file,
1044        test_msg("File $test_file does not exist as expected"));
1045    };
1046
1047    if ($@) {
1048      $ex = $@;
1049    }
1050
1051    $wfh->print("done\n");
1052    $wfh->flush();
1053
1054  } else {
1055    eval { server_wait($config_file, $rfh) };
1056    if ($@) {
1057      warn($@);
1058      exit 1;
1059    }
1060
1061    exit 0;
1062  }
1063
1064  # Stop server
1065  server_stop($pid_file);
1066
1067  $self->assert_child_ok($pid);
1068
1069  if ($ex) {
1070    die($ex);
1071  }
1072
1073  unlink($log_file);
1074}
1075
10761;
1077