]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.0.16-20030917
authorWietse Venema <wietse@porcupine.org>
Wed, 17 Sep 2003 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:29:07 +0000 (06:29 +0000)
15 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/LDAP_README
postfix/RELEASE_NOTES
postfix/conf/sample-smtpd.cf
postfix/html/uce.html
postfix/src/dns/dns_lookup.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_check_access
postfix/src/smtpd/smtpd_exp.in
postfix/src/smtpd/smtpd_exp.ref
postfix/src/trivial-rewrite/resolve.c
postfix/src/util/dict_ldap.c

index 35ef0bc9f89e89f192b636f1c84fa38370f68884..d092777b46d25a2f271401a2f3b8b7a6c37211b7 100644 (file)
@@ -77,6 +77,7 @@
 -TINTV
 -TINT_TABLE
 -TJMP_BUF_WRAPPER
+-TLDAP_CONN
 -TLMTP_ATTR
 -TLMTP_RESP
 -TLMTP_SESSION
index 07311ca928af337cb6ba7a2d2fcbe2251519cf5f..e3008675f98aa017f2602609add66ff3d164d221 100644 (file)
@@ -8558,6 +8558,38 @@ Apologies for any names omitted.
        Cleanup: postcat is now null-byte transparent. File:
        postcat/postcat.c.
 
+20030916
+
+       Feature: ``check_{sender,recipient}_mx_access maptype:mapname''
+       applies the named Postfix access table to the MX host name
+       and IP addresses for the sender or recipient address. If
+       no MX record is found, the A record is used instead. File:
+       smtpd/smtpd_check.c.
+
+       Experimental feature: ``check_{sender,recipient}_ns_access
+       maptype:mapname'' applies the named Postfix access table
+       to the DNS server hostname and IP addresses for the sender
+       or recipient address. If no NS record is found, the parent
+       domain is used instead. File: smtpd/smtpd_check.c.
+
+20030917
+
+       Feature: ``check_helo_{ns,mx}_access maptype:mapname'',
+       same semantics as sender and recipient.
+
+       Multiple LDAP lookup tables in the one Postfix process now
+       share one LDAP connection. Code by Victor Duchovni, Morgan
+       Stanley.  File: util/dict_ldap.c.
+
+       Performance: with prefix_domain specified for an LDAP lookup
+       table, lookups of @domain are skipped. Code by Victor
+       Duchovni, Morgan Stanley.  File: util/dict_ldap.c.
+
+       Safety: check_mumble_{mx,ns}_access refuses to be used for
+       whitelisting. The Postfix SMTP server will reject the
+       request with "451 server configuration error" and will log
+       a warning explaining why. File: smtpd/smtpd_check.c.
+
 Open problems:
 
        High: when virtual aliasing is turned off after content
index e41eb66721e54cb60469bac05379f393c9454ea2..98d17618808df254c9fbe118fd3bdbe70316f3bc 100644 (file)
@@ -423,6 +423,12 @@ NOTES AND THINGS TO THINK ABOUT
   they won't be a bottleneck, but it's a good idea to know how to tune
   your directory service.
 
+- Multiple LDAP maps share the same LDAP connection if they differ
+  only in their query related parameters: base, scope, query_filter, and
+  so on. To take advantage of this avoid spurious differences in the
+  definitions of LDAP maps: host selection order, version, bind, tls
+  parameters, ... should be the same for multiple maps whenever possible.
+
 FEEDBACK
 ========
 
@@ -450,9 +456,10 @@ Samuel Tardieu: Noticed that searches could include wildcards, prompting
 Sami Haahtinen: Referral chasing and v3 support.
 Victor Duchovni: ldap_bind() timeout. With fixes from LaMont Jones:
                 OpenLDAP cache deprecation. Limits on recursion, expansion
-                and query results size.
+                and query results size. LDAP connection sharing for maps
+                differing only in the query parameters.
 Liviu Daia: Support for SSL/STARTTLS. Support for storing map definitions in
-           external files (ldap:/path/ldap.cf) need to securely store
+           external files (ldap:/path/ldap.cf) needed to securely store
            passwords for plain auth.
 
 And of course Wietse.
index 5824fc4ff551aa9ce4b76952df04330701d88224..65de58f6b0b713bdb538056fe31ee50bae9c6434 100644 (file)
@@ -22,14 +22,42 @@ snapshot release).  Patches change the patchlevel and the release
 date. Snapshots change only the release date, unless they include
 the same bugfixes as a patch release.
 
