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

postfix-2.6-20080510

This commit is contained in:
Wietse Venema 2008-05-10 00:00:00 -05:00 committed by Viktor Dukhovni
parent aa25e2edae
commit d3c01305cb
17 changed files with 377 additions and 291 deletions

View File

@ -14439,7 +14439,26 @@ Apologies for any names omitted.
(Postfix 2.1) and smtp/lmtp_generic_maps (Postfix 2.3).
File: global/mail_params.h.
Cleanup: the SMTP server's XFORWARD and XCLIENT support
was not updated when the smtpd_client_port_logging configuration
Cleanup: the SMTP server's XFORWARD and XCLIENT support was
not updated when the smtpd_client_port_logging configuration
parameter was added. Code by Victor Duchovni. Files:
smtpd/smtpd.c, smtpd/smtpd_peer.c.
20080508
Cleanup: delivery status notifications now prepend a
Return-Path: message header to the returned message.
File: bounce/bounce_notify_util.c.
20080509
Bugfix: null-terminate CN comment string after sanitization.
File: smtpd/smtpd.c.
20080510
Cleanup: when extracting common name and issuer name from
TLS certificates, convert the result into UTF-8, and use
RFC 2047 encoding when logging these as Received: header
comment fields. Based remotely on code by Victor Duchovni.
Files: smtpd/smtpd.c, tls/tls_verify.c.

View File

@ -126,7 +126,10 @@ Notes:
how the client was authenticated via TLS. These attributes are empty in
case of no certificate authentication. As of Postfix 2.2.11 these attribute
values are encoded as xtext: some characters are represented by +XX, where
XX is the two-digit hexadecimal representation of the character value.
XX is the two-digit hexadecimal representation of the character value. With
Postfix 2.5 and later, the decoded string may contain non-ASCII characters.
If so, this is a UTF-8 string; xtext encoding works with the bytes of the
UTF-8 string, not the characters.
* The "encryption_*" attributes (Postfix 2.3 and later) specify information
about how the connection is encrypted. With plaintext connections the

View File

@ -17,6 +17,13 @@ Incompatibility with Postfix 2.4 and earlier
If you upgrade from Postfix 2.4 or earlier, read RELEASE_NOTES-2.5
before proceeding.
Incompatibility with snapshot 20080510
======================================
In the policy delegation protocol, certificate common name attributes
are now xtext encoded UTF-8. The xtext decoded attributes may contain
any UTF-8 value including control characters.
Incompatibility with snapshot 20080428
======================================

View File

@ -192,14 +192,14 @@
#
# REJECT optional text...
# Reject the address etc. that matches the pattern.
# Reply with $reject_code optional text... when the
# optional text is specified, otherwise reply with a
# generic error response message.
# Reply with "$access_map_reject_code optional
# text..." when the optional text is specified, oth-
# erwise reply with a generic error response message.
#
# DEFER_IF_REJECT optional text...
# Defer the request if some later restriction would
# result in a REJECT action. Reply with "450 4.7.1
# optional text... when the optional text is speci-
# optional text..." when the optional text is speci-
# fied, otherwise reply with a generic error response
# message.
#
@ -208,7 +208,7 @@
# DEFER_IF_PERMIT optional text...
# Defer the request if some later restriction would
# result in a an explicit or implicit PERMIT action.
# Reply with "450 4.7.1 optional text... when the
# Reply with "450 4.7.1 optional text..." when the
# optional text is specified, otherwise reply with a
# generic error response message.
#

View File

@ -173,7 +173,10 @@ stress=
These attributes are empty in case of no certificate authentication.
As of Postfix 2.2.11 these attribute values are encoded as
xtext: some characters are represented by +XX, where XX is the
two-digit hexadecimal representation of the character value.
two-digit hexadecimal representation of the character value. With
Postfix 2.5 and later, the decoded string may contain non-ASCII
characters. If so, this is a UTF-8 string; xtext encoding works
with the bytes of the UTF-8 string, not the characters.
</p>
<li> <p> The "encryption_*" attributes (Postfix 2.3 and later)

View File

