Imported Upstream version 1.16
This commit is contained in:
		
							parent
							
								
									2357dc9ae5
								
							
						
					
					
						commit
						a7ab4e32cf
					
				
					 5 changed files with 752 additions and 168 deletions
				
			
		| 
						 | 
				
			
			@ -1,3 +1,28 @@
 | 
			
		|||
1.16
 | 
			
		||||
=====
 | 
			
		||||
- bugfix:  this is a bugfix release for 1.15. anyone affected is encouraged to upgrade.
 | 
			
		||||
	   detail: the default behavior for the '=' operator with numeric items
 | 
			
		||||
	   (size, recipient_count, ...) changed with version 1.15 to '==' (equals to).
 | 
			
		||||
	   now these items are compared '>=' (greater than) again.
 | 
			
		||||
	   note: if you are using 1.15 and you are not able upgrade for some reason,
 | 
			
		||||
	   please change '=' to '>=' in your ruleset where you mean 'greater than'.
 | 
			
		||||
 | 
			
		||||
1.15
 | 
			
		||||
=====
 | 
			
		||||
- feature: items may now be retrieved from files using "item=file:/some/where"
 | 
			
		||||
	   more information in the postfwd manual (FILES section)
 | 
			
		||||
- feature: helo_address, and sender_(ns|mx)_addrs can now be csv items
 | 
			
		||||
- feature: new rcpt() command counts recipients for rate limits (thanks to Sahil Tandon)
 | 
			
		||||
- code:    redirect syslog to stdout for --kill, --reload and --showconfig
 | 
			
		||||
- code:    option --reload (HUP signal) now reloads config, if the file is unchanged
 | 
			
		||||
- code:	   configuration parser improvements:
 | 
			
		||||
	   * rules without defined action will be skipped at configuration stage
 | 
			
		||||
	   * undefined ACLs will now be detected and skipped at configuration stage
 | 
			
		||||
	   * parser timeout skips loading a rule after 4s, to prevent problems with
 | 
			
		||||
	     large files or loops. use --config_timeout to override
 | 
			
		||||
- bugfix:  documentation fixed (missing "action=" in ask() examples)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
1.14
 | 
			
		||||
=====
 | 
			
		||||
- feature: new compare operators *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										126
									
								
								doc/postfwd.html
									
										
									
									
									
								
							
							
						
						
									
										126
									
								
								doc/postfwd.html
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,10 +1,8 @@
 | 
			
		|||
<?xml version="1.0" ?>
 | 
			
		||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 | 
			
		||||
<html xmlns="http://www.w3.org/1999/xhtml">
 | 
			
		||||
<head>
 | 
			
		||||
<title>postfwd - postfix firewall daemon</title>
 | 
			
		||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
 | 
			
		||||
<link rev="made" href="mailto:feedback@suse.de" />
 | 
			
		||||
<link rev="made" href="mailto:root@localhost" />
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body style="background-color: white">
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +20,7 @@
 | 
			
		|||
		<li><a href="#introduction">INTRODUCTION</a></li>
 | 
			
		||||
		<li><a href="#configuration">CONFIGURATION</a></li>
 | 
			
		||||
		<li><a href="#items">ITEMS</a></li>
 | 
			
		||||
		<li><a href="#files">FILES</a></li>
 | 
			
		||||
		<li><a href="#actions">ACTIONS</a></li>
 | 
			
		||||
		<li><a href="#macros_acls">MACROS/ACLS</a></li>
 | 
			
		||||
		<li><a href="#plugins">PLUGINS</a></li>
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +96,8 @@
 | 
			
		|||
            --dns_timeout_interval  interval in seconds for dns timeout maximum counter
 | 
			
		||||
            --dns_max_ns_lookups    max names to look up with sender_ns_addrs
 | 
			
		||||
            --dns_max_mx_lookups    max names to look up with sender_mx_addrs
 | 
			
		||||
        -I, --instantcfg            re-reads rulefiles for every new request</pre>
 | 
			
		||||
        -I, --instantcfg            re-reads rulefiles for every new request
 | 
			
		||||
            --config_timeout <i>        parser timeout in seconds</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        Informational (use only at command-line!):
 | 
			
		||||
        -C, --showconfig            shows ruleset summary, -v for verbose
 | 
			
		||||
| 
						 | 
				
			
			@ -148,11 +148,15 @@ is not important. So the following would lead to the same result as the previous
 | 
			
		|||
<p>The way how request items are compared to the ruleset can be influenced in the following way:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        ====================================================================
 | 
			
		||||
         ITEM==VALUE                  true if ITEM equals VALUE
 | 
			
		||||
         ITEM>=VALUE                  true if ITEM >= VALUE
 | 
			
		||||
         ITEM<=VALUE                  true if ITEM <= VALUE
 | 
			
		||||
         ITEM~=VALUE                  true if ITEM ~= /^VALUE$/i
 | 
			
		||||
         ITEM=VALUE                   default behaviour (see ITEMS section)
 | 
			
		||||
         ITEM == VALUE                true if ITEM equals VALUE
 | 
			
		||||
         ITEM => VALUE                true if ITEM >= VALUE
 | 
			
		||||
         ITEM =< VALUE                true if ITEM <= VALUE
 | 
			
		||||
         ITEM =~ VALUE                true if ITEM ~= /^VALUE$/i
 | 
			
		||||
         ITEM != VALUE                false if ITEM equals VALUE
 | 
			
		||||
         ITEM !> VALUE                false if ITEM >= VALUE
 | 
			
		||||
         ITEM !< VALUE                false if ITEM <= VALUE
 | 
			
		||||
         ITEM !~ VALUE                false if ITEM ~= /^VALUE$/i
 | 
			
		||||
         ITEM =  VALUE                default behaviour (see ITEMS section)
 | 
			
		||||
        ====================================================================</pre>
 | 
			
		||||
<p>To identify single rules in your log files, you may add an unique identifier for each of it:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
| 
						 | 
				
			
			@ -213,13 +217,19 @@ arguments. Please see the COMMAND LINE section below for more information on thi
 | 
			
		|||
        recipient_domain</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        helo_address            - postfwd tries to look up the helo_name. use
 | 
			
		||||
                                  helo_address=!!(0.0.0.0/0) to check for unknown.</pre>
 | 
			
		||||
                                  helo_address=!!(0.0.0.0/0) to check for unknown.
 | 
			
		||||
                                  Please do not use this for positive access control
 | 
			
		||||
                                  (whitelisting), as it might be forged.</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        sender_ns_names,        - postfwd tries to look up the names/ip addresses
 | 
			
		||||
        sender_ns_addrs           of the nameservers for the sender domain part.</pre>
 | 
			
		||||
        sender_ns_addrs           of the nameservers for the sender domain part.
 | 
			
		||||
                                  Please do not use this for positive access control
 | 
			
		||||
                                  (whitelisting), as it might be forged.</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        sender_mx_names,        - postfwd tries to look up the names/ip addresses
 | 
			
		||||
        sender_mx_addrs           of the mx records for the sender domain part.</pre>
 | 
			
		||||
        sender_mx_addrs           of the mx records for the sender domain part.
 | 
			
		||||
                                  Please do not use this for positive access control
 | 
			
		||||
                                  (whitelisting), as it might be forged.</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        version                 - postfwd version, contains "postfwd n.nn"
 | 
			
		||||
                                  this enables version based checks in your rulesets
 | 
			
		||||
| 
						 | 
				
			
			@ -298,6 +308,9 @@ Pattern matching is performed case insensitive.</p>
 | 
			
		|||
<p>Any item can be negated by preceeding '!!' to it, e.g.:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        id=TLS001 ;  hostname=!!^secure\.trust\.local$ ;  action=REJECT only secure.trust.local please</pre>
 | 
			
		||||
<p>or using the right compare operator:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        id=USER01 ;  sasl_username !~ /^(bob|alice)$/ ;  action=REJECT who is that?</pre>
 | 
			
		||||
<p>To avoid confusion with regexps or simply for better visibility you can use '!!(...)':</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        id=USER01 ;  sasl_username=!!( (bob|alice) )  ;  action=REJECT who is that?</pre>
 | 
			
		||||
| 
						 | 
				
			
			@ -310,6 +323,78 @@ Pattern matching is performed case insensitive.</p>
 | 
			
		|||
Use the '-vv' option to debug.</p>
 | 
			
		||||
<p>
 | 
			
		||||
</p>
 | 
			
		||||
<h2><a name="files">FILES</a></h2>
 | 
			
		||||
<p>Since postfwd1 v1.15 and postfwd2 v0.18 long item lists can be stored in separate files:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        id=R001 ;  ccert_fingerprint==<a href="file:/etc/postfwd/wl_ccerts">file:/etc/postfwd/wl_ccerts</a> ;  action=DUNNO</pre>
 | 
			
		||||
<p>postfwd will read a list of items (one item per line) from /etc/postfwd/wl_ccerts. comments are allowed:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        # client1
 | 
			
		||||
        11:22:33:44:55:66:77:88:99
 | 
			
		||||
        # client2
 | 
			
		||||
        22:33:44:55:66:77:88:99:00
 | 
			
		||||
        # client3
 | 
			
		||||
        33:44:55:66:77:88:99:00:11</pre>
 | 
			
		||||
<p>To use existing tables in key=value format, you can use:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        id=R001 ;  ccert_fingerprint==table:/etc/postfwd/wl_ccerts ;  action=DUNNO</pre>
 | 
			
		||||
<p>This will ignore the right-hand value. Items can be mixed:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        id=R002 ;  action=REJECT \
 | 
			
		||||
                client_name==unknown; \
 | 
			
		||||
                client_name==<a href="file:/etc/postfwd/blacklisted">file:/etc/postfwd/blacklisted</a></pre>
 | 
			
		||||
<p>and for non pcre (comma separated) items:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        id=R003 ;  action=REJECT \
 | 
			
		||||
                client_address==10.1.1.1, <a href="file:/etc/postfwd/blacklisted">file:/etc/postfwd/blacklisted</a></pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        id=R004 ;  action=REJECT \
 | 
			
		||||
                rbl=myrbl.home.local, zen.spamhaus.org, <a href="file:/etc/postfwd/rbls_changing">file:/etc/postfwd/rbls_changing</a></pre>
 | 
			
		||||
<p>You can check your configuration with the --show_config option at the command line:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        # postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, <a href="file:/etc/postfwd/wl_clients">file:/etc/postfwd/wl_clients</a>, 192.168.2.1'</pre>
 | 
			
		||||
<p>should give something like:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        Rule   0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;194.123.86.10, =;186.4.6.12, =;192.168.2.1"</pre>
 | 
			
		||||
<p>If a file can not be read, it will be ignored:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        # postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, <a href="file:/etc/postfwd/wl_clients">file:/etc/postfwd/wl_clients</a>, 192.168.2.1'
 | 
			
		||||
        [LOG warning]: error: file /etc/postfwd/wl_clients not found - file will be ignored ?
 | 
			
		||||
        Rule   0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;192.168.2.1"</pre>
 | 
			
		||||
<p>File items are evaluated at configuration stage. Therefore postfwd needs to be reloaded if a file has changed.</p>
 | 
			
		||||
<p>If you want to specify a file, that will be reloaded for each request, you can use lfile: and ltable:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        id=R001; client_address=lfile:/etc/postfwd/client_whitelist; action=dunno</pre>
 | 
			
		||||
<p>This will check the modification time of /etc/postfwd/client_whitelist every time the rule is evaluated and reload it as
 | 
			
		||||
necessary. Of course this might increase the system load, so please use it with care.</p>
 | 
			
		||||
<p>The --showconfig option illustrates the difference:</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        ## evaluated at configuration stage
 | 
			
		||||
        # postfwd2 --nodaemon -L --rule='client_address=table:/etc/postfwd/clients; action=dunno' -C
 | 
			
		||||
        Rule   0: id->"R-0"; action->"dunno"; client_address->"=;1.1.1.1, =;1.1.1.2, =;1.1.1.3"</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        ## evaluated for any rulehit
 | 
			
		||||
        # postfwd2 --nodaemon -L --rule='client_address=ltable:/etc/postfwd/clients; action=dunno' -C
 | 
			
		||||
        Rule   0: id->"R-0"; action->"dunno"; client_address->"=;ltable:/etc/postfwd/clients"</pre>
 | 
			
		||||
<p>Files can refer to other files. The following is valid.</p>
 | 
			
		||||
