2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 05:38:06 +00:00

postfix-2.0.16-20031130

This commit is contained in:
Wietse Venema 2003-11-30 00:00:00 -05:00 committed by Viktor Dukhovni
parent 196cfcd300
commit edb9945f66
12 changed files with 453 additions and 477 deletions

View File

@ -11,53 +11,51 @@ the SMTP client hostname, network address, and other information.
2 - Logging after content filter. With Internet->MTA1->filter->MTA2
style content filter applications, remote client information is
lost when MTA1 gives the mail to the content filter. This complicates
the interpretation of the audit trail. In order maintain audit
trail continuity, MTA1 needs the ability to forward client information
through the content filter to MTA2.
lost when MTA1 gives the mail to the content filter. To simplify
the interpretation of MTA2 logging, it would help if MTA1 could
forward client information through the content filter to MTA2.
3 - Post-filter access control and logging. With Internet->filter->MTA
style content filter applications, the content filter can be greatly
simplified if it can delegate all decisions concerning mail relay
and other access control to the MTA. This requires that the filter
can forward client information to the MTA for access control
purposes.
style content filter applications, the filter can be simplified if
it can delegate decisions concerning mail relay and other access
control to the MTA. As in the first example, this requires that
the filter can override the MTA's idea of the SMTP client hostname,
network address, and other information.
The preceding suggests that there is a need for two modes of
operation:
The preceding suggests that there is a need for two functions:
- Operation mode 1: override the SMTP server's idea of SMTP
client information for access control and audit trail purposes.
Attributes that one may want to override are:
1 - Override the MTA's idea of SMTP client information for access
control and other purposes. This function is generally useful
for trouble shooting. The implementation can be relatively
straightforward because it updates already existing attributes.
client network address
client hostname
hostname lookup failure type (permanent or temporary)
client protocol (e.g., SMTP or ESMTP)
2 - Forward remote client information for logging purposes. This
function is limited mainly to environments that use SMTP-based
content filters. The implementation requires more invasive
changes to the MTA to store the additional attributes and to
choose between the normal attributes and the forwarded ones.
- Operation mode 2: forward remote client information for audit
trail purposes only. Examples of attributes are:
Command overview
================
client network address
client hostname
client protocol (e.g., SMTP or ESMTP)
client SMTP HELO parameter
The EHLO keyword associated with this extension is XCLIENT.
General XCLIENT command syntax
==============================
The XCLIENT OVERRIDE command updates the remote client attributes
that the MTA normally uses for access control, message headers,
logging and so on, while the XCLIENT FORWARD command maintains an
additional set of attributes that is meant to be used for logging
purposes. In the absence of forwarded attributes the MTA must use
the normal remote client attribute values.
The XCLIENT command maintains one set of attributes with surrogate
client information. The general XCLIENT command syntax is described
below. Upper case and quoted strings specify terminals, lowercase
strings specify meta terminals, SP is whitespace, and descriptive
text is in {}. Although shown below in upper case, command and
attribute names are in fact case insensitive.
The general command syntax is described below. Upper case and
quoted strings specify terminals, lowercase strings specify meta
terminals, SP is whitespace, and descriptive text is enclosed in
{}. Although command and attribute names are shown below in upper
case, they are in fact case insensitive.
xclient-command = XCLIENT *( SP argument )
xclient-command = XCLIENT SP function SP 1*( attribute )
argument = ( request | attribute )
request = ( RST | ACL | LOG )
function = ( OVERRIDE | FORWARD )
attribute = name"="value
@ -67,10 +65,10 @@ attribute names are in fact case insensitive.
xtext = { attribute value encoded as per RFC 1891 }
The XCLIENT command is typically sent immediately before or after
the EHLO command, may be pipelined after the server announces ESMTP
pipelining support, and must not be used in the middle of an SMTP
transaction (i.e. between MAIL and DOT).
The XCLIENT command can be sent at any time except in the middle
of a mail delivery transaction (i.e. between MAIL and DOT). The
command may be pipelined after the server EHLO reply announces
ESMTP pipelining support.
The server reply codes are as follows:
@ -79,96 +77,94 @@ The server reply codes are as follows:
250 | success
501 | command syntax error
502 | unrecognized request name
503 | command out of order (name=value without prior ACL or LOG request)
421 | unable to proceed
The server must report success in case of an unrecognized attribute
name, although it may log a warning.
Specific XCLIENT usage scenarios
================================
Specific usage scenarios
========================
This section discusses the semantics of XCLIENT requests. Specific
syntax information is given in the next section.
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.

View File

