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
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:
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.
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.
#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.
*/
* 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
int mail_from_rejected;
int downgrading;
int mime_errs;
- int send_xclient_addr = 0;
- int send_xclient_helo = 0;
/*
* Macros for readability.
* 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;
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;
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:
#include <errno.h>
#include <ctype.h>
#include <signal.h>
+#include <stddef.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
* XCLIENT command.
*/
static NAMADR_LIST *xclient_hosts;
+static int xclient_allowed;
/*
* Client connection and rate limiting.
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);
}
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);
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
*/
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);
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) {
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)
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");
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.
*/
} 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] */
* 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,
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);
}
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);
}
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);
}
/*
* 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);
/*
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",
* 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)));
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++;
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;
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,
} 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);
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)
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)
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);
}
}
/*
* Initialize.
*/
- if (ACL_NAME(state) == 0 || ACL_ADDR(state) == 0)
+ if (state->name == 0 || state->addr == 0)
return (0);
#define SMTPD_CHECK_RESET() { \
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;
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;
*/
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);
*/
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);
/*
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);
#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=", ""),
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)
/* 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);
}
* 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