@ -198,14 +198,14 @@ ACCESS(5) ACCESS(5)
<b>REJECT</b> <i>optional text...</i>
Reject the address etc. that matches the pattern.
Reply with <i>$reject</i><b>_</b><i>code optional text...</i> when the
optional text is specified, otherwise reply with a
generic error response message.
Reply with "<b>$<a href="postconf.5.html#access_map_reject_code">access_map_reject_code</a></b> <i>optional</i>
<i>text...</i>" when the optional text is specified, oth-
erwise reply with a generic error response message.
<b>DEFER_IF_REJECT</b> <i>optional text...</i>
Defer the request if some later restriction would
result in a REJECT action. Reply with "<b>450 4.7.1</b>
<i>optional text...</i> when the optional text is speci-
<i>optional text...</i>" when the optional text is speci-
fied, otherwise reply with a generic error response
message.
@ -214,7 +214,7 @@ ACCESS(5) ACCESS(5)
<b>DEFER_IF_PERMIT</b> <i>optional text...</i>
Defer the request if some later restriction would
result in a an explicit or implicit PERMIT action.
Reply with "<b>450 4.7.1</b> <i>optional text...</i> when the
Reply with "<b>450 4.7.1</b> <i>optional text...</i>" when the
optional text is specified, otherwise reply with a
generic error response message.

View File