<pre>
 | 
			
		||||
        -- FILE /etc/postfwd/rules.cf --
 | 
			
		||||
        id=R001; client_address=<a href="file:/etc/postfwd/clients_master.cf">file:/etc/postfwd/clients_master.cf</a>; action=DUNNO</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        -- FILE /etc/postfwd/clients_master.cf --
 | 
			
		||||
        192.168.1.0/24
 | 
			
		||||
        <a href="file:/etc/postfwd/clients_east.cf">file:/etc/postfwd/clients_east.cf</a>
 | 
			
		||||
        <a href="file:/etc/postfwd/clients_west.cf">file:/etc/postfwd/clients_west.cf</a></pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        -- FILE /etc/postfwd/clients_east.cf --
 | 
			
		||||
        192.168.2.0/24</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        -- FILE /etc/postfwd/clients_west.cf --
 | 
			
		||||
        192.168.3.0/24</pre>
 | 
			
		||||
<p>Remind that there is currently no loop detection (/a/file calls /a/file) and that this feature is only available
 | 
			
		||||
with postfwd1 v1.15 and postfwd2 v0.18 and higher.</p>
 | 
			
		||||
<p>
 | 
			
		||||
</p>
 | 
			
		||||
<h2><a name="actions">ACTIONS</a></h2>
 | 
			
		||||
<p><em>General</em></p>
 | 
			
		||||
<p>Actions will be executed, when all rule items have matched a request (or at least one of any item list). You can refer to
 | 
			
		||||
| 
						 | 
				
			
			@ -370,16 +455,25 @@ rule containing only an action statement:</p>
 | 
			
		|||
           # size limit 1.5mb per hour per client
 | 
			
		||||
           id=SIZE01 ;  state==END_OF_DATA ;  client_address==!!(10.1.1.1); \
 | 
			
		||||
              action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        rcpt (<item>/<max>/<time>/<action>)
 | 
			
		||||
        this command works similar to the rate() command with the difference, that the rate counter is
 | 
			
		||||
        increased by the request's recipient_count attribute. to do this reliably you should call postfwd
 | 
			
		||||
        from smtpd_data_restrictions or smtpd_end_of_data_restrictions. if you want to be sure, you could
 | 
			
		||||
        check it within the ruleset:
 | 
			
		||||
           # recipient count limit 3 per hour per client
 | 
			
		||||
           id=RCPT01 ;  state==END_OF_DATA ;  client_address==!!(10.1.1.1); \
 | 
			
		||||
              action==rcpt($$client_address/3/3600/450 4.7.1 sorry, max 3 recipients per hour)</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        ask (<addr>:<port>[:<ignore>])
 | 
			
		||||
        allows to delegate the policy decision to another policy service (e.g. postgrey). the first
 | 
			
		||||
        and the second argument (address and port) are mandatory. a third optional argument may be
 | 
			
		||||
        specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
 | 
			
		||||
           # example1: query postgrey and return it's answer to postfix
 | 
			
		||||
           id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031)
 | 
			
		||||
           id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031)
 | 
			
		||||
           # example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
 | 
			
		||||
           # and continue parsing postfwd's ruleset
 | 
			
		||||
           id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031:^dunno$)</pre>
 | 
			
		||||
           id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031:^dunno$)</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        wait (<delay>)
 | 
			
		||||
        pauses the program execution for <delay> seconds. use this for
 | 
			
		||||
| 
						 | 
				
			
			@ -650,6 +744,10 @@ The following arguments will control it's behaviour in this case.</p>
 | 
			
		|||
        without restarting. Though files will be read only if necessary
 | 
			
		||||
        (which means their access times changed since last read) this might
 | 
			
		||||
        significantly increase system load.</pre>
 | 
			
		||||
<pre>
 | 
			
		||||
        --config_timeout    (default=3)
 | 
			
		||||
        timeout in seconds to parse a single configuration line. if exceeded, the rule will
 | 
			
		||||
        be skipped. this is used to prevent problems due to large files or loops.</pre>
 | 
			
		||||
<p><em>Informational arguments</em></p>
 | 
			
		||||
<p>These arguments are for command line usage only. Never ever use them with postfix spawn!</p>
 | 
			
		||||
<pre>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										133
									
								
								doc/postfwd.txt
									
										
									
									
									
								
							
							
						
						
									
										133
									
								
								doc/postfwd.txt
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -51,6 +51,7 @@ SYNOPSIS
 | 
			
		|||
                --dns_max_ns_lookups    max names to look up with sender_ns_addrs
 | 
			
		||||
                --dns_max_mx_lookups    max names to look up with sender_mx_addrs
 | 
			
		||||
            -I, --instantcfg            re-reads rulefiles for every new request
 | 
			
		||||
                --config_timeout <i>        parser timeout in seconds
 | 
			
		||||
 | 
			
		||||
            Informational (use only at command-line!):
 | 
			
		||||
            -C, --showconfig            shows ruleset summary, -v for verbose
 | 
			
		||||
| 
						 | 
				
			
			@ -115,11 +116,15 @@ DESCRIPTION
 | 
			
		|||
    in the following way:
 | 
			
		||||
 | 
			
		||||
            ====================================================================
 | 
			
		||||
             ITEM==VALUE                  true if ITEM equals VALUE
 | 
			
		||||
             ITEM>=VALUE                  true if ITEM >= VALUE
 | 
			
		||||
             ITEM<=VALUE                  true if ITEM <= VALUE
 | 
			
		||||
             ITEM~=VALUE                  true if ITEM ~= /^VALUE$/i
 | 
			
		||||
             ITEM=VALUE                   default behaviour (see ITEMS section)
 | 
			
		||||
             ITEM == VALUE                true if ITEM equals VALUE
 | 
			
		||||
             ITEM => VALUE                true if ITEM >= VALUE
 | 
			
		||||
             ITEM =< VALUE                true if ITEM <= VALUE
 | 
			
		||||
             ITEM =~ VALUE                true if ITEM ~= /^VALUE$/i
 | 
			
		||||
             ITEM != VALUE                false if ITEM equals VALUE
 | 
			
		||||
             ITEM !> VALUE                false if ITEM >= VALUE
 | 
			
		||||
             ITEM !< VALUE                false if ITEM <= VALUE
 | 
			
		||||
             ITEM !~ VALUE                false if ITEM ~= /^VALUE$/i
 | 
			
		||||
             ITEM =  VALUE                default behaviour (see ITEMS section)
 | 
			
		||||
            ====================================================================
 | 
			
		||||
 | 
			
		||||
    To identify single rules in your log files, you may add an unique
 | 
			
		||||
| 
						 | 
				
			
			@ -187,12 +192,18 @@ DESCRIPTION
 | 
			
		|||
 | 
			
		||||
            helo_address            - postfwd tries to look up the helo_name. use
 | 
			
		||||
                                      helo_address=!!(0.0.0.0/0) to check for unknown.
 | 
			
		||||
                                      Please do not use this for positive access control
 | 
			
		||||
                                      (whitelisting), as it might be forged.
 | 
			
		||||
 | 
			
		||||
            sender_ns_names,        - postfwd tries to look up the names/ip addresses
 | 
			
		||||
            sender_ns_addrs           of the nameservers for the sender domain part.
 | 
			
		||||
                                      Please do not use this for positive access control
 | 
			
		||||
                                      (whitelisting), as it might be forged.
 | 
			
		||||
 | 
			
		||||
            sender_mx_names,        - postfwd tries to look up the names/ip addresses
 | 
			
		||||
            sender_mx_addrs           of the mx records for the sender domain part.
 | 
			
		||||
                                      Please do not use this for positive access control
 | 
			
		||||
                                      (whitelisting), as it might be forged.
 | 
			
		||||
 | 
			
		||||
            version                 - postfwd version, contains "postfwd n.nn"
 | 
			
		||||
                                      this enables version based checks in your rulesets
 | 
			
		||||
| 
						 | 
				
			
			@ -284,6 +295,10 @@ DESCRIPTION
 | 
			
		|||
 | 
			
		||||
            id=TLS001 ;  hostname=!!^secure\.trust\.local$ ;  action=REJECT only secure.trust.local please
 | 
			
		||||
 | 
			
		||||
    or using the right compare operator:
 | 
			
		||||
 | 
			
		||||
            id=USER01 ;  sasl_username !~ /^(bob|alice)$/ ;  action=REJECT who is that?
 | 
			
		||||
 | 
			
		||||
    To avoid confusion with regexps or simply for better visibility you can
 | 
			
		||||
    use '!!(...)':
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -299,6 +314,97 @@ DESCRIPTION
 | 
			
		|||
    be performed as case insensitive exact match. Use the '-vv' option to
 | 
			
		||||
    debug.
 | 
			
		||||
 | 
			
		||||
  FILES
 | 
			
		||||
    Since postfwd1 v1.15 and postfwd2 v0.18 long item lists can be stored in
 | 
			
		||||
    separate files:
 | 
			
		||||
 | 
			
		||||
            id=R001 ;  ccert_fingerprint==file:/etc/postfwd/wl_ccerts ;  action=DUNNO
 | 
			
		||||
 | 
			
		||||
    postfwd will read a list of items (one item per line) from
 | 
			
		||||
    /etc/postfwd/wl_ccerts. comments are allowed:
 | 
			
		||||
 | 
			
		||||
            # client1
 | 
			
		||||
            11:22:33:44:55:66:77:88:99
 | 
			
		||||
            # client2
 | 
			
		||||
            22:33:44:55:66:77:88:99:00
 | 
			
		||||
            # client3
 | 
			
		||||
            33:44:55:66:77:88:99:00:11
 | 
			
		||||
 | 
			
		||||
    To use existing tables in key=value format, you can use:
 | 
			
		||||
 | 
			
		||||
            id=R001 ;  ccert_fingerprint==table:/etc/postfwd/wl_ccerts ;  action=DUNNO
 | 
			
		||||
 | 
			
		||||
    This will ignore the right-hand value. Items can be mixed:
 | 
			
		||||
 | 
			
		||||
            id=R002 ;  action=REJECT \
 | 
			
		||||
                    client_name==unknown; \
 | 
			
		||||
                    client_name==file:/etc/postfwd/blacklisted
 | 
			
		||||
 | 
			
		||||
    and for non pcre (comma separated) items:
 | 
			
		||||
 | 
			
		||||
            id=R003 ;  action=REJECT \
 | 
			
		||||
                    client_address==10.1.1.1, file:/etc/postfwd/blacklisted
 | 
			
		||||
 | 
			
		||||
            id=R004 ;  action=REJECT \
 | 
			
		||||
                    rbl=myrbl.home.local, zen.spamhaus.org, file:/etc/postfwd/rbls_changing
 | 
			
		||||
 | 
			
		||||
    You can check your configuration with the --show_config option at the
 | 
			
		||||
    command line:
 | 
			
		||||
 | 
			
		||||
            # postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
 | 
			
		||||
 | 
			
		||||
    should give something like:
 | 
			
		||||
 | 
			
		||||
            Rule   0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;194.123.86.10, =;186.4.6.12, =;192.168.2.1"
 | 
			
		||||
 | 
			
		||||
    If a file can not be read, it will be ignored:
 | 
			
		||||
 | 
			
		||||
            # postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
 | 
			
		||||
            [LOG warning]: error: file /etc/postfwd/wl_clients not found - file will be ignored ?
 | 
			
		||||
            Rule   0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;192.168.2.1"
 | 
			
		||||
 | 
			
		||||
    File items are evaluated at configuration stage. Therefore postfwd needs
 | 
			
		||||
    to be reloaded if a file has changed.
 | 
			
		||||
 | 
			
		||||
    If you want to specify a file, that will be reloaded for each request,
 | 
			
		||||
    you can use lfile: and ltable:
 | 
			
		||||
 | 
			
		||||
            id=R001; client_address=lfile:/etc/postfwd/client_whitelist; action=dunno
 | 
			
		||||
 | 
			
		||||
    This will check the modification time of /etc/postfwd/client_whitelist
 | 
			
		||||
    every time the rule is evaluated and reload it as necessary. Of course
 | 
			
		||||
    this might increase the system load, so please use it with care.
 | 
			
		||||
 | 
			
		||||
    The --showconfig option illustrates the difference:
 | 
			
		||||
 | 
			
		||||
            ## evaluated at configuration stage
 | 
			
		||||
            # postfwd2 --nodaemon -L --rule='client_address=table:/etc/postfwd/clients; action=dunno' -C
 | 
			
		||||
            Rule   0: id->"R-0"; action->"dunno"; client_address->"=;1.1.1.1, =;1.1.1.2, =;1.1.1.3"
 | 
			
		||||
 | 
			
		||||
            ## evaluated for any rulehit
 | 
			
		||||
            # postfwd2 --nodaemon -L --rule='client_address=ltable:/etc/postfwd/clients; action=dunno' -C
 | 
			
		||||
            Rule   0: id->"R-0"; action->"dunno"; client_address->"=;ltable:/etc/postfwd/clients"
 | 
			
		||||
 | 
			
		||||
    Files can refer to other files. The following is valid.
 | 
			
		||||
 | 
			
		||||
            -- FILE /etc/postfwd/rules.cf --
 | 
			
		||||
            id=R001; client_address=file:/etc/postfwd/clients_master.cf; action=DUNNO
 | 
			
		||||
 | 
			
		||||
            -- FILE /etc/postfwd/clients_master.cf --
 | 
			
		||||
            192.168.1.0/24
 | 
			
		||||
            file:/etc/postfwd/clients_east.cf
 | 
			
		||||
            file:/etc/postfwd/clients_west.cf
 | 
			
		||||
 | 
			
		||||
            -- FILE /etc/postfwd/clients_east.cf --
 | 
			
		||||
            192.168.2.0/24
 | 
			
		||||
 | 
			
		||||
            -- FILE /etc/postfwd/clients_west.cf --
 | 
			
		||||
            192.168.3.0/24
 | 
			
		||||
 | 
			
		||||
    Remind that there is currently no loop detection (/a/file calls /a/file)
 | 
			
		||||
    and that this feature is only available with postfwd1 v1.15 and postfwd2
 | 
			
		||||
    v0.18 and higher.
 | 
			
		||||
 | 
			
		||||
  ACTIONS
 | 
			
		||||
    *General*
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -371,15 +477,24 @@ DESCRIPTION
 | 
			
		|||
               id=SIZE01 ;  state==END_OF_DATA ;  client_address==!!(10.1.1.1); \
 | 
			
		||||
                  action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)
 | 
			
		||||
 | 
			
		||||
            rcpt (<item>/<max>/<time>/<action>)
 | 
			
		||||
            this command works similar to the rate() command with the difference, that the rate counter is
 | 
			
		||||
            increased by the request's recipient_count attribute. to do this reliably you should call postfwd
 | 
			
		||||
            from smtpd_data_restrictions or smtpd_end_of_data_restrictions. if you want to be sure, you could
 | 
			
		||||
            check it within the ruleset:
 | 
			
		||||
               # recipient count limit 3 per hour per client
 | 
			
		||||
               id=RCPT01 ;  state==END_OF_DATA ;  client_address==!!(10.1.1.1); \
 | 
			
		||||
                  action==rcpt($$client_address/3/3600/450 4.7.1 sorry, max 3 recipients per hour)
 | 
			
		||||
 | 
			
		||||
            ask (<addr>:<port>[:<ignore>])
 | 
			
		||||
            allows to delegate the policy decision to another policy service (e.g. postgrey). the first
 | 
			
		||||
            and the second argument (address and port) are mandatory. a third optional argument may be
 | 
			
		||||
            specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
 | 
			
		||||
               # example1: query postgrey and return it's answer to postfix
 | 
			
		||||
               id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031)
 | 
			
		||||
               id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031)
 | 
			
		||||
               # example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
 | 
			
		||||
               # and continue parsing postfwd's ruleset
 | 
			
		||||
               id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031:^dunno$)
 | 
			
		||||
               id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031:^dunno$)
 | 
			
		||||
 | 
			
		||||
            wait (<delay>)
 | 
			
		||||
            pauses the program execution for <delay> seconds. use this for
 | 
			
		||||
