]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.0.16-20031130
authorWietse Venema <wietse@porcupine.org>
Sun, 30 Nov 2003 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:29:15 +0000 (06:29 +0000)
12 files changed:
postfix/README_FILES/XCLIENT_README
postfix/src/global/mail_proto.h
postfix/src/global/mail_version.h
postfix/src/smtp/smtp_proto.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd.h
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_proxy.c
postfix/src/smtpd/smtpd_sasl_proto.c
postfix/src/smtpd/smtpd_state.c
postfix/src/smtpd/smtpd_xclient.c
postfix/src/util/sys_defs.h

index 3624bc89a690a098b52c8fecf12f975496e098cb..4337221eda8939ad92758c4fca38cf3c8fd956bf 100644 (file)
@@ -11,53 +11,51 @@ the SMTP client hostname, network address, and other information.
 
 2 - Logging after content filter. With Internet->MTA1->filter->MTA2
 style content filter applications, remote client information is
-lost when MTA1 gives the mail to the content filter.  This complicates
-the interpretation of the audit trail.  In order maintain audit
-trail continuity, MTA1 needs the ability to forward client information
-through the content filter to MTA2.
+lost when MTA1 gives the mail to the content filter.  To simplify
+the interpretation of MTA2 logging, it would help if MTA1 could
+forward client information through the content filter to MTA2.
 
 3 - Post-filter access control and logging. With Internet->filter->MTA
-style content filter applications, the content filter can be greatly
-simplified if it can delegate all decisions concerning mail relay
-and other access control to the MTA. This requires that the filter
-can forward client information to the MTA for access control
-purposes.
+style content filter applications, the filter can be simplified if
+it can delegate decisions concerning mail relay and other access
+control to the MTA. As in the first example, this requires that
+the filter can override the MTA's idea of the SMTP client hostname,
+network address, and other information.
 
-The preceding suggests that there is a need for two modes of
-operation:
+The preceding suggests that there is a need for two functions:
 
-  - Operation mode 1: override the SMTP server's idea of SMTP
-    client information for access control and audit trail purposes.
-    Attributes that one may want to override are:
+1 - Override the MTA's idea of SMTP client information for access
+    control and other purposes. This function is generally useful
+    for trouble shooting. The implementation can be relatively
+    straightforward because it updates already existing attributes.
 
-        client network address 
-        client hostname 
-        hostname lookup failure type (permanent or temporary)
-       client protocol (e.g., SMTP or ESMTP)
+2 - Forward remote client information for logging purposes.  This
+    function is limited mainly to environments that use SMTP-based
+    content filters. The implementation requires more invasive
+    changes to the MTA to store the additional attributes and to
+    choose between the normal attributes and the forwarded ones.
 
-  - Operation mode 2: forward remote client information for audit
-    trail purposes only.  Examples of attributes are:
+Command overview
+================
 
-        client network address 
-        client hostname 
-       client protocol (e.g., SMTP or ESMTP)
-       client SMTP HELO parameter
+The EHLO keyword associated with this extension is XCLIENT.
 
-General XCLIENT command syntax
-==============================
+The XCLIENT OVERRIDE command updates the remote client attributes
+that the MTA normally uses for access control, message headers,
+logging and so on, while the XCLIENT FORWARD command maintains an
+additional set of attributes that is meant to be used for logging
+purposes.  In the absence of forwarded attributes the MTA must use
+the normal remote client attribute values.
 
-The XCLIENT command maintains one set of attributes with surrogate
-client information. The general XCLIENT command syntax is described
-below.  Upper case and quoted strings specify terminals, lowercase
-strings specify meta terminals, SP is whitespace, and descriptive
-text is in {}.  Although shown below in upper case, command and
-attribute names are in fact case insensitive.
+The general command syntax is described below.  Upper case and
+quoted strings specify terminals, lowercase strings specify meta
+terminals, SP is whitespace, and descriptive text is enclosed in
+{}.  Although command and attribute names are shown below in upper
+case, they are in fact case insensitive.
 
-    xclient-command = XCLIENT *( SP argument )
+    xclient-command = XCLIENT SP function SP 1*( attribute )
 
-    argument = ( request | attribute )
-
-    request = ( RST | ACL | LOG )
+    function = ( OVERRIDE | FORWARD )
 
     attribute = name"="value
 
@@ -67,10 +65,10 @@ attribute names are in fact case insensitive.
 
     xtext = { attribute value encoded as per RFC 1891 }
 
-The XCLIENT command is typically sent immediately before or after
-the EHLO command, may be pipelined after the server announces ESMTP
-pipelining support, and must not be used in the middle of an SMTP
-transaction (i.e. between MAIL and DOT).
+The XCLIENT command can be sent at any time except in the middle
+of a mail delivery transaction (i.e.  between MAIL and DOT).  The
+command may be pipelined after the server EHLO reply announces
+ESMTP pipelining support.
 
 The server reply codes are as follows:
 
@@ -79,96 +77,94 @@ The server reply codes are as follows:
     250 | success 
     501 | command syntax error
     502 | unrecognized request name
-    503 | command out of order (name=value without prior ACL or LOG request)
     421 | unable to proceed
 
 The server must report success in case of an unrecognized attribute
 name, although it may log a warning.
 
-Specific XCLIENT usage scenarios
-================================
+Specific usage scenarios
+========================
 
 This section discusses the semantics of XCLIENT requests.  Specific
-syntax information is given in the next section.
-
-The RST request resets all XCLIENT attributes and disables any
-override of access control or audit trail attributes. Example:
+syntax details are given in the next section.
 
-    XCLIENT RST
+The XCLIENT OVERRIDE request modifies the attributes that the MTA
+normally uses for access control, message headers, logging, and
+for other purposes.  Attributes that are not specified in XCLIENT
+OVERRIDE requests are not modified.
 
-The ACL request resets all XCLIENT attributes and must be specified
-before sending surrogate attributes for access control and audit
-trail purposes.  Attributes that are not explicitly specified will
-default to their actual value.  Example:
+The following example overrides only the client hostname and network
+address, leaving unchanged all other client attributes such as the
+mail protocol or the hostname given in the HELO command:
 
-    XCLIENT ACL CLIENT_NAME=spike.porcupine.org
-    XCLIENT CLIENT_ADDR=168.100.189.2
+    XCLIENT OVERRIDE CLIENT_NAME=spike.porcupine.org
+    XCLIENT OVERRIDE CLIENT_ADDR=168.100.189.2
 
-This overrides only the client hostname and network address, but
-none of the other client attributes.
+The XCLIENT FORWARD request specifies surrogate client attributes for
+logging purposes. In the absence of any XCLIENT FORWARD attributes, the
+MTA must use the normal client attributes. 
 
-The LOG request resets all XCLIENT attributes and must be specified
-before sending surrogate attributes for audit trail purposes.
-Attributes that are not explicitly specified will default to the
-unknown value.  Example:
+If only a subset of all possible XCLIENT FORWARD attributes is
+specified, the unspecified attributes must either not be logged at
+all, or they must be logged as if they are unknown. This avoids
+the logging of attributes from mixed origins.
 
-    XCLIENT LOG CLIENT_NAME=spike.porcupine.org CLIENT_ADDR=168.100.189.2
-    XCLIENT HELO_NAME=spike.porcupine.org PROTOCOL=ESMTP
+The following example updates all forwarded client attributes that
+are defined in this document, leaving none at their default unknown
+value:
 
-This overrides all client attributes that are defined in this
-document, leaving none at their default unknown value.
+    XCLIENT FORWARD CLIENT_NAME=spike.porcupine.org CLIENT_ADDR=168.100.189.2
+    XCLIENT FORWARD HELO_NAME=spike.porcupine.org PROTOCOL=ESMTP
 
-Note 1: it is an error to specify name=value pairs without prior
-ACL or LOG request.
+Note 1: attributes specified with successive XCLIENT commands
+accumulate.
 
-Note 2: after an ACL or LOG request, attributes specified with
-successive XCLIENT commands accumulate until the next RST, ACL or
-LOG request.
+Note 2: XCLIENT FORWARD attributes take precedence over XCLIENT
+OVERRIDE attributes.
 
-Note 3: if one XCLIENT command specifies multiple requests (e.g.,
-both ACL and LOG), only the last request takes effect.
-
-XCLIENT attribute value details
-===============================
+Attribute value details
+=======================
 
 Attribute values are encoded as RFC 1891 xtext strings. To explicitly
 specify that an attribute value is unknown, the value must be empty;
-the client is not allowed to send its own internal representation
-for unknown information.
+the client must not send its own internal representation of unknown
+information.
 
 CLIENT_CODE specifies CLIENT_NAME hostname lookup status information.
 Values are OK (success), TEMP (temporary lookup failure) or PERM
 (permanent lookup failure). When CLIENT_CODE is set to any value