-Incompatible changes with Postfix snapshot 2.0.13-20030914
+Major changes with Postfix snapshot 2.0.16-20030917
+===================================================
+
+New check_{helo,sender,recipient}_{ns,mx}_access maptype:mapname
+restriction that applies the specified access table to the NS or
+MX hosts of the host/domain given in HELO, EHLO, MAIL FROM or RCPT
+TO commands.  
+
+This can be used to block mail from so-called spammer havens, or
+from sender addresses that resolve to Verisign's wild-card mail
+responder, currently at IP address 64.94.110.11.
+
+    /etc/postfix/main.cf:
+       smtpd_mumble_restrictions = 
+           ...
+           reject_unknown_sender_domain 
+           check_sender_mx_access hash:/etc/postfix/mx_access
+           ...
+
+    /etc/postfix/mx_access:
+       spammer.haven.tld reject spammer mx host
+       64.94.110.11 reject verisign wild-card domain
+
+Note: OK actions are not allowed for security reasons. Instead of
+OK, use DUNNO in order to exclude specific hosts from blacklists.
+If an OK result is found for an NS or MX host, Postfix rejects the
+SMTP command with "451 Server configuration error".
+
+Incompatible changes with Postfix snapshot 2.0.16-20030915
 ==========================================================
 
 In header/body_checks actions, the OK action is being phased out,
 and the DUNNO action is being phased in. Both actions still work
 and do the same thing, but hopefully DUNNO causes less confusion.
 
-Major changes with Postfix snapshot 2.0.13-20030914
+Major changes with Postfix snapshot 2.0.16-20030915
 ===================================================
 
 LDAP parameters can now be defined in external files.  Specify the
index a3fdb46b01166ffbda5b585906f06504b0bfb611..7e07cec123eebe8f8ca7f408663451665c6ef729 100644 (file)
@@ -358,6 +358,11 @@ smtpd_helo_required = no
 #   check_helo_access maptype:mapname
 #      look up HELO hostname or parent domains.
 #      see access(5) for possible lookup results.
+#   check_helo_mx_access maptype:mapname
+#   check_helo_ns_access maptype:mapname
+#      look up the helo hostname MX hosts (or name servers) and apply the
+#      specified access table to those hosts.
+#      Note: the OK result is not allowed here for security reasons.
 #   check_policy_service transport:endpoint: delegate the decision to
 #      an external policy server. See SMTPD_POLICY_README for details.
 #   reject_rhsbl_helo domain.tld: reject if the helo argument is listed
@@ -396,6 +401,11 @@ smtpd_helo_restrictions =
 #   check_sender_access maptype:mapname
 #      look up sender address, parent domain, or localpart@.
 #      see access(5) for possible lookup results.
+#   check_sender_mx_access maptype:mapname
+#   check_sender_ns_access maptype:mapname
+#      look up sender address MX hosts (or name servers) and apply the
+#      specified access table to those hosts.
+#      Note: the OK result is not allowed here for security reasons.
 #   reject_sender_login_mismatch: reject if $smtpd_sender_login_maps specifies
 #      a MAIL FROM address owner, but the client is not (SASL) logged in as
 #      that MAIL FROM address owner; or if the client is (SASL) logged in, but
@@ -467,6 +477,11 @@ smtpd_sender_restrictions =
 #   check_recipient_access maptype:mapname
 #      look up recipient address, parent domain, or localpart@.
 #      see access(5) for possible lookup results.
+#   check_recipient_mx_access maptype:mapname
+#   check_recipient_ns_access maptype:mapname
+#      look up the recipient address MX hosts (or name servers) and apply the
+#      specified access table to those hosts.
+#      Note: the OK result is not allowed here for security reasons.
 #   reject_non_fqdn_recipient: reject recipient address that is not in FQDN form
 #   check_policy_service transport:endpoint: delegate the decision to
 #      an external policy server. See SMTPD_POLICY_README for details.
index f75848d09da8c61c69e150b63dfe2d65ab978051..4392efa21e9b501196bf163bf60a077a7d753e03 100644 (file)
@@ -599,6 +599,22 @@ or parent domains.
 
 <p>
 
+<a name="check_helo_ns_access">
+
+<dt> <b>check_helo_ns_access</b> <i>maptype</i>:<i>mapname</i>
+
+<a name="check_helo_mx_access">
+
+<dt> <b>check_helo_mx_access</b> <i>maptype</i>:<i>mapname</i>
+
+<dd> Apply the specified <a href="access.5.html">access database</a>
+to the DNS (or MX) servers for the host or domain name given with
+the HELO (or EHLO) command.
+
+<dd> Note: an OK result is not allowed for safety reasons.
+
+<p>
+
 <a name="reject_rhsbl_helo">
 
 <dt> <b>reject_rhsbl_helo</b> <i>domain.tld=127.0.0.2</i>
@@ -783,6 +799,22 @@ sender domain and parent domain, or <i>localpart</i>@.
 
 <p>
 
+<a name="check_sender_ns_access">
+
+<dt> <b>check_sender_ns_access</b> <i>maptype</i>:<i>mapname</i>
+
+<a name="check_sender_mx_access">
+
+<dt> <b>check_sender_mx_access</b> <i>maptype</i>:<i>mapname</i>
+
+<dd> Apply the specified <a href="access.5.html">access database</a>
+to the DNS (or MX) servers for the host or domain name given with
+the MAIL FROM command.
+
+<dd> Note: an OK result is not allowed for safety reasons.
+
+<p>
+
 <a name="reject_non_fqdn_sender">
 
 <dt> <b>reject_non_fqdn_sender</b> <dd> Reject the request when