| 
						 | 
				
			
			@ -671,6 +786,10 @@ DESCRIPTION
 | 
			
		|||
            (which means their access times changed since last read) this might
 | 
			
		||||
            significantly increase system load.
 | 
			
		||||
 | 
			
		||||
            --config_timeout    (default=3)
 | 
			
		||||
            timeout in seconds to parse a single configuration line. if exceeded, the rule will
 | 
			
		||||
            be skipped. this is used to prevent problems due to large files or loops.
 | 
			
		||||
        
 | 
			
		||||
    *Informational arguments*
 | 
			
		||||
 | 
			
		||||
    These arguments are for command line usage only. Never ever use them
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32
 | 
			
		||||
.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14
 | 
			
		||||
.\"
 | 
			
		||||
.\" Standard preamble:
 | 
			
		||||
.\" ========================================================================
 | 
			
		||||
| 
						 | 
				
			
			@ -128,8 +128,8 @@
 | 
			
		|||
.rm #[ #] #H #V #F C
 | 
			
		||||
.\" ========================================================================
 | 
			
		||||
.\"
 | 
			
		||||
.IX Title "MANUAL1 8"
 | 
			
		||||
.TH MANUAL1 8 "2009-06-27" "perl v5.8.8" "User Contributed Perl Documentation"
 | 
			
		||||
.IX Title "POSTFWD 1"
 | 
			
		||||
.TH POSTFWD 1 "2009-09-03" "perl v5.8.5" "User Contributed Perl Documentation"
 | 
			
		||||
.SH "NAME"
 | 
			
		||||
postfwd \- postfix firewall daemon
 | 
			
		||||
.SH "SYNOPSIS"
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +175,7 @@ postfwd [\s-1OPTIONS\s0] [\s-1SOURCE1\s0, \s-1SOURCE2\s0, ...]
 | 
			
		|||
\&            --cleanup-rates         cleanup interval in seconds for rate cache
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 16
 | 
			
		||||
.Vb 17
 | 
			
		||||
\&        Optional:
 | 
			
		||||
\&        -t, --test                  testing, always returns "dunno"
 | 
			
		||||
\&        -v, --verbose               verbose logging, use twice (-vv) to increase level
 | 
			
		||||
| 
						 | 
				
			
			@ -192,6 +192,7 @@ postfwd [\s-1OPTIONS\s0] [\s-1SOURCE1\s0, \s-1SOURCE2\s0, ...]
 | 
			
		|||
\&            --dns_max_ns_lookups    max names to look up with sender_ns_addrs
 | 
			
		||||
\&            --dns_max_mx_lookups    max names to look up with sender_mx_addrs
 | 
			
		||||
\&        -I, --instantcfg            re-reads rulefiles for every new request
 | 
			
		||||
\&            --config_timeout <i>        parser timeout in seconds
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 7
 | 
			
		||||
| 
						 | 
				
			
			@ -261,13 +262,17 @@ is not important. So the following would lead to the same result as the previous
 | 
			
		|||
.PP
 | 
			
		||||
The way how request items are compared to the ruleset can be influenced in the following way:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 7
 | 
			
		||||
.Vb 11
 | 
			
		||||
\&        ====================================================================
 | 
			
		||||
\&         ITEM==VALUE                  true if ITEM equals VALUE
 | 
			
		||||
\&         ITEM>=VALUE                  true if ITEM >= VALUE
 | 
			
		||||
\&         ITEM<=VALUE                  true if ITEM <= VALUE
 | 
			
		||||
\&         ITEM~=VALUE                  true if ITEM ~= /^VALUE$/i
 | 
			
		||||
\&         ITEM=VALUE                   default behaviour (see ITEMS section)
 | 
			
		||||
\&         ITEM == VALUE                true if ITEM equals VALUE
 | 
			
		||||
\&         ITEM => VALUE                true if ITEM >= VALUE
 | 
			
		||||
\&         ITEM =< VALUE                true if ITEM <= VALUE
 | 
			
		||||
\&         ITEM =~ VALUE                true if ITEM ~= /^VALUE$/i
 | 
			
		||||
\&         ITEM != VALUE                false if ITEM equals VALUE
 | 
			
		||||
\&         ITEM !> VALUE                false if ITEM >= VALUE
 | 
			
		||||
\&         ITEM !< VALUE                false if ITEM <= VALUE
 | 
			
		||||
\&         ITEM !~ VALUE                false if ITEM ~= /^VALUE$/i
 | 
			
		||||
\&         ITEM =  VALUE                default behaviour (see ITEMS section)
 | 
			
		||||
\&        ====================================================================
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
| 
						 | 
				
			
			@ -352,19 +357,25 @@ Rules can span multiple lines by adding a trailing backslash \*(L"\e\*(R" charac
 | 
			
		|||
\&        recipient_domain
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 2
 | 
			
		||||
.Vb 4
 | 
			
		||||
\&        helo_address            - postfwd tries to look up the helo_name. use
 | 
			
		||||
\&                                  helo_address=!!(0.0.0.0/0) to check for unknown.
 | 
			
		||||
\&                                  Please do not use this for positive access control
 | 
			
		||||
\&                                  (whitelisting), as it might be forged.
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 2
 | 
			
		||||
.Vb 4
 | 
			
		||||
\&        sender_ns_names,        - postfwd tries to look up the names/ip addresses
 | 
			
		||||
\&        sender_ns_addrs           of the nameservers for the sender domain part.
 | 
			
		||||
\&                                  Please do not use this for positive access control
 | 
			
		||||
\&                                  (whitelisting), as it might be forged.
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 2
 | 
			
		||||
.Vb 4
 | 
			
		||||
\&        sender_mx_names,        - postfwd tries to look up the names/ip addresses
 | 
			
		||||
\&        sender_mx_addrs           of the mx records for the sender domain part.
 | 
			
		||||
\&                                  Please do not use this for positive access control
 | 
			
		||||
\&                                  (whitelisting), as it might be forged.
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 6
 | 
			
		||||
| 
						 | 
				
			
			@ -464,6 +475,12 @@ Any item can be negated by preceeding '!!' to it, e.g.:
 | 
			
		|||
\&        id=TLS001 ;  hostname=!!^secure\e.trust\e.local$ ;  action=REJECT only secure.trust.local please
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
or using the right compare operator:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 1
 | 
			
		||||
\&        id=USER01 ;  sasl_username !~ /^(bob|alice)$/ ;  action=REJECT who is that?
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
To avoid confusion with regexps or simply for better visibility you can use '!!(...)':
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 1
 | 
			
		||||
| 
						 | 
				
			
			@ -480,6 +497,122 @@ Request attributes can be compared by preceeding '$$' characters, e.g.:
 | 
			
		|||
.PP
 | 
			
		||||
This is only valid for \s-1PCRE\s0 values (see list above). The comparison will be performed as case insensitive exact match.
 | 
			
		||||
Use the '\-vv' option to debug.
 | 
			
		||||
.Sh "\s-1FILES\s0"
 | 
			
		||||
.IX Subsection "FILES"
 | 
			
		||||
Since postfwd1 v1.15 and postfwd2 v0.18 long item lists can be stored in separate files:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 1
 | 
			
		||||
\&        id=R001 ;  ccert_fingerprint==file:/etc/postfwd/wl_ccerts ;  action=DUNNO
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
postfwd will read a list of items (one item per line) from /etc/postfwd/wl_ccerts. comments are allowed:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 6
 | 
			
		||||
\&        # client1
 | 
			
		||||
\&        11:22:33:44:55:66:77:88:99
 | 
			
		||||
\&        # client2
 | 
			
		||||
\&        22:33:44:55:66:77:88:99:00
 | 
			
		||||
\&        # client3
 | 
			
		||||
\&        33:44:55:66:77:88:99:00:11
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
To use existing tables in key=value format, you can use:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 1
 | 
			
		||||
\&        id=R001 ;  ccert_fingerprint==table:/etc/postfwd/wl_ccerts ;  action=DUNNO
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
This will ignore the right-hand value. Items can be mixed:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 3
 | 
			
		||||
\&        id=R002 ;  action=REJECT \e
 | 
			
		||||
\&                client_name==unknown; \e
 | 
			
		||||
\&                client_name==file:/etc/postfwd/blacklisted
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
and for non pcre (comma separated) items:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 2
 | 
			
		||||
\&        id=R003 ;  action=REJECT \e
 | 
			
		||||
\&                client_address==10.1.1.1, file:/etc/postfwd/blacklisted
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 2
 | 
			
		||||
\&        id=R004 ;  action=REJECT \e
 | 
			
		||||
\&                rbl=myrbl.home.local, zen.spamhaus.org, file:/etc/postfwd/rbls_changing
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
You can check your configuration with the \-\-show_config option at the command line:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 1
 | 
			
		||||
\&        # postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
should give something like:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 1
 | 
			
		||||
\&        Rule   0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;194.123.86.10, =;186.4.6.12, =;192.168.2.1"
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
If a file can not be read, it will be ignored:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 3
 | 
			
		||||
\&        # postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
 | 
			
		||||
\&        [LOG warning]: error: file /etc/postfwd/wl_clients not found - file will be ignored ?
 | 
			
		||||
\&        Rule   0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;192.168.2.1"
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
File items are evaluated at configuration stage. Therefore postfwd needs to be reloaded if a file has changed.
 | 
			
		||||
.PP
 | 
			
		||||
If you want to specify a file, that will be reloaded for each request, you can use lfile: and ltable:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 1
 | 
			
		||||
\&        id=R001; client_address=lfile:/etc/postfwd/client_whitelist; action=dunno
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
This will check the modification time of /etc/postfwd/client_whitelist every time the rule is evaluated and reload it as
 | 
			
		||||
necessary. Of course this might increase the system load, so please use it with care.
 | 
			
		||||
.PP
 | 
			
		||||
The \-\-showconfig option illustrates the difference:
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 3
 | 
			
		||||
\&        ## evaluated at configuration stage
 | 
			
		||||
\&        # postfwd2 --nodaemon -L --rule='client_address=table:/etc/postfwd/clients; action=dunno' -C
 | 
			
		||||
\&        Rule   0: id->"R-0"; action->"dunno"; client_address->"=;1.1.1.1, =;1.1.1.2, =;1.1.1.3"
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 3
 | 
			
		||||
\&        ## evaluated for any rulehit
 | 
			
		||||
\&        # postfwd2 --nodaemon -L --rule='client_address=ltable:/etc/postfwd/clients; action=dunno' -C
 | 
			
		||||
\&        Rule   0: id->"R-0"; action->"dunno"; client_address->"=;ltable:/etc/postfwd/clients"
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
Files can refer to other files. The following is valid.
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 2
 | 
			
		||||
\&        -- FILE /etc/postfwd/rules.cf --
 | 
			
		||||
\&        id=R001; client_address=file:/etc/postfwd/clients_master.cf; action=DUNNO
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 4
 | 
			
		||||
\&        -- FILE /etc/postfwd/clients_master.cf --
 | 
			
		||||
\&        192.168.1.0/24
 | 
			
		||||
\&        file:/etc/postfwd/clients_east.cf
 | 
			
		||||
\&        file:/etc/postfwd/clients_west.cf
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 2
 | 
			
		||||
\&        -- FILE /etc/postfwd/clients_east.cf --
 | 
			
		||||
\&        192.168.2.0/24
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 2
 | 
			
		||||
\&        -- FILE /etc/postfwd/clients_west.cf --
 | 
			
		||||
\&        192.168.3.0/24
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
Remind that there is currently no loop detection (/a/file calls /a/file) and that this feature is only available
 | 
			
		||||
with postfwd1 v1.15 and postfwd2 v0.18 and higher.
 | 
			
		||||
.Sh "\s-1ACTIONS\s0"
 | 
			
		||||
.IX Subsection "ACTIONS"
 | 
			
		||||
\&\fIGeneral\fR
 | 
			
		||||
| 
						 | 
				
			
			@ -562,16 +695,27 @@ postfwd actions control the behaviour of the program. Currently you can specify
 | 
			
		|||
\&              action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 8
 | 
			
		||||
\&        rcpt (<item>/<max>/<time>/<action>)
 | 
			
		||||
\&        this command works similar to the rate() command with the difference, that the rate counter is
 | 
			
		||||
\&        increased by the request's recipient_count attribute. to do this reliably you should call postfwd
 | 
			
		||||
\&        from smtpd_data_restrictions or smtpd_end_of_data_restrictions. if you want to be sure, you could
 | 
			
		||||
\&        check it within the ruleset:
 | 
			
		||||
\&           # recipient count limit 3 per hour per client
 | 
			
		||||
\&           id=RCPT01 ;  state==END_OF_DATA ;  client_address==!!(10.1.1.1); \e
 | 
			
		||||
\&              action==rcpt($$client_address/3/3600/450 4.7.1 sorry, max 3 recipients per hour)
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 9
 | 
			
		||||
\&        ask (<addr>:<port>[:<ignore>])
 | 
			
		||||
\&        allows to delegate the policy decision to another policy service (e.g. postgrey). the first
 | 
			
		||||
\&        and the second argument (address and port) are mandatory. a third optional argument may be
 | 
			
		||||
\&        specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
 | 
			
		||||
\&           # example1: query postgrey and return it's answer to postfix
 | 
			
		||||
\&           id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031)
 | 
			
		||||