-other than OK, the CLIENT_NAME attribute is set to the unknown
-value.
+other than OK, the CLIENT_NAME attribute is automatically set to
+the unknown value.
 
 CLIENT_NAME should specify a syntactically valid domain name and
 not a numerical address.  When a null client name is specified
 (i.e.  the client name is unknown), the CLIENT_CODE attribute is
-set to PERM. When a valid domain name is specified, CLIENT_CODE is
-set to OK. The server may process a syntactically invalid domain
-name as if it were unknown.
+implicitly set to PERM. When a valid domain name is specified,
+CLIENT_CODE is implicitly set to OK. The server may process a
+syntactically invalid domain name as if it were unknown.
 
 CLIENT_ADDR must specify a numerical network address without [].
 
-PROTOCOL is a string of up to 64 printable characters.
+PROTOCOL is a string of up to 64 printable characters, where
+printable is defined by the ANSI C isascii() and isprint() predicates.
 
-HELO_NAME should be a syntactically valid domain name.
+HELO_NAME should be a syntactically valid HELO parameter value.
 
-Note 4: syntactically valid CLIENT_NAME and HELO_NAME attributes
-can be up to 255 characters long. The client must not send XCLIENT
-commands that exceed the 512 character limit of SMTP commands.
+Note 3: syntactically valid CLIENT_NAME and HELO_NAME attributes
+can be up to 255 characters long. The client must not send XCLIENT OVERRIDE
+or XCLIENT FORWARD commands that exceed the 512 character limit of SMTP
+commands.
 
-Note 5: attribute values may end up in Received: or other message
-headers.  The server may substitute any characters that are special
-according to RFC 822 or RFC 2822.
+Note 4: attribute values may end up in Received: or other message
+headers.  The receiving MTA may substitute characters in order to
+not violate RFC 822 or RFC 2822.
 
 Security
 ========
 
-The XCLIENT command changes audit trails and changes client access
-permissions. For these reasons, use of the XCLIENT command must be
-restricted to authorized clients only. 
+The XCLIENT command changes audit trails and/or client access
+permissions. For these reasons, use of these commands must be
+restricted to authorized clients only.
 
 The examples in this document assume that XCLIENT does not override
 its own access control mechanism.
@@ -179,7 +175,6 @@ SMTP connection caching
 SMTP connection caching makes it possible to deliver multiple
 messages within the same SMTP session. Thus, one persistent SMTP
 session with a content filter can carry messages from unrelated
-clients.  Attributes from one remote client should not affect the
-delivery of mail from a different remote client.  In order to
-prevent such information leakage, use the XCLIENT RST, ACL or LOG
-requests as appropriate.
+clients.  Applications should ensure that XCLIENT information from
+one remote client is properly updated before commencing delivery
+of mail from a different remote client.
index 10718cfd1b5f29192fcfc98161b7b6b081258b27..fab5dcdaa0d3016c8d51e91098e2f57fe1a3ee36 100644 (file)
@@ -151,6 +151,18 @@ extern char *mail_pathname(const char *, const char *);
 #define MAIL_ATTR_ORG_NONE     "unknown"       /* origin unknown */
 #define MAIL_ATTR_ORG_LOCAL    "local" /* local submission */
 
+ /*
+  * XCLIENT in SMTP.
+  */
+#define XCLIENT_CMD            "XCLIENT"       /* XCLIENT command */
+#define XCLIENT_OVERRIDE       "OVERRIDE"      /* override function */
+#define XCLIENT_FORWARD                "FORWARD"       /* forward function */
+#define XCLIENT_NAME           "CLIENT_NAME"   /* client name */
+#define XCLIENT_ADDR           "CLIENT_ADDR"   /* client address */
+#define XCLIENT_PROTO          "PROTOCOL"      /* client protocol */
+#define XCLIENT_CODE           "CLIENT_CODE"   /* client name status */
+#define XCLIENT_HELO           "HELO_NAME"     /* client helo */
+
  /*
   * Internal forms for unknown XCLIENT information.
   */
index ce00d7d18f7150a7c2cf7de8b91dd8921477fcaf..b5ae497c68fc0bda2189ed66f5808383eca48f89 100644 (file)
@@ -20,7 +20,7 @@
   * 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      "20031128"
+#define MAIL_RELEASE_DATE      "20031130"
 
 #define VAR_MAIL_VERSION       "mail_version"
 #define DEF_MAIL_VERSION       "2.0.16-" MAIL_RELEASE_DATE
index 243f1fab6d3c8f900ddb04409b1532cb908641dc..cb403aa36925b5e123e09e7b80ef8b163ddc093c 100644 (file)
@@ -385,8 +385,6 @@ int     smtp_xfer(SMTP_STATE *state)
     int     mail_from_rejected;
     int     downgrading;
     int     mime_errs;
-    int     send_xclient_addr = 0;
-    int     send_xclient_helo = 0;
 
     /*
      * Macros for readability.
@@ -486,21 +484,13 @@ int     smtp_xfer(SMTP_STATE *state)
      * commands rejected, DATA rejected) it forces the sender to abort the
      * SMTP dialog with RSET and QUIT.
      * 
-     * Send "XCLIENT LOG" information only if we have a surrogate remote client
-     * name and address, i.e. the mail was actually received from the
-     * network. Since "XCLIENT LOG" overrides all remote client logging
-     * attributes, there is no need to send helo or protocol information that
-     * we do not have.
+     * Update the server's remote client information to avoid leakage of past
+     * client attributes into an unrelated mail delivery.
      */
     nrcpt = 0;
