diff --git a/postfix/README_FILES/XCLIENT_README b/postfix/README_FILES/XCLIENT_README index 3624bc89a..4337221ed 100644 --- a/postfix/README_FILES/XCLIENT_README +++ b/postfix/README_FILES/XCLIENT_README @@ -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. +syntax details are given in the next section. -The RST request resets all XCLIENT attributes and disables any -override of access control or audit trail attributes. Example: +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. - XCLIENT RST +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: -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: + XCLIENT OVERRIDE CLIENT_NAME=spike.porcupine.org + XCLIENT OVERRIDE CLIENT_ADDR=168.100.189.2 - XCLIENT ACL CLIENT_NAME=spike.porcupine.org - XCLIENT CLIENT_ADDR=168.100.189.2 +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. -This overrides only the client hostname and network address, but -none of the other client attributes. +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. -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: +The following example updates all forwarded client attributes that +are defined in this document, leaving none at their default unknown +value: - XCLIENT LOG CLIENT_NAME=spike.porcupine.org CLIENT_ADDR=168.100.189.2 - XCLIENT HELO_NAME=spike.porcupine.org PROTOCOL=ESMTP + XCLIENT FORWARD CLIENT_NAME=spike.porcupine.org CLIENT_ADDR=168.100.189.2 + XCLIENT FORWARD HELO_NAME=spike.porcupine.org PROTOCOL=ESMTP -This overrides all client attributes that are defined in this -document, leaving none at their default unknown value. +Note 1: attributes specified with successive XCLIENT commands +accumulate. -Note 1: it is an error to specify name=value pairs without prior -ACL or LOG request. +Note 2: XCLIENT FORWARD attributes take precedence over XCLIENT +OVERRIDE attributes. -Note 2: after an ACL or LOG request, attributes specified with -successive XCLIENT commands accumulate until the next RST, ACL or -LOG request. - -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. diff --git a/postfix/src/global/mail_proto.h b/postfix/src/global/mail_proto.h index 10718cfd1..fab5dcdaa 100644 --- a/postfix/src/global/mail_proto.h +++ b/postfix/src/global/mail_proto.h @@ -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. */ diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index ce00d7d18..b5ae497c6 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -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 diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 243f1fab6..cb403aa36 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -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: diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 301ebf39f..38d2424ea 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -410,6 +410,7 @@ #include #include #include +#include #ifdef STRCASECMP_IN_STRINGS_H #include @@ -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[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); + } + + /* + * Iterate over all NAME=VALUE attributes. + */ + for (arg_no = 2; arg_no < argc; arg_no++) { arg_val = argv[arg_no].strval; /* - * 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); - } - } - - /* - * 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, '?'); + 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_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; - } - - /* - * 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); - } - 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_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 status: %s", + smtpd_chat_reply(state, "501 Bad hostname syntax: %s", cooked_value); return (-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; + } - /* - * 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_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, function, addr, cooked_value); + } else { + UPDATE_STR(state, function, name, CLIENT_ADDR_UNKNOWN); } + update_namaddr = 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); + /* + * 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); + } + } + + /* + * 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. */ diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index 0b418f59c..3b0a8d037 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -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, diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index b1688b59a..ab45aa321 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -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); /* diff --git a/postfix/src/smtpd/smtpd_proxy.c b/postfix/src/smtpd/smtpd_proxy.c index 3999c993b..f81ce2dea 100644 --- a/postfix/src/smtpd/smtpd_proxy.c +++ b/postfix/src/smtpd/smtpd_proxy.c @@ -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); diff --git a/postfix/src/smtpd/smtpd_sasl_proto.c b/postfix/src/smtpd/smtpd_sasl_proto.c index 4e412c50b..74f0110cf 100644 --- a/postfix/src/smtpd/smtpd_sasl_proto.c +++ b/postfix/src/smtpd/smtpd_sasl_proto.c @@ -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=", ""), diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c index d5988b416..bbfe325ea 100644 --- a/postfix/src/smtpd/smtpd_state.c +++ b/postfix/src/smtpd/smtpd_state.c @@ -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) diff --git a/postfix/src/smtpd/smtpd_xclient.c b/postfix/src/smtpd/smtpd_xclient.c index 75c92d946..d021c4585 100644 --- a/postfix/src/smtpd/smtpd_xclient.c +++ b/postfix/src/smtpd/smtpd_xclient.c @@ -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. @@ -19,14 +18,7 @@ /* 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 @@ -59,58 +51,22 @@ 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); } diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index 6d556e257..3ddc8e554 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -1129,7 +1129,7 @@ typedef int pid_t; * that works as long as off_t is some two's complement number. */ #include -#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