\&           id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031)
 | 
			
		||||
\&           # example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
 | 
			
		||||
\&           # and continue parsing postfwd's ruleset
 | 
			
		||||
\&           id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031:^dunno$)
 | 
			
		||||
\&           id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031:^dunno$)
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 3
 | 
			
		||||
| 
						 | 
				
			
			@ -962,6 +1106,12 @@ These parameters influence the way postfwd is working. Any of them can be combin
 | 
			
		|||
\&        significantly increase system load.
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
.Vb 3
 | 
			
		||||
\&        --config_timeout    (default=3)
 | 
			
		||||
\&        timeout in seconds to parse a single configuration line. if exceeded, the rule will
 | 
			
		||||
\&        be skipped. this is used to prevent problems due to large files or loops.
 | 
			
		||||
.Ve
 | 
			
		||||
.PP
 | 
			
		||||
\&\fIInformational arguments\fR
 | 
			
		||||
.PP
 | 
			
		||||
These arguments are for command line usage only. Never ever use them with postfix spawn!
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										456
									
								
								sbin/postfwd
									
										
									
									
									
								
							
							
						
						
									
										456
									
								
								sbin/postfwd
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -25,7 +25,7 @@ use vars qw(@ISA);
 | 
			
		|||
 | 
			
		||||
# Program constants
 | 
			
		||||
our($NAME) 			= 'postfwd';
 | 
			
		||||
our($VERSION)			= '1.14';
 | 
			
		||||
our($VERSION)			= '1.16';
 | 
			
		||||
 | 
			
		||||
# Networking options (use -i, -p and -R to change)
 | 
			
		||||
our($def_net_pid)		= "/var/run/".$NAME.".pid";
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +38,7 @@ our($def_net_group)		= "nobody";
 | 
			
		|||
our($def_dns_queuesize)		= "300";
 | 
			
		||||
our($def_dns_retries)		= "3";
 | 
			
		||||
our($def_dns_timeout)		= "14";
 | 
			
		||||
our($def_config_timeout)	= "3";
 | 
			
		||||
our($reply_maxlen)		= "512";
 | 
			
		||||
 | 
			
		||||
# change this, to match your POD requirements
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +50,7 @@ our($cmd_pager)			= "more";
 | 
			
		|||
 | 
			
		||||
# default action, do not change
 | 
			
		||||
# unless you really know why
 | 
			
		||||
our($default_action)		= "dunno";
 | 
			
		||||
our($default_action)		= "DUNNO";
 | 
			
		||||
 | 
			
		||||
# default maximum values for the score() command
 | 
			
		||||
# if exceeded, the specified action will be returned
 | 
			
		||||
| 
						 | 
				
			
			@ -113,6 +114,14 @@ our($COMP_RHSBL_KEY_CLIENT)	= "rhsbl_client";
 | 
			
		|||
our($COMP_RHSBL_KEY_SENDER)	= "rhsbl_sender";
 | 
			
		||||
our($COMP_RHSBL_KEY_RCLIENT)	= "rhsbl_reverse_client";
 | 
			
		||||
our($COMP_RHSBL_KEY_HELO)	= "rhsbl_helo";
 | 
			
		||||
# file items
 | 
			
		||||
our($COMP_CONF_FILE)            = 'cfile|file';
 | 
			
		||||
our($COMP_CONF_TABLE)           = 'ctable|table';
 | 
			
		||||
our($COMP_LIVE_FILE)            = 'lfile';
 | 
			
		||||
our($COMP_LIVE_TABLE)           = 'ltable';
 | 
			
		||||
our($COMP_TABLES)               = qr/^($COMP_CONF_TABLE|$COMP_LIVE_TABLE)$/i;
 | 
			
		||||
our($COMP_CONF_FILE_TABLE)      = qr/^($COMP_CONF_FILE|$COMP_CONF_TABLE):(.+)$/i;
 | 
			
		||||
our($COMP_LIVE_FILE_TABLE)      = qr/^($COMP_LIVE_FILE|$COMP_LIVE_TABLE):(.+)$/i;
 | 
			
		||||
# date checks
 | 
			
		||||
our($COMP_DATE)			= "date";
 | 
			
		||||
our($COMP_TIME)			= "time";
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +145,7 @@ our($COMP_VAR)			= "[\$][\$]";
 | 
			
		|||
# date calculations
 | 
			
		||||
our($COMP_DATECALC)		= "($COMP_DATE|$COMP_TIME|$COMP_DAYS|$COMP_MONTHS)";
 | 
			
		||||
# these items allow whitespace-or-comma-separated values
 | 
			
		||||
our($COMP_CSV)			= "($COMP_NETWORK_CIDRS|$COMP_RBL_KEY|$COMP_RHSBL_KEY|$COMP_RHSBL_KEY_CLIENT|$COMP_RHSBL_KEY_HELO|$COMP_RHSBL_KEY_SENDER|$COMP_RHSBL_KEY_RCLIENT|$COMP_DATECALC)";
 | 
			
		||||
our($COMP_CSV)                  = "($COMP_NETWORK_CIDRS|$COMP_RBL_KEY|$COMP_RHSBL_KEY|$COMP_RHSBL_KEY_CLIENT|$COMP_RHSBL_KEY_HELO|$COMP_RHSBL_KEY_SENDER|$COMP_RHSBL_KEY_RCLIENT|$COMP_DATECALC|$COMP_HELO_ADDR|$COMP_NS_ADDR|$COMP_MX_ADDR)";
 | 
			
		||||
# dont treat these as lists
 | 
			
		||||
our($COMP_SINGLE)		= "($COMP_ID|$COMP_ACTION|$COMP_SCORES|$COMP_RBL_CNT|$COMP_RHSBL_CNT)";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -148,6 +157,7 @@ our($syslog_options)		= "pid";
 | 
			
		|||
our($syslog_socktype)	 	= 'unix';
 | 
			
		||||
our($syslog_maxlen)	 	= 0;
 | 
			
		||||
our($syslog_safe)	 	= 0;
 | 
			
		||||
our($syslog_unsafe_charset)	= qr/[^\x20-\x7E]/;
 | 
			
		||||