-    send_xclient_addr = (state->features & SMTP_FEATURE_XCLIENT)
-       && !IS_UNK_CLNT_NAME(request->client_name)
-       && !IS_UNK_CLNT_ADDR(request->client_addr);
-    if (send_xclient_addr) {
-       send_xclient_helo = !IS_UNK_HELO_NAME(request->client_helo)
-           || !IS_UNK_PROTOCOL(request->client_proto);
+    if (var_smtp_send_xclient && (state->features & SMTP_FEATURE_XCLIENT))
        recv_state = send_state = SMTP_STATE_XCLIENT_ADDR;
-    else
+    else
        recv_state = send_state = SMTP_STATE_MAIL;
     next_rcpt = send_rcpt = recv_rcpt = 0;
     mail_from_rejected = 0;
@@ -519,37 +509,29 @@ int     smtp_xfer(SMTP_STATE *state)
            msg_panic("%s: bad sender state %d", myname, send_state);
 
            /*
-            * Build the XCLIENT command. Send what we know, converting
-            * internal form to external form. With properly sanitized
-            * information, this stays within the 512 byte command line
-            * length limit.
+            * Build the XCLIENT command. With properly sanitized
+            * information, the command length stays within the 512 byte
+            * command line length limit.
             */
        case SMTP_STATE_XCLIENT_ADDR:
-           vstring_sprintf(next_command, "XCLIENT LOG");
-           if (!IS_UNK_CLNT_NAME(request->client_name)) {
-               vstring_strcat(next_command, " CLIENT_NAME=");
+           vstring_strcpy(next_command,
+                     XCLIENT_CMD " " XCLIENT_FORWARD " " XCLIENT_NAME "=");
+           if (!IS_UNK_CLNT_NAME(request->client_name))
                xtext_quote_append(next_command, request->client_name, "");
-           }
-           if (!IS_UNK_CLNT_ADDR(request->client_addr)) {
-               vstring_strcat(next_command, " CLIENT_ADDR=");
+           vstring_strcat(next_command, " " XCLIENT_ADDR "=");
+           if (!IS_UNK_CLNT_ADDR(request->client_addr))
                xtext_quote_append(next_command, request->client_addr, "");
-           }
-           if (send_xclient_helo)
-               next_state = SMTP_STATE_XCLIENT_HELO;
-           else
-               next_state = SMTP_STATE_MAIL;
+           next_state = SMTP_STATE_XCLIENT_HELO;
            break;
 
        case SMTP_STATE_XCLIENT_HELO:
-           vstring_sprintf(next_command, "XCLIENT");
-           if (!IS_UNK_HELO_NAME(request->client_helo)) {
-               vstring_strcat(next_command, " HELO_NAME=");
+           vstring_strcpy(next_command,
+                     XCLIENT_CMD " " XCLIENT_FORWARD " " XCLIENT_HELO "=");
+           if (!IS_UNK_HELO_NAME(request->client_helo))
                xtext_quote_append(next_command, request->client_helo, "");
-           }
-           if (!IS_UNK_PROTOCOL(request->client_proto)) {
-               vstring_strcat(next_command, " PROTOCOL=");
+           vstring_strcat(next_command, " " XCLIENT_PROTO "=");
+           if (!IS_UNK_PROTOCOL(request->client_proto))
                xtext_quote_append(next_command, request->client_proto, "");
-           }
            next_state = SMTP_STATE_MAIL;
            break;
 
@@ -684,13 +666,10 @@ int     smtp_xfer(SMTP_STATE *state)
                switch (recv_state) {
 
                    /*
-                    * Ignore the XCLIENT response. No Duff device needed.
+                    * Process the XCLIENT response.
                     */
                case SMTP_STATE_XCLIENT_ADDR:
-                   if (send_xclient_helo)
-                       recv_state = SMTP_STATE_XCLIENT_HELO;
-                   else
-                       recv_state = SMTP_STATE_MAIL;
+                   recv_state = SMTP_STATE_XCLIENT_HELO;
                    break;
 
                case SMTP_STATE_XCLIENT_HELO:
index 301ebf39f7731a0c7cacf3a3be441e87da366a34..38d2424eacc1c4b1ad308df2c8370993fe7ac8b4 100644 (file)
 #include <errno.h>
 #include <ctype.h>
 #include <signal.h>
+#include <stddef.h>
 
 #ifdef STRCASECMP_IN_STRINGS_H
 #include <strings.h>
@@ -574,6 +575,7 @@ static NAMADR_LIST *verp_clients;
   * XCLIENT command.
   */
 static NAMADR_LIST *xclient_hosts;
+static int xclient_allowed;
 
  /*
   * Client connection and rate limiting.
@@ -616,10 +618,10 @@ static int sasl_client_exception(SMTPD_STATE *state)
        return (0);
 
     match = namadr_list_match(sasl_exceptions_networks,
-                             ACL_NAME(state), ACL_ADDR(state));
+                             state->name, state->addr);
 
     if (msg_verbose)
-       msg_info("sasl_exceptions: %s, match=%d", ACL_NAMADDR(state), match);
+       msg_info("sasl_exceptions: %s, match=%d", state->namaddr, match);
 
     return (match);
 }
@@ -721,10 +723,10 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
            smtpd_chat_reply(state, "250-AUTH=%s", state->sasl_mechanism_list);
     }
 #endif
-    if (namadr_list_match(verp_clients, ACL_NAME(state), ACL_ADDR(state)))
+    if (namadr_list_match(verp_clients, state->name, state->addr))
        smtpd_chat_reply(state, "250-%s", VERP_CMD);
     /* XCLIENT must not override its own access control. */
-    if (namadr_list_match(xclient_hosts, state->name, state->addr))
+    if (xclient_allowed)
        smtpd_chat_reply(state, "250-%s", XCLIENT_CMD);
     smtpd_chat_reply(state, "250 8BITMIME");
     return (0);
@@ -802,7 +804,7 @@ static void mail_open_stream(SMTPD_STATE *state, SMTPD_TOKEN *argv)
        smtpd_sasl_mail_log(state);
     else
 #endif
-       msg_info("%s: client=%s", state->queue_id, LOG_NAMADDR(state));
+       msg_info("%s: client=%s", state->queue_id, FORWARD_NAMADDR(state));
 
     /*
      * Record the time of arrival, the sender envelope address, some session
@@ -823,17 +825,17 @@ static void mail_open_stream(SMTPD_STATE *state, SMTPD_TOKEN *argv)
      */
     if (SMTPD_STAND_ALONE(state) == 0) {
        rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                   MAIL_ATTR_CLIENT_NAME, LOG_NAME(state));
+                   MAIL_ATTR_CLIENT_NAME, FORWARD_NAME(state));
        rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                   MAIL_ATTR_CLIENT_ADDR, LOG_ADDR(state));
+                   MAIL_ATTR_CLIENT_ADDR, FORWARD_ADDR(state));
        rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                   MAIL_ATTR_ORIGIN, LOG_NAMADDR(state));
+                   MAIL_ATTR_ORIGIN, FORWARD_NAMADDR(state));
        rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
                    MAIL_ATTR_HELO_NAME,
-                   IS_UNK_HELO_NAME(LOG_HELO_NAME(state)) ?
-                   HELO_NAME_UNKNOWN : LOG_HELO_NAME(state));
+                   IS_UNK_HELO_NAME(FORWARD_HELO(state)) ?
+                   HELO_NAME_UNKNOWN : FORWARD_HELO(state));
        rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
-                   MAIL_ATTR_PROTO_NAME, LOG_PROTOCOL(state));
+                   MAIL_ATTR_PROTO_NAME, FORWARD_PROTO(state));
     }
     if (state->verp_delims)
        rec_fputs(state->cleanup, REC_TYPE_VERP, state->verp_delims);
@@ -1023,8 +1025,7 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
                return (-1);
            }
 #endif
-       } else if (namadr_list_match(verp_clients, ACL_NAME(state),
-                                    ACL_ADDR(state))
+       } else if (namadr_list_match(verp_clients, state->name, state->addr)
                   && strncasecmp(arg, VERP_CMD, VERP_CMD_LEN) == 0
                   && (arg[VERP_CMD_LEN] == '=' || arg[VERP_CMD_LEN] == 0)) {
            if (arg[VERP_CMD_LEN] == 0) {
@@ -1669,6 +1670,48 @@ static int quit_cmd(SMTPD_STATE *state, int unused_argc, SMTPD_TOKEN *unused_arg
     return (0);
 }
 
+ /*
+  * Lookup tables with xclient/normal attribute offsets. Maybe we should not
+  * try so hard to make XOVERRIDE and XFORWARD attribute lists identical.
+  */
+#define FUNC_OVERRIDE  0
+#define FUNC_FORWARD   1
+
+struct attr_offset {
+    int     name[2];
+    int     addr[2];
+    int     namaddr[2];
+    int     peer_code[2];
+    int     protocol[2];
+    int     helo_name[2];
+};
+
+#define ATTR_OFFSETS(member) \
+       offsetof(SMTPD_STATE, member), offsetof(SMTPD_STATE, xclient.member)
+
+static const struct attr_offset attr_offset = {
+    ATTR_OFFSETS(name),
+    ATTR_OFFSETS(addr),
+    ATTR_OFFSETS(namaddr),
+    ATTR_OFFSETS(peer_code),
+    ATTR_OFFSETS(protocol),
+    ATTR_OFFSETS(helo_name)
+};
+
+#define PTR_ATTR(state, func, attr) (((char *) state) + attr_offset.attr[func])
+#define STR_ATTR(state, func, attr) *((char **) PTR_ATTR(state, func, attr))
+#define INT_ATTR(state, func, attr) *((int *) PTR_ATTR(state, func, attr))
+
+#define UPDATE_STR(state, func, attr, value) { \
+       if (STR_ATTR(state, func, attr)) \
+           myfree(STR_ATTR(state, func, attr)); \
+       STR_ATTR(state, func, attr) = mystrdup(value); \
+    }
+
+#define UPDATE_INT(state, func, attr, value) { \
+       INT_ATTR(state, func, attr) = (value); \
+    }
+
 /* xclient_cmd - process XCLIENT */
 
 static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
@@ -1678,202 +1721,205 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     char   *cooked_value;
     char   *arg_val;
     int     update_namaddr = 0;
+    int     function;
 
     /*
      * Sanity checks. The XCLIENT command does not override its own access
      * control.
      */