@ -151,6 +151,18 @@ extern char *mail_pathname(const char *, const char *);
#define MAIL_ATTR_ORG_NONE "unknown" /* origin unknown */
#define MAIL_ATTR_ORG_LOCAL "local" /* local submission */
/*
* XCLIENT in SMTP.
*/
#define XCLIENT_CMD "XCLIENT" /* XCLIENT command */
#define XCLIENT_OVERRIDE "OVERRIDE" /* override function */
#define XCLIENT_FORWARD "FORWARD" /* forward function */
#define XCLIENT_NAME "CLIENT_NAME" /* client name */
#define XCLIENT_ADDR "CLIENT_ADDR" /* client address */
#define XCLIENT_PROTO "PROTOCOL" /* client protocol */
#define XCLIENT_CODE "CLIENT_CODE" /* client name status */
#define XCLIENT_HELO "HELO_NAME" /* client helo */
/*
* Internal forms for unknown XCLIENT information.
*/

View File

@ -20,7 +20,7 @@
* Patches change the patchlevel and the release date. Snapshots change the
* release date only, unless they include the same bugfix as a patch release.
*/
#define MAIL_RELEASE_DATE "20031128"
#define MAIL_RELEASE_DATE "20031130"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE

View File

@ -385,8 +385,6 @@ int smtp_xfer(SMTP_STATE *state)
int mail_from_rejected;
int downgrading;
int mime_errs;
int send_xclient_addr = 0;
int send_xclient_helo = 0;
/*
* Macros for readability.
@ -486,21 +484,13 @@ int smtp_xfer(SMTP_STATE *state)
* commands rejected, DATA rejected) it forces the sender to abort the
* SMTP dialog with RSET and QUIT.
*
* Send "XCLIENT LOG" information only if we have a surrogate remote client
* name and address, i.e. the mail was actually received from the
* network. Since "XCLIENT LOG" overrides all remote client logging
* attributes, there is no need to send helo or protocol information that
* we do not have.
* Update the server's remote client information to avoid leakage of past
* client attributes into an unrelated mail delivery.
*/
nrcpt = 0;
send_xclient_addr = (state->features & SMTP_FEATURE_XCLIENT)
&& !IS_UNK_CLNT_NAME(request->client_name)
&& !IS_UNK_CLNT_ADDR(request->client_addr);
if (send_xclient_addr) {
send_xclient_helo = !IS_UNK_HELO_NAME(request->client_helo)
|| !IS_UNK_PROTOCOL(request->client_proto);
if (var_smtp_send_xclient && (state->features & SMTP_FEATURE_XCLIENT))
recv_state = send_state = SMTP_STATE_XCLIENT_ADDR;
} else
else
recv_state = send_state = SMTP_STATE_MAIL;
next_rcpt = send_rcpt = recv_rcpt = 0;
mail_from_rejected = 0;
@ -519,37 +509,29 @@ int smtp_xfer(SMTP_STATE *state)
msg_panic("%s: bad sender state %d", myname, send_state);
/*
* Build the XCLIENT command. Send what we know, converting
* internal form to external form. With properly sanitized
* information, this stays within the 512 byte command line
* length limit.
* Build the XCLIENT command. With properly sanitized
* information, the command length stays within the 512 byte
* command line length limit.
*/
case SMTP_STATE_XCLIENT_ADDR:
vstring_sprintf(next_command, "XCLIENT LOG");
if (!IS_UNK_CLNT_NAME(request->client_name)) {
vstring_strcat(next_command, " CLIENT_NAME=");
vstring_strcpy(next_command,
XCLIENT_CMD " " XCLIENT_FORWARD " " XCLIENT_NAME "=");
if (!IS_UNK_CLNT_NAME(request->client_name))
xtext_quote_append(next_command, request->client_name, "");
}
if (!IS_UNK_CLNT_ADDR(request->client_addr)) {
vstring_strcat(next_command, " CLIENT_ADDR=");
vstring_strcat(next_command, " " XCLIENT_ADDR "=");
if (!IS_UNK_CLNT_ADDR(request->client_addr))
xtext_quote_append(next_command, request->client_addr, "");
}
if (send_xclient_helo)
next_state = SMTP_STATE_XCLIENT_HELO;
else
next_state = SMTP_STATE_MAIL;
next_state = SMTP_STATE_XCLIENT_HELO;
break;
case SMTP_STATE_XCLIENT_HELO:
vstring_sprintf(next_command, "XCLIENT");
if (!IS_UNK_HELO_NAME(request->client_helo)) {
vstring_strcat(next_command, " HELO_NAME=");
vstring_strcpy(next_command,
XCLIENT_CMD " " XCLIENT_FORWARD " " XCLIENT_HELO "=");
if (!IS_UNK_HELO_NAME(request->client_helo))
xtext_quote_append(next_command, request->client_helo, "");
}
if (!IS_UNK_PROTOCOL(request->client_proto)) {
vstring_strcat(next_command, " PROTOCOL=");
vstring_strcat(next_command, " " XCLIENT_PROTO "=");
if (!IS_UNK_PROTOCOL(request->client_proto))
xtext_quote_append(next_command, request->client_proto, "");
}
next_state = SMTP_STATE_MAIL;
break;
@ -684,13 +666,10 @@ int smtp_xfer(SMTP_STATE *state)
switch (recv_state) {
/*
* Ignore the XCLIENT response. No Duff device needed.
* Process the XCLIENT response.
*/
case SMTP_STATE_XCLIENT_ADDR:
if (send_xclient_helo)
recv_state = SMTP_STATE_XCLIENT_HELO;
else
recv_state = SMTP_STATE_MAIL;
recv_state = SMTP_STATE_XCLIENT_HELO;
break;
case SMTP_STATE_XCLIENT_HELO:

View File

@ -410,6 +410,7 @@
#include <errno.h>
#include <ctype.h>
#include <signal.h>
#include <stddef.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
@ -574,6 +575,7 @@ static NAMADR_LIST *verp_clients;
* XCLIENT command.
*/
static NAMADR_LIST *xclient_hosts;
static int xclient_allowed;
/*
* Client connection and rate limiting.
@ -616,10 +618,10 @@ static int sasl_client_exception(SMTPD_STATE *state)
return (0);
match = namadr_list_match(sasl_exceptions_networks,
ACL_NAME(state), ACL_ADDR(state));
state->name, state->addr);
if (msg_verbose)
msg_info("sasl_exceptions: %s, match=%d", ACL_NAMADDR(state), match);
msg_info("sasl_exceptions: %s, match=%d", state->namaddr, match);
return (match);
}
@ -721,10 +723,10 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_chat_reply(state, "250-AUTH=%s", state->sasl_mechanism_list);
}
#endif
if (namadr_list_match(verp_clients, ACL_NAME(state), ACL_ADDR(state)))
if (namadr_list_match(verp_clients, state->name, state->addr))
smtpd_chat_reply(state, "250-%s", VERP_CMD);
/* XCLIENT must not override its own access control. */
if (namadr_list_match(xclient_hosts, state->name, state->addr))
if (xclient_allowed)
smtpd_chat_reply(state, "250-%s", XCLIENT_CMD);
smtpd_chat_reply(state, "250 8BITMIME");
return (0);
@ -802,7 +804,7 @@ static void mail_open_stream(SMTPD_STATE *state, SMTPD_TOKEN *argv)
smtpd_sasl_mail_log(state);
else
#endif
msg_info("%s: client=%s", state->queue_id, LOG_NAMADDR(state));
msg_info("%s: client=%s", state->queue_id, FORWARD_NAMADDR(state));
/*
* Record the time of arrival, the sender envelope address, some session
@ -823,17 +825,17 @@ static void mail_open_stream(SMTPD_STATE *state, SMTPD_TOKEN *argv)
*/
if (SMTPD_STAND_ALONE(state) == 0) {
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_CLIENT_NAME, LOG_NAME(state));
MAIL_ATTR_CLIENT_NAME, FORWARD_NAME(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_CLIENT_ADDR, LOG_ADDR(state));
MAIL_ATTR_CLIENT_ADDR, FORWARD_ADDR(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ORIGIN, LOG_NAMADDR(state));
MAIL_ATTR_ORIGIN, FORWARD_NAMADDR(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_HELO_NAME,
IS_UNK_HELO_NAME(LOG_HELO_NAME(state)) ?
HELO_NAME_UNKNOWN : LOG_HELO_NAME(state));
IS_UNK_HELO_NAME(FORWARD_HELO(state)) ?
HELO_NAME_UNKNOWN : FORWARD_HELO(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_PROTO_NAME, LOG_PROTOCOL(state));
MAIL_ATTR_PROTO_NAME, FORWARD_PROTO(state));
}
if (state->verp_delims)
rec_fputs(state->cleanup, REC_TYPE_VERP, state->verp_delims);
@ -1023,8 +1025,7 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
return (-1);
}
#endif
} else if (namadr_list_match(verp_clients, ACL_NAME(state),
ACL_ADDR(state))
} else if (namadr_list_match(verp_clients, state->name, state->addr)
&& strncasecmp(arg, VERP_CMD, VERP_CMD_LEN) == 0
&& (arg[VERP_CMD_LEN] == '=' || arg[VERP_CMD_LEN] == 0)) {
if (arg[VERP_CMD_LEN] == 0) {
@ -1669,6 +1670,48 @@ static int quit_cmd(SMTPD_STATE *state, int unused_argc, SMTPD_TOKEN *unused_arg
return (0);
}
/*
* Lookup tables with xclient/normal attribute offsets. Maybe we should not
* try so hard to make XOVERRIDE and XFORWARD attribute lists identical.
*/
#define FUNC_OVERRIDE 0
#define FUNC_FORWARD 1
struct attr_offset {
int name[2];
int addr[2];
int namaddr[2];
int peer_code[2];
int protocol[2];
int helo_name[2];
};
#define ATTR_OFFSETS(member) \
offsetof(SMTPD_STATE, member), offsetof(SMTPD_STATE, xclient.member)
static const struct attr_offset attr_offset = {
ATTR_OFFSETS(name),
ATTR_OFFSETS(addr),
ATTR_OFFSETS(namaddr),
ATTR_OFFSETS(peer_code),
ATTR_OFFSETS(protocol),
ATTR_OFFSETS(helo_name)
};
#define PTR_ATTR(state, func, attr) (((char *) state) + attr_offset.attr[func])
#define STR_ATTR(state, func, attr) *((char **) PTR_ATTR(state, func, attr))
#define INT_ATTR(state, func, attr) *((int *) PTR_ATTR(state, func, attr))
#define UPDATE_STR(state, func, attr, value) { \
if (STR_ATTR(state, func, attr)) \
myfree(STR_ATTR(state, func, attr)); \
STR_ATTR(state, func, attr) = mystrdup(value); \
}
#define UPDATE_INT(state, func, attr, value) { \
INT_ATTR(state, func, attr) = (value); \
}
/* xclient_cmd - process XCLIENT */
static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
@ -1678,202 +1721,205 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
char *cooked_value;
char *arg_val;
int update_namaddr = 0;
int function;
/*
* Sanity checks. The XCLIENT command does not override its own access
* control.
*/
if (namadr_list_match(xclient_hosts, state->name, state->addr) == 0) {
if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
return (-1);
}
if (argc < 3) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: %s function name=value...",
XCLIENT_CMD);
return (-1);
}
if (!xclient_allowed) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "554 Error: insufficient authorization");
return (-1);
}
#define STREQ(x,y) (strcasecmp((x), (y)) == 0)
#define UPDATE_STR(s, v) { if (s) myfree(s); s = mystrdup(v); }
/*
* Iterate over all XCLIENT arguments.
* Function name: what attributes to update. Complain about unrecognized
* request names. The set of requests is unlikely to change.
*/
for (arg_no = 1; arg_no < argc; arg_no++) {
arg_val = argv[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.
*/

View File

@ -46,7 +46,7 @@ typedef struct SMTPD_DEFER {
} SMTPD_DEFER;
typedef struct SMTPD_XCLIENT_ATTR {
int mode; /* none, log, acl */
int used; /* status */
char *name; /* name for access control */
char *addr; /* address for access control */
char *namaddr; /* name[address] */
@ -146,34 +146,20 @@ extern void smtpd_peer_reset(SMTPD_STATE *state);
* makes no sense to maintain separate attribute sets for XCLIENT LOG or
* XCLIENT ACL, so we set a flag to distinguish purpose.
*/
#define XCLIENT_CMD "XCLIENT" /* XCLIENT command */
#define XCLIENT_EHLO "XCLIENT" /* ESMTP advertisement */
#define SMTPD_FEATURE_XCLIENT (1<<0) /* XCLIENT supported */
#define SMTPD_FEATURE_XCLIENT (1<<0) /* server supports XCLIENT */
#define MAYBE_FORWARD(s, a) \
((s)->xclient.used ? (s)->xclient.a : (s)->a)
#define XCLIENT_OVER_NONE (0) /* override reset */
#define XCLIENT_OVER_ACL (1<<0) /* override access control */
#define XCLIENT_OVER_LOG (1<<1) /* override logging */
#define XCLIENT_OVER(s, m, a) \
(((s)->xclient.mode & (m)) && (s)->xclient.a ? (s)->xclient.a : (s)->a)
#define ACL_ADDR(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, addr)
#define ACL_NAME(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, name)
#define ACL_NAMADDR(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, namaddr)
#define ACL_PEER_CODE(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, peer_code)
#define ACL_PROTOCOL(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, protocol)
#define ACL_HELO_NAME(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, helo_name)
#define LOG_ADDR(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, addr)
#define LOG_NAME(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, name)
#define LOG_NAMADDR(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, namaddr)
#define LOG_PEER_CODE(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, peer_code)
#define LOG_PROTOCOL(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, protocol)
#define LOG_HELO_NAME(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, helo_name)
#define FORWARD_ADDR(s) MAYBE_FORWARD((s), addr)
#define FORWARD_NAME(s) MAYBE_FORWARD((s), name)
#define FORWARD_NAMADDR(s) MAYBE_FORWARD((s), namaddr)
#define FORWARD_CODE(s) MAYBE_FORWARD((s), peer_code)
#define FORWARD_PROTO(s) MAYBE_FORWARD((s), protocol)
#define FORWARD_HELO(s) MAYBE_FORWARD((s), helo_name)
extern void smtpd_xclient_init(SMTPD_STATE *state);
extern void smtpd_xclient_reset(SMTPD_STATE *state, int);
extern void smtpd_xclient_reset(SMTPD_STATE *state);
/*
* Transparency: before mail is queued, do we check for unknown recipients,

View File

@ -809,15 +809,15 @@ static void log_whatsup(SMTPD_STATE *state, const char *whatsup,
vstring_sprintf(buf, "%s: %s: %s from %s: %s;",
state->queue_id ? state->queue_id : "NOQUEUE",
whatsup, state->where, LOG_NAMADDR(state), text);
whatsup, state->where, state->namaddr, text);
if (state->sender)
vstring_sprintf_append(buf, " from=<%s>", state->sender);
if (state->recipient)
vstring_sprintf_append(buf, " to=<%s>", state->recipient);
if (!IS_UNK_PROTOCOL(ACL_PROTOCOL(state)))
vstring_sprintf_append(buf, " proto=%s", ACL_PROTOCOL(state));
if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state)))
vstring_sprintf_append(buf, " helo=<%s>", ACL_HELO_NAME(state));
if (state->protocol)
vstring_sprintf_append(buf, " proto=%s", state->protocol);
if (state->helo_name)
vstring_sprintf_append(buf, " helo=<%s>", state->helo_name);
msg_info("%s", STR(buf));
vstring_free(buf);
}
@ -968,14 +968,14 @@ static int reject_unknown_client(SMTPD_STATE *state)
char *myname = "reject_unknown_client";
if (msg_verbose)
msg_info("%s: %s %s", myname, ACL_NAME(state), ACL_ADDR(state));
msg_info("%s: %s %s", myname, state->name, state->addr);
if (IS_UNK_CLNT_NAME(ACL_NAME(state)))
if (IS_UNK_CLNT_NAME(state->name))
return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
"%d Client host rejected: cannot find your hostname, [%s]",
ACL_PEER_CODE(state) == SMTPD_PEER_CODE_PERM ?
state->peer_code == SMTPD_PEER_CODE_PERM ?
var_unk_client_code : 450,
ACL_ADDR(state)));
state->addr));
return (SMTPD_CHECK_DUNNO);
}
@ -986,9 +986,9 @@ static int permit_mynetworks(SMTPD_STATE *state)
char *myname = "permit_mynetworks";
if (msg_verbose)
msg_info("%s: %s %s", myname, ACL_NAME(state), ACL_ADDR(state));
msg_info("%s: %s %s", myname, state->name, state->addr);
if (namadr_list_match(mynetworks, ACL_NAME(state), ACL_ADDR(state)))
if (namadr_list_match(mynetworks, state->name, state->addr))
return (SMTPD_CHECK_OK);
return (SMTPD_CHECK_DUNNO);
}
@ -1207,7 +1207,7 @@ static int check_relay_domains(SMTPD_STATE *state, char *recipient,
/*
* Permit if the client matches the relay_domains list.
*/
if (domain_list_match(relay_domains, ACL_NAME(state)))
if (domain_list_match(relay_domains, state->name))
return (SMTPD_CHECK_OK);
/*
@ -1309,7 +1309,7 @@ static int reject_unauth_pipelining(SMTPD_STATE *state,
if (state->client != 0
&& SMTPD_STAND_ALONE(state) == 0
&& vstream_peek(state->client) > 0
&& (strcasecmp(ACL_PROTOCOL(state), MAIL_PROTO_ESMTP) != 0
&& (strcasecmp(state->protocol, MAIL_PROTO_ESMTP) != 0
|| strcasecmp(state->where, "DATA") == 0)) {
return (smtpd_check_reject(state, MAIL_ERROR_PROTOCOL,
"503 <%s>: %s rejected: Improper use of SMTP command pipelining",
@ -2518,14 +2518,13 @@ static const char *smtpd_expand_lookup(const char *name, int unused_mode,
* Return NULL only for non-existent names.
*/
if (STREQ(name, MAIL_ATTR_CLIENT)) {
return (ACL_NAMADDR(state));
return (state->namaddr);
} else if (STREQ(name, MAIL_ATTR_CLIENT_ADDR)) {
return (ACL_ADDR(state));
return (state->addr);
} else if (STREQ(name, MAIL_ATTR_CLIENT_NAME)) {
return (ACL_NAME(state));
return (state->name);
} else if (STREQ(name, MAIL_ATTR_HELO_NAME)) {
return (IS_UNK_HELO_NAME(ACL_HELO_NAME(state)) ?
"" : ACL_HELO_NAME(state));
return (state->helo_name ? state->helo_name : "");
} else if (STREQN(name, MAIL_ATTR_SENDER, CONST_LEN(MAIL_ATTR_SENDER))) {
return (smtpd_expand_addr(state->expand_buf, state->sender,
name, CONST_LEN(MAIL_ATTR_SENDER)));
@ -2821,7 +2820,7 @@ static int reject_maps_rbl(SMTPD_STATE *state)
static int warned;
if (msg_verbose)
msg_info("%s: %s", myname, ACL_ADDR(state));
msg_info("%s: %s", myname, state->addr);
if (warned == 0) {
warned++;
@ -2830,7 +2829,7 @@ static int reject_maps_rbl(SMTPD_STATE *state)
REJECT_MAPS_RBL, var_mail_name, REJECT_RBL_CLIENT);
}
while ((rbl_domain = mystrtok(&bp, " \t\r\n,")) != 0) {
result = reject_rbl_addr(state, rbl_domain, ACL_ADDR(state),
result = reject_rbl_addr(state, rbl_domain, state->addr,
SMTPD_NAME_CLIENT);
if (result != SMTPD_CHECK_DUNNO)
break;
@ -2907,12 +2906,11 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
ATTR_FLAG_NONE, /* Query attributes. */
ATTR_TYPE_STR, MAIL_ATTR_REQ, "smtpd_access_policy",
ATTR_TYPE_STR, MAIL_ATTR_PROTO_STATE, state->where,
ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, ACL_PROTOCOL(state),
ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, ACL_ADDR(state),
ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, ACL_NAME(state),
ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, state->protocol,
ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, state->addr,
ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, state->name,
ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME,
IS_UNK_HELO_NAME(ACL_HELO_NAME(state)) ?
"" : ACL_HELO_NAME(state),
state->helo_name ? state->helo_name : "",
ATTR_TYPE_STR, MAIL_ATTR_SENDER,
state->sender ? state->sender : "",
ATTR_TYPE_STR, MAIL_ATTR_RECIP,
@ -3077,8 +3075,8 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
} else if (strcasecmp(name, PERMIT_MYNETWORKS) == 0) {
status = permit_mynetworks(state);
} else if (is_map_command(state, name, CHECK_CLIENT_ACL, &cpp)) {
status = check_namadr_access(state, *cpp, ACL_NAME(state), ACL_ADDR(state),
FULL, &found, ACL_NAMADDR(state),
status = check_namadr_access(state, *cpp, state->name, state->addr,
FULL, &found, state->namaddr,
SMTPD_NAME_CLIENT, def_acl);
} else if (strcasecmp(name, REJECT_MAPS_RBL) == 0) {
status = reject_maps_rbl(state);
@ -3087,7 +3085,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
if (cpp[1] == 0)
msg_warn("restriction %s requires domain name argument", name);
else
status = reject_rbl_addr(state, *(cpp += 1), ACL_ADDR(state),
status = reject_rbl_addr(state, *(cpp += 1), state->addr,
SMTPD_NAME_CLIENT);
} else if (strcasecmp(name, REJECT_RHSBL_CLIENT) == 0) {
if (cpp[1] == 0)
@ -3095,73 +3093,69 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
name);
else {
cpp += 1;
if (!IS_UNK_CLNT_NAME(ACL_NAME(state)))
status = reject_rbl_domain(state, *cpp, ACL_NAME(state),
if (!IS_UNK_CLNT_NAME(state->name))
status = reject_rbl_domain(state, *cpp, state->name,
SMTPD_NAME_CLIENT);
}
}
/*
* HELO/EHLO parameter restrictions.
*
* XXX With XCLIENT overrides, a zero-length name means the client did
* not send a HELO/EHLO command. Do not fall back to the non-XCLIENT
* HELO/EHLO value.
*/
else if (is_map_command(state, name, CHECK_HELO_ACL, &cpp)) {
if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state)))
status = check_domain_access(state, *cpp, ACL_HELO_NAME(state),
FULL, &found, ACL_HELO_NAME(state),
if (state->helo_name)
status = check_domain_access(state, *cpp, state->helo_name,
FULL, &found, state->helo_name,
SMTPD_NAME_HELO, def_acl);
} else if (strcasecmp(name, REJECT_INVALID_HOSTNAME) == 0) {
if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
if (*ACL_HELO_NAME(state) != '[')
status = reject_invalid_hostname(state, ACL_HELO_NAME(state),
ACL_HELO_NAME(state), SMTPD_NAME_HELO);
if (state->helo_name) {
if (*state->helo_name != '[')
status = reject_invalid_hostname(state, state->helo_name,
state->helo_name, SMTPD_NAME_HELO);
else
status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
ACL_HELO_NAME(state), SMTPD_NAME_HELO);
status = reject_invalid_hostaddr(state, state->helo_name,
state->helo_name, SMTPD_NAME_HELO);
}
} else if (strcasecmp(name, REJECT_UNKNOWN_HOSTNAME) == 0) {
if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
if (*ACL_HELO_NAME(state) != '[')
status = reject_unknown_hostname(state, ACL_HELO_NAME(state),
ACL_HELO_NAME(state), SMTPD_NAME_HELO);
if (state->helo_name) {
if (*state->helo_name != '[')
status = reject_unknown_hostname(state, state->helo_name,
state->helo_name, SMTPD_NAME_HELO);
else
status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
ACL_HELO_NAME(state), SMTPD_NAME_HELO);
status = reject_invalid_hostaddr(state, state->helo_name,
state->helo_name, SMTPD_NAME_HELO);
}
} else if (strcasecmp(name, PERMIT_NAKED_IP_ADDR) == 0) {
msg_warn("restriction %s is deprecated. Use %s instead",
PERMIT_NAKED_IP_ADDR, PERMIT_MYNETWORKS);
if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
if (ACL_HELO_NAME(state)[strspn(ACL_HELO_NAME(state), "0123456789.")] == 0
&& (status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
ACL_HELO_NAME(state), SMTPD_NAME_HELO)) == 0)
if (state->helo_name) {
if (state->helo_name[strspn(state->helo_name, "0123456789.")] == 0
&& (status = reject_invalid_hostaddr(state, state->helo_name,
state->helo_name, SMTPD_NAME_HELO)) == 0)
status = SMTPD_CHECK_OK;
}
} else if (is_map_command(state, name, CHECK_HELO_NS_ACL, &cpp)) {
if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
status = check_server_access(state, *cpp, ACL_HELO_NAME(state),
T_NS, ACL_HELO_NAME(state),
if (state->helo_name) {
status = check_server_access(state, *cpp, state->helo_name,
T_NS, state->helo_name,
SMTPD_NAME_HELO, def_acl);
forbid_whitelist(state, name, status, ACL_HELO_NAME(state));
forbid_whitelist(state, name, status, state->helo_name);
}
} else if (is_map_command(state, name, CHECK_HELO_MX_ACL, &cpp)) {
if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
status = check_server_access(state, *cpp, ACL_HELO_NAME(state),
T_MX, ACL_HELO_NAME(state),
if (state->helo_name) {
status = check_server_access(state, *cpp, state->helo_name,
T_MX, state->helo_name,
SMTPD_NAME_HELO, def_acl);
forbid_whitelist(state, name, status, ACL_HELO_NAME(state));
forbid_whitelist(state, name, status, state->helo_name);
}
} else if (strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) {
if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
if (*ACL_HELO_NAME(state) != '[')
status = reject_non_fqdn_hostname(state, ACL_HELO_NAME(state),
ACL_HELO_NAME(state), SMTPD_NAME_HELO);
if (state->helo_name) {
if (*state->helo_name != '[')
status = reject_non_fqdn_hostname(state, state->helo_name,
state->helo_name, SMTPD_NAME_HELO);
else
status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
ACL_HELO_NAME(state), SMTPD_NAME_HELO);
status = reject_invalid_hostaddr(state, state->helo_name,
state->helo_name, SMTPD_NAME_HELO);
}
} else if (strcasecmp(name, REJECT_RHSBL_HELO) == 0) {
if (cpp[1] == 0)
@ -3169,8 +3163,8 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
name);
else {
cpp += 1;
if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state)))
status = reject_rbl_domain(state, *cpp, ACL_HELO_NAME(state),
if (state->helo_name)
status = reject_rbl_domain(state, *cpp, state->helo_name,
SMTPD_NAME_HELO);
}
}
@ -3367,7 +3361,7 @@ char *smtpd_check_client(SMTPD_STATE *state)
/*
* Initialize.
*/
if (ACL_NAME(state) == 0 || ACL_ADDR(state) == 0)
if (state->name == 0 || state->addr == 0)
return (0);
#define SMTPD_CHECK_RESET() { \
@ -3387,7 +3381,7 @@ char *smtpd_check_client(SMTPD_STATE *state)
SMTPD_CHECK_RESET();
status = setjmp(smtpd_check_buf);
if (status == 0 && client_restrctions->argc)
status = generic_checks(state, client_restrctions, ACL_NAMADDR(state),
status = generic_checks(state, client_restrctions, state->namaddr,
SMTPD_NAME_CLIENT, CHECK_CLIENT_ACL);
state->defer_if_permit_client = state->defer_if_permit.active;
@ -3441,7 +3435,7 @@ char *smtpd_check_helo(SMTPD_STATE *state, char *helohost)
SMTPD_CHECK_RESET();
status = setjmp(smtpd_check_buf);
if (status == 0 && helo_restrctions->argc)
status = generic_checks(state, helo_restrctions, ACL_HELO_NAME(state),
status = generic_checks(state, helo_restrctions, state->helo_name,
SMTPD_NAME_HELO, CHECK_HELO_ACL);
state->defer_if_permit_helo = state->defer_if_permit.active;
@ -3553,7 +3547,7 @@ char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient)
*/
if (var_smtpd_delay_reject)
if ((err = smtpd_check_client(state)) != 0
|| (err = smtpd_check_helo(state, ACL_HELO_NAME(state))) != 0
|| (err = smtpd_check_helo(state, state->helo_name)) != 0
|| (err = smtpd_check_mail(state, state->sender)) != 0)
SMTPD_CHECK_RCPT_RETURN(err);
@ -3622,7 +3616,7 @@ char *smtpd_check_etrn(SMTPD_STATE *state, char *domain)
*/
if (var_smtpd_delay_reject)
if ((err = smtpd_check_client(state)) != 0
|| (err = smtpd_check_helo(state, ACL_HELO_NAME(state))) != 0)
|| (err = smtpd_check_helo(state, state->helo_name)) != 0)
SMTPD_CHECK_ETRN_RETURN(err);
/*

View File

@ -241,29 +241,31 @@ int smtpd_proxy_open(SMTPD_STATE *state, const char *service,
while ((line = mystrtok(&lines, "\n")) != 0)
if (ISDIGIT(line[0]) && ISDIGIT(line[1]) && ISDIGIT(line[2])
&& (line[3] == ' ' || line[3] == '-')
&& strcmp(line + 4, XCLIENT_EHLO) == 0)
&& strcmp(line + 4, XCLIENT_CMD) == 0)
state->proxy_features |= SMTPD_FEATURE_XCLIENT;
/*
* Send our XCLIENT attributes. Transform internal forms to external
* Send all XCLIENT attributes. Transform internal forms to external
* forms and encode the result as xtext.
*/
if (state->proxy_features & SMTPD_FEATURE_XCLIENT) {
buf = vstring_alloc(100);
vstring_sprintf(buf, "%s LOG CLIENT_NAME=", XCLIENT_CMD);
if (!IS_UNK_CLNT_NAME(LOG_NAME(state)))
xtext_quote_append(buf, LOG_NAME(state), "");
vstring_strcat(buf, " CLIENT_ADDR=");
if (!IS_UNK_CLNT_ADDR(LOG_ADDR(state)))
xtext_quote_append(buf, LOG_ADDR(state), "");
vstring_strcpy(buf, XCLIENT_CMD " " XCLIENT_FORWARD
" " XCLIENT_NAME "=");
if (!IS_UNK_CLNT_NAME(FORWARD_NAME(state)))
xtext_quote_append(buf, FORWARD_NAME(state), "");
vstring_strcat(buf, " " XCLIENT_ADDR "=");
if (!IS_UNK_CLNT_ADDR(FORWARD_ADDR(state)))
xtext_quote_append(buf, FORWARD_ADDR(state), "");
bad = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, "%s", STR(buf));
if (bad == 0) {
vstring_sprintf(buf, "%s HELO_NAME=", XCLIENT_CMD);
if (!IS_UNK_HELO_NAME(LOG_HELO_NAME(state)))
xtext_quote_append(buf, LOG_HELO_NAME(state), "");
vstring_strcat(buf, " PROTOCOL=");
if (!IS_UNK_PROTOCOL(LOG_PROTOCOL(state)))
xtext_quote_append(buf, LOG_PROTOCOL(state), "");
vstring_strcpy(buf, XCLIENT_CMD " " XCLIENT_FORWARD
" " XCLIENT_HELO "=");
if (!IS_UNK_HELO_NAME(FORWARD_HELO(state)))
xtext_quote_append(buf, FORWARD_HELO(state), "");
vstring_strcat(buf, " " XCLIENT_PROTO "=");
if (!IS_UNK_PROTOCOL(FORWARD_PROTO(state)))
xtext_quote_append(buf, FORWARD_PROTO(state), "");
bad = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, "%s", STR(buf));
}
vstring_free(buf);