@@ -995,6 +1027,22 @@ address, recipient domain or parent domain, or <i>localpart</i>@.
 
 <p>
 
+<a name="check_recipient_ns_access">
+
+<dt> <b>check_recipient_ns_access</b> <i>maptype</i>:<i>mapname</i>
+
+<a name="check_recipient_mx_access">
+
+<dt> <b>check_recipient_mx_access</b> <i>maptype</i>:<i>mapname</i>
+
+<dd> Apply the specified <a href="access.5.html">access database</a>
+to the DNS servers (or MX hosts) for the host or domain name given
+with the RCPT TO command.
+
+<dd> Note: an OK result is not allowed for safety reasons.
+
+<p>
+
 <a name="check_recipient_maps">
 
 <dt> <b>check_recipient_maps</b> <dd> Reject the request
index c3533815967677d86a84782607c803f65638af2c..e7816e442ec55db0f7364f7305bd9cd9898d54fe 100644 (file)
@@ -509,6 +509,7 @@ int     dns_lookup(const char *name, unsigned type, unsigned flags,
            vstring_sprintf(why,
                   "Name service error for %s: invalid host or domain name",
                            name);
+       h_errno = HOST_NOT_FOUND;
        return (DNS_NOTFOUND);
     }
 
@@ -520,6 +521,7 @@ int     dns_lookup(const char *name, unsigned type, unsigned flags,
            vstring_sprintf(why,
                   "Name service error for %s: invalid host or domain name",
                            name);
+       h_errno = HOST_NOT_FOUND;
        return (DNS_NOTFOUND);
     }
 
index 0d16455bff2c427c7e8d8a432f4e350d96a68eb9..b35f5617bd67d404a6768c27d2d13f45554a2a73 100644 (file)
@@ -1314,6 +1314,13 @@ extern int var_access_map_code;
 #define CHECK_RECIP_ACL                "check_recipient_access"
 #define CHECK_ETRN_ACL         "check_etrn_access"
 
+#define CHECK_HELO_MX_ACL      "check_helo_mx_access"
+#define CHECK_SENDER_MX_ACL    "check_sender_mx_access"
+#define CHECK_RECIP_MX_ACL     "check_recipient_mx_access"
+#define CHECK_HELO_NS_ACL      "check_helo_ns_access"
+#define CHECK_SENDER_NS_ACL    "check_sender_ns_access"
+#define CHECK_RECIP_NS_ACL     "check_recipient_ns_access"
+
 #define WARN_IF_REJECT         "warn_if_reject"
 
 #define REJECT_RBL             "reject_rbl"    /* LaMont compatibility */
index f64592d5b7e43bc1e771a0a4f5ec86e3f8d5b3a4..0b394b79b6c32189b403a790cbcd6554e6fd17d4 100644 (file)
   * Patches change the patchlevel and the release date. Snapshots change the
   * release date only, unless they include the same bugfix as a patch release.
   */
-#define MAIL_RELEASE_DATE      "20030915"
+#define MAIL_RELEASE_DATE      "20030917"
 
 #define VAR_MAIL_VERSION       "mail_version"