-    if (namadr_list_match(xclient_hosts, state->name, state->addr) == 0) {
+    if (IN_MAIL_TRANSACTION(state)) {
+       state->error_mask |= MAIL_ERROR_PROTOCOL;
+       smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
+       return (-1);
+    }
+    if (argc < 3) {
+       state->error_mask |= MAIL_ERROR_PROTOCOL;
+       smtpd_chat_reply(state, "501 Syntax: %s function name=value...",
+                        XCLIENT_CMD);
+       return (-1);
+    }
+    if (!xclient_allowed) {
        state->error_mask |= MAIL_ERROR_POLICY;
        smtpd_chat_reply(state, "554 Error: insufficient authorization");
        return (-1);
     }
 #define STREQ(x,y) (strcasecmp((x), (y)) == 0)
-#define UPDATE_STR(s, v) { if (s) myfree(s); s = mystrdup(v); }
 
     /*
-     * Iterate over all XCLIENT arguments.
+     * Function name: what attributes to update. Complain about unrecognized
+     * request names. The set of requests is unlikely to change.
      */
-    for (arg_no = 1; arg_no < argc; arg_no++) {
-       arg_val = argv[arg_no].strval;
+    arg_val = argv[1].strval;
+    printable(arg_val, '?');
+    if (STREQ(arg_val, XCLIENT_OVERRIDE)) {
+       function = FUNC_OVERRIDE;
+    } else if (STREQ(arg_val, XCLIENT_FORWARD)) {
+       function = FUNC_FORWARD;
+       state->xclient.used = 1;
+    } else {                                   /* error */
+       state->error_mask |= MAIL_ERROR_PROTOCOL;
+       smtpd_chat_reply(state, "501 Bad %s function: %s",
+                        XCLIENT_CMD, arg_val);
+       return (-1);
+    }
 
-       /*
-        * Request name: what should happen with the stored attributes.
-        * Complain about unrecognized request names. The set of requests is
-        * unlikely to change.
-        */
-       if ((raw_value = split_at(arg_val, '=')) == 0) {
-           printable(arg_val, '?');
-           if (STREQ(arg_val, "RST")) {        /* blow them away */
-               smtpd_xclient_reset(state, XCLIENT_OVER_NONE);
-           } else if (STREQ(arg_val, "ACL")) { /* access control and logging */
-               smtpd_xclient_reset(state, XCLIENT_OVER_ACL | XCLIENT_OVER_LOG);
-           } else if (STREQ(arg_val, "LOG")) { /* logging only */
-               smtpd_xclient_reset(state, XCLIENT_OVER_LOG);
-           } else {                            /* error */
-               state->error_mask |= MAIL_ERROR_PROTOCOL;
-               smtpd_chat_reply(state, "501 Bad request: %s", arg_val);
-               return (-1);
-           }
-       }
+    /*
+     * Iterate over all NAME=VALUE attributes.
+     */
+    for (arg_no = 2; arg_no < argc; arg_no++) {
+       arg_val = argv[arg_no].strval;
 
        /*
-        * NAME=VALUE attribute. Decode the attribute and for safety's sake
-        * mask non-printable characters in the raw and decoded values; we
-        * don't want to handle unexploded munitions. Do not complain about
+        * Decode the attribute value and for safety's sake mask
+        * non-printable characters in the raw and decoded values; we don't
+        * want to handle unexploded munitions. Do not complain about
         * unrecognized attribute names. The set of attributes may change
         * over time.
         * 
         * The client can send multiple XCLIENT attributes in a single command,
         * or multiple XCLIENT commands with fewer attributes.
         * 
-        * Note: XCLIENT ACL overrides only specific logging and access control
-        * attributes (desirable for testing), while XCLIENT LOG overrides
-        * all logging attributes (for audit trail consistency).
+        * Note: XCLIENT OVERRIDE overrides only the specified logging and
+        * access control attributes (desirable for testing), while XCLIENT
+        * FORWARD overrides all logging attributes (for audit trail
+        * consistency).
         */
-       else {
-           if (state->xclient.mode == 0) {
-               state->error_mask |= MAIL_ERROR_PROTOCOL;
-               smtpd_chat_reply(state, "503 Error: send %s ACL or LOG first",
-                                XCLIENT_CMD);
-               return (-1);
-           }
-           if (xtext_unquote(state->buffer, raw_value) == 0) {
-               state->error_mask |= MAIL_ERROR_PROTOCOL;
-               smtpd_chat_reply(state, "501 Bad attribute value syntax: %s",
-                                printable(raw_value, '?'));
-               return (-1);
-           }
-           cooked_value = printable(STR(state->buffer), '?');
-           (void) printable(raw_value, '?');
-
-           /*
-            * CLIENT_NAME=hostname. Also updates the client hostname lookup
-            * status code. Treat a numerical hostname as an unavailable
-            * name.
-            */
-           if (STREQ(arg_val, "CLIENT_NAME")) {
-               if (*raw_value && !valid_hostaddr(cooked_value, DONT_GRIPE)) {
-                   if (!valid_hostname(cooked_value, DONT_GRIPE)) {
-                       state->error_mask |= MAIL_ERROR_PROTOCOL;
-                       smtpd_chat_reply(state, "501 Bad hostname syntax: %s",
-                                        cooked_value);
-                       return (-1);
-                   }
-                   UPDATE_STR(state->xclient.name, cooked_value);
-                   state->xclient.peer_code = SMTPD_PEER_CODE_OK;
-               } else {
-                   UPDATE_STR(state->xclient.name, CLIENT_NAME_UNKNOWN);
-                   state->xclient.peer_code = SMTPD_PEER_CODE_PERM;
-               }
-               update_namaddr = 1;
-           }
+       if ((raw_value = split_at(arg_val, '=')) == 0) {
+           state->error_mask |= MAIL_ERROR_PROTOCOL;
+           smtpd_chat_reply(state, "503 Error: name=value expected");
+           return (-1);
+       }
+       if (xtext_unquote(state->buffer, raw_value) == 0) {
+           state->error_mask |= MAIL_ERROR_PROTOCOL;
+           smtpd_chat_reply(state, "501 Bad attribute value syntax: %s",
+                            printable(raw_value, '?'));
+           return (-1);
+       }
+       cooked_value = printable(STR(state->buffer), '?');
+       (void) printable(raw_value, '?');
 
-           /*
-            * CLIENT_ADDR=client network address.
-            */
-           else if (STREQ(arg_val, "CLIENT_ADDR")) {
-               if (*raw_value) {
-                   if (!valid_hostaddr(cooked_value, DONT_GRIPE)) {
-                       state->error_mask |= MAIL_ERROR_PROTOCOL;
-                       smtpd_chat_reply(state, "501 Bad address syntax: %s",
-                                        cooked_value);
-                       return (-1);
-                   }
-                   UPDATE_STR(state->xclient.addr, cooked_value);
-               } else {
-                   UPDATE_STR(state->xclient.name, CLIENT_NAME_UNKNOWN);
+       /*
+        * CLIENT_NAME=hostname. Also updates the client hostname lookup
+        * status code. Treat a numerical hostname as an unavailable name.
+        */
+       if (STREQ(arg_val, XCLIENT_NAME)) {
+           if (*raw_value && !valid_hostaddr(cooked_value, DONT_GRIPE)) {
+               if (!valid_hostname(cooked_value, DONT_GRIPE)) {
+                   state->error_mask |= MAIL_ERROR_PROTOCOL;
+                   smtpd_chat_reply(state, "501 Bad hostname syntax: %s",
+                                    cooked_value);
+                   return (-1);
                }
-               update_namaddr = 1;
+               UPDATE_STR(state, function, name, cooked_value);
+               UPDATE_INT(state, function, peer_code, SMTPD_PEER_CODE_OK);
+           } else {
+               UPDATE_STR(state, function, name, CLIENT_NAME_UNKNOWN);
+               UPDATE_INT(state, function, peer_code, SMTPD_PEER_CODE_PERM);
            }
+           update_namaddr = 1;
+       }
 
-           /*
-            * CLIENT_CODE=status. Reset the client hostname if the hostname
-            * lookup status is not OK.
-            */
-           else if (STREQ(arg_val, "CLIENT_CODE")) {
-               if (STREQ(cooked_value, "OK")) {
-                   state->xclient.peer_code = SMTPD_PEER_CODE_OK;
-               } else if (STREQ(cooked_value, "TEMP")) {
-                   state->xclient.peer_code = SMTPD_PEER_CODE_TEMP;
-                   UPDATE_STR(state->xclient.name, CLIENT_NAME_UNKNOWN);
-                   update_namaddr = 1;
-               } else if (STREQ(cooked_value, "PERM")) {
-                   state->xclient.peer_code = SMTPD_PEER_CODE_PERM;
-                   UPDATE_STR(state->xclient.name, CLIENT_NAME_UNKNOWN);
-                   update_namaddr = 1;
-               } else {
+       /*
+        * CLIENT_ADDR=client network address.
+        */
+       else if (STREQ(arg_val, "CLIENT_ADDR")) {
+           if (*raw_value) {
+               if (!valid_hostaddr(cooked_value, DONT_GRIPE)) {
                    state->error_mask |= MAIL_ERROR_PROTOCOL;
-                   smtpd_chat_reply(state, "501 Bad hostname status: %s",
+                   smtpd_chat_reply(state, "501 Bad address syntax: %s",
                                     cooked_value);
                    return (-1);
                }
+               UPDATE_STR(state, function, addr, cooked_value);
+           } else {
+               UPDATE_STR(state, function, name, CLIENT_ADDR_UNKNOWN);
            }
+           update_namaddr = 1;
+       }
 
-           /*
-            * HELO_NAME=hostname. An empty value means the information was
-            * not provided by the client and that we must not fall back to
-            * the non-XCLIENT value. Disallow characters that could mess up
-            * our own Received: message headers but allow [].
-            */
-           else if (STREQ(arg_val, "HELO_NAME")) {
-               if (*raw_value) {
-                   if (strlen(cooked_value) > VALID_HOSTNAME_LEN) {
-                       state->error_mask |= MAIL_ERROR_PROTOCOL;
-                       smtpd_chat_reply(state, "501 Bad HELO syntax: %s",
-                                        cooked_value);
-                       return (-1);
-                   }
-                   neuter(cooked_value, "<>()\\\";:@", '?');
-                   UPDATE_STR(state->xclient.helo_name, cooked_value);
-               } else {
-                   UPDATE_STR(state->xclient.helo_name, HELO_NAME_UNKNOWN);
-               }
+       /*
+        * CLIENT_CODE=status. Reset the client hostname if the hostname
+        * lookup status is not OK.
+        */
+       else if (STREQ(arg_val, "CLIENT_CODE")) {
+           if (STREQ(cooked_value, "OK")) {
+               UPDATE_INT(state, function, peer_code, SMTPD_PEER_CODE_OK);
+           } else if (STREQ(cooked_value, "TEMP")) {
+               UPDATE_INT(state, function, peer_code, SMTPD_PEER_CODE_TEMP);
+               UPDATE_STR(state, function, name, CLIENT_NAME_UNKNOWN);
+               update_namaddr = 1;
+           } else if (STREQ(cooked_value, "PERM")) {
+               UPDATE_INT(state, function, peer_code, SMTPD_PEER_CODE_PERM);
+               UPDATE_STR(state, function, name, CLIENT_NAME_UNKNOWN);
+               update_namaddr = 1;
+           } else {
+               state->error_mask |= MAIL_ERROR_PROTOCOL;
+               smtpd_chat_reply(state, "501 Bad hostname status: %s",
+                                cooked_value);
+               return (-1);
            }
+       }
 
-           /*
-            * PROTOCOL=protocol name. Disallow characters that could mess up
-            * our own Received: message headers.
-            */
-           else if (STREQ(arg_val, "PROTOCOL")) {
-               if (*raw_value) {
-                   if (*cooked_value == 0 || strlen(cooked_value) > 64) {
-                       state->error_mask |= MAIL_ERROR_PROTOCOL;
-                       smtpd_chat_reply(state, "501 Bad protocol syntax: %s",
-                                        cooked_value);
-                       return (-1);
-                   }
-                   neuter(cooked_value, "[]<>()\\\";:@", '?');
-                   UPDATE_STR(state->xclient.protocol, cooked_value);
-               } else {
-                   UPDATE_STR(state->xclient.protocol, PROTOCOL_UNKNOWN);
+       /*
+        * HELO_NAME=hostname. An empty value means the information was not
+        * provided by the client and that we must not fall back to the
+        * non-XCLIENT value. Disallow characters that could mess up our own
+        * Received: message headers but allow [].
+        */
+       else if (STREQ(arg_val, "HELO_NAME")) {
+           if (*raw_value) {
+               if (strlen(cooked_value) > VALID_HOSTNAME_LEN) {
+                   state->error_mask |= MAIL_ERROR_PROTOCOL;
+                   smtpd_chat_reply(state, "501 Bad HELO syntax: %s",
+                                    cooked_value);
+                   return (-1);
                }
+               neuter(cooked_value, "<>()\\\";:@", '?');
+               UPDATE_STR(state, function, helo_name, cooked_value);
+           } else {
+               UPDATE_STR(state, function, helo_name, HELO_NAME_UNKNOWN);
            }
+       }
 
-           /*
-            * Unknown attribute name. Don't complain, and log a warning.
-            * Logging is safe because only authorized clients can issue
-            * XCLIENT commands.
-            */
-           else {
-               msg_warn("unknown %s attribute from %s: %s=%s",
-                        XCLIENT_CMD, state->namaddr, arg_val, cooked_value);
+       /*
+        * PROTOCOL=protocol name. Disallow characters that could mess up our
+        * own Received: message headers.
+        */
+       else if (STREQ(arg_val, "PROTOCOL")) {
+           if (*raw_value) {
+               if (*cooked_value == 0 || strlen(cooked_value) > 64) {
+                   state->error_mask |= MAIL_ERROR_PROTOCOL;
+                   smtpd_chat_reply(state, "501 Bad protocol syntax: %s",
+                                    cooked_value);
+                   return (-1);
+               }
+               neuter(cooked_value, "[]<>()\\\";:@", '?');
+               UPDATE_STR(state, function, protocol, cooked_value);
+           } else {
+               UPDATE_STR(state, function, protocol, PROTOCOL_UNKNOWN);
            }
        }
+
+       /*
+        * Unknown attribute name. Don't complain, and log a warning. Logging
+        * is safe because only authorized clients can issue XCLIENT
+        * commands.
+        */
+       else {
+           msg_warn("unknown %s attribute from %s: %s=%s",
+                    XCLIENT_CMD, state->namaddr, arg_val, cooked_value);
+       }
     }
 
     /*
      * Update the combined name and address when either has changed.
      */
-#define MAYBE_OVERRIDE(state, attr) \
-       ((state)->xclient.attr ? (state)->xclient.attr : (state)->attr)
-
     if (update_namaddr) {
-       if (state->xclient.namaddr)
-           myfree(state->xclient.namaddr);
-       state->xclient.namaddr =
-           concatenate(MAYBE_OVERRIDE(state, name), "[",
-                       MAYBE_OVERRIDE(state, addr), "]",
+       if (STR_ATTR(state, function, namaddr))
+           myfree(STR_ATTR(state, function, namaddr));
+       STR_ATTR(state, function, namaddr) =
+           concatenate(STR_ATTR(state, function, name), "[",
+                       STR_ATTR(state, function, addr), "]",
                        (char *) 0);
     }
     smtpd_chat_reply(state, "250 Ok");
@@ -2142,6 +2188,12 @@ static void smtpd_service(VSTREAM *stream, char *service, char **argv)
     smtpd_state_init(&state, stream);
     msg_info("connect from %s[%s]", state.name, state.addr);
 
+    /*
+     * XCLIENT must not override its own access control.
+     */
+    xclient_allowed =
+       namadr_list_match(xclient_hosts, state.name, state.addr);
+
     /*
      * See if we need to turn on verbose logging for this client.
      */
index 0b418f59cdcaff6e72f83df1052ffbd2573cc413..3b0a8d037fa19ae17efa779852e0281addd05928 100644 (file)
@@ -46,7 +46,7 @@ typedef struct SMTPD_DEFER {
 } SMTPD_DEFER;
 
 typedef struct SMTPD_XCLIENT_ATTR {
-    int     mode;                      /* none, log, acl */
+    int     used;                      /* status */
     char   *name;                      /* name for access control */
     char   *addr;                      /* address for access control */
     char   *namaddr;                   /* name[address] */
@@ -146,34 +146,20 @@ extern void smtpd_peer_reset(SMTPD_STATE *state);
   * makes no sense to maintain separate attribute sets for XCLIENT LOG or
   * XCLIENT ACL, so we set a flag to distinguish purpose.
   */
-#define XCLIENT_CMD            "XCLIENT"       /* XCLIENT command */
-#define XCLIENT_EHLO           "XCLIENT"       /* ESMTP advertisement */
+#define SMTPD_FEATURE_XCLIENT (1<<0)   /* XCLIENT supported */
 
-#define SMTPD_FEATURE_XCLIENT  (1<<0)  /* server supports XCLIENT */
+#define MAYBE_FORWARD(s, a) \
+       ((s)->xclient.used ? (s)->xclient.a : (s)->a)
 
-#define XCLIENT_OVER_NONE      (0)     /* override reset */
-#define XCLIENT_OVER_ACL       (1<<0)  /* override access control */
-#define XCLIENT_OVER_LOG       (1<<1)  /* override logging */
-
-#define XCLIENT_OVER(s, m, a) \
-       (((s)->xclient.mode & (m)) && (s)->xclient.a ? (s)->xclient.a : (s)->a)
-
-#define ACL_ADDR(s)            XCLIENT_OVER((s), XCLIENT_OVER_ACL, addr)
-#define ACL_NAME(s)            XCLIENT_OVER((s), XCLIENT_OVER_ACL, name)
-#define ACL_NAMADDR(s)         XCLIENT_OVER((s), XCLIENT_OVER_ACL, namaddr)
-#define ACL_PEER_CODE(s)       XCLIENT_OVER((s), XCLIENT_OVER_ACL, peer_code)
-#define ACL_PROTOCOL(s)                XCLIENT_OVER((s), XCLIENT_OVER_ACL, protocol)
-#define ACL_HELO_NAME(s)       XCLIENT_OVER((s), XCLIENT_OVER_ACL, helo_name)
-
-#define LOG_ADDR(s)            XCLIENT_OVER((s), XCLIENT_OVER_LOG, addr)
-#define LOG_NAME(s)            XCLIENT_OVER((s), XCLIENT_OVER_LOG, name)
-#define LOG_NAMADDR(s)         XCLIENT_OVER((s), XCLIENT_OVER_LOG, namaddr)
-#define LOG_PEER_CODE(s)       XCLIENT_OVER((s), XCLIENT_OVER_LOG, peer_code)
-#define LOG_PROTOCOL(s)                XCLIENT_OVER((s), XCLIENT_OVER_LOG, protocol)
-#define LOG_HELO_NAME(s)       XCLIENT_OVER((s), XCLIENT_OVER_LOG, helo_name)
+#define FORWARD_ADDR(s)                MAYBE_FORWARD((s), addr)
+#define FORWARD_NAME(s)                MAYBE_FORWARD((s), name)
+#define FORWARD_NAMADDR(s)     MAYBE_FORWARD((s), namaddr)
+#define FORWARD_CODE(s)                MAYBE_FORWARD((s), peer_code)
+#define FORWARD_PROTO(s)       MAYBE_FORWARD((s), protocol)
+#define FORWARD_HELO(s)                MAYBE_FORWARD((s), helo_name)
 
 extern void smtpd_xclient_init(SMTPD_STATE *state);
-extern void smtpd_xclient_reset(SMTPD_STATE *state, int);
+extern void smtpd_xclient_reset(SMTPD_STATE *state);
 
  /*
   * Transparency: before mail is queued, do we check for unknown recipients,
index b1688b59a738bb112676e1d62c1eb509b0ddff6f..ab45aa3213b01b005cd16cc83a5f9be5e0de079a 100644 (file)
@@ -809,15 +809,15 @@ static void log_whatsup(SMTPD_STATE *state, const char *whatsup,
 
     vstring_sprintf(buf, "%s: %s: %s from %s: %s;",
                    state->queue_id ? state->queue_id : "NOQUEUE",
-                   whatsup, state->where, LOG_NAMADDR(state), text);
+                   whatsup, state->where, state->namaddr, text);
     if (state->sender)
        vstring_sprintf_append(buf, " from=<%s>", state->sender);
     if (state->recipient)
        vstring_sprintf_append(buf, " to=<%s>", state->recipient);
-    if (!IS_UNK_PROTOCOL(ACL_PROTOCOL(state)))
-       vstring_sprintf_append(buf, " proto=%s", ACL_PROTOCOL(state));
-    if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state)))
-       vstring_sprintf_append(buf, " helo=<%s>", ACL_HELO_NAME(state));
+    if (state->protocol)
+       vstring_sprintf_append(buf, " proto=%s", state->protocol);
+    if (state->helo_name)
+       vstring_sprintf_append(buf, " helo=<%s>", state->helo_name);
     msg_info("%s", STR(buf));
     vstring_free(buf);
 }
@@ -968,14 +968,14 @@ static int reject_unknown_client(SMTPD_STATE *state)
     char   *myname = "reject_unknown_client";
 
     if (msg_verbose)
-       msg_info("%s: %s %s", myname, ACL_NAME(state), ACL_ADDR(state));
+       msg_info("%s: %s %s", myname, state->name, state->addr);
 
-    if (IS_UNK_CLNT_NAME(ACL_NAME(state)))
+    if (IS_UNK_CLNT_NAME(state->name))
        return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
                 "%d Client host rejected: cannot find your hostname, [%s]",
-                             ACL_PEER_CODE(state) == SMTPD_PEER_CODE_PERM ?
+                                  state->peer_code == SMTPD_PEER_CODE_PERM ?
                                   var_unk_client_code : 450,
-                                  ACL_ADDR(state)));
+                                  state->addr));
     return (SMTPD_CHECK_DUNNO);
 }
 
@@ -986,9 +986,9 @@ static int permit_mynetworks(SMTPD_STATE *state)
     char   *myname = "permit_mynetworks";
 
     if (msg_verbose)
-       msg_info("%s: %s %s", myname, ACL_NAME(state), ACL_ADDR(state));
+       msg_info("%s: %s %s", myname, state->name, state->addr);
 
-    if (namadr_list_match(mynetworks, ACL_NAME(state), ACL_ADDR(state)))
+    if (namadr_list_match(mynetworks, state->name, state->addr))
        return (SMTPD_CHECK_OK);
     return (SMTPD_CHECK_DUNNO);
 }
@@ -1207,7 +1207,7 @@ static int check_relay_domains(SMTPD_STATE *state, char *recipient,
     /*
      * Permit if the client matches the relay_domains list.
      */
-    if (domain_list_match(relay_domains, ACL_NAME(state)))
+    if (domain_list_match(relay_domains, state->name))
        return (SMTPD_CHECK_OK);
 
     /*
@@ -1309,7 +1309,7 @@ static int reject_unauth_pipelining(SMTPD_STATE *state,
     if (state->client != 0
        && SMTPD_STAND_ALONE(state) == 0
        && vstream_peek(state->client) > 0
-       && (strcasecmp(ACL_PROTOCOL(state), MAIL_PROTO_ESMTP) != 0
+       && (strcasecmp(state->protocol, MAIL_PROTO_ESMTP) != 0
            || strcasecmp(state->where, "DATA") == 0)) {
        return (smtpd_check_reject(state, MAIL_ERROR_PROTOCOL,
           "503 <%s>: %s rejected: Improper use of SMTP command pipelining",
@@ -2518,14 +2518,13 @@ static const char *smtpd_expand_lookup(const char *name, int unused_mode,
      * Return NULL only for non-existent names.
      */
     if (STREQ(name, MAIL_ATTR_CLIENT)) {
-       return (ACL_NAMADDR(state));
+       return (state->namaddr);
     } else if (STREQ(name, MAIL_ATTR_CLIENT_ADDR)) {
-       return (ACL_ADDR(state));
+       return (state->addr);
     } else if (STREQ(name, MAIL_ATTR_CLIENT_NAME)) {
-       return (ACL_NAME(state));
+       return (state->name);
     } else if (STREQ(name, MAIL_ATTR_HELO_NAME)) {
-       return (IS_UNK_HELO_NAME(ACL_HELO_NAME(state)) ?
-               "" : ACL_HELO_NAME(state));
+       return (state->helo_name ?  state->helo_name : "");
     } else if (STREQN(name, MAIL_ATTR_SENDER, CONST_LEN(MAIL_ATTR_SENDER))) {
        return (smtpd_expand_addr(state->expand_buf, state->sender,
                                  name, CONST_LEN(MAIL_ATTR_SENDER)));
@@ -2821,7 +2820,7 @@ static int reject_maps_rbl(SMTPD_STATE *state)
     static int warned;
 
     if (msg_verbose)
-       msg_info("%s: %s", myname, ACL_ADDR(state));
+       msg_info("%s: %s", myname, state->addr);
 
     if (warned == 0) {
        warned++;
@@ -2830,7 +2829,7 @@ static int reject_maps_rbl(SMTPD_STATE *state)
                 REJECT_MAPS_RBL, var_mail_name, REJECT_RBL_CLIENT);
     }
     while ((rbl_domain = mystrtok(&bp, " \t\r\n,")) != 0) {
-       result = reject_rbl_addr(state, rbl_domain, ACL_ADDR(state),
+       result = reject_rbl_addr(state, rbl_domain, state->addr,
                                 SMTPD_NAME_CLIENT);
        if (result != SMTPD_CHECK_DUNNO)
            break;
@@ -2907,12 +2906,11 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
                          ATTR_FLAG_NONE,       /* Query attributes. */
                        ATTR_TYPE_STR, MAIL_ATTR_REQ, "smtpd_access_policy",
                          ATTR_TYPE_STR, MAIL_ATTR_PROTO_STATE, state->where,
-                  ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, ACL_PROTOCOL(state),
-                     ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, ACL_ADDR(state),
-                     ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, ACL_NAME(state),
+                      ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, state->protocol,
+                         ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, state->addr,
+                         ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, state->name,
                          ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME,
-                         IS_UNK_HELO_NAME(ACL_HELO_NAME(state)) ?
-                         "" : ACL_HELO_NAME(state),
+                         state->helo_name ?  state->helo_name : "",
                          ATTR_TYPE_STR, MAIL_ATTR_SENDER,
                          state->sender ? state->sender : "",
                          ATTR_TYPE_STR, MAIL_ATTR_RECIP,
@@ -3077,8 +3075,8 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
        } else if (strcasecmp(name, PERMIT_MYNETWORKS) == 0) {
            status = permit_mynetworks(state);
        } else if (is_map_command(state, name, CHECK_CLIENT_ACL, &cpp)) {
-           status = check_namadr_access(state, *cpp, ACL_NAME(state), ACL_ADDR(state),
-                                        FULL, &found, ACL_NAMADDR(state),
+           status = check_namadr_access(state, *cpp, state->name, state->addr,
+                                        FULL, &found, state->namaddr,
                                         SMTPD_NAME_CLIENT, def_acl);
        } else if (strcasecmp(name, REJECT_MAPS_RBL) == 0) {
            status = reject_maps_rbl(state);
@@ -3087,7 +3085,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
            if (cpp[1] == 0)
                msg_warn("restriction %s requires domain name argument", name);
            else
-               status = reject_rbl_addr(state, *(cpp += 1), ACL_ADDR(state),
+               status = reject_rbl_addr(state, *(cpp += 1), state->addr,
                                         SMTPD_NAME_CLIENT);
        } else if (strcasecmp(name, REJECT_RHSBL_CLIENT) == 0) {
            if (cpp[1] == 0)
@@ -3095,73 +3093,69 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
                         name);
            else {
                cpp += 1;
-               if (!IS_UNK_CLNT_NAME(ACL_NAME(state)))
-                   status = reject_rbl_domain(state, *cpp, ACL_NAME(state),
+               if (!IS_UNK_CLNT_NAME(state->name))
+                   status = reject_rbl_domain(state, *cpp, state->name,
                                               SMTPD_NAME_CLIENT);
            }
        }
 
        /*
         * HELO/EHLO parameter restrictions.
-        * 
-        * XXX With XCLIENT overrides, a zero-length name means the client did
-        * not send a HELO/EHLO command. Do not fall back to the non-XCLIENT
-        * HELO/EHLO value.
         */
        else if (is_map_command(state, name, CHECK_HELO_ACL, &cpp)) {
-           if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state)))
-               status = check_domain_access(state, *cpp, ACL_HELO_NAME(state),
-                                        FULL, &found, ACL_HELO_NAME(state),
+           if (state->helo_name)
+               status = check_domain_access(state, *cpp, state->helo_name,
+                                            FULL, &found, state->helo_name,
                                             SMTPD_NAME_HELO, def_acl);
        } else if (strcasecmp(name, REJECT_INVALID_HOSTNAME) == 0) {
-           if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
-               if (*ACL_HELO_NAME(state) != '[')
-                   status = reject_invalid_hostname(state, ACL_HELO_NAME(state),
-                                    ACL_HELO_NAME(state), SMTPD_NAME_HELO);
+           if (state->helo_name) {
+               if (*state->helo_name != '[')
+                   status = reject_invalid_hostname(state, state->helo_name,
+                                        state->helo_name, SMTPD_NAME_HELO);
                else
-                   status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
-                                    ACL_HELO_NAME(state), SMTPD_NAME_HELO);
+                   status = reject_invalid_hostaddr(state, state->helo_name,
+                                        state->helo_name, SMTPD_NAME_HELO);
            }
        } else if (strcasecmp(name, REJECT_UNKNOWN_HOSTNAME) == 0) {
-           if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
-               if (*ACL_HELO_NAME(state) != '[')
-                   status = reject_unknown_hostname(state, ACL_HELO_NAME(state),
-                                    ACL_HELO_NAME(state), SMTPD_NAME_HELO);
+           if (state->helo_name) {
+               if (*state->helo_name != '[')
+                   status = reject_unknown_hostname(state, state->helo_name,
+                                        state->helo_name, SMTPD_NAME_HELO);
                else
-                   status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
-                                    ACL_HELO_NAME(state), SMTPD_NAME_HELO);
+                   status = reject_invalid_hostaddr(state, state->helo_name,
+                                        state->helo_name, SMTPD_NAME_HELO);
            }
        } else if (strcasecmp(name, PERMIT_NAKED_IP_ADDR) == 0) {
            msg_warn("restriction %s is deprecated. Use %s instead",
                     PERMIT_NAKED_IP_ADDR, PERMIT_MYNETWORKS);
-           if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
-               if (ACL_HELO_NAME(state)[strspn(ACL_HELO_NAME(state), "0123456789.")] == 0
-                   && (status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
-                              ACL_HELO_NAME(state), SMTPD_NAME_HELO)) == 0)
+           if (state->helo_name) {
+               if (state->helo_name[strspn(state->helo_name, "0123456789.")] == 0
+               && (status = reject_invalid_hostaddr(state, state->helo_name,
+                                  state->helo_name, SMTPD_NAME_HELO)) == 0)
                    status = SMTPD_CHECK_OK;
            }
        } else if (is_map_command(state, name, CHECK_HELO_NS_ACL, &cpp)) {
-           if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
-               status = check_server_access(state, *cpp, ACL_HELO_NAME(state),
-                                            T_NS, ACL_HELO_NAME(state),
+           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, ACL_HELO_NAME(state));
+               forbid_whitelist(state, name, status, state->helo_name);
            }
        } else if (is_map_command(state, name, CHECK_HELO_MX_ACL, &cpp)) {
-           if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
-               status = check_server_access(state, *cpp, ACL_HELO_NAME(state),
-                                            T_MX, ACL_HELO_NAME(state),
+           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, ACL_HELO_NAME(state));
+               forbid_whitelist(state, name, status, state->helo_name);
            }
        } else if (strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) {
-           if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
-               if (*ACL_HELO_NAME(state) != '[')
-                   status = reject_non_fqdn_hostname(state, ACL_HELO_NAME(state),
-                                    ACL_HELO_NAME(state), SMTPD_NAME_HELO);
+           if (state->helo_name) {
+               if (*state->helo_name != '[')
+                   status = reject_non_fqdn_hostname(state, state->helo_name,
+                                        state->helo_name, SMTPD_NAME_HELO);
                else
-                   status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
-                                    ACL_HELO_NAME(state), SMTPD_NAME_HELO);
+                   status = reject_invalid_hostaddr(state, state->helo_name,
+                                        state->helo_name, SMTPD_NAME_HELO);
            }
        } else if (strcasecmp(name, REJECT_RHSBL_HELO) == 0) {
            if (cpp[1] == 0)
@@ -3169,8 +3163,8 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
                         name);
            else {
                cpp += 1;
-               if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state)))
-                   status = reject_rbl_domain(state, *cpp, ACL_HELO_NAME(state),
+               if (state->helo_name)
+                   status = reject_rbl_domain(state, *cpp, state->helo_name,
                                               SMTPD_NAME_HELO);
            }
        }
@@ -3367,7 +3361,7 @@ char   *smtpd_check_client(SMTPD_STATE *state)
     /*
      * Initialize.
      */
-    if (ACL_NAME(state) == 0 || ACL_ADDR(state) == 0)
+    if (state->name == 0 || state->addr == 0)
        return (0);
 
 #define SMTPD_CHECK_RESET() { \
@@ -3387,7 +3381,7 @@ char   *smtpd_check_client(SMTPD_STATE *state)
     SMTPD_CHECK_RESET();
     status = setjmp(smtpd_check_buf);
     if (status == 0 && client_restrctions->argc)
-       status = generic_checks(state, client_restrctions, ACL_NAMADDR(state),
+       status = generic_checks(state, client_restrctions, state->namaddr,
                                SMTPD_NAME_CLIENT, CHECK_CLIENT_ACL);
     state->defer_if_permit_client = state->defer_if_permit.active;
 
@@ -3441,7 +3435,7 @@ char   *smtpd_check_helo(SMTPD_STATE *state, char *helohost)
     SMTPD_CHECK_RESET();
     status = setjmp(smtpd_check_buf);
     if (status == 0 && helo_restrctions->argc)
-       status = generic_checks(state, helo_restrctions, ACL_HELO_NAME(state),
+       status = generic_checks(state, helo_restrctions, state->helo_name,
                                SMTPD_NAME_HELO, CHECK_HELO_ACL);
     state->defer_if_permit_helo = state->defer_if_permit.active;
 
@@ -3553,7 +3547,7 @@ char   *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient)
      */
     if (var_smtpd_delay_reject)
        if ((err = smtpd_check_client(state)) != 0
-           || (err = smtpd_check_helo(state, ACL_HELO_NAME(state))) != 0
+           || (err = smtpd_check_helo(state, state->helo_name)) != 0
            || (err = smtpd_check_mail(state, state->sender)) != 0)
            SMTPD_CHECK_RCPT_RETURN(err);
 
@@ -3622,7 +3616,7 @@ char   *smtpd_check_etrn(SMTPD_STATE *state, char *domain)
      */
     if (var_smtpd_delay_reject)
        if ((err = smtpd_check_client(state)) != 0
-           || (err = smtpd_check_helo(state, ACL_HELO_NAME(state))) != 0)
+           || (err = smtpd_check_helo(state, state->helo_name)) != 0)
            SMTPD_CHECK_ETRN_RETURN(err);
 
     /*
index 3999c993bda422f9a71703131f4f34e064fe18a1..f81ce2dea3d21dfba614f26386efd6533dc835c3 100644 (file)
@@ -241,29 +241,31 @@ int     smtpd_proxy_open(SMTPD_STATE *state, const char *service,
     while ((line = mystrtok(&lines, "\n")) != 0)
        if (ISDIGIT(line[0]) && ISDIGIT(line[1]) && ISDIGIT(line[2])
            && (line[3] == ' ' || line[3] == '-')
-           && strcmp(line + 4, XCLIENT_EHLO) == 0)
+           && strcmp(line + 4, XCLIENT_CMD) == 0)
            state->proxy_features |= SMTPD_FEATURE_XCLIENT;
 
     /*
-     * Send our XCLIENT attributes. Transform internal forms to external
+     * Send all XCLIENT attributes. Transform internal forms to external
      * forms and encode the result as xtext.
      */
     if (state->proxy_features & SMTPD_FEATURE_XCLIENT) {
        buf = vstring_alloc(100);
-       vstring_sprintf(buf, "%s LOG CLIENT_NAME=", XCLIENT_CMD);
-       if (!IS_UNK_CLNT_NAME(LOG_NAME(state)))
-           xtext_quote_append(buf, LOG_NAME(state), "");
-       vstring_strcat(buf, " CLIENT_ADDR=");
-       if (!IS_UNK_CLNT_ADDR(LOG_ADDR(state)))
-           xtext_quote_append(buf, LOG_ADDR(state), "");
+       vstring_strcpy(buf, XCLIENT_CMD " " XCLIENT_FORWARD
+                      " " XCLIENT_NAME "=");
+       if (!IS_UNK_CLNT_NAME(FORWARD_NAME(state)))
+           xtext_quote_append(buf, FORWARD_NAME(state), "");
+       vstring_strcat(buf, " " XCLIENT_ADDR "=");
+       if (!IS_UNK_CLNT_ADDR(FORWARD_ADDR(state)))
+           xtext_quote_append(buf, FORWARD_ADDR(state), "");
        bad = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, "%s", STR(buf));
        if (bad == 0) {
-           vstring_sprintf(buf, "%s HELO_NAME=", XCLIENT_CMD);
-           if (!IS_UNK_HELO_NAME(LOG_HELO_NAME(state)))
-               xtext_quote_append(buf, LOG_HELO_NAME(state), "");
-           vstring_strcat(buf, " PROTOCOL=");
-           if (!IS_UNK_PROTOCOL(LOG_PROTOCOL(state)))
-               xtext_quote_append(buf, LOG_PROTOCOL(state), "");
+           vstring_strcpy(buf, XCLIENT_CMD " " XCLIENT_FORWARD
+                          " " XCLIENT_HELO "=");
+           if (!IS_UNK_HELO_NAME(FORWARD_HELO(state)))
+               xtext_quote_append(buf, FORWARD_HELO(state), "");
+           vstring_strcat(buf, " " XCLIENT_PROTO "=");
+           if (!IS_UNK_PROTOCOL(FORWARD_PROTO(state)))
+               xtext_quote_append(buf, FORWARD_PROTO(state), "");
            bad = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, "%s", STR(buf));
        }
        vstring_free(buf);
index 4e412c50b7af5a2f5985c7ea363dddafa6aacc5f..74f0110cff57c88247c1e739f059c36d3d9a6980 100644 (file)
@@ -198,7 +198,7 @@ void    smtpd_sasl_mail_log(SMTPD_STATE *state)
 #define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))
 
     msg_info("%s: client=%s%s%s%s%s%s%s",
-            state->queue_id, LOG_NAMADDR(state),
+            state->queue_id, FORWARD_NAMADDR(state),
             IFELSE(state->sasl_method, ", sasl_method=", ""),
             IFELSE(state->sasl_method, state->sasl_method, ""),
             IFELSE(state->sasl_username, ", sasl_username=", ""),
index d5988b41681b67f79f9ab0e113d2f6d035ae5d48..bbfe325ea3006733fc78c0e353c1f11dd6a09b54 100644 (file)
@@ -141,7 +141,7 @@ void    smtpd_state_reset(SMTPD_STATE *state)
     if (state->buffer)
        vstring_free(state->buffer);
     smtpd_peer_reset(state);
-    smtpd_xclient_reset(state, XCLIENT_OVER_NONE);
+    smtpd_xclient_reset(state);
     if (state->defer_if_permit.reason)
        vstring_free(state->defer_if_permit.reason);
     if (state->defer_if_reject.reason)
index 75c92d9460b51094c4b8630d5f798ba42d407a8f..d021c4585fb2d5ff2235b18630129cbaa89402a0 100644 (file)
@@ -9,9 +9,8 @@
 /*     void    smtpd_xclient_init(state)
 /*     SMTPD_STATE *state;
 /*
-/*     void    smtpd_xclient_reset(state, mode)
+/*     void    smtpd_xclient_reset(state)
 /*     SMTPD_STATE *state;
-/*     int     mode;
 /* DESCRIPTION
 /*     smtpd_xclient_init() initializes state variables that are
 /*     used for storage of XCLIENT command parameters.
 /*     structure for access control or logging purposes.
 /*
 /*     smtpd_xclient_reset() releases memory allocated after the return
-/*     from smtpd_xclient_init() and optionally presets the state variables 
-/*     to defaults that are suitable for the specified mode:
-/* .IP XCLIENT_OVER_NONE
-/*     This should be used after the XCLIENT RST request.
-/* .IP XCLIENT_OVER_ACL|XCLIENT_OVER_LOG
-/*     This should be used after the XCLIENT ACL request.
-/* .IP XCLIENT_OVER_LOG
-/*     This should be used after the XCLIENT LOG request.
+/*     from smtpd_xclient_init().
 /* LICENSE
 /* .ad
 /* .fi
 
 void    smtpd_xclient_init(SMTPD_STATE *state)
 {
-    state->xclient.mode = 0;
-    state->xclient.name = 0;
-    state->xclient.addr = 0;
-    state->xclient.namaddr = 0;
+    state->xclient.used = 0;
+    state->xclient.name = mystrdup(CLIENT_NAME_UNKNOWN);
+    state->xclient.addr = mystrdup(CLIENT_ADDR_UNKNOWN);
+    state->xclient.namaddr = mystrdup(CLIENT_NAMADDR_UNKNOWN);
     state->xclient.peer_code = 0;
-    state->xclient.protocol = 0;
-    state->xclient.helo_name = 0;
+    state->xclient.protocol = mystrdup(PROTOCOL_UNKNOWN);
+    state->xclient.helo_name = mystrdup(HELO_NAME_UNKNOWN);
 }
 
 /* smtpd_xclient_reset - reset XCLIENT attributes */
 
-void    smtpd_xclient_reset(SMTPD_STATE *state, int mode)
+void    smtpd_xclient_reset(SMTPD_STATE *state)
 {
-    switch (mode) {
-
-       /*
-        * Can't happen.
-        */
-    default:
-       msg_panic("smtpd_xclient_reset: unknown mode 0x%x", mode);
-
-       /*
-        * Restore smtpd_xclient_init() result. Allow selective override.
-        * This is desirable for access rule testing.
-        */
-#define FREE_AND_WIPE(s) { if (s) { myfree(s); s = 0; } }
-
-    case XCLIENT_OVER_NONE:
-    case XCLIENT_OVER_ACL | XCLIENT_OVER_LOG:
-       FREE_AND_WIPE(state->xclient.name);
-       FREE_AND_WIPE(state->xclient.addr);
-       FREE_AND_WIPE(state->xclient.namaddr);
-       state->xclient.peer_code = 0;
-       FREE_AND_WIPE(state->xclient.protocol);
-       FREE_AND_WIPE(state->xclient.helo_name);
-       break;
-
-       /*
-        * Non-selective override. Set all attributes to "unknown". This is
-        * desirable to avoid polluting the audit trail with data from mixed
-        * origins.
-        */
-#define FREE_AND_DUP(s,v) { if (s) { myfree(s); s = mystrdup(v); } }
-
-    case XCLIENT_OVER_LOG:
-       FREE_AND_DUP(state->xclient.name, CLIENT_NAME_UNKNOWN);
-       FREE_AND_DUP(state->xclient.addr, CLIENT_ADDR_UNKNOWN);
-       FREE_AND_DUP(state->xclient.namaddr, CLIENT_NAMADDR_UNKNOWN);
-       state->xclient.peer_code = 0;
-       FREE_AND_DUP(state->xclient.protocol, PROTOCOL_UNKNOWN);
-       FREE_AND_DUP(state->xclient.helo_name, HELO_NAME_UNKNOWN);
-       break;
-    }
-    state->xclient.mode = mode;
+    myfree(state->xclient.name);
+    myfree(state->xclient.addr);
+    myfree(state->xclient.namaddr);
+    myfree(state->xclient.protocol);
+    myfree(state->xclient.helo_name);
 }
index 6d556e257399869abe8d79b7d4b00146911fe8cf..3ddc8e554b2931febe18bdc2e578f299701b3403 100644 (file)
@@ -1129,7 +1129,7 @@ typedef int pid_t;
   * that works as long as off_t is some two's complement number.
   */
 #include <limits.h>
-#define __MAXINT__(T) ((T) (((T)1 << ((sizeof(T) * CHAR_BIT) - 1) ^ ((T) -1))))
+#define __MAXINT__(T) ((T) (((((T) 1) << ((sizeof(T) * CHAR_BIT) - 1)) ^ ((T) -1))))
 #ifndef OFF_T_MAX
 #define OFF_T_MAX __MAXINT__(off_t)
 #endif