View File

@ -198,7 +198,7 @@ void smtpd_sasl_mail_log(SMTPD_STATE *state)
#define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))
msg_info("%s: client=%s%s%s%s%s%s%s",
state->queue_id, LOG_NAMADDR(state),
state->queue_id, FORWARD_NAMADDR(state),
IFELSE(state->sasl_method, ", sasl_method=", ""),
IFELSE(state->sasl_method, state->sasl_method, ""),
IFELSE(state->sasl_username, ", sasl_username=", ""),

View File

@ -141,7 +141,7 @@ void smtpd_state_reset(SMTPD_STATE *state)
if (state->buffer)
vstring_free(state->buffer);
smtpd_peer_reset(state);
smtpd_xclient_reset(state, XCLIENT_OVER_NONE);
smtpd_xclient_reset(state);
if (state->defer_if_permit.reason)
vstring_free(state->defer_if_permit.reason);
if (state->defer_if_reject.reason)

View File

@ -9,9 +9,8 @@
/* void smtpd_xclient_init(state)
/* SMTPD_STATE *state;
/*
/* void smtpd_xclient_reset(state, mode)
/* void smtpd_xclient_reset(state)
/* SMTPD_STATE *state;
/* int mode;
/* DESCRIPTION
/* smtpd_xclient_init() initializes state variables that are
/* used for storage of XCLIENT command parameters.
@ -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);
}

View File

@ -1129,7 +1129,7 @@ typedef int pid_t;
* that works as long as off_t is some two's complement number.
*/
#include <limits.h>
#define __MAXINT__(T) ((T) (((T)1 << ((sizeof(T) * CHAR_BIT) - 1) ^ ((T) -1))))
#define __MAXINT__(T) ((T) (((((T) 1) << ((sizeof(T) * CHAR_BIT) - 1)) ^ ((T) -1))))
#ifndef OFF_T_MAX
#define OFF_T_MAX __MAXINT__(off_t)
#endif