-#define DEF_MAIL_VERSION       "2.0.14-" MAIL_RELEASE_DATE
+#define DEF_MAIL_VERSION       "2.0.16-" MAIL_RELEASE_DATE
 extern char *var_mail_version;
 
  /*
index 387ffa65defc7bc568a004fdb2bf5c19fbded2b8..2f0f0cc200f38080a3dd6b3d89271270f8585862 100644 (file)
 /* .IP "check_recipient_access maptype:mapname"
 /*     Look up the resolved recipient address in the named access table,
 /*     any parent domains of the recipient domain, and the localpart@.
+/* .IP "check_helo_mx_access maptype:mapname"
+/* .IP "check_sender_mx_access maptype:mapname"
+/* .IP "check_recipient_mx_access maptype:mapname"
+/*     Apply the specified access table to the MX server host name and IP
+/*     addresses for the helo hostname, sender, or recipient, respectively.
+/*     If no MX record is found the A record is used instead.
+/* .IP "check_helo_ns_access maptype:mapname"
+/* .IP "check_sender_ns_access maptype:mapname"
+/* .IP "check_recipient_ns_access maptype:mapname"
+/*     Apply the specified access table to the DNS server host name and IP
+/*     addresses for the helo hostname, sender, or recipient, respectively.
+/*     If no NS record is found, the parent domain is used instead.
 /* .IP "check_recipient_maps"
 /*     Reject recipients not listed as valid local, virtual or relay
 /*     recipients.
@@ -482,6 +494,12 @@ static void PRINTFLIKE(3, 4) defer_if(SMTPD_DEFER *, int, const char *,...);
     else \
        (void) smtpd_check_reject((state), (class), (fmt), (a1), (a2), (a3)); \
     } while (0)
+#define DEFER_IF_PERMIT4(state, class, fmt, a1, a2, a3, a4) do { \
+    if ((state)->warn_if_reject == 0) \
+       defer_if(&(state)->defer_if_permit, (class), (fmt), (a1), (a2), (a3), (a4)); \
+    else \
+       (void) smtpd_check_reject((state), (class), (fmt), (a1), (a2), (a3), (a4)); \
+    } while (0)
 
  /*
   * Cached RBL lookup state.
@@ -2153,6 +2171,127 @@ static int check_namadr_access(SMTPD_STATE *state, const char *table,
     return (SMTPD_CHECK_DUNNO);
 }
 
+/* check_server_access - access control by server host name or address */
+
+static int check_server_access(SMTPD_STATE *state, const char *table,
+                                      const char *name,
+                                      int type,
+                                      const char *reply_name,
+                                      const char *reply_class,
+                                      const char *def_acl)
+{
+    const char *myname = "check_server_access";
+    const char *domain;
+    int     dns_status;
+    DNS_RR *server_list;
+    DNS_RR *server;
+    int     found = 0;
+    struct in_addr addr;
+    struct hostent *hp;
+    char   *addr_string;
+    int     status;
+    char  **cpp;
+    static DNS_FIXED fixed;
+
+    /*
+     * Sanity check.
+     */
+    if (type != T_MX && type != T_NS)
+       msg_panic("%s: unexpected resource type \"%s\" in request",
+                 myname, dns_strtype(type));
+
+    if (msg_verbose)
+       msg_info("%s: %s %s", myname, dns_strtype(type), name);
+
+    /*
+     * Skip over local-part.
+     */
+    if ((domain = strrchr(name, '@')) != 0)
+       domain += 1;
+    else
+       domain = name;
+
+    /*
+     * If the domain does not exist then we apply no restriction. In all
+     * other cases, DNS lookup failure results in a "try again" status.
+     * 
+     * If the domain name exists but MX lookup fails, fabricate an MX record
+     * that points to the domain name itself.
+     * 
+     * If the domain name exists but NS lookup fails, look up the parent domain
+     * NS record.
+     */
+    dns_status = dns_lookup(domain, type, 0, &server_list,
+                           (VSTRING *) 0, (VSTRING *) 0);
+    if (dns_status == DNS_NOTFOUND && h_errno != HOST_NOT_FOUND) {
+       if (type == T_MX) {
+           server_list = dns_rr_create(domain, &fixed, 0,
+                                       domain, strlen(domain) + 1);
+           dns_status = DNS_OK;
+       } else if (type == T_NS && (domain = strchr(domain, '.')) != 0
+                  && strchr(++domain, '.') != 0) {
+           dns_status = dns_lookup(domain, T_NS, 0, &server_list,
+                                   (VSTRING *) 0, (VSTRING *) 0);
+           if (dns_status != DNS_OK)
+               dns_status = DNS_RETRY;
+       }
+    }
+    if (dns_status == DNS_NOTFOUND)
+       return (SMTPD_CHECK_DUNNO);
+    if (dns_status != DNS_OK) {
+       DEFER_IF_PERMIT3(state, MAIL_ERROR_POLICY,
+                        "450 <%s>: %s rejected: unable to look up %s host",
+                        reply_name, reply_class, dns_strtype(type));
+       return (SMTPD_CHECK_DUNNO);
+    }
+
+    /*
+     * No bare returns after this point or we have a memory leak.
+     */
+#define CHECK_SERVER_RETURN(x) { dns_rr_free(server_list); return(x); }
+
+    /*
+     * Check the hostnames first, then the addresses.
+     */
+    for (server = server_list; server != 0; server = server->next) {
+       if ((hp = gethostbyname((char *) server->data)) == 0) {
+           DEFER_IF_PERMIT4(state, MAIL_ERROR_POLICY,
+                            "450 <%s>: %s rejected: "
+                            "Unable to look up %s host %s",
+                            reply_name, reply_class,
+                            dns_strtype(type), (char *) server->data);
+           CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO);
+       }
+       if (hp->h_addrtype != AF_INET || hp->h_length != sizeof(addr)) {
+           if (msg_verbose)
+               msg_warn("address type %d length %d for %s",
+                      hp->h_addrtype, hp->h_length, (char *) server->data);
+           continue;                           /* XXX */
+       }
+       if (msg_verbose)
+           msg_info("%s: %s hostname check: %s",
+                    myname, dns_strtype(type), (char *) server->data);
+       if ((status = check_domain_access(state, table, (char *) server->data,
+                                     FULL, &found, reply_name, reply_class,
+                                         def_acl)) != 0 || found)
+           CHECK_SERVER_RETURN(status);
+       if (msg_verbose)
+           msg_info("%s: %s host address check: %s",
+                    myname, dns_strtype(type), (char *) server->data);
+       for (cpp = hp->h_addr_list; *cpp; cpp++) {
+           memcpy((char *) &addr, *cpp, sizeof(addr));
+           addr_string = mystrdup(inet_ntoa(addr));
+           status = check_addr_access(state, table, addr_string, FULL,
+                                      &found, reply_name, reply_class,
+                                      def_acl);
+           myfree(addr_string);
+           if (status != 0 || found)
+               CHECK_SERVER_RETURN(status);
+       }
+    }
+    CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO);
+}
+
 /* check_mail_access - OK/FAIL based on mail address lookup */
 
 static int check_mail_access(SMTPD_STATE *state, const char *table,
@@ -2818,6 +2957,20 @@ static int is_map_command(SMTPD_STATE *state, const char *name,
     }
 }
 