if ( defined $Sys::Syslog::VERSION and $Sys::Syslog::VERSION ge '0.15' ) {
 | 
			
		||||
	# use 'native' when Sys::Syslog >= 0.15
 | 
			
		||||
	$syslog_socktype	=  'native';
 | 
			
		||||
| 
						 | 
				
			
			@ -203,7 +213,7 @@ use vars qw(
 | 
			
		|||
	$opt_norulelog $opt_summary $net_interface $net_port
 | 
			
		||||
	$net_user $net_group $net_chroot $net_pid $net_proto
 | 
			
		||||
	$opt_perfmon $opt_test $opt_verbose $opt_noidlestats
 | 
			
		||||
	$opt_cache_rdomain_only $opt_cache_no_size
 | 
			
		||||
	$opt_cache_rdomain_only $opt_cache_no_size $config_timeout
 | 
			
		||||
	$opt_cache_no_sender $opt_no_rulestats $opt_kill $opt_hup
 | 
			
		||||
	$opt_showconfig $opt_stdoutlog $opt_shortlog $dns_async_txt
 | 
			
		||||
	$DNS $Reload_Conf $dns_queuesize $dns_retries $dns_timeout
 | 
			
		||||
| 
						 | 
				
			
			@ -239,7 +249,7 @@ sub mylog {
 | 
			
		|||
sub mylogs {
 | 
			
		||||
    my($prio) = shift(@_);
 | 
			
		||||
    my($msg)  = shift(@_);
 | 
			
		||||
    $msg =~ s/\%/%%/g;
 | 
			
		||||
    $msg =~ s/\%/%%/g; $msg =~ s/$syslog_unsafe_charset/?/g;
 | 
			
		||||
    mylog $prio, $msg;
 | 
			
		||||
};
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			@ -486,96 +496,160 @@ sub devar_item {
 | 
			
		|||
# preparses configuration line for ACL syntax
 | 
			
		||||
#
 | 
			
		||||
sub acl_parser {
 | 
			
		||||
    my($myline) = @_;
 | 
			
		||||
    if ( $myline =~ /^\s*($COMP_ACL[\-\w]+)\s*{\s*(.*?)\s*;\s*}[\s;]*$/ ) {
 | 
			
		||||
	$ACLs{$1} = $2; $myline = "";
 | 
			
		||||
    } else {
 | 
			
		||||
	while ( $myline =~ /($COMP_ACL[\-\w]+)/) {
 | 
			
		||||
		my($acl)  = $1; $myline =~ s/\s*$acl\s*/$ACLs{$acl}/g if exists($ACLs{$acl});
 | 
			
		||||
	my($file,$num,$myline) = @_;
 | 
			
		||||
	if ( $myline =~ /^\s*($COMP_ACL[\-\w]+)\s*{\s*(.*?)\s*;\s*}[\s;]*$/ ) {
 | 
			
		||||
		$ACLs{$1} = $2; $myline = "";
 | 
			
		||||
	} else {
 | 
			
		||||
		while ( $myline =~ /($COMP_ACL[\-\w]+)/) {
 | 
			
		||||
			my($acl)  = $1;
 | 
			
		||||
			if ( $acl and defined $ACLs{$acl} ) {
 | 
			
		||||
				$myline =~ s/\s*$acl\s*/$ACLs{$acl}/g;
 | 
			
		||||
			} else {
 | 
			
		||||
				#return "action=note(undefined macro '$acl')";
 | 
			
		||||
				mylogs 'warning', "file $file, ignoring line $num: undefined macro '$acl'";
 | 
			
		||||
				return "";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
    };
 | 
			
		||||
    return $myline;
 | 
			
		||||
}
 | 
			
		||||
	return $myline;
 | 
			
		||||
};
 | 
			
		||||
#
 | 
			
		||||
# prepares pcre item
 | 
			
		||||
#
 | 
			
		||||
sub prepare_pcre {
 | 
			
		||||
	my($item) = shift; undef my $neg;
 | 
			
		||||
	# temporarily remove negation
 | 
			
		||||
	$item = $neg if ($neg = deneg_item($item));
 | 
			
		||||
	# allow // regex
 | 
			
		||||
	$item =~ s/^\/?(.*?)\/?$/$1/;
 | 
			
		||||
	# tested slow
 | 
			
		||||
	#$item = qr/$item/i;
 | 
			
		||||
	# re-enable negation
 | 
			
		||||
	$item = "!!($item)" if $neg;
 | 
			
		||||
	return $item;
 | 
			
		||||
};
 | 
			
		||||
#
 | 
			
		||||
# prepares file item
 | 
			
		||||
#
 | 
			
		||||
sub prepare_file {
 | 
			
		||||
	my($forced_reload,$type,$cmp,$file) = @_; my(@result) = (); undef my $fh;
 | 
			
		||||
	my($is_table) = ($type =~ /^$COMP_TABLES$/);
 | 
			
		||||
	unless (-e $file) {
 | 
			
		||||
		mylogs 'warning', "error: $type:$file not found - will be ignored";
 | 
			
		||||
		return @result;
 | 
			
		||||
	};
 | 
			
		||||
	if ( not($forced_reload) and (defined $Config_Cache{$file}{lastread}) and ($Config_Cache{$file}{lastread} > (stat $file)[9]) ) {
 | 
			
		||||
		mylogs  $syslog_priority, "$type:$file unchanged - using cached content (mtime: "
 | 
			
		||||
				.(stat $file)[9].", cache: $Config_Cache{$file}{lastread})"
 | 
			
		||||
                                if ($opt_verbose > 1);
 | 
			
		||||
		return  @{$Config_Cache{$file}{content}};
 | 
			
		||||
	};
 | 
			
		||||
	unless (open ($fh, "<$file")) {
 | 
			
		||||
		mylogs 'warning', "error: could not open $type:$file - $! - will be ignored";
 | 
			
		||||
		return @result;
 | 
			
		||||
	};
 | 
			
		||||
	mylogs $syslog_priority, "reading $type:$file" if ($opt_verbose > 1);
 | 
			
		||||
	while (<$fh>) {
 | 
			
		||||
		chomp;
 | 
			
		||||
		s/#.*//g;
 | 
			
		||||
		next if /^\s*$/;
 | 
			
		||||
		s/\s+[^\s]+$// if $is_table;
 | 
			
		||||
		s/^\s+//; s/\s+$//;
 | 
			
		||||
		push @result, prepare_item($forced_reload, $cmp, $_);
 | 
			
		||||
	}; close ($fh);
 | 
			
		||||
	# update Config_Cache
 | 
			
		||||
	$Config_Cache{$file}{lastread}   = time;
 | 
			
		||||
	@{$Config_Cache{$file}{content}} = @result;
 | 
			
		||||
	mylogs $syslog_priority, "read ".($#result + 1)." items from $type:$file" if ($opt_verbose > 1);
 | 
			
		||||
	return @result;
 | 
			
		||||
};
 | 
			
		||||
#
 | 
			
		||||
# prepares ruleset item
 | 
			
		||||
#
 | 
			
		||||
sub prepare_item {
 | 
			
		||||
	my($forced_reload,$cmp,$item) = @_; my(@result) = (); undef my $type;
 | 
			
		||||
	if ($item =~ /$COMP_CONF_FILE_TABLE/) {
 | 
			
		||||
		return prepare_file ($forced_reload, $1, $cmp, $2);
 | 
			
		||||
	} elsif ($cmp eq '=~' or $cmp eq '!~') {
 | 
			
		||||
		return $cmp.";".prepare_pcre($item);
 | 
			
		||||
	} else {
 | 
			
		||||
		return $cmp.";".$item;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
#
 | 
			
		||||
# parses configuration line
 | 
			
		||||
#
 | 
			
		||||
sub parse_config_line {
 | 
			
		||||
    my($mynum, $myindex, $myline) = @_;
 | 
			
		||||
    my(%myrule) = ();
 | 
			
		||||
    my($mykey, $myvalue, $mycomp, $neg);
 | 
			
		||||
 | 
			
		||||
    if ( $myline = acl_parser ($myline) ) {
 | 
			
		||||
	unless ( $myline =~ /^\s*[^=\s]+\s*$COMP_SEPARATOR\s*([^;\s]+\s*)+(;\s*[^=\s]+\s*$COMP_SEPARATOR\s*([^;\s]+\s*)+)*[;\s]*$/ ) {
 | 
			
		||||
		warn "warning: ignoring invalid line ".$mynum.": \"".$myline."\"";
 | 
			
		||||
	    } else {
 | 
			
		||||
		# separate items
 | 
			
		||||
		foreach (split ";", $myline) {
 | 
			
		||||
			# remove whitespaces around
 | 
			
		||||
			s/^\s*(.*?)\s*($COMP_SEPARATOR)\s*(.*?)\s*$/$1$2$3/;
 | 
			
		||||
			( ($mycomp = $2) =~ /^([\<\>\~])=$/ ) and $mycomp = "=$1";
 | 
			
		||||
			($mykey, $myvalue) = split /$COMP_SEPARATOR/, $_, 2;
 | 
			
		||||
			if ($mykey =~ /^$COMP_CSV$/) {
 | 
			
		||||
				$myvalue =~ s/\s*-\s*/-/g if ($mykey =~ /^$COMP_DATECALC$/);
 | 
			
		||||
				$myvalue =~ s/\s*,\s*/,/g;
 | 
			
		||||
				map { push ( @{$myrule{$mykey}}, $mycomp.";".$_ ) } ( split ",", $myvalue );
 | 
			
		||||
			} elsif ($mykey =~ /^$COMP_SINGLE$/) {
 | 
			
		||||
				mylogs "notice", "warning: Rule $myindex (line $mynum):"
 | 
			
		||||
					." overriding $mykey=\"".$myrule{$mykey}."\""
 | 
			
		||||
					." with $mykey=\"$myvalue\""
 | 
			
		||||
					if (defined $myrule{$mykey});
 | 
			
		||||
				$myrule{$mykey} = $myvalue;
 | 
			
		||||
			} else {
 | 
			
		||||
				if ( $mycomp eq '=~' or $mycomp eq '!~') {
 | 
			
		||||
					# temporarily remove negation
 | 
			
		||||
					$myvalue = $neg if ($neg = deneg_item($myvalue));
 | 
			
		||||
					# allow // regex
 | 
			
		||||
					$myvalue =~ s/^\/?(.*?)\/?$/$1/;
 | 
			
		||||
					# tested, slower
 | 
			
		||||
					#$myvalue = qr/$myvalue/i;
 | 
			
		||||
					# re-enable negation
 | 
			
		||||
					$myvalue = "!!($myvalue)" if $neg;
 | 
			
		||||
	my($forced_reload, $myfile, $mynum, $myindex, $myline) = @_;
 | 
			
		||||
	my(%myrule) = ();
 | 
			
		||||
	my($mykey, $myvalue, $mycomp);
 | 
			
		||||
	eval {
 | 
			
		||||
	    local $SIG{'__DIE__'};
 | 
			
		||||
	    local $SIG{'ALRM'}  = sub { $myline =~ s/[ \t][ \t]*/ /g; mylogs 'warning', "timeout after ".$config_timeout."s at parsing Rule $myindex ($myfile line $mynum): \"$myline\""; %myrule = (); die };
 | 
			
		||||
	    my $prevalert = alarm($config_timeout) if $config_timeout;
 | 
			
		||||
	    if ( $myline = acl_parser ($myfile, $mynum, $myline) ) {
 | 
			
		||||
		unless ( $myline =~ /^\s*[^=\s]+\s*$COMP_SEPARATOR\s*([^;\s]+\s*)+(;\s*[^=\s]+\s*$COMP_SEPARATOR\s*([^;\s]+\s*)+)*[;\s]*$/ ) {
 | 
			
		||||
			mylogs 'warning', "ignoring invalid $myfile line ".$mynum.": \"".$myline."\"";
 | 
			
		||||
		} else {
 | 
			
		||||
			# separate items
 | 
			
		||||
			foreach (split ";", $myline) {
 | 
			
		||||
				# remove whitespaces around
 | 
			
		||||
				s/^\s*(.*?)\s*($COMP_SEPARATOR)\s*(.*?)\s*$/$1$2$3/;
 | 
			
		||||
				( ($mycomp = $2) =~ /^([\<\>\~])=$/ ) and $mycomp = "=$1";
 | 
			
		||||
				($mykey, $myvalue) = split /$COMP_SEPARATOR/, $_, 2;
 | 
			
		||||
				if ($mykey =~ /^$COMP_SINGLE$/) {
 | 
			
		||||
					mylogs 'notice', "notice: Rule $myindex ($myfile line $mynum):"
 | 
			
		||||
						." overriding $mykey=\"".$myrule{$mykey}."\""
 | 
			
		||||
						." with $mykey=\"$myvalue\""
 | 
			
		||||
						if (defined $myrule{$mykey});
 | 
			
		||||
					$myrule{$mykey} = $myvalue;
 | 
			
		||||
				} elsif ($mykey =~ /^$COMP_CSV$/) {
 | 
			
		||||
					$myvalue =~ s/\s*,\s*/,/g;
 | 
			
		||||
					map { push @{$myrule{$mykey}}, prepare_item ($forced_reload, $mycomp, $_) } ( split /\s*,\s*/, $myvalue );
 | 
			
		||||
				} else {
 | 
			
		||||
					push @{$myrule{$mykey}}, prepare_item ($forced_reload, $mycomp, $myvalue);
 | 
			
		||||
				};
 | 
			
		||||
				push ( @{$myrule{$mykey}}, $mycomp.";".$myvalue );
 | 
			
		||||
			};
 | 
			
		||||
			unless (exists($myrule{$COMP_ACTION})) {
 | 
			
		||||
				mylogs 'warning', "Rule ".$myindex." ($myfile line ".$mynum."): contains no action and will be ignored";
 | 
			
		||||
				return (%myrule = ());
 | 
			
		||||
			};
 | 
			
		||||
			unless (exists($myrule{$COMP_ID})) {
 | 
			
		||||
				$myrule{$COMP_ID} = "R-".$myindex;
 | 
			
		||||
				 mylogs 'notice', "notice: Rule $myindex ($myfile line $mynum): contains no rule identifier - will use \"$myrule{id}\"" if $opt_verbose;
 | 
			
		||||
			};
 | 
			
		||||
			mylogs $syslog_priority, "loaded: Rule $myindex ($myfile line $mynum): id->\"$myrule{id}\" action->\"$myrule{action}\"" if $opt_verbose;
 | 
			
		||||
		};
 | 
			
		||||
		unless (exists($myrule{$COMP_ACTION})) {
 | 
			
		||||
			$myrule{$COMP_ACTION} = "WARN rule found but no action was defined";
 | 
			
		||||
			mylogs "notice", "warning: Rule ".$myindex." (line ".$mynum."): contains no action - default will be used";
 | 
			
		||||
		};
 | 
			
		||||
		unless (exists($myrule{$COMP_ID})) {
 | 
			
		||||
			$myrule{$COMP_ID} = "R-".$myindex;
 | 
			
		||||
			mylogs "notice", "notice: Rule $myindex (line $mynum): contains no rule identifier - will use \"$myrule{id}\"" if $opt_verbose;
 | 
			
		||||
		};
 | 
			
		||||
		mylogs $syslog_priority, "loaded: Rule $myindex (line $mynum): id->\"$myrule{id}\" action->\"$myrule{action}\"" if $opt_verbose;
 | 
			
		||||
	    };
 | 
			
		||||
	    alarm($prevalert) if $config_timeout;
 | 
			
		||||
	};
 | 
			
		||||
    };
 | 
			
		||||
    return %myrule;
 | 
			
		||||
}
 | 
			
		||||
	return %myrule;
 | 
			
		||||
};
 | 
			
		||||
#
 | 
			
		||||
# parses configuration file
 | 
			
		||||
#
 | 
			
		||||
sub read_config_file {
 | 
			
		||||
    my($myindex, $myfile) = @_;
 | 
			
		||||
    my($forced_reload, $myindex, $myfile) = @_;
 | 
			
		||||
    my(%myrule, @myruleset) = ();
 | 
			
		||||
    my($mybuffer) = "";
 | 
			
		||||
    my($mybuffer) = ""; undef my $fh;
 | 
			
		||||
 | 
			
		||||
    unless (-e $myfile) {
 | 
			
		||||
	warn "error: file ".$myfile." not found - file will be ignored";
 | 
			
		||||
    } else {
 | 
			
		||||
	unless (open (IN, "<$myfile")) {
 | 
			
		||||
		warn "error: could not open ".$myfile." - file will be ignored";
 | 
			
		||||
	unless (open ($fh, "<$myfile")) {
 | 
			
		||||
		warn "error: could not open ".$myfile." - $! - file will be ignored";
 | 
			
		||||
	} else {
 | 
			
		||||
		mylogs $syslog_priority, "reading file $myfile" if $opt_verbose;
 | 
			
		||||
		while (<IN>) {
 | 
			
		||||
		while (<$fh>) {
 | 
			
		||||
			chomp;
 | 
			
		||||
			s/(\"|#.*)//g;
 | 
			
		||||
			next if /^\s*$/;
 | 
			
		||||
			if (/(.*)\\\s*$/) { $mybuffer = $mybuffer.$1; next; };
 | 
			
		||||
			%myrule = parse_config_line ($., ($#myruleset+$myindex+1), $mybuffer.$_);
 | 
			
		||||
			%myrule = parse_config_line ($forced_reload, $myfile, $., ($#myruleset+$myindex+1), $mybuffer.$_);
 | 
			
		||||
			push ( @myruleset, { %myrule } ) if (%myrule);
 | 
			
		||||
			$mybuffer = "";
 | 
			
		||||
		};
 | 
			
		||||
		close (IN);
 | 
			
		||||
		close ($fh);
 | 
			
		||||
		mylogs $syslog_priority, "loaded: Rules $myindex - ".($myindex + $#myruleset)." from file \"$myfile\"" if $opt_verbose;
 | 
			
		||||
	};
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			@ -585,6 +659,7 @@ sub read_config_file {
 | 
			
		|||
# reads all configuration items
 | 
			
		||||
#
 | 
			
		||||
sub read_config {
 | 
			
		||||
    my($forced_reload) = shift;
 | 
			
		||||
    my(%myrule, @myruleset) = ();
 | 
			
		||||
    my($mytype,$myitem,$config);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -595,17 +670,17 @@ sub read_config {
 | 
			
		|||
    for $config (@Configs) {
 | 
			
		||||
	($mytype,$myitem) = split '::', $config;
 | 
			
		||||
	if ($mytype eq "r" or $mytype eq "rule") {
 | 
			
		||||
		%myrule = parse_config_line (0, ($#Rules + 1), $myitem);
 | 
			
		||||
		%myrule = parse_config_line ($forced_reload, 'RULE', 0, ($#Rules + 1), $myitem);
 | 
			
		||||
		push ( @Rules, { %myrule } ) if (%myrule);
 | 
			
		||||
	} elsif	($mytype eq "f" or $mytype eq "file") {
 | 
			
		||||
		if ( (defined $Config_Cache{$myitem}{lastread}) and ($Config_Cache{$myitem}{lastread} > (stat $myitem)[9]) ) {
 | 
			
		||||
		if ( not($forced_reload) and (defined $Config_Cache{$myitem}{lastread}) and ($Config_Cache{$myitem}{lastread} > (stat $myitem)[9]) ) {
 | 
			
		||||
			mylogs	$syslog_priority,
 | 
			
		||||
				"file \"$myitem\" unchanged - using cached ruleset (mtime: ".(stat $myitem)[9].",
 | 
			
		||||
				cache: $Config_Cache{$myitem}{lastread})"
 | 
			
		||||
				if $opt_verbose;
 | 
			
		||||
			push ( @Rules, @{$Config_Cache{$myitem}{ruleset}} );
 | 
			
		||||
		} else {
 | 
			
		||||
			@myruleset = read_config_file (($#Rules+1), $myitem);
 | 
			
		||||
			@myruleset = read_config_file ($forced_reload, ($#Rules+1), $myitem);
 | 
			
		||||
			if (@myruleset) {
 | 
			
		||||
				push ( @Rules, @myruleset );
 | 
			
		||||
				$Config_Cache{$myitem}{lastread} = time;
 | 
			
		||||
| 
						 | 
				
			
			@ -614,8 +689,12 @@ sub read_config {
 | 
			
		|||
		};
 | 
			
		||||
	};
 | 
			
		||||
    };
 | 
			
		||||
    # update Rule by ID hash
 | 
			
		||||
    map { $Rule_by_ID{$Rules[$_]{$COMP_ID}} = $_ } (0 .. $#Rules);
 | 
			
		||||
    if ($#Rules < 0) {
 | 
			
		||||
	mylogs 'warning', "critical: no rules found - i feel useless (have you set -f or -r?)";
 | 
			
		||||
    } else {
 | 
			
		||||
	# update Rule by ID hash
 | 
			
		||||
	map { $Rule_by_ID{$Rules[$_]{$COMP_ID}} = $_ } (0 .. $#Rules);
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
#
 | 
			
		||||
# displays configuration
 | 
			
		||||
| 
						 | 
				
			
			@ -931,7 +1010,7 @@ sub postfwd_items {
 | 
			
		|||
		my($myresult) = undef;
 | 
			
		||||
		mylogs $syslog_priority, "type numeric :  \"$myitem\"  \"$cmp\"  \"$val\"" if ($opt_verbose > 1);
 | 
			
		||||
		$myitem ||= "0"; $val ||= "0";
 | 
			
		||||
		if ( ($cmp eq '==') or ($cmp eq '=') ) {
 | 
			
		||||
		if ($cmp eq '==') {
 | 
			
		||||
			$myresult = ($myitem == $val);
 | 
			
		||||
		} elsif ($cmp eq '=<') {
 | 
			
		||||
			$myresult = ($myitem <= $val);
 | 
			
		||||
| 
						 | 
				
			
			@ -968,7 +1047,7 @@ sub postfwd_items {
 | 
			
		|||
		my($cmp,$val,$myitem,%request) = @_;
 | 
			
		||||
		my($myresult) = undef;
 | 
			
		||||
		my($imon) = (split (',', $myitem))[4]; $imon ||= 0;
 | 
			
		||||
		my($rmin,$rmax) = split ('-', $val);
 | 
			
		||||
		my($rmin,$rmax) = split (/\s*-\s*/, $val);
 | 
			
		||||
		$rmin = ($rmin) ? (($rmin =~ /^\d$/) ? $rmin : $months{$rmin}) : $imon;
 | 
			
		||||
		$rmax = ($rmax) ? (($rmax =~ /^\d$/) ? $rmax : $months{$rmax}) : (($val =~ /-/) ? $imon : $rmin);
 | 
			
		||||
		mylogs $syslog_priority, "type months :  \"$imon\"  \"$cmp\"  \"$rmin\"-\"$rmax\""
 | 
			
		||||
| 
						 | 
				
			
			@ -981,7 +1060,7 @@ sub postfwd_items {
 | 
			
		|||
		my($cmp,$val,$myitem,%request) = @_;
 | 
			
		||||
		my($myresult) = undef;
 | 
			
		||||
		my($iday) = (split (',', $myitem))[6]; $iday ||= 0;
 | 
			
		||||
		my($rmin,$rmax) = split ('-', $val);
 | 
			
		||||
		my($rmin,$rmax) = split (/\s*-\s*/, $val);
 | 
			
		||||
		$rmin = ($rmin) ? (($rmin =~ /^\d$/) ? $rmin : $weekdays{$rmin}) : $iday;
 | 
			
		||||
		$rmax = ($rmax) ? (($rmax =~ /^\d$/) ? $rmax : $weekdays{$rmax}) : (($val =~ /-/) ? $iday : $rmin);
 | 
			
		||||
		mylogs $syslog_priority, "type days :  \"$iday\"  \"$cmp\"  \"$rmin\"-\"$rmax\""
 | 
			
		||||
| 
						 | 
				
			
			@ -994,7 +1073,7 @@ sub postfwd_items {
 | 
			
		|||
		my($cmp,$val,$myitem,%request) = @_;
 | 
			
		||||
		my($myresult) = undef;
 | 
			
		||||
		my($isec,$imin,$ihour,$iday,$imon,$iyear) = split (',', $myitem);
 | 
			
		||||
		my($rmin,$rmax) = split ('-', $val);
 | 
			
		||||
		my($rmin,$rmax) = split (/\s*-\s*/, $val);
 | 
			
		||||
		my($idat) = ($iyear + 1900) . ((($imon+1) < 10) ? '0'.($imon+1) : ($imon+1)) . (($iday < 10) ? '0'.$iday : $iday);
 | 
			
		||||
		$rmin = ($rmin) ? join ('', reverse split ('\.', $rmin)) : $idat;
 | 
			
		||||
		$rmax = ($rmax) ? join ('', reverse split ('\.', $rmax)) : (($val =~ /-/) ? $idat : $rmin);
 | 
			
		||||
| 
						 | 
				
			
			@ -1008,7 +1087,7 @@ sub postfwd_items {
 | 
			
		|||
		my($cmp,$val,$myitem,%request) = @_;
 | 
			
		||||
		my($myresult) = undef;
 | 
			
		||||
		my($isec,$imin,$ihour,$iday,$imon,$iyear) = split (',', $myitem);
 | 
			
		||||
		my($rmin,$rmax) = split ('-', $val);
 | 
			
		||||
		my($rmin,$rmax) = split (/\s*-\s*/, $val);
 | 
			
		||||
		my($idat) = (($ihour < 10) ? '0'.$ihour : $ihour) . (($imin < 10) ? '0'.$imin : $imin) . (($isec < 10) ? '0'.$isec : $isec);
 | 
			
		||||
		$rmin = ($rmin) ? join ('', split ('\:', $rmin)) : $idat;
 | 
			
		||||
		$rmax = ($rmax) ? join ('', split ('\:', $rmax)) : (($val =~ /-/) ? $idat : $rmin);
 | 
			
		||||
| 
						 | 
				
			
			@ -1229,7 +1308,7 @@ sub postfwd_items {
 | 
			
		|||
					type 		=> $mycmd,
 | 
			
		||||
					maxcount	=> $ratecount,
 | 
			
		||||
					ttl		=> $ratetime,
 | 
			
		||||
					count		=> ( ($mycmd eq 'size') ? $request{size} : 1 ),
 | 
			
		||||
					count		=> ( ($mycmd eq 'size') ? $request{size} : (($mycmd eq 'rcpt') ? $request{recipient_count} : 1 ) ),
 | 
			
		||||
					time		=> $now,
 | 
			
		||||
					rule		=> $Rules[$index]{$COMP_ID},
 | 
			
		||||
					action		=> $ratecmd,
 | 
			
		||||
| 
						 | 
				
			
			@ -1246,6 +1325,8 @@ sub postfwd_items {
 | 
			
		|||
	},
 | 
			
		||||
	# size() command
 | 
			
		||||
	"size"	=> sub { return &{$postfwd_actions{rate}}(@_); },
 | 
			
		||||
	# rcpt() command
 | 
			
		||||
	"rcpt"	=> sub { return &{$postfwd_actions{rate}}(@_); },
 | 
			
		||||
	# wait() command
 | 
			
		||||
	"wait"	=> sub {
 | 
			
		||||
		my($index,$now,$mycmd,$myarg,$myline,%request) = @_;
 | 
			
		||||
| 
						 | 
				
			
			@ -1309,7 +1390,7 @@ sub postfwd_items {
 | 
			
		|||
					mylogs ('notice', "rule: $index got invalid answer '$sendstr' from $myarg");
 | 
			
		||||
				};
 | 
			
		||||
		} else {
 | 
			
		||||
			mylogs ('notice', "Could not open socket to '$myarg'");
 | 
			
		||||
			mylogs ('notice', "Could not open socket to '$myarg' - $!");
 | 
			
		||||
		};
 | 
			
		||||
		return ($stop,$index,$myaction,$myline,%request);
 | 
			
		||||
	},
 | 
			
		||||
| 
						 | 
				
			
			@ -1356,19 +1437,24 @@ sub get_plugins {
 | 
			
		|||
# use: compare_item ( $TYPE, $RULEITEM, $MINIMUMHITS, $REQUESTITEM, %REQUEST, %REQUESTINFO );
 | 
			
		||||
#
 | 
			
		||||
sub compare_item {
 | 
			
		||||
    my($mykey,$mymask,$mymin,$myitem, %request) = @_;
 | 
			
		||||
    my($mykey,$mymask,$mymin,$myitem,%request) = @_;
 | 
			
		||||
    my($val,$var,$cmp,$neg,$myresult,$postfwd_compare_proc);
 | 
			
		||||
    my($rcount) = 0;
 | 
			
		||||
    $mymin ||= 1;
 | 
			
		||||
 | 
			
		||||
    #
 | 
			
		||||
    # determine the right compare function
 | 
			
		||||
    $postfwd_compare_proc = (defined $postfwd_compare{$mykey}) ? $mykey : "default";
 | 
			
		||||
    #
 | 
			
		||||
    # save list due to possible modification
 | 
			
		||||
    my @items = @{$mymask};
 | 
			
		||||
    # now compare request to every single item
 | 
			
		||||
    ITEM: foreach (@{$mymask}) {
 | 
			
		||||
    ITEM: foreach (@items) {
 | 
			
		||||
	($cmp, $val) = split ";";
 | 
			
		||||
	next ITEM unless ($cmp and $val and $mykey);
 | 
			
		||||
	# prepare_file
 | 
			
		||||
	if ($val =~ /$COMP_LIVE_FILE_TABLE/) {
 | 
			
		||||
		push @items, prepare_file (0, $1, $cmp, $2);
 | 
			
		||||
		next ITEM;
 | 
			
		||||
	};
 | 
			
		||||
	mylogs $syslog_priority, "compare $mykey:  \"$myitem\"  \"$cmp\"  \"$val\"" if ($opt_verbose > 1);
 | 
			
		||||
	$val = $neg if ($neg = deneg_item($val));
 | 
			
		||||
	mylogs $syslog_priority, "deneg $mykey:  \"$myitem\"  \"$cmp\"  \"$val\"" if ($neg and ($opt_verbose > 1));
 | 
			
		||||
| 
						 | 
				
			
			@ -1695,7 +1781,7 @@ sub smtpd_access_policy {
 | 
			
		|||
    if ( $Reload_Conf ) {
 | 
			
		||||
	undef $Reload_Conf;
 | 
			
		||||
	show_stats;
 | 
			
		||||
	read_config;
 | 
			
		||||
	read_config(1);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    # clear dnsbl timeout counters
 | 
			
		||||
| 
						 | 
				
			
			@ -1724,7 +1810,8 @@ sub smtpd_access_policy {
 | 
			
		|||
	next RATES unless ( $request{$checkreq} and (defined $Rates{$request{$checkreq}}) );
 | 
			
		||||
	if ( ($now - $Rates{$request{$checkreq}}{"time"}) > $Rates{$request{$checkreq}}{ttl} ) {
 | 
			
		||||
		# renew rate
 | 
			
		||||
		$Rates{$request{$checkreq}}{count} = ( ($Rates{$request{$checkreq}}{type} eq 'size') ? $request{size} : 1 );
 | 
			
		||||
		$Rates{$request{$checkreq}}{count} = ( ($Rates{$request{$checkreq}}{type} eq 'size') ? $request{size} :
 | 
			
		||||
			(($Rates{$request{$checkreq}}{type} eq 'rcpt') ? $request{recipient_count} : 1 ) );
 | 
			
		||||
		$Rates{$request{$checkreq}}{"time"} = $now;
 | 
			
		||||
		mylogs $syslog_priority, "[RATE] renewing rate object ".$request{$checkreq}
 | 
			
		||||
			." [type: ".$Rates{$request{$checkreq}}{type}
 | 
			
		||||
| 
						 | 
				
			
			@ -1733,7 +1820,8 @@ sub smtpd_access_policy {
 | 
			
		|||
			if ($opt_verbose > 1);
 | 
			
		||||
	} else {
 | 
			
		||||
		# increase rate
 | 
			
		||||
		$Rates{$request{$checkreq}}{count} += ( ($Rates{$request{$checkreq}}{type} eq 'size') ? $request{size} : 1 );
 | 
			
		||||
		$Rates{$request{$checkreq}}{count} += ( ($Rates{$request{$checkreq}}{type} eq 'size') ? $request{size} :
 | 
			
		||||
			(($Rates{$request{$checkreq}}{type} eq 'rcpt') ? $request{recipient_count} : 1 ) );
 | 
			
		||||
		mylogs $syslog_priority, "[RATE] increasing rate object ".$request{$checkreq}
 | 
			
		||||
			." to ".$Rates{$request{$checkreq}}{count}
 | 
			
		||||
			." [type: ".$Rates{$request{$checkreq}}{type}
 | 
			
		||||
| 
						 | 
				
			
			@ -1829,10 +1917,10 @@ sub smtpd_access_policy {
 | 
			
		|||
    } else {
 | 
			
		||||
 | 
			
		||||
	# refresh config if '-I' was set
 | 
			
		||||
	read_config if $opt_instantconfig;
 | 
			
		||||
	read_config(0) if $opt_instantconfig;
 | 
			
		||||
 | 
			
		||||
	if ($#Rules < 0) {
 | 
			
		||||
		warn "critical: no rules found - i feel useless (have you set -f or -r?)";
 | 
			
		||||
		mylogs 'warning', "critical: no rules found - i feel useless (have you set -f or -r?)";
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1918,6 +2006,33 @@ sub smtpd_access_policy {
 | 
			
		|||
    return $myaction;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
# process delegation protocol input
 | 
			
		||||
sub process_input {
 | 
			
		||||
        my($client,$msg,$attr) = @_;
 | 
			
		||||
        # remember argument=value
 | 
			
		||||
        if ( $msg =~ /^([^=]{1,512})=(.{0,512})/ ) {
 | 
			
		||||
                $$attr{$1} = $2;
 | 
			
		||||
        # evaluate request
 | 
			
		||||
        } elsif ( $msg eq '' ) {
 | 
			
		||||
                map { mylogs $syslog_priority, "Attribute: $_=$$attr{$_}" } (keys %$attr) if ($opt_verbose > 1);
 | 
			
		||||
                unless ( (defined $$attr{request}) and ($$attr{request} eq "smtpd_access_policy") ) {
 | 
			
		||||
                        mylogs 'warning', "Ignoring unrecognized request type: '".((defined $$attr{request}) ? substr($$attr{request},0,100) : '')."'";
 | 
			
		||||
                } else {
 | 
			
		||||
                        my $action = smtpd_access_policy(%$attr) || $default_action;
 | 
			
		||||
                        mylogs $syslog_priority, "Action: $action" if ($opt_verbose > 1);
 | 
			
		||||
                        if ($client) {
 | 
			
		||||
                                print $client ("action=$action\n\n");
 | 
			
		||||
                        } else {
 | 
			
		||||
                                print STDOUT ("action=$action\n\n");
 | 
			
		||||
                        };
 | 
			
		||||
                        %$attr = ();
 | 
			
		||||
                };
 | 
			
		||||
        # unknown command
 | 
			
		||||
        } else {
 | 
			
		||||
                mylogs 'warning', "Ignoring garbage '".substr($msg, 0, 100)."'";
 | 
			
		||||
        };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
####  MAIN  ####
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1970,6 +2085,7 @@ GetOptions (	"term|kill|stop|k"	 => \$opt_kill,
 | 
			
		|||
		'noidlestats'		 => \$opt_noidlestats,
 | 
			
		||||
		'no-idlestats'		 => \$opt_noidlestats,
 | 
			
		||||
		's|scores=s'		 => \%opt_scores,
 | 
			
		||||
		'config_timeout=i'	 => \$config_timeout,
 | 
			
		||||
		'f|file=s'		 => sub{ my($opt,$value) = @_; push (@Configs, $opt.'::'.$value) },
 | 
			
		||||
		'r|rule=s'		 => sub{ my($opt,$value) = @_; push (@Configs, $opt.'::'.$value) },
 | 
			
		||||
		'plugins=s'	 	=> \@Plugins,
 | 
			
		||||
| 
						 | 
				
			
			@ -1983,6 +2099,7 @@ GetOptions (	"term|kill|stop|k"	 => \$opt_kill,
 | 
			
		|||
) or pod2usage (-msg => "\nPlease see \"".$NAME." -m\" for detailed instructions.\n", -verbose => 1);
 | 
			
		||||
 | 
			
		||||
$opt_verbose = 0 unless $opt_verbose;
 | 
			
		||||
$opt_stdoutlog = 1 if ($opt_kill or $opt_hup or $opt_showconfig);
 | 
			
		||||
 | 
			
		||||
# terminate at -k or --kill
 | 
			
		||||
if ($opt_kill) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2002,7 +2119,7 @@ openlog $syslog_name, $syslog_options, $syslog_facility;
 | 
			
		|||
mylogs "notice", $NAME." ".$VERSION." starting" if $opt_daemon;
 | 
			
		||||
 | 
			
		||||
# read configuration
 | 
			
		||||
read_config;
 | 
			
		||||
read_config(1);
 | 
			
		||||
if ($opt_showconfig) {
 | 
			
		||||
	show_config;
 | 
			
		||||
	exit 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -2041,6 +2158,7 @@ $net_pid	||= $def_net_pid;
 | 
			
		|||
$dns_queuesize	||= $def_dns_queuesize;
 | 
			
		||||
$dns_retries	||= $def_dns_retries;
 | 
			
		||||
$dns_timeout	||= $def_dns_timeout;
 | 
			
		||||
$config_timeout ||= $def_config_timeout;
 | 
			
		||||
$syslog_name	||= $NAME;
 | 
			
		||||
$net_interface	= ( $net_interface	=~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/ )	? $1 : $def_net_interface;
 | 
			
		||||
$net_port 	= ( $net_port		=~ /^(\d+|[-\|\@\/\w. ]+)$/ )			? $1 : $def_net_port;
 | 
			
		||||
| 
						 | 
				
			
			@ -2053,6 +2171,7 @@ $dns_queuesize	= ( $dns_queuesize	=~ /^(\d+)$/ )					? $1 : $dns_queuesize;
 | 
			
		|||
$dns_retries	= ( $dns_retries	=~ /^(\d+)$/ )					? $1 : $dns_retries;
 | 
			
		||||
$dns_timeout	= ( $dns_timeout	=~ /^(\d+)$/ )					? $1 : $dns_timeout;
 | 
			
		||||
$syslog_name	= ( $syslog_name	=~ /^(.+)$/ )					? $1 : $NAME;
 | 
			
		||||
$config_timeout	= ( $config_timeout	=~ /^(\d+)$/ )					? $1 : $def_config_timeout;
 | 
			
		||||
 | 
			
		||||
# Unbuffer standard output.
 | 
			
		||||
select((select(STDOUT), $| = 1)[0]);
 | 
			
		||||
| 
						 | 
				
			
			@ -2144,26 +2263,7 @@ if ($opt_daemon) {
 | 
			
		|||
			# check request line and print output
 | 
			
		||||
			next unless defined $1;
 | 
			
		||||
			$request = $1;
 | 
			
		||||
			if ($request =~ /([^=]+)=(.*)/) {
 | 
			
		||||
				$myattr{substr($1, 0, 512)} = substr($2, 0, 512);
 | 
			
		||||
			} elsif ($request eq '') {
 | 
			
		||||
				if ($opt_verbose > 1) {
 | 
			
		||||
				    for (keys %myattr) {
 | 
			
		||||
					mylogs $syslog_priority, "Client: $client  Attribute: $_=$myattr{$_}";
 | 
			
		||||
				    };
 | 
			
		||||
				};
 | 
			
		||||
				unless ( (defined $myattr{request}) and ($myattr{request} eq "smtpd_access_policy") ) {
 | 
			
		||||
					warn "ignoring unrecognized request type: '".($myattr{request} || '')."'";
 | 
			
		||||
				} else {
 | 
			
		||||
					my($action) = substr ( smtpd_access_policy(%myattr), 0, $reply_maxlen ) if $reply_maxlen;
 | 
			
		||||
					mylogs $syslog_priority, "Client: $client  Action: $action" if $opt_verbose;
 | 
			
		||||
					print $client "action=$action\n\n";
 | 
			
		||||
					$Counter_Requests++; $Counter_Interval++;
 | 
			
		||||
				};
 | 
			
		||||
			} else {
 | 
			
		||||
				chop $request if $request;
 | 
			
		||||
				warn "error: ignoring garbage".( ($opt_verbose) ? " from $client" : "")." \"".$request."\"";
 | 
			
		||||
			};
 | 
			
		||||
			process_input ($client, $request, \%myattr);
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2178,26 +2278,7 @@ if ($opt_daemon) {
 | 
			
		|||
		s/^([^\r\n]*)\r?\n//;
 | 
			
		||||
		next unless defined $1;
 | 
			
		||||
		$request = $1;
 | 
			
		||||
		if ($request =~ /([^=]+)=(.*)/) {
 | 
			
		||||
			$myattr{substr($1, 0, 512)} = substr($2, 0, 512);
 | 
			
		||||
		} elsif ($request eq '') {
 | 
			
		||||
			if ($opt_verbose > 1) {
 | 
			
		||||
				for (keys %myattr) {
 | 
			
		||||
					mylogs $syslog_priority, "Attribute: $_=$myattr{$_}";
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
			unless ( (defined $myattr{request}) and ($myattr{request} eq "smtpd_access_policy") ) {
 | 
			
		||||
				warn "ignoring unrecognized request type: '".($myattr{request} || '')."'";
 | 
			
		||||
			} else {
 | 
			
		||||
				my($action) = substr ( smtpd_access_policy(%myattr), 0, $reply_maxlen ) if $reply_maxlen;
 | 
			
		||||
				mylogs $syslog_priority, "Action: $action" if $opt_verbose;
 | 
			
		||||
				myprint "action=$action\n\n";
 | 
			
		||||
				$Counter_Requests++; $Counter_Interval++;
 | 
			
		||||
			};
 | 
			
		||||
		} else {
 | 
			
		||||
			chop $request if $request;
 | 
			
		||||
			warn "error: ignoring garbage \"".$request."\"";
 | 
			
		||||
		};
 | 
			
		||||
		process_input (undef, $request, \%myattr);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	# finishing
 | 
			
		||||
| 
						 | 
				
			
			@ -2264,6 +2345,7 @@ postfwd [OPTIONS] [SOURCE1, SOURCE2, ...]
 | 
			
		|||
	    --dns_max_ns_lookups    max names to look up with sender_ns_addrs
 | 
			
		||||
	    --dns_max_mx_lookups    max names to look up with sender_mx_addrs
 | 
			
		||||
	-I, --instantcfg            re-reads rulefiles for every new request
 | 
			
		||||
	    --config_timeout <i>	parser timeout in seconds
 | 
			
		||||
 | 
			
		||||
	Informational (use only at command-line!):
 | 
			
		||||
	-C, --showconfig            shows ruleset summary, -v for verbose
 | 
			
		||||
| 
						 | 
				
			
			@ -2402,12 +2484,18 @@ Rules can span multiple lines by adding a trailing backslash "\" character:
 | 
			
		|||
 | 
			
		||||
	helo_address		- postfwd tries to look up the helo_name. use
 | 
			
		||||
				  helo_address=!!(0.0.0.0/0) to check for unknown.
 | 
			
		||||
				  Please do not use this for positive access control
 | 
			
		||||
				  (whitelisting), as it might be forged.
 | 
			
		||||
 | 
			
		||||
	sender_ns_names,	- postfwd tries to look up the names/ip addresses
 | 
			
		||||
	sender_ns_addrs		  of the nameservers for the sender domain part.
 | 
			
		||||
				  Please do not use this for positive access control
 | 
			
		||||
				  (whitelisting), as it might be forged.
 | 
			
		||||
 | 
			
		||||
	sender_mx_names,	- postfwd tries to look up the names/ip addresses
 | 
			
		||||
	sender_mx_addrs		  of the mx records for the sender domain part.
 | 
			
		||||
				  Please do not use this for positive access control
 | 
			
		||||
				  (whitelisting), as it might be forged.
 | 
			
		||||
 | 
			
		||||
	version			- postfwd version, contains "postfwd n.nn"
 | 
			
		||||
				  this enables version based checks in your rulesets
 | 
			
		||||
| 
						 | 
				
			
			@ -2494,6 +2582,10 @@ Any item can be negated by preceeding '!!' to it, e.g.:
 | 
			
		|||
 | 
			
		||||
	id=TLS001 ;  hostname=!!^secure\.trust\.local$ ;  action=REJECT only secure.trust.local please
 | 
			
		||||
 | 
			
		||||
or using the right compare operator:
 | 
			
		||||
 | 
			
		||||
	id=USER01 ;  sasl_username !~ /^(bob|alice)$/ ;  action=REJECT who is that?
 | 
			
		||||
 | 
			
		||||
To avoid confusion with regexps or simply for better visibility you can use '!!(...)':
 | 
			
		||||
 | 
			
		||||
	id=USER01 ;  sasl_username=!!( (bob|alice) )  ;  action=REJECT who is that?
 | 
			
		||||
| 
						 | 
				
			
			@ -2508,6 +2600,92 @@ This is only valid for PCRE values (see list above). The comparison will be perf
 | 
			
		|||
Use the '-vv' option to debug.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=head2 FILES
 | 
			
		||||
 | 
			
		||||
Since postfwd1 v1.15 and postfwd2 v0.18 long item lists can be stored in separate files:
 | 
			
		||||
 | 
			
		||||
	id=R001 ;  ccert_fingerprint==file:/etc/postfwd/wl_ccerts ;  action=DUNNO
 | 
			
		||||
 | 
			
		||||
postfwd will read a list of items (one item per line) from /etc/postfwd/wl_ccerts. comments are allowed:
 | 
			
		||||
 | 
			
		||||
	# client1
 | 
			
		||||
	11:22:33:44:55:66:77:88:99
 | 
			
		||||
	# client2
 | 
			
		||||
	22:33:44:55:66:77:88:99:00
 | 
			
		||||
	# client3
 | 
			
		||||
	33:44:55:66:77:88:99:00:11
 | 
			
		||||
 | 
			
		||||
To use existing tables in key=value format, you can use:
 | 
			
		||||
 | 
			
		||||
	id=R001 ;  ccert_fingerprint==table:/etc/postfwd/wl_ccerts ;  action=DUNNO
 | 
			
		||||
 | 
			
		||||
This will ignore the right-hand value. Items can be mixed:
 | 
			
		||||
 | 
			
		||||
	id=R002 ;  action=REJECT \
 | 
			
		||||
		client_name==unknown; \
 | 
			
		||||
		client_name==file:/etc/postfwd/blacklisted
 | 
			
		||||
 | 
			
		||||
and for non pcre (comma separated) items:
 | 
			
		||||
 | 
			
		||||
	id=R003 ;  action=REJECT \
 | 
			
		||||
		client_address==10.1.1.1, file:/etc/postfwd/blacklisted
 | 
			
		||||
 | 
			
		||||
	id=R004 ;  action=REJECT \
 | 
			
		||||
		rbl=myrbl.home.local, zen.spamhaus.org, file:/etc/postfwd/rbls_changing
 | 
			
		||||
 | 
			
		||||
You can check your configuration with the --show_config option at the command line:
 | 
			
		||||
 | 
			
		||||
	# postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
 | 
			
		||||
 | 
			
		||||
should give something like:
 | 
			
		||||
 | 
			
		||||
	Rule   0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;194.123.86.10, =;186.4.6.12, =;192.168.2.1"
 | 
			
		||||
 | 
			
		||||
If a file can not be read, it will be ignored:
 | 
			
		||||
 | 
			
		||||
	# postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
 | 
			
		||||
	[LOG warning]: error: file /etc/postfwd/wl_clients not found - file will be ignored ?
 | 
			
		||||
	Rule   0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;192.168.2.1"
 | 
			
		||||
 | 
			
		||||
File items are evaluated at configuration stage. Therefore postfwd needs to be reloaded if a file has changed.
 | 
			
		||||
 | 
			
		||||
If you want to specify a file, that will be reloaded for each request, you can use lfile: and ltable:
 | 
			
		||||
 | 
			
		||||
	id=R001; client_address=lfile:/etc/postfwd/client_whitelist; action=dunno
 | 
			
		||||
 | 
			
		||||
This will check the modification time of /etc/postfwd/client_whitelist every time the rule is evaluated and reload it as
 | 
			
		||||
necessary. Of course this might increase the system load, so please use it with care.
 | 
			
		||||
 | 
			
		||||
The --showconfig option illustrates the difference:
 | 
			
		||||
 | 
			
		||||
	## evaluated at configuration stage
 | 
			
		||||
	# postfwd2 --nodaemon -L --rule='client_address=table:/etc/postfwd/clients; action=dunno' -C
 | 
			
		||||
	Rule   0: id->"R-0"; action->"dunno"; client_address->"=;1.1.1.1, =;1.1.1.2, =;1.1.1.3"
 | 
			
		||||
 | 
			
		||||
	## evaluated for any rulehit
 | 
			
		||||
	# postfwd2 --nodaemon -L --rule='client_address=ltable:/etc/postfwd/clients; action=dunno' -C
 | 
			
		||||
	Rule   0: id->"R-0"; action->"dunno"; client_address->"=;ltable:/etc/postfwd/clients"
 | 
			
		||||
 | 
			
		||||
Files can refer to other files. The following is valid.
 | 
			
		||||
 | 
			
		||||
	-- FILE /etc/postfwd/rules.cf --
 | 
			
		||||
	id=R001; client_address=file:/etc/postfwd/clients_master.cf; action=DUNNO
 | 
			
		||||
 | 
			
		||||
	-- FILE /etc/postfwd/clients_master.cf --
 | 
			
		||||
	192.168.1.0/24
 | 
			
		||||
	file:/etc/postfwd/clients_east.cf
 | 
			
		||||
	file:/etc/postfwd/clients_west.cf
 | 
			
		||||
 | 
			
		||||
	-- FILE /etc/postfwd/clients_east.cf --
 | 
			
		||||
	192.168.2.0/24
 | 
			
		||||
 | 
			
		||||
	-- FILE /etc/postfwd/clients_west.cf --
 | 
			
		||||
	192.168.3.0/24
 | 
			
		||||
 | 
			
		||||
Remind that there is currently no loop detection (/a/file calls /a/file) and that this feature is only available
 | 
			
		||||
with postfwd1 v1.15 and postfwd2 v0.18 and higher.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=head2 ACTIONS
 | 
			
		||||
 | 
			
		||||
I<General>
 | 
			
		||||
| 
						 | 
				
			
			@ -2576,15 +2754,24 @@ postfwd actions control the behaviour of the program. Currently you can specify
 | 
			
		|||
	   id=SIZE01 ;  state==END_OF_DATA ;  client_address==!!(10.1.1.1); \
 | 
			
		||||
	      action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)
 | 
			
		||||
 | 
			
		||||
	rcpt (<item>/<max>/<time>/<action>)
 | 
			
		||||
	this command works similar to the rate() command with the difference, that the rate counter is
 | 
			
		||||
	increased by the request's recipient_count attribute. to do this reliably you should call postfwd
 | 
			
		||||
	from smtpd_data_restrictions or smtpd_end_of_data_restrictions. if you want to be sure, you could
 | 
			
		||||
	check it within the ruleset:
 | 
			
		||||
	   # recipient count limit 3 per hour per client
 | 
			
		||||
	   id=RCPT01 ;  state==END_OF_DATA ;  client_address==!!(10.1.1.1); \
 | 
			
		||||
	      action==rcpt($$client_address/3/3600/450 4.7.1 sorry, max 3 recipients per hour)
 | 
			
		||||
 | 
			
		||||
	ask (<addr>:<port>[:<ignore>])
 | 
			
		||||
	allows to delegate the policy decision to another policy service (e.g. postgrey). the first
 | 
			
		||||
	and the second argument (address and port) are mandatory. a third optional argument may be
 | 
			
		||||
	specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
 | 
			
		||||
	   # example1: query postgrey and return it's answer to postfix
 | 
			
		||||
	   id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031)
 | 
			
		||||
	   id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031)
 | 
			
		||||
	   # example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
 | 
			
		||||
	   # and continue parsing postfwd's ruleset
 | 
			
		||||
	   id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031:^dunno$)
 | 
			
		||||
	   id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031:^dunno$)
 | 
			
		||||
 | 
			
		||||
	wait (<delay>)
 | 
			
		||||
	pauses the program execution for <delay> seconds. use this for
 | 
			
		||||
| 
						 | 
				
			
			@ -2876,6 +3063,11 @@ These parameters influence the way postfwd is working. Any of them can be combin
 | 
			
		|||
	(which means their access times changed since last read) this might
 | 
			
		||||
	significantly increase system load.
 | 
			
		||||
 | 
			
		||||
	--config_timeout    (default=3)
 | 
			
		||||
	timeout in seconds to parse a single configuration line. if exceeded, the rule will
 | 
			
		||||
	be skipped. this is used to prevent problems due to large files or loops.
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
I<Informational arguments>
 | 
			
		||||
 | 
			
		||||
These arguments are for command line usage only. Never ever use them with postfix spawn!
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue