]> git.pld-linux.org Git - packages/postfix.git/blob - postfix-ns-mx-acl.patch
- ``unofficial patch for Postfix 2.0 to black-list domain names
[packages/postfix.git] / postfix-ns-mx-acl.patch
1 This is an unofficial patch for Postfix stable release 2.0 that
2 back-ports a new feature from the after-2.0 snapshot releases.
3
4 This feature can be used to block mail from so-called spammer
5 havens, from sender addresses that resolve to Verisign's wild-card
6 mail responder, or from domains that claim to have mail servers in
7 reserved networks such as 127.0.0.1.
8
9 This patch is a revised version; the first implementation of the
10 check_{helo,sender,recipient}_ns_access feature did not correctly
11 look up name server records and caused mail to be deferred with a
12 450 status code; the second revision no longer defers mail when
13 some NS or MX host lookup fails.
14
15 -----------------
16
17 The check_{helo,sender,recipient}_{ns,mx}_access maptype:mapname
18 restriction applies the specified access table to the NS or MX
19 hosts of the host/domain given in HELO, EHLO, MAIL FROM or RCPT TO
20 commands.
21
22     /etc/postfix/main.cf:
23         smtpd_mumble_restrictions =
24             ...
25             reject_unknown_sender_domain
26             check_sender_mx_access hash:/etc/postfix/mx_access
27             ...
28
29     /etc/postfix/mx_access:
30         spammer.haven.tld reject spammer mx host
31         64.94.110.11      reject mail server in verisign wild-card domain
32         127               reject mail server in loopback network
33         0                 reject mail server in broadcast network
34
35 The following entries block mail from domains that claim to have
36 have mail servers or domain servers in reserved networks.
37
38         10                reject mail server in RFC 1918 private network
39         169.254           reject mail server in link local network
40         172.16            reject mail server in RFC 1918 private network
41         ...similar entries for 172.17...172.31
42         192.0.2           reject mail server in TEST-NET network
43         192.168           reject mail server in RFC 1918 private network
44         224               reject mail server in class D multicast network
45         ...similar entries for 225...239
46         240               reject mail server in class E reserved network
47         ...similar entries for 241...247
48         248               reject mail server in reserved network
49         ...similar entries for 249...255
50
51 diff -cr /tmp/postfix-2.0.16/conf/sample-smtpd.cf ./conf/sample-smtpd.cf
52 *** /tmp/postfix-2.0.16/conf/sample-smtpd.cf    Tue Aug 12 12:28:46 2003
53 --- ./conf/sample-smtpd.cf      Fri Sep 19 11:17:34 2003
54 ***************
55 *** 311,316 ****
56 --- 311,321 ----
57   #   check_helo_access maptype:mapname
58   #     look up HELO hostname or parent domains.
59   #     see access(5) for possible lookup results.
60 + #   check_helo_mx_access maptype:mapname
61 + #   check_helo_ns_access maptype:mapname
62 + #     look up the helo hostname MX hosts (or name servers) and apply the
63 + #     specified access table to those hosts.
64 + #     Note: the OK result is not allowed here for security reasons.
65   #   reject: reject the request. Place this at the end of a restriction.
66   #   permit: permit the request. Place this at the end of a restriction.
67   #   warn_if_reject: next restriction logs a warning instead of rejecting.
68 ***************
69 *** 343,348 ****
70 --- 348,358 ----
71   #   check_sender_access maptype:mapname
72   #     look up sender address, parent domain, or localpart@.
73   #     see access(5) for possible lookup results.
74 + #   check_sender_mx_access maptype:mapname
75 + #   check_sender_ns_access maptype:mapname
76 + #     look up sender address MX hosts (or name servers) and apply the
77 + #     specified access table to those hosts.
78 + #     Note: the OK result is not allowed here for security reasons.
79   #   reject_sender_login_mismatch: reject if $smtpd_sender_login_maps specifies
80   #     a MAIL FROM address owner, but the client is not (SASL) logged in as
81   #     that MAIL FROM address owner; or if the client is (SASL) logged in, but
82 ***************
83 *** 409,414 ****
84 --- 419,429 ----
85   #   check_recipient_access maptype:mapname
86   #     look up recipient address, parent domain, or localpart@.
87   #     see access(5) for possible lookup results.
88 + #   check_recipient_mx_access maptype:mapname
89 + #   check_recipient_ns_access maptype:mapname
90 + #     look up the recipient address MX hosts (or name servers) and apply the
91 + #     specified access table to those hosts.
92 + #     Note: the OK result is not allowed here for security reasons.
93   #   reject_non_fqdn_recipient: reject recipient address that is not in FQDN form
94   #   reject: reject the request. Place this at the end of a restriction.
95   #   permit: permit the request. Place this at the end of a restriction.
96 diff -cr /tmp/postfix-2.0.16/html/uce.html ./html/uce.html
97 *** /tmp/postfix-2.0.16/html/uce.html   Mon Aug 25 09:54:11 2003
98 --- ./html/uce.html     Fri Sep 19 11:17:34 2003
99 ***************
100 *** 567,572 ****
101 --- 567,588 ----
102   
103   <p>
104   
105 + <a name="check_helo_ns_access">
106
107 + <dt> <b>check_helo_ns_access</b> <i>maptype</i>:<i>mapname</i>
108
109 + <a name="check_helo_mx_access">
110
111 + <dt> <b>check_helo_mx_access</b> <i>maptype</i>:<i>mapname</i>
112
113 + <dd> Apply the specified <a href="access.5.html">access database</a>
114 + to the DNS (or MX) servers for the host or domain name given with
115 + the HELO (or EHLO) command.
116
117 + <dd> Note: an OK result is not allowed for safety reasons.
118
119 + <p>
120
121   <dt> <b><a href="#permit">permit</a></b>
122   
123   <dt> <b><a href="#defer">defer</a></b>
124 ***************
125 *** 714,719 ****
126 --- 730,751 ----
127   
128   <p>
129   
130 + <a name="check_sender_ns_access">
131
132 + <dt> <b>check_sender_ns_access</b> <i>maptype</i>:<i>mapname</i>
133
134 + <a name="check_sender_mx_access">
135
136 + <dt> <b>check_sender_mx_access</b> <i>maptype</i>:<i>mapname</i>
137
138 + <dd> Apply the specified <a href="access.5.html">access database</a>
139 + to the DNS (or MX) servers for the host or domain name given with
140 + the MAIL FROM command.
141
142 + <dd> Note: an OK result is not allowed for safety reasons.
143
144 + <p>
145
146   <a name="reject_non_fqdn_sender">
147   
148   <dt> <b>reject_non_fqdn_sender</b> <dd> Reject the request when
149 ***************
150 *** 921,926 ****
151 --- 953,974 ----
152   <dt> <i>maptype</i>:<i>mapname</i> <dd> Search the named <a
153   href="access.5.html">access database</a> for the resolved destination
154   address, recipient domain or parent domain, or <i>localpart</i>@. 
155
156 + <p>
157
158 + <a name="check_recipient_ns_access">
159
160 + <dt> <b>check_recipient_ns_access</b> <i>maptype</i>:<i>mapname</i>
161
162 + <a name="check_recipient_mx_access">
163
164 + <dt> <b>check_recipient_mx_access</b> <i>maptype</i>:<i>mapname</i>
165
166 + <dd> Apply the specified <a href="access.5.html">access database</a>
167 + to the DNS servers (or MX hosts) for the host or domain name given
168 + with the RCPT TO command.
169
170 + <dd> Note: an OK result is not allowed for safety reasons.
171   
172   <p>
173   
174 diff -cr /tmp/postfix-2.0.16/src/dns/dns_lookup.c ./src/dns/dns_lookup.c
175 *** /tmp/postfix-2.0.16/src/dns/dns_lookup.c    Sun Dec  8 09:09:11 2002
176 --- ./src/dns/dns_lookup.c      Fri Sep 19 11:17:34 2003
177 ***************
178 *** 509,514 ****
179 --- 509,515 ----
180             vstring_sprintf(why,
181                    "Name service error for %s: invalid host or domain name",
182                             name);
183 +       h_errno = HOST_NOT_FOUND;
184         return (DNS_NOTFOUND);
185       }
186   
187 ***************
188 *** 520,525 ****
189 --- 521,527 ----
190             vstring_sprintf(why,
191                    "Name service error for %s: invalid host or domain name",
192                             name);
193 +       h_errno = HOST_NOT_FOUND;
194         return (DNS_NOTFOUND);
195       }
196   
197 diff -cr /tmp/postfix-2.0.16/src/global/mail_params.h ./src/global/mail_params.h
198 *** /tmp/postfix-2.0.16/src/global/mail_params.h        Mon Mar  3 17:07:03 2003
199 --- ./src/global/mail_params.h  Fri Sep 19 11:17:35 2003
200 ***************
201 *** 1249,1254 ****
202 --- 1249,1261 ----
203   #define CHECK_RECIP_ACL               "check_recipient_access"
204   #define CHECK_ETRN_ACL                "check_etrn_access"
205   
206 + #define CHECK_HELO_MX_ACL     "check_helo_mx_access"
207 + #define CHECK_SENDER_MX_ACL   "check_sender_mx_access"
208 + #define CHECK_RECIP_MX_ACL    "check_recipient_mx_access"
209 + #define CHECK_HELO_NS_ACL     "check_helo_ns_access"
210 + #define CHECK_SENDER_NS_ACL   "check_sender_ns_access"
211 + #define CHECK_RECIP_NS_ACL    "check_recipient_ns_access"
212
213   #define WARN_IF_REJECT                "warn_if_reject"
214   
215   #define REJECT_RBL            "reject_rbl"    /* LaMont compatibility */
216 diff -cr /tmp/postfix-2.0.16/src/smtpd/smtpd_check.c ./src/smtpd/smtpd_check.c
217 *** /tmp/postfix-2.0.16/src/smtpd/smtpd_check.c Tue Aug 12 10:53:25 2003
218 --- ./src/smtpd/smtpd_check.c   Fri Sep 19 11:17:52 2003
219 ***************
220 *** 86,91 ****
221 --- 86,103 ----
222   /* .IP "check_recipient_access maptype:mapname"
223   /*    Look up the resolved recipient address in the named access table,
224   /*    any parent domains of the recipient domain, and the localpart@.
225 + /* .IP "check_helo_mx_access maptype:mapname"
226 + /* .IP "check_sender_mx_access maptype:mapname"
227 + /* .IP "check_recipient_mx_access maptype:mapname"
228 + /*    Apply the specified access table to the MX server host name and IP
229 + /*    addresses for the helo hostname, sender, or recipient, respectively.
230 + /*    If no MX record is found the A record is used instead.
231 + /* .IP "check_helo_ns_access maptype:mapname"
232 + /* .IP "check_sender_ns_access maptype:mapname"
233 + /* .IP "check_recipient_ns_access maptype:mapname"
234 + /*    Apply the specified access table to the DNS server host name and IP
235 + /*    addresses for the helo hostname, sender, or recipient, respectively.
236 + /*    If no NS record is found, the parent domain is used instead.
237   /* .IP "check_recipient_maps"
238   /*    Reject recipients not listed as valid local, virtual or relay
239   /*    recipients.
240 ***************
241 *** 455,460 ****
242 --- 467,484 ----
243       else \
244         (void) smtpd_check_reject((state), (class), (fmt), (a1), (a2)); \
245       } while (0)
246 + #define DEFER_IF_PERMIT3(state, class, fmt, a1, a2, a3) do { \
247 +     if ((state)->warn_if_reject == 0) \
248 +       defer_if(&(state)->defer_if_permit, (class), (fmt), (a1), (a2), (a3)); \
249 +     else \
250 +       (void) smtpd_check_reject((state), (class), (fmt), (a1), (a2), (a3)); \
251 +     } while (0)
252 + #define DEFER_IF_PERMIT4(state, class, fmt, a1, a2, a3, a4) do { \
253 +     if ((state)->warn_if_reject == 0) \
254 +       defer_if(&(state)->defer_if_permit, (class), (fmt), (a1), (a2), (a3), (a4)); \
255 +     else \
256 +       (void) smtpd_check_reject((state), (class), (fmt), (a1), (a2), (a3), (a4)); \
257 +     } while (0)
258   
259    /*
260     * Cached RBL lookup state.
261 ***************
262 *** 2005,2010 ****
263 --- 2029,2151 ----
264       return (SMTPD_CHECK_DUNNO);
265   }
266   
267 + /* check_server_access - access control by server host name or address */
268
269 + static int check_server_access(SMTPD_STATE *state, const char *table,
270 +                                      const char *name,
271 +                                      int type,
272 +                                      const char *reply_name,
273 +                                      const char *reply_class,
274 +                                      const char *def_acl)
275 + {
276 +     const char *myname = "check_server_access";
277 +     const char *domain;
278 +     int     dns_status;
279 +     DNS_RR *server_list;
280 +     DNS_RR *server;
281 +     int     found = 0;
282 +     struct in_addr addr;
283 +     struct hostent *hp;
284 +     char   *addr_string;
285 +     int     status;
286 +     char  **cpp;
287 +     static DNS_FIXED fixed;
288
289 +     /*
290 +      * Sanity check.
291 +      */
292 +     if (type != T_MX && type != T_NS)
293 +       msg_panic("%s: unexpected resource type \"%s\" in request",
294 +                 myname, dns_strtype(type));
295
296 +     if (msg_verbose)
297 +       msg_info("%s: %s %s", myname, dns_strtype(type), name);
298
299 +     /*
300 +      * Skip over local-part.
301 +      */
302 +     if ((domain = strrchr(name, '@')) != 0)
303 +       domain += 1;
304 +     else
305 +       domain = name;
306
307 +     /*
308 +      * If the domain name does not exist then we apply no restriction.
309 +      * 
310 +      * If the domain name exists but no MX record exists, fabricate an MX record
311 +      * that points to the domain name itself.
312 +      * 
313 +      * If the domain name exists but no NS record exists, look up parent domain
314 +      * NS records.
315 +      */
316 +     dns_status = dns_lookup(domain, type, 0, &server_list,
317 +                           (VSTRING *) 0, (VSTRING *) 0);
318 +     if (dns_status == DNS_NOTFOUND && h_errno == NO_DATA) {
319 +       if (type == T_MX) {
320 +           server_list = dns_rr_create(domain, &fixed, 0,
321 +                                       domain, strlen(domain) + 1);
322 +           dns_status = DNS_OK;
323 +       } else if (type == T_NS) {
324 +           while ((domain = strchr(domain, '.')) != 0 && domain[1]) {
325 +               domain += 1;
326 +               dns_status = dns_lookup(domain, type, 0, &server_list,
327 +                                       (VSTRING *) 0, (VSTRING *) 0);
328 +               if (dns_status != DNS_NOTFOUND || h_errno != NO_DATA)
329 +                   break;
330 +           }
331 +       }
332 +     }
333 +     if (dns_status != DNS_OK) {
334 +       msg_warn("Unable to look up %s host for %s", dns_strtype(type),
335 +                domain && domain[1] ? domain : reply_name);
336 +       return (SMTPD_CHECK_DUNNO);
337 +     }
338
339 +     /*
340 +      * No bare returns after this point or we have a memory leak.
341 +      */
342 + #define CHECK_SERVER_RETURN(x) { dns_rr_free(server_list); return(x); }
343
344 +     /*
345 +      * Check the hostnames first, then the addresses.
346 +      */
347 +     for (server = server_list; server != 0; server = server->next) {
348 +       if ((hp = gethostbyname((char *) server->data)) == 0) {
349 +           msg_warn("Unable to look up %s host %s for %s %s",
350 +                    dns_strtype(type), (char *) server->data,
351 +                    reply_class, reply_name);
352 +           continue;
353 +       }
354 +       if (hp->h_addrtype != AF_INET || hp->h_length != sizeof(addr)) {
355 +           if (msg_verbose)
356 +               msg_warn("address type %d length %d for %s",
357 +                      hp->h_addrtype, hp->h_length, (char *) server->data);
358 +           continue;                           /* XXX */
359 +       }
360 +       if (msg_verbose)
361 +           msg_info("%s: %s hostname check: %s",
362 +                    myname, dns_strtype(type), (char *) server->data);
363 +       if ((status = check_domain_access(state, table, (char *) server->data,
364 +                                     FULL, &found, reply_name, reply_class,
365 +                                         def_acl)) != 0 || found)
366 +           CHECK_SERVER_RETURN(status);
367 +       if (msg_verbose)
368 +           msg_info("%s: %s host address check: %s",
369 +                    myname, dns_strtype(type), (char *) server->data);
370 +       for (cpp = hp->h_addr_list; *cpp; cpp++) {
371 +           memcpy((char *) &addr, *cpp, sizeof(addr));
372 +           addr_string = mystrdup(inet_ntoa(addr));
373 +           status = check_addr_access(state, table, addr_string, FULL,
374 +                                      &found, reply_name, reply_class,
375 +                                      def_acl);
376 +           myfree(addr_string);
377 +           if (status != 0 || found)
378 +               CHECK_SERVER_RETURN(status);
379 +       }
380 +     }
381 +     CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO);
382 + }
383
384   /* check_mail_access - OK/FAIL based on mail address lookup */
385   
386   static int check_mail_access(SMTPD_STATE *state, const char *table,
387 ***************
388 *** 2568,2573 ****
389 --- 2709,2728 ----
390       }
391   }
392   
393 + /* forbid_whitelist - disallow whitelisting */
394
395 + static void forbid_whitelist(SMTPD_STATE *state, const char *name,
396 +                                    int status, const char *target)
397 + {
398 +     if (status == SMTPD_CHECK_OK) {
399 +       msg_warn("restriction %s returns OK for %s", name, target);
400 +       msg_warn("this is not allowed for security reasons");
401 +       msg_warn("use DUNNO instead of OK if you want to make an exception");
402 +       longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
403 +                                        "451 Server configuration error"));
404 +     }
405 + }
406
407   /* generic_checks - generic restrictions */
408   
409   static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
410 ***************
411 *** 2722,2727 ****
412 --- 2877,2896 ----
413                                    state->helo_name, SMTPD_NAME_HELO)) == 0)
414                     status = SMTPD_CHECK_OK;
415             }
416 +       } else if (is_map_command(state, name, CHECK_HELO_NS_ACL, &cpp)) {
417 +           if (state->helo_name) {
418 +               status = check_server_access(state, *cpp, state->helo_name,
419 +                                            T_NS, state->helo_name,
420 +                                            SMTPD_NAME_HELO, def_acl);
421 +               forbid_whitelist(state, name, status, state->helo_name);
422 +           }
423 +       } else if (is_map_command(state, name, CHECK_HELO_MX_ACL, &cpp)) {
424 +           if (state->helo_name) {
425 +               status = check_server_access(state, *cpp, state->helo_name,
426 +                                            T_MX, state->helo_name,
427 +                                            SMTPD_NAME_HELO, def_acl);
428 +               forbid_whitelist(state, name, status, state->helo_name);
429 +           }
430         } else if (strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) {
431             if (state->helo_name) {
432                 if (*state->helo_name != '[')
433 ***************
434 *** 2760,2765 ****
435 --- 2929,2948 ----
436         } else if (strcasecmp(name, REJECT_SENDER_LOGIN_MISMATCH) == 0) {
437             if (state->sender && *state->sender)
438                 status = reject_sender_login_mismatch(state, state->sender);
439 +       } else if (is_map_command(state, name, CHECK_SENDER_NS_ACL, &cpp)) {
440 +           if (state->sender && *state->sender) {
441 +               status = check_server_access(state, *cpp, state->sender,
442 +                                            T_NS, state->sender,
443 +                                            SMTPD_NAME_SENDER, def_acl);
444 +               forbid_whitelist(state, name, status, state->sender);
445 +           }
446 +       } else if (is_map_command(state, name, CHECK_SENDER_MX_ACL, &cpp)) {
447 +           if (state->sender && *state->sender) {
448 +               status = check_server_access(state, *cpp, state->sender,
449 +                                            T_MX, state->sender,
450 +                                            SMTPD_NAME_SENDER, def_acl);
451 +               forbid_whitelist(state, name, status, state->sender);
452 +           }
453         } else if (strcasecmp(name, REJECT_RHSBL_SENDER) == 0) {
454             if (cpp[1] == 0)
455                 msg_warn("restriction %s requires domain name argument", name);
456 ***************
457 *** 2812,2817 ****
458 --- 2995,3014 ----
459             if (state->recipient)
460                 status = reject_non_fqdn_address(state, state->recipient,
461                                     state->recipient, SMTPD_NAME_RECIPIENT);
462 +       } else if (is_map_command(state, name, CHECK_RECIP_NS_ACL, &cpp)) {
463 +           if (state->recipient && *state->recipient) {
464 +               status = check_server_access(state, *cpp, state->recipient,
465 +                                            T_NS, state->recipient,
466 +                                            SMTPD_NAME_RECIPIENT, def_acl);
467 +               forbid_whitelist(state, name, status, state->recipient);
468 +           }
469 +       } else if (is_map_command(state, name, CHECK_RECIP_MX_ACL, &cpp)) {
470 +           if (state->recipient && *state->recipient) {
471 +               status = check_server_access(state, *cpp, state->recipient,
472 +                                            T_MX, state->recipient,
473 +                                            SMTPD_NAME_RECIPIENT, def_acl);
474 +               forbid_whitelist(state, name, status, state->recipient);
475 +           }
476         } else if (strcasecmp(name, REJECT_RHSBL_RECIPIENT) == 0) {
477             if (cpp[1] == 0)
478                 msg_warn("restriction %s requires domain name argument", name);
This page took 0.088807 seconds and 3 git commands to generate.