+/* forbid_whitelist - disallow whitelisting */
+
+static void forbid_whitelist(SMTPD_STATE *state, const char *name,
+                                    int status, const char *target)
+{
+    if (status == SMTPD_CHECK_OK) {
+       msg_warn("restriction %s returns OK for %s", name, target);
+       msg_warn("this is not allowed for security reasons");
+       msg_warn("use DUNNO instead of OK if you want to make an exception");
+       longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
+                                        "451 Server configuration error"));
+    }
+}
+
 /* generic_checks - generic restrictions */
 
 static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
@@ -2979,6 +3132,20 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
                                   state->helo_name, SMTPD_NAME_HELO)) == 0)
                    status = SMTPD_CHECK_OK;
            }
+       } else if (is_map_command(state, name, CHECK_HELO_NS_ACL, &cpp)) {
+           if (state->helo_name) {
+               status = check_server_access(state, *cpp, state->helo_name,
+                                            T_NS, state->helo_name,
+                                            SMTPD_NAME_HELO, def_acl);
+               forbid_whitelist(state, name, status, state->helo_name);
+           }
+       } else if (is_map_command(state, name, CHECK_HELO_MX_ACL, &cpp)) {
+           if (state->helo_name) {
+               status = check_server_access(state, *cpp, state->helo_name,
+                                            T_MX, state->helo_name,
+                                            SMTPD_NAME_HELO, def_acl);
+               forbid_whitelist(state, name, status, state->helo_name);
+           }
        } else if (strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) {
            if (state->helo_name) {
                if (*state->helo_name != '[')
@@ -3032,6 +3199,20 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
        } else if (strcasecmp(name, REJECT_SENDER_LOGIN_MISMATCH) == 0) {
            if (state->sender && *state->sender)
                status = reject_sender_login_mismatch(state, state->sender);
+       } else if (is_map_command(state, name, CHECK_SENDER_NS_ACL, &cpp)) {
+           if (state->sender && *state->sender) {
+               status = check_server_access(state, *cpp, state->sender,
+                                            T_NS, state->sender,
+                                            SMTPD_NAME_SENDER, def_acl);
+               forbid_whitelist(state, name, status, state->sender);
+           }
+       } else if (is_map_command(state, name, CHECK_SENDER_MX_ACL, &cpp)) {
+           if (state->sender && *state->sender) {
+               status = check_server_access(state, *cpp, state->sender,
+                                            T_MX, state->sender,
+                                            SMTPD_NAME_SENDER, def_acl);
+               forbid_whitelist(state, name, status, state->sender);
+           }
        } else if (strcasecmp(name, REJECT_RHSBL_SENDER) == 0) {
            if (cpp[1] == 0)
                msg_warn("restriction %s requires domain name argument", name);
@@ -3084,6 +3265,20 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
            if (state->recipient)
                status = reject_non_fqdn_address(state, state->recipient,
                                    state->recipient, SMTPD_NAME_RECIPIENT);
+       } else if (is_map_command(state, name, CHECK_RECIP_NS_ACL, &cpp)) {
+           if (state->recipient && *state->recipient) {
+               status = check_server_access(state, *cpp, state->recipient,
+                                            T_NS, state->recipient,
+                                            SMTPD_NAME_RECIPIENT, def_acl);
+               forbid_whitelist(state, name, status, state->recipient);
+           }
+       } else if (is_map_command(state, name, CHECK_RECIP_MX_ACL, &cpp)) {
+           if (state->recipient && *state->recipient) {
+               status = check_server_access(state, *cpp, state->recipient,
+                                            T_MX, state->recipient,
+                                            SMTPD_NAME_RECIPIENT, def_acl);
+               forbid_whitelist(state, name, status, state->recipient);
+           }
        } else if (strcasecmp(name, REJECT_RHSBL_RECIPIENT) == 0) {
            if (cpp[1] == 0)
                msg_warn("restriction %s requires domain name argument", name);
index bfcb3d35f4b17ebb3ada02b3937d772c20c4e7ed..1ee24aa483cb40a72227ea87d546820876dc1041 100644 (file)
@@ -56,3 +56,5 @@ holdtext@hold.domain  hold text
 discard@hold.domain    discard
 discardtext@hold.domain        discard text
 dunnotext@dunno.domain dunno text
+64.94.110.11           reject Verisign wild-card
+topica.com             reject
index 8c05d106934356a3d1119f061e65e0bb90b9bcfc..71b188ef3df22763760d4142a62a7dcb7bea9551 100644 (file)
@@ -60,3 +60,32 @@ recipient_restrictions reject_rhsbl_helo,abuse.rfc-ignorant.org
 helo example.tld
 mail sname@sdomain
 rcpt rname@rdomain
+#
+# Check MX access
+#
+helo_restrictions check_helo_mx_access,hash:smtpd_check_access
+helo verisign-wildcard.com
+helo verisign.com
+helo example.tld
+sender_restrictions check_sender_mx_access,hash:smtpd_check_access
+mail foo@verisign-wildcard.com
+mail foo@verisign.com
+recipient_restrictions check_recipient_mx_access,hash:smtpd_check_access
+rcpt foo@verisign-wildcard.com
+rcpt foo@verisign.com
+#
+# Check NS access
+#
+helo_restrictions check_helo_ns_access,hash:smtpd_check_access
+helo email-publisher.com
+helo ns1.topica.com
+helo verisign-wildcard.com
+helo example.tld
+sender_restrictions check_sender_ns_access,hash:smtpd_check_access
+mail foo@email-publisher.com
+mail foo@ns1.topica.com
+mail foo@verisign-wildcard.com
+recipient_restrictions check_recipient_ns_access,hash:smtpd_check_access
+rcpt foo@email-publisher.com
+rcpt foo@ns1.topica.com
+rcpt foo@verisign-wildcard.com
index 95034987439064adab35384258a7651ebabd5025..2495de5f0b232c94371ca65dab39b5b104e575da 100644 (file)
@@ -109,3 +109,64 @@ OK
 >>> rcpt rname@rdomain
 ./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 Service unavailable; Helo command [example.tld] blocked using abuse.rfc-ignorant.org; Not supporting abuse@domain; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<example.tld>
 554 Service unavailable; Helo command [example.tld] blocked using abuse.rfc-ignorant.org; Not supporting abuse@domain
+>>> #
+>>> # Check MX access
+>>> #
+>>> helo_restrictions check_helo_mx_access,hash:smtpd_check_access
+OK
+>>> helo verisign-wildcard.com
+./smtpd_check: <queue id>: reject: HELO from spike.porcupine.org[168.100.189.2]: 554 <verisign-wildcard.com>: Helo command rejected: Verisign wild-card; from=<sname@sdomain> proto=SMTP helo=<verisign-wildcard.com>
+554 <verisign-wildcard.com>: Helo command rejected: Verisign wild-card
+>>> helo verisign.com
+OK
+>>> helo example.tld
+OK
+>>> sender_restrictions check_sender_mx_access,hash:smtpd_check_access
+OK
+>>> mail foo@verisign-wildcard.com
+./smtpd_check: <queue id>: reject: MAIL from spike.porcupine.org[168.100.189.2]: 554 <foo@verisign-wildcard.com>: Sender address rejected: Verisign wild-card; from=<foo@verisign-wildcard.com> proto=SMTP helo=<example.tld>
+554 <foo@verisign-wildcard.com>: Sender address rejected: Verisign wild-card
+>>> mail foo@verisign.com
+OK
+>>> recipient_restrictions check_recipient_mx_access,hash:smtpd_check_access
+OK
+>>> rcpt foo@verisign-wildcard.com
+./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 <foo@verisign-wildcard.com>: Recipient address rejected: Verisign wild-card; from=<foo@verisign.com> to=<foo@verisign-wildcard.com> proto=SMTP helo=<example.tld>
+554 <foo@verisign-wildcard.com>: Recipient address rejected: Verisign wild-card
+>>> rcpt foo@verisign.com
+OK
+>>> #
+>>> # Check NS access
+>>> #
+>>> helo_restrictions check_helo_ns_access,hash:smtpd_check_access
+OK
+>>> helo email-publisher.com
+./smtpd_check: <queue id>: reject: HELO from spike.porcupine.org[168.100.189.2]: 554 <email-publisher.com>: Helo command rejected: Access denied; from=<foo@verisign.com> proto=SMTP helo=<email-publisher.com>
+554 <email-publisher.com>: Helo command rejected: Access denied
+>>> helo ns1.topica.com
+./smtpd_check: <queue id>: reject: HELO from spike.porcupine.org[168.100.189.2]: 554 <ns1.topica.com>: Helo command rejected: Access denied; from=<foo@verisign.com> proto=SMTP helo=<ns1.topica.com>
+554 <ns1.topica.com>: Helo command rejected: Access denied
+>>> helo verisign-wildcard.com
+OK
+>>> helo example.tld
+OK
+>>> sender_restrictions check_sender_ns_access,hash:smtpd_check_access
+OK
+>>> mail foo@email-publisher.com
+./smtpd_check: <queue id>: reject: MAIL from spike.porcupine.org[168.100.189.2]: 554 <foo@email-publisher.com>: Sender address rejected: Access denied; from=<foo@email-publisher.com> proto=SMTP helo=<example.tld>
+554 <foo@email-publisher.com>: Sender address rejected: Access denied
+>>> mail foo@ns1.topica.com
+./smtpd_check: <queue id>: reject: MAIL from spike.porcupine.org[168.100.189.2]: 554 <foo@ns1.topica.com>: Sender address rejected: Access denied; from=<foo@ns1.topica.com> proto=SMTP helo=<example.tld>
+554 <foo@ns1.topica.com>: Sender address rejected: Access denied
+>>> mail foo@verisign-wildcard.com
+OK
+>>> recipient_restrictions check_recipient_ns_access,hash:smtpd_check_access
+OK
+>>> rcpt foo@email-publisher.com
+./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 <foo@email-publisher.com>: Recipient address rejected: Access denied; from=<foo@verisign-wildcard.com> to=<foo@email-publisher.com> proto=SMTP helo=<example.tld>
+554 <foo@email-publisher.com>: Recipient address rejected: Access denied
+>>> rcpt foo@ns1.topica.com
+./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 <foo@ns1.topica.com>: Recipient address rejected: Access denied; from=<foo@verisign-wildcard.com> to=<foo@ns1.topica.com> proto=SMTP helo=<example.tld>
+554 <foo@ns1.topica.com>: Recipient address rejected: Access denied
+>>> rcpt foo@verisign-wildcard.com
+OK
index 0ea2b369898f7f20c43fac194443d92fe5e348f6..90c3d7850f1c150cf8f76976a89a47a328891c75 100644 (file)
@@ -409,9 +409,11 @@ static void resolve_addr(RES_CONTEXT *rp, char *addr,
                    msg_warn("do not list domain %s in BOTH %s and %s",
                             rcpt_domain, VAR_VIRT_ALIAS_DOMS,
                             VAR_RELAY_DOMAINS);
+#if 0
                if (strcasecmp(rcpt_domain, var_myorigin) == 0)
                    msg_warn("do not list $%s (%s) in %s",
                             VAR_MYORIGIN, var_myorigin, VAR_VIRT_ALIAS_DOMS);
+#endif
            }
            vstring_strcpy(channel, MAIL_SERVICE_ERROR);
            vstring_sprintf(nexthop, "User unknown%s",
index 1523c3397789a80f21ef2e2c1b6e9b0559cb51bb..e1c4bca7447752fb8e0b6cabbb733e9ce56eaf0c 100644 (file)
 #include "dict.h"
 #include "dict_ldap.h"
 #include "stringops.h"
+#include "binhash.h"
 
 /* AAARGH!! */
 
 #include "../global/mail_conf.h"
 
+typedef struct {
+    LDAP   *conn_ld;
+    int     conn_refcount;
+} LDAP_CONN;
+
 /*
  * Structure containing all the configuration parameters for a given
  * LDAP source, plus its connection handle.
@@ -223,9 +229,14 @@ typedef struct {
     char   *tls_random_file;
     char   *tls_cipher_suite;
 #endif
-    LDAP   *ld;
+    BINHASH_INFO *ht;                  /* hash entry for LDAP connection */
+    LDAP   *ld;                                /* duplicated from conn->conn_ld */
 } DICT_LDAP;
 
+#define DICT_LDAP_CONN(d) ((LDAP_CONN *)((d)->ht->value))
+
+static BINHASH *conn_hash = 0;
+
 typedef struct {
     char   *(*get_str) (const char *, const char *, const char *, int, int);
     int     (*get_int) (const char *, const char *, int, int, int);
@@ -641,6 +652,9 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
            msg_info("%s: Successful bind to server %s as %s ",
                     myname, dict_ldap->server_host, dict_ldap->bind_dn);
     }
+    /* Save connection handle in shared container */
+    DICT_LDAP_CONN(dict_ldap)->conn_ld = dict_ldap->ld;
+
     if (msg_verbose)
        msg_info("%s: Cached connection handle for LDAP source %s",
                 myname, dict_ldap->ldapsource);
@@ -648,6 +662,58 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
     return (0);
 }
 
+/*
+ * Locate or allocate connection cache entry.
+ */
+static void dict_ldap_conn_find(DICT_LDAP *dict_ldap)
+{
+    VSTRING *keybuf = vstring_alloc(10);
+    char   *key;
+    int     len;
+    int     sslon = dict_ldap->start_tls || dict_ldap->ldap_ssl;
+    LDAP_CONN *conn;
+
+#define ADDSTR(vp, s) vstring_memcat((vp), (s), strlen((s))+1)
+#define ADDINT(vp, i) vstring_sprintf_append((vp), "%lu", (unsigned long)(i))
+
+    ADDSTR(keybuf, dict_ldap->server_host);
+    ADDINT(keybuf, dict_ldap->server_port);
+    ADDINT(keybuf, dict_ldap->bind);
+    ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_dn : "");
+    ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_pw : "");
+    ADDINT(keybuf, dict_ldap->dereference);
+    ADDINT(keybuf, dict_ldap->chase_referrals);
+    ADDINT(keybuf, dict_ldap->debuglevel);
+    ADDINT(keybuf, dict_ldap->version);
+#ifdef LDAP_API_FEATURE_X_OPENLDAP
+    ADDINT(keybuf, dict_ldap->ldap_ssl);
+    ADDINT(keybuf, dict_ldap->start_tls);
+    ADDINT(keybuf, sslon ? dict_ldap->tls_require_cert : 0);
+    ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_file : "");
+    ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_dir : "");
+    ADDSTR(keybuf, sslon ? dict_ldap->tls_cert : "");
+    ADDSTR(keybuf, sslon ? dict_ldap->tls_key : "");
+    ADDSTR(keybuf, sslon ? dict_ldap->tls_random_file : "");
+    ADDSTR(keybuf, sslon ? dict_ldap->tls_cipher_suite : "");
+#endif
+
+    key = vstring_str(keybuf);
+    len = VSTRING_LEN(keybuf);
+
+    if (conn_hash == 0)
+       conn_hash = binhash_create(0);
+
+    if ((dict_ldap->ht = binhash_locate(conn_hash, key, len)) == 0) {
+       conn = (LDAP_CONN *) mymalloc(sizeof(LDAP_CONN));
+       conn->conn_ld = 0;
+       conn->conn_refcount = 0;
+       dict_ldap->ht = binhash_enter(conn_hash, key, len, (char *) conn);
+    }
+    ++DICT_LDAP_CONN(dict_ldap)->conn_refcount;
+
+    vstring_free(keybuf);
+}
+
 /*
  * expand a filter (lookup or result)
  */