@ -479,7 +479,7 @@ esac
: ${CC='gcc $(WARN)'} ${OPT='-O'} ${DEBUG='-g'} ${AWK=awk} \
${WARN='-Wall -Wno-comment -Wformat -Wimplicit -Wmissing-prototypes \
-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
-Wunused'}
-Wunused -Wno-missing-braces'}
export SYSTYPE AR ARFL RANLIB SYSLIBS CC OPT DEBUG AWK OPTS

View File

@ -190,12 +190,13 @@ The reply code "421" causes Postfix to disconnect immediately
(Postfix version 2.3 and later).
.IP "\fBREJECT \fIoptional text...\fR
Reject the address etc. that matches the pattern. Reply with
\fI$reject_code optional text...\fR when the optional text is
"\fB$access_map_reject_code \fIoptional text...\fR" when the
optional text is
specified, otherwise reply with a generic error response message.
.IP "\fBDEFER_IF_REJECT \fIoptional text...\fR
Defer the request if some later restriction would result in a
REJECT action. Reply with "\fB450 4.7.1 \fI optional
text...\fR when the
text...\fR" when the
optional text is specified, otherwise reply with a generic error
response message.
.sp
@ -203,7 +204,7 @@ This feature is available in Postfix 2.1 and later.
.IP "\fBDEFER_IF_PERMIT \fIoptional text...\fR
Defer the request if some later restriction would result in a
an explicit or implicit PERMIT action.
Reply with "\fB450 4.7.1 \fI optional text...\fR when the
Reply with "\fB450 4.7.1 \fI optional text...\fR" when the
optional text is specified, otherwise reply with a generic error
response message.
.sp

View File

@ -173,7 +173,10 @@ stress=
These attributes are empty in case of no certificate authentication.
As of Postfix 2.2.11 these attribute values are encoded as
xtext: some characters are represented by +XX, where XX is the
two-digit hexadecimal representation of the character value.
two-digit hexadecimal representation of the character value. With
Postfix 2.5 and later, the decoded string may contain non-ASCII
characters. If so, this is a UTF-8 string; xtext encoding works
with the bytes of the UTF-8 string, not the characters.
</p>
<li> <p> The "encryption_*" attributes (Postfix 2.3 and later)

View File

@ -170,12 +170,13 @@
# (Postfix version 2.3 and later).
# .IP "\fBREJECT \fIoptional text...\fR
# Reject the address etc. that matches the pattern. Reply with
# \fI$reject_code optional text...\fR when the optional text is
# "\fB$access_map_reject_code \fIoptional text...\fR" when the
# optional text is
# specified, otherwise reply with a generic error response message.
# .IP "\fBDEFER_IF_REJECT \fIoptional text...\fR
# Defer the request if some later restriction would result in a
# REJECT action. Reply with "\fB450 4.7.1 \fI optional
# text...\fR when the
# text...\fR" when the
# optional text is specified, otherwise reply with a generic error
# response message.
# .sp
@ -183,7 +184,7 @@
# .IP "\fBDEFER_IF_PERMIT \fIoptional text...\fR
# Defer the request if some later restriction would result in a
# an explicit or implicit PERMIT action.
# Reply with "\fB450 4.7.1 \fI optional text...\fR when the
# Reply with "\fB450 4.7.1 \fI optional text...\fR" when the
# optional text is specified, otherwise reply with a generic error
# response message.
# .sp

View File

@ -772,6 +772,16 @@ int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
return (vstream_ferror(bounce));
}
/*
* XXX The cleanup server removes Return-Path: headers. This should be
* done only with mail that enters via a non-SMTP channel, but changing
* this now could break other software. Removing Return-Path: could break
* digital signatures, though this is unlikely. In any case,
* header_checks are more effective when the Return-Path: header is
* present, so we prepend one to the bounce message.
*/
post_mail_fprintf(bounce, "Return-Path: <%s>", STR(bounce_info->sender));
/*
* Copy the original message contents. We're doing raw record output here
* so that we don't throw away binary transparency yet.

View File

@ -47,6 +47,10 @@
/*
* Header names are given in the preferred capitalization. The lookups are
* case-insensitive.
*
* XXX Removing Return-Path: headers should probably be done only with mail
* that enters via a non-SMTP channel. Changing this now could break other
* software. See also comments in bounce_notify_util.c.
*/
static const HEADER_OPTS header_opts[] = {
"Apparently-To", HDR_APPARENTLY_TO, HDR_OPT_RECIP,

View File

@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
#define MAIL_RELEASE_DATE "20080428"
#define MAIL_RELEASE_DATE "20080510"
#define MAIL_VERSION_NUMBER "2.6"
#ifdef SNAPSHOT

View File

@ -2496,6 +2496,60 @@ static void rcpt_reset(SMTPD_STATE *state)
state->rcpt_overshoot = 0;
}
/* rfc2047_comment_encode - encode comment string */
static VSTRING *rfc2047_comment_encode(const char *str, const char *charset)
{
VSTRING *buf = vstring_alloc(30);
const unsigned char *cp;
int ch;
/*
* XXX Most of the RFC 2047 "especials" are not special in RFC*822
* comments, but we encode them anyway to avoid complaints.
*
* XXX In Received: header comments we enclose peer and issuer common names
* with "" quotes. This is the cause of several quirks.
*
* 1) We encode text that contains the " character, even though that
* character is not special for RFC*822.
*
* 2) Long comments look ugly when folded in-between quotes, so we ignore
* the recommended limit of 75 characters per encoded word.
*
* 3) We must encode the the enclosing quotes, to avoid producing invalid
* encoded words.
*/
#define ESPECIALS "()<>@,;:\"/[]?.=" /* Special in RFC 2047 */
#define CSPECIALS "\\\"()" /* Special in our comments */
/* Don't encode if not needed. */
for (cp = (unsigned char *) str; /* see below */ ; ++cp) {
if ((ch = *cp) == 0) {
vstring_sprintf(buf, "\"%s\"", str);
return (buf);
}
if (!ISPRINT(ch) || strchr(CSPECIALS, ch))
break;
}
/*
* Use quoted-printable (like) encoding with spaces mapped to underscore.
*/
vstring_sprintf(buf, "=?%s?Q?=%02X", charset, '"');
for (cp = (unsigned char *) str; (ch = *cp) != 0; ++cp) {
if (!ISPRINT(ch) || strchr(ESPECIALS CSPECIALS, ch)) {
vstring_sprintf_append(buf, "=%02X", ch);
} else if (ch == ' ') {
VSTRING_ADDCH(buf, '_');
} else {
VSTRING_ADDCH(buf, ch);
}
}
vstring_sprintf_append(buf, "=%02X?=", '"');
return (buf);
}
/* comment_sanitize - clean up comment string */
static void comment_sanitize(VSTRING *comment_string)
@ -2526,6 +2580,7 @@ static void comment_sanitize(VSTRING *comment_string)
}
while (pc-- > 0)
VSTRING_ADDCH(comment_string, ')');
VSTRING_TERMINATE(comment_string);
}
/* data_cmd - process DATA command */
@ -2654,6 +2709,10 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
#define VSTRING_STRDUP(s) vstring_strcpy(vstring_alloc(strlen(s) + 1), (s))
/*
* Certificate CN information is arbitrary content in the UTF-8
* character set.
*/
#ifdef USE_TLS
if (var_smtpd_tls_received_header && state->tls_context) {
out_fprintf(out_stream, REC_TYPE_NORM,
@ -2663,13 +2722,14 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
state->tls_context->cipher_usebits,
state->tls_context->cipher_algbits);
if (TLS_CERT_IS_PRESENT(state->tls_context)) {
peer_CN = VSTRING_STRDUP(state->tls_context->peer_CN);
comment_sanitize(peer_CN);
issuer_CN = VSTRING_STRDUP(state->tls_context->issuer_CN ?
state->tls_context->issuer_CN : "");
comment_sanitize(issuer_CN);
peer_CN =
rfc2047_comment_encode(state->tls_context->peer_CN,
"utf-8");
issuer_CN =
rfc2047_comment_encode(state->tls_context->issuer_CN,
"utf-8");
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(Client CN \"%s\", Issuer \"%s\" (%s))",
"\t(Client CN %s, Issuer %s (%s))",
STR(peer_CN), STR(issuer_CN),
TLS_CERT_IS_TRUSTED(state->tls_context) ?
"verified OK" : "not verified");

View File

@ -180,14 +180,6 @@ typedef struct SMTPD_STATE {
ssize_t milter_argc;
} SMTPD_STATE;
/*
* Construct name[addr] or name[addr]:port as appropriate
*/
#define SMTPD_BUILD_NAMADDRPORT(name, addr, port) \
concatenate((name), "[", (addr), "]", \
var_smtpd_client_port_log ? ":" : (char *) 0, \
(port), (char *) 0)
#define SMTPD_FLAG_HANGUP (1<<0) /* disconnect */
#define SMTPD_STATE_XFORWARD_INIT (1<<0) /* xforward preset done */
@ -287,6 +279,14 @@ extern void smtpd_peer_reset(SMTPD_STATE *state);
#define SMTPD_PEER_CODE_PERM 5
#define SMTPD_PEER_CODE_FORGED 6
/*
* Construct name[addr] or name[addr]:port as appropriate
*/
#define SMTPD_BUILD_NAMADDRPORT(name, addr, port) \
concatenate((name), "[", (addr), "]", \
var_smtpd_client_port_log ? ":" : (char *) 0, \
(port), (char *) 0)
/*
* Choose between normal or forwarded attributes.
*

View File

@ -1813,7 +1813,6 @@ static int reject_unverified_address(SMTPD_STATE *state, const char *addr,
SND_DSN : "4.1.1",
"<%s>: %s rejected: address verification problem",
reply_name, reply_class);
rqst_status = SMTPD_CHECK_DUNNO;
} else {
switch (rcpt_status) {
default:

View File

@ -30,12 +30,12 @@
/* tls_peer_CN() returns the text CommonName for the peer
/* certificate subject, or an empty string if no CommonName was
/* found. The result is allocated with mymalloc() and must be
/* freed by the caller.
/* freed by the caller; it is arbitrary UTF-8 content.
/*
/* tls_issuer_CN() returns the text CommonName for the peer
/* certificate issuer, or an empty string if no CommonName was
/* found. The result is allocated with mymalloc() and must be
/* freed by the caller.
/* freed by the caller; it is arbitrary UTF-8 content.
/*
/* tls_dns_name() returns the string value of a GENERAL_NAME
/* from a DNS subjectAltName extension. If non-printable characters
@ -278,11 +278,9 @@ static char *tls_text_name(X509_NAME *name, int nid, const char *label,
int pos;
X509_NAME_ENTRY *entry;
ASN1_STRING *entry_str;
int typ;
int len;
unsigned char *val;
unsigned char *utf;
char *cp;
int asn1_type;
int utf8_length;
unsigned char *utf8_value;
if (name == 0 || (pos = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) {
if (gripe != DONT_GRIPE) {
@ -321,96 +319,72 @@ static char *tls_text_name(X509_NAME *name, int nid, const char *label,
}
/*
* Peername checks are security sensitive, carefully scrutinize the
* input!
* XXX Convert everything into UTF-8. This is a super-set of ASCII, so we
* don't have to bother with separate code paths for ASCII-like content.
* If the payload is ASCII then we won't waste lots of CPU cycles
* converting it into UTF-8. It's up to OpenSSL to do something
* reasonable when converting ASCII formats that contain non-ASCII
* content.
*
* XXX Don't bother optimizing the string length error check. It is not
* worth the complexity.
*/
typ = ASN1_STRING_type(entry_str);
len = ASN1_STRING_length(entry_str);
val = ASN1_STRING_data(entry_str);
/*
* http://www.apps.ietf.org/rfc/rfc3280.html#sec-4.1.2.4 Quick Summary:
*
* The DirectoryString type is defined as a choice of PrintableString,
* TeletexString, BMPString, UTF8String, and UniversalString. The
* UTF8String encoding is the preferred encoding, and all certificates
* issued after December 31, 2003 MUST use the UTF8String encoding of
* DirectoryString (except as noted below).
*
* XXX: 2007, the above has not happened yet (of course), and we continue to
* see new certificates with T61STRING (Teletex) attribute values.
*
* XXX: 2007, at this time there are only two ASN.1 fixed width multi-byte
* string encodings, BMPSTRING (16 bit Unicode) and UniversalString
* (32-bit Unicode). The only variable width ASN.1 string encoding is
* UTF8 with all the other encodings being 1 byte wide subsets or subsets
* of ASCII.
*
* Relying on this could simplify the code, because we would never convert
* unexpected single-byte encodings, but is involves too many cases to be
* sure that we have a complete set and the assumptions may become false.
* So, we pessimistically convert encodings not blessed by RFC 2459, and
* filter out all types that are not string types as a side-effect of
* UTF8 conversion (the ASN.1 library knows which types are string types
* and how wide they are...).
*
* XXX: Two possible states after switch, either "utf == val" and it MUST
* NOT be freed with OPENSSL_free(), or "utf != val" and it MUST be freed
* with OPENSSL_free().
*/
switch (typ) {
case V_ASN1_PRINTABLESTRING: /* X.500 portable ASCII
* printables */
case V_ASN1_IA5STRING: /* ISO 646 ~ ASCII */
case V_ASN1_T61STRING: /* Essentially ISO-Latin */
case V_ASN1_UTF8STRING: /* UTF8 */
utf = val;
break;
default:
/*
* May shrink in wash, but BMPSTRING only shrinks by 50%. Others may
* shrink by up to 75%. We Sanity check the length before bothering
* to copy any large strings to convert to UTF8, only to find out
* they don't fit. So long as no new MB types are introduced, and
* weird string encodings unsanctioned by RFC 3280, are used in the
* issuer or subject DN, this "conservative" estimate will be exact.
*/
len >>= (typ == V_ASN1_BMPSTRING) ? 1 : 2;
if (len >= CCERT_BUFSIZ) {
msg_warn("%s: %s: peer %s too long: %d",
myname, TLScontext->namaddr, label, len);
return (0);
}
if ((len = ASN1_STRING_to_UTF8(&utf, entry_str)) < 0) {
asn1_type = ASN1_STRING_type(entry_str);
if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, entry_str)) < 0) {
msg_warn("%s: %s: error decoding peer %s of ASN.1 type=%d",
myname, TLScontext->namaddr, label, typ);
myname, TLScontext->namaddr, label, asn1_type);
tls_print_errors();
return (0);
}
}
#define RETURN(x) do { if (utf!=val) OPENSSL_free(utf); return (x); } while (0)
if (len >= CCERT_BUFSIZ) {
/*
* No returns without cleaning up. A good optimizer will replace multiple
* blocks of identical code by jumps to just one such block.
*/
#define TLS_TEXT_NAME_RETURN(x) do { \
char *__tls_text_name_temp = (x); \
OPENSSL_free(utf8_value); \
return (__tls_text_name_temp); \
} while (0)
#if 0
for (cp = utf8_value; (ch = *cp) != 0; cp++) {
if (ISASCII(ch) && !ISPRINT(ch)) {
msg_warn("%s: %s: non-printable content in peer %s",
myname, TLScontext->namaddr, label);
TLS_TEXT_NAME_RETURN(0);
}
}
#endif
/*
* Remove trailing null characters. They would give false alarms with the
* length check and with the embedded null check.
*/
#define TRIM0(s, l) do { while ((l) > 0 && (s)[(l)-1] == 0) --(l); } while (0)
TRIM0(utf8_value, utf8_length);
/*
* Enforce the length limit, because the caller will copy the result into
* a fixed-length buffer.
*/
if (utf8_length >= CCERT_BUFSIZ) {
msg_warn("%s: %s: peer %s too long: %d",
myname, TLScontext->namaddr, label, len);
RETURN(0);
myname, TLScontext->namaddr, label, utf8_length);
TLS_TEXT_NAME_RETURN(0);
}
if (len != strlen((char *) utf)) {
msg_warn("%s: %s: internal NUL in peer %s",
/*
* Don't allow embedded nulls in ASCII or UTF-8 names. OpenSSL is
* responsible for producing properly-formatted UTF-8.
*/
if (utf8_length != strlen((char *) utf8_value)) {
msg_warn("%s: %s: NULL character in peer %s",
myname, TLScontext->namaddr, label);
RETURN(0);
TLS_TEXT_NAME_RETURN(0);
}
for (cp = (char *) utf; *cp; cp++) {
if (!ISASCII(*cp) || !ISPRINT(*cp)) {
msg_warn("%s: %s: non-printable characters in peer %s",
myname, TLScontext->namaddr, label);
RETURN(0);
}
}
cp = mystrdup((char *) utf);
RETURN(cp);
TLS_TEXT_NAME_RETURN(mystrdup((char *) utf8_value));
}
/* tls_dns_name - Extract valid DNS name from subjectAltName value */
@ -421,6 +395,7 @@ const char *tls_dns_name(const GENERAL_NAME * gn,
const char *myname = "tls_dns_name";
char *cp;
const char *dnsname;
int len;
/*
* Peername checks are security sensitive, carefully scrutinize the
@ -443,6 +418,8 @@ const char *tls_dns_name(const GENERAL_NAME * gn,
* Safe to treat as an ASCII string possibly holding a DNS name
*/
dnsname = (char *) ASN1_STRING_data(gn->d.ia5);
len = ASN1_STRING_length(gn->d.ia5);
TRIM0(dnsname, len);
/*
* Per Dr. Steven Henson of the OpenSSL development team, ASN1_IA5STRING
@ -451,7 +428,7 @@ const char *tls_dns_name(const GENERAL_NAME * gn,
* always appended to make sure that the string is terminated, but the
* ASN.1 length may differ from strlen().
*/
if (ASN1_STRING_length(gn->d.ia5) != strlen(dnsname)) {
if (len != strlen(dnsname)) {
msg_warn("%s: %s: internal NUL in subjectAltName",
myname, TLScontext->namaddr);
return 0;
@ -463,10 +440,9 @@ const char *tls_dns_name(const GENERAL_NAME * gn,
* compare equal to the expected peername, so being more strict than
* "printable" is likely excessive...
*/
for (cp = (char *) dnsname; cp && *cp; cp++)
if (!ISASCII(*cp) || !ISPRINT(*cp)) {
if (*dnsname && !allprint(dnsname)) {
cp = mystrdup(dnsname);
msg_warn("%s: %s: non-printable characters in subjectAltName: %s",
msg_warn("%s: %s: non-printable characters in subjectAltName: %.100s",
myname, TLScontext->namaddr, printable(cp, '?'));
myfree(cp);
return 0;