Server IP : 195.201.23.43 / Your IP : 13.59.203.127 Web Server : Apache System : Linux webserver2.vercom.be 5.4.0-192-generic #212-Ubuntu SMP Fri Jul 5 09:47:39 UTC 2024 x86_64 User : kdecoratie ( 1041) PHP Version : 7.1.33-63+ubuntu20.04.1+deb.sury.org+1 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals, MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /usr/share/webmin/virtual-server/ |
Upload File : |
# Functions for setting up email rate limits sub get_ratelimit_type { if (-r "/usr/local/etc/mail/greylist.conf" && -e "/usr/local/bin/milter-greylist") { # Installed from source return 'source'; } elsif ($gconfig{'os_type'} eq 'debian-linux') { # Debian or Ubuntu packages return 'debian'; } elsif ($gconfig{'os_type'} eq 'redhat-linux') { # Redhat / CentOS packages return 'redhat'; } return undef; # Not supported } sub get_ratelimit_config_file { my $type = &get_ratelimit_type(); return $type eq 'redhat' ? '/etc/mail/greylist.conf' : $type eq 'debian' ? '/etc/milter-greylist/greylist.conf' : $type eq 'source' ? '/usr/local/etc/mail/greylist.conf' : undef; } sub get_ratelimit_init_name { return 'milter-greylist'; } sub get_ratelimit_user { my $type = &get_ratelimit_type(); return $type eq 'redhat' ? 'grmilter' : $type eq 'debian' ? 'greylist' : undef; } # check_ratelimit() # Returns undef if all the commands needed for ratelimiting are installed sub check_ratelimit { &foreign_require("init"); if (!&get_ratelimit_type()) { # Not supported on this OS return $text{'ratelimit_eos'}; } my $config_file = &get_ratelimit_config_file(); return &text('ratelimit_econfig', "<tt>$config_file</tt>") if (!-r $config_file); if (&get_ratelimit_type() ne 'source') { my $init = &get_ratelimit_init_name(); return &text('ratelimit_einit', "<tt>$init</tt>") if (!&init::action_status($init)); } if (!&get_milter_greylist_path()) { return &text('ratelimit_ecmd', "<tt>milter-greylist</tt>"); } # Check mail server &require_mail(); if ($config{'mail_system'} > 1) { return $text{'ratelimit_emailsystem'}; } elsif ($config{'mail_system'} == 1) { -r $sendmail::config{'sendmail_mc'} || return $text{'ratelimit_esendmailmc'}; } return undef; } # can_install_ratelimit() # Returns 1 if milter-greylist package installation is supported on this OS sub can_install_ratelimit { if ($gconfig{'os_type'} eq 'debian-linux' || $gconfig{'os_type'} eq 'redhat-linux') { &foreign_require("software"); return defined(&software::update_system_install); } return 0; } # install_ratelimit_package() # Attempt to install milter-greylist, outputting progress messages sub install_ratelimit_package { &foreign_require("software"); my $pkg = 'milter-greylist'; my @inst = &software::update_system_install($pkg); return scalar(@inst) || !&check_ratelimit(); } # get_ratelimit_config() # Returns the current rate-limiting config, parsed into an array ref sub get_ratelimit_config { my $cfile = &get_ratelimit_config_file(); my @rv; my $lref = &read_file_lines($cfile, 1); for(my $i=0; $i<@$lref; $i++) { my $l = $lref->[$i]; $l =~ s/#.*$//; next if ($l !~ /\S/); my $lnum = $i; while($l =~ s/\\\s*$//) { # Ends with / .. continue on next line $nl = $lref->[++$i]; $nl =~ s/#.*$//; $l .= $nl; } # Split up line like foo bar { smeg spod } my $toks = &wsplit_with_quotes($l); next if (!@$toks); my $dir = { 'line' => $lnum, 'eline' => $i, 'file' => $cfile, 'name' => shift(@$toks), 'values' => [ ] }; while(@$toks && $toks->[0] ne "{") { push(@{$dir->{'values'}}, shift(@$toks)); } $dir->{'value'} = $dir->{'values'}->[0]; $dir->{'value'} =~ s/^'(.*)'$/$1/; $dir->{'value'} =~ s/^"(.*)"$/$1/; if ($toks->[0] eq "{") { # Has sub-members $dir->{'members'} = [ ]; shift(@$toks); while(@$toks && $toks->[0] ne "{") { push(@{$dir->{'members'}}, shift(@$toks)); } } push(@rv, $dir); } return \@rv; } # save_ratelimit_directive(&config, &old, &new, [&create-before]) # Create, update or delete a ratelimiting directive sub save_ratelimit_directive { my ($conf, $o, $n, $b4) = @_; my $file = $o ? $o->{'file'} : $b4 ? $b4->{'file'} : &get_ratelimit_config_file(); $file || &error("No file to save config to!"); my $lref = &read_file_lines($file); my @lines = $n ? &make_ratelimit_lines($n) : (); my $idx = &indexof($o, @$conf); my ($roffset, $rlines); if ($o && $n) { # Replace existing directive $roffset = $o->{'line'}; $rlines = scalar(@lines) - ($o->{'eline'} - $o->{'line'} + 1); splice(@$lref, $o->{'line'}, $o->{'eline'} - $o->{'line'} + 1, @lines); $n->{'line'} = $o->{'line'}; $n->{'eline'} = $n->{'line'} + scalar(@lines) - 1; $n->{'file'} = $o->{'file'}; if ($idx >= 0) { $conf->[$idx] = $n; } } elsif ($o && !$n) { # Delete existing directive $roffset = $o->{'line'}; $rlines = $o->{'eline'} - $o->{'line'} + 1; splice(@$lref, $o->{'line'}, $rlines); if ($idx >= 0) { splice(@$conf, $idx, 1); } $rlines = -$rlines; } elsif (!$o && $n) { # Add new directive if ($b4) { # Before some directive $rlines = scalar(@lines); $roffset = $b4->{'line'} - 1; my $b4idx = &indexof($b4, @$conf); $b4idx >= 0 || &error("Directive to add before was not found ". "in config!"); splice(@$conf, $b4idx, 0, $n); splice(@$lref, $b4->{'line'}, 0, @lines); $n->{'line'} = $b4->{'line'}; $n->{'eline'} = $n->{'line'} + scalar(@lines) - 1; $n->{'file'} = $file; } else { # At end of file push(@$conf, $n); $n->{'line'} = scalar(@$lref); $n->{'eline'} = $n->{'line'} + scalar(@lines) - 1; $n->{'file'} = $file; push(@$lref, @lines); } } if ($rlines) { foreach my $c (@$conf) { $c->{'line'} += $rlines if ($c->{'line'} > $roffset); $c->{'eline'} += $rlines if ($c->{'eline'} > $roffset); } } } # make_ratelimit_lines(&directive) # Returns an array of lines for some directive sub make_ratelimit_lines { my ($dir) = @_; my @w = ( $dir->{'name'}, @{$dir->{'values'}} ); if ($dir->{'members'}) { push(@w, "{", @{$dir->{'members'}}, "}"); } return join(" ", @w); } # get_milter_greylist_path() # Returns the full path to milter-greylist, if installed sub get_milter_greylist_path { return &has_command("milter-greylist"); } # get_milter_greylist_version() # Returns the installed version number, or undef sub get_milter_greylist_version { my $path = &get_milter_greylist_path(); return undef if (!$path || !-x $path); my $out = &backquote_command("$path -r 2>&1 </dev/null"); return $out =~ /milter-greylist-([0-9\.]+)/ ? $1 : undef; } # get_mailserver_chroot() # Returns the chroot directory that the mailserver runs under. The # milter-greylist socket file must be relative to this. sub get_mailserver_chroot { if ($config{'mail_system'} == 0) { &foreign_require("postfix"); my $postfix_conf = &postfix::get_master_config(); my ($postsmtpchroot) = grep { $_->{'name'} = 'smtp' && $_->{'chroot'} eq 'y' } @{$postfix_conf}; if ($postsmtpchroot) { return "/var/spool/postfix"; } } return ""; } # is_ratelimit_enabled() # Returns 1 if the ratelimit server is running and the mail server is using it sub is_ratelimit_enabled { # Enabled at boot? &foreign_require("init"); my $init = &get_ratelimit_init_name(); return 0 if (&init::action_status($init) != 2); # Check mail server my $conf = &get_ratelimit_config(); my ($socket) = grep { $_->{'name'} eq 'socket' } @$conf; return 0 if (!$socket); # No socket in config?! my $socketfile = $socket->{'value'}; my $chroot = &get_mailserver_chroot(); if ($chroot) { $socketfile =~ s/^\Q$chroot\E//; } my $wantmilter = "local:$socketfile"; &require_mail(); if ($config{'mail_system'} == 0) { # Check Postfix config my $milters = &postfix::get_real_value("smtpd_milters"); if ($milters !~ /\Q$wantmilter\E/) { # Postfix not using the milter return 0; } } elsif ($config{'mail_system'} == 1) { # Check Sendmail config my @feats = &sendmail::list_features(); my ($milter) = grep { $_->{'text'} =~ /INPUT_MAIL_FILTER/ && $_->{'text'} =~ /\Q$wantmilter\E/ } @feats; if (!$milter) { # Sendmail not using the milter return 0; } } else { # Unsupported mail server return 0; } return 1; } # enable_ratelimit() # Turn on rate limiting, while printing progress sub enable_ratelimit { # Build stop and start commands (needed when installed from source) my $conf = &get_ratelimit_config(); my ($pidfile) = grep { $_->{'name'} eq 'pidfile' } @$conf; if (!$pidfile) { $pidfile = { 'name' => 'pidfile', 'value' => '/var/run/milter-greylist.pid', 'values' => [ '"/var/run/milter-greylist.pid"' ] }; &save_ratelimit_directive($conf, undef, $pidfile); &flush_file_lines($pidfile->{'file'}); } my $stopcmd = "kill `cat $pidfile->{'value'}` && sleep 5"; my $startcmd = &has_command("milter-greylist"). " -f ".&get_ratelimit_config_file(); # Enable at boot &foreign_require("init"); my $init = &get_ratelimit_init_name(); &$first_print(&text('ratelimit_atboot', "<tt>$init</tt>")); &init::enable_at_boot($init, "Start milter-greylist", $startcmd, $stopcmd, undef, { 'fork' => 1 }); # On Debian, update the defaults file my $dfile = "/etc/default/milter-greylist"; if (&get_ratelimit_type() eq 'debian' && -r $dfile) { my $lref = &read_file_lines($dfile); foreach my $l (@$lref) { if ($l =~ /^\s*ENABLED=/) { $l = "ENABLED=1"; } } &flush_file_lines($dfile); } &$second_print($text{'setup_done'}); # Pick a socket file under the mail server's chroot &$first_print($text{'ratelimit_socket'}); my $chroot = &get_mailserver_chroot(); my ($oldsocket) = grep { $_->{'name'} eq 'socket' } @$conf; if (!$oldsocket) { &$second_print($text{'ratelimit_esocket'}); return 0; } my $socketfile = $oldsocket->{'value'}; # Socket path used by postfix if ($chroot) { # Adjust the socket to ensure it is under the chroot if (!&is_under_directory($chroot, $socketfile) && $socketfile !~ /^\Q$chroot\E/) { # Not currently under the chroot .. make it so my $newsocket = { 'name' => 'socket', 'values' => [ "\"$chroot$socketfile\"" ] }; &save_ratelimit_directive($conf, $oldsocket, $newsocket); &flush_file_lines($oldsocket->{'file'}); } else { # Already under the chroot, so remove the chroot prefix $socketfile =~ s/^\Q$chroot\E//; } # Change path in init script and defaults file if needed foreach my $ifile (&init::action_filename($init), "/etc/default/milter-greylist") { if ($ifile && -r $ifile) { my $lref = &read_file_lines($ifile); foreach my $l (@$lref) { if ($l =~ /^SOCKET\s*=/) { $l = "SOCKET=$chroot$socketfile"; } } &flush_file_lines($ifile); &init::restart_systemd(); } } # Make sure the socket file directory exists my $socketdir = "$chroot$socketfile"; $socketdir =~ s/\/[^\/]+$//; if (!-d $socketdir) { &make_dir($socketdir, 0775, 1); my $user = &get_ratelimit_user(); if ($user) { &set_ownership_permissions($user, undef, undef, $socketdir); } } } # Set socket to 666 permissions my ($socket) = grep { $_->{'name'} eq 'socket' } @$conf; if ($socket && $socket->{'values'}->[1] ne '666') { $socket->{'values'}->[1] = '666'; &save_ratelimit_directive($conf, $socket, $socket); &flush_file_lines($socket->{'file'}); } &$second_print($text{'setup_done'}); # Start up now, if not running &$first_print($text{'ratelimit_start'}); &init::stop_action($init); sleep(5); # Seems to need time to stop my ($ok, $err) = &init::start_action($init); if (!$ok) { &$second_print(&text('ratelimit_estart', $err)); return 0; } else { &$second_print($text{'setup_done'}); } # Configure mail server &$first_print($text{'ratelimit_mailserver'}); &require_mail(); my $newmilter = "local:$socketfile"; if ($config{'mail_system'} == 0) { # Configure Postfix to use filter &lock_file($postfix::config{'postfix_config_file'}); &postfix::set_current_value("milter_default_action", "accept"); if (!&postfix::get_current_value("milter_protocol")) { &postfix::set_current_value("milter_protocol", 2); } my $milters = &postfix::get_current_value("smtpd_milters"); if ($milters !~ /\Q$newmilter\E/) { $milters = $milters ? $milters.",".$newmilter : $newmilter; &postfix::set_current_value("smtpd_milters", $milters); &postfix::set_current_value("non_smtpd_milters", $milters); } &unlock_file($postfix::config{'postfix_config_file'}); # Apply Postfix config &postfix::reload_postfix(); } elsif ($config{'mail_system'} == 1) { # Configure Sendmail to use filter &lock_file($sendmail::config{'sendmail_mc'}); my $changed = 0; my @feats = &sendmail::list_features(); # Check for filter definition my ($milter) = grep { $_->{'text'} =~ /INPUT_MAIL_FILTER/ && $_->{'text'} =~ /\Q$newmilter\E/ } @feats; if (!$milter) { # Add to .mc file &sendmail::create_feature({ 'type' => 0, 'text' => "INPUT_MAIL_FILTER(`ratelimit-filter', `S=$newmilter')" }); $changed++; } # Check for config for filters to call my ($def) = grep { $_->{'type'} == 2 && $_->{'name'} eq 'confINPUT_MAIL_FILTERS' } @feats; if ($def) { my @filters = split(/,/, $def->{'value'}); if (&indexof("ratelimit-filter", @filters) < 0) { # Add to existing define push(@filters, 'ratelimit-filter'); $def->{'value'} = join(',', @filters); &sendmail::modify_feature($def); $changed++; } } else { # Add the define &sendmail::create_feature({ 'type' => 2, 'name' => 'confINPUT_MAIL_FILTERS', 'value' => 'ratelimit-filter' }); $changed++; } # Add other defines to change milter behavior foreach my $l ([ 'confMILTER_MACROS_CONNECT', 'j, {if_addr}' ], [ 'confMILTER_MACROS_HELO', '{verify}, {cert_subject}' ], [ 'confMILTER_MACROS_ENVFROM', 'i, {auth_authen}' ], [ 'confMILTER_MACROS_ENVRCPT', '{greylist}' ]) { my ($def) = grep { $_->{'type'} == 2 && $_->{'name'} eq $l->[0] } @$conf; if (!$def) { &sendmail::create_feature({ 'type' => 2, 'name' => $l->[0], 'value' => $l->[1] }); $changed++; } } if ($changed) { &rebuild_sendmail_cf(); } &unlock_file($sendmail::config{'sendmail_mc'}); if ($changed) { &sendmail::restart_sendmail(); } } &$second_print($text{'setup_done'}); return 1; } # disable_ratelimit() # Shut down the greylist server and stop the mail sever from using it sub disable_ratelimit { &$first_print($text{'ratelimit_unmailserver'}); my $conf = &get_ratelimit_config(); my ($socket) = grep { $_->{'name'} eq 'socket' } @$conf; if (!$socket) { &$second_print($text{'ratelimit_esocket'}); return 0; } my $socketfile = $socket->{'value'}; my $chroot = &get_mailserver_chroot(); if ($chroot) { $socketfile =~ s/^\Q$chroot\E//; } my $oldmilter = "local:".$socketfile; &require_mail(); if ($config{'mail_system'} == 0) { # Configure Postfix to not use filter &lock_file($postfix::config{'postfix_config_file'}); my $milters = &postfix::get_current_value("smtpd_milters"); if ($milters =~ /\Q$oldmilter\E/) { $milters = join(",", grep { $_ ne $oldmilter } split(/\s*,\s*/, $milters)); &postfix::set_current_value("smtpd_milters", $milters); &postfix::set_current_value("non_smtpd_milters", $milters); } &unlock_file($postfix::config{'postfix_config_file'}); # Apply Postfix config &postfix::reload_postfix(); } elsif ($config{'mail_system'} == 1) { # Configure Sendmail to not use filter &lock_file($sendmail::config{'sendmail_mc'}); my @feats = &sendmail::list_features(); my $changed = 0; # Remove from list of milter to call my ($def) = grep { $_->{'type'} == 2 && $_->{'name'} eq 'confINPUT_MAIL_FILTERS' } @feats; if ($def) { my @filters = split(/,/, $def->{'value'}); @filters = grep { $_ ne 'ratelimit-filter' } @filters; if (@filters) { # Some still left, so update $def->{'value'} = join(',', @filters); &sendmail::modify_feature($def); } else { # Delete completely &sendmail::delete_feature($def); } $changed++; } # Remove milter definition my ($milter) = grep { $_->{'text'} =~ /INPUT_MAIL_FILTER/ && $_->{'text'} =~ /\Q$oldmilter\E/ } @feats; if ($milter) { &sendmail::delete_feature($milter); $changed++; } if ($changed) { &rebuild_sendmail_cf(); } &unlock_file($sendmail::config{'sendmail_mc'}); if ($changed) { &sendmail::restart_sendmail(); } } &$second_print($text{'setup_done'}); # Stop filter now &$first_print($text{'ratelimit_stop'}); my $init = &get_ratelimit_init_name(); &init::stop_action($init); &$second_print($text{'setup_done'}); # Disable filter at boot time &$first_print($text{'ratelimit_unboot'}); &init::disable_at_boot($init); &$second_print($text{'setup_done'}); return 1; } # wsplit_with_quotes(string) # Splits a string like foo "foo bar" bazzz into an array of words, preserving # the quotes around them sub wsplit_with_quotes { my $s = $_[0]; my @rv; $s =~ s/\\\"/\0/g; while($s =~ /^("[^"]*")\s*(.*)$/ || $s =~ /^(\S+)\s*(.*)$/) { my $w = $1; $s = $2; $w =~ s/\0/"/g; push(@rv, $w); } return \@rv; } 1;Private