@@ -907,6 +973,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
     VSTRING *escaped_name = 0,
            *filter_buf = 0;
     int     rc = 0;
+    int     sizelimit;
     char   *sub,
            *end;
 
@@ -923,11 +990,8 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
     if (dict_ldap->domain) {
        const char *p = strrchr(name, '@');
 
-       if (p != 0)
-           p = p + 1;
-       else
-           p = name;
-       if (match_list_match(dict_ldap->domain, p) == 0) {
+       if (p == 0 || p == name ||
+           match_list_match(dict_ldap->domain, ++p) == 0) {
            if (msg_verbose)
                msg_info("%s: domain of %s not found in domain list", myname,
                         name);
@@ -942,6 +1006,13 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
        result = vstring_alloc(2);
     vstring_strcpy(result, "");
 
+    /*
+     * Because the connection may be shared and invalidated via queries for
+     * another map, update private copy of "ld" from shared connection
+     * container.
+     */
+    dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld;
+
     /*
      * Connect to the LDAP server, if necessary.
      */
@@ -962,6 +1033,18 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
        msg_info("%s: Using existing connection for LDAP source %s",
                 myname, dict_ldap->ldapsource);
 
+    /*
+     * Connection caching, means that the connection handle may have the
+     * wrong size limit. Re-adjust before each query. This is cheap, just
+     * sets a field in the ldap connection handle. We also do this in the
+     * connect code, because we sometimes reconnect (below) in the middle of
+     * a query.
+     */
+    sizelimit = dict_ldap->size_limit ? dict_ldap->size_limit : LDAP_NO_LIMIT;
+    if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT, &sizelimit)
+       != LDAP_OPT_SUCCESS)
+       msg_warn("%s: %s: Unable to set query result size limit to %ld.",
+                myname, dict_ldap->ldapsource, dict_ldap->size_limit);
 
     /*
      * Prepare the query.
@@ -1046,7 +1129,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
                     myname, dict_ldap->ldapsource);
 
        ldap_unbind(dict_ldap->ld);
-       dict_ldap->ld = NULL;
+       dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
        dict_ldap_connect(dict_ldap);
 
        /*
@@ -1098,7 +1181,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
         * next lookup.
         */
        ldap_unbind(dict_ldap->ld);
-       dict_ldap->ld = NULL;
+       dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
 
        /*
         * And tell the caller to try again later.
@@ -1129,10 +1212,18 @@ static void dict_ldap_close(DICT *dict)
 {
     char   *myname = "dict_ldap_close";
     DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
+    LDAP_CONN *conn = DICT_LDAP_CONN(dict_ldap);
+    BINHASH_INFO *ht = dict_ldap->ht;
 
-    if (dict_ldap->ld)
-       ldap_unbind(dict_ldap->ld);
-
+    if (--conn->conn_refcount == 0) {
+       if (conn->conn_ld) {
+           if (msg_verbose)
+               msg_info("%s: Closed connection handle for LDAP source %s",
+                        myname, dict_ldap->ldapsource);
+           ldap_unbind(conn->conn_ld);
+       }
+       binhash_delete(conn_hash, ht->key, ht->key_len, myfree);
+    }
     myfree(dict_ldap->ldapsource);
     myfree(dict_ldap->server_host);
     myfree(dict_ldap->search_base);
@@ -1272,6 +1363,11 @@ DICT   *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
        }
     }
     dict_ldap->server_host = mystrdup(vstring_str(url_list) + 1);
+
+    /*
+     * With URL scheme, clear port to normalize connection cache key
+     */
+    dict_ldap->server_port = 0;
     if (msg_verbose)
        msg_info("%s: %s server_host URL is %s", myname, ldapsource,
                 dict_ldap->server_host);
@@ -1534,6 +1630,11 @@ DICT   *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
                 dict_ldap->debuglevel);
 #endif
 
+    /*
+     * Find or allocate shared LDAP connection container.
+     */
+    dict_ldap_conn_find(dict_ldap);
+
     /*
      * Return the new dict_ldap structure.
      */