mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-29 05:07:58 +00:00
postfix-2.3-20050920
This commit is contained in:
parent
04571e906b
commit
b406b5fe04
2
postfix/.indent.pro
vendored
2
postfix/.indent.pro
vendored
@ -224,6 +224,8 @@
|
||||
-TWAIT_STATUS_T
|
||||
-TWATCHDOG
|
||||
-TWATCH_FD
|
||||
-TX509
|
||||
-TX509_NAME
|
||||
-TX509_STORE_CTX
|
||||
-Tregex_t
|
||||
-Tregmatch_t
|
||||
|
@ -11115,6 +11115,15 @@ Apologies for any names omitted.
|
||||
needed before cleanup_bounce() can seek to the start of the
|
||||
queue file after a file size error. File: util/vstream.c.
|
||||
|
||||
20050920
|
||||
|
||||
Cleanup: removed the legacy "tls_info" structure, factored
|
||||
out common code for peer_CN and issuer_CN lookup, and added
|
||||
sanity check to not verify subject common names that contain
|
||||
nulls or that are execessively long. Patch by Victor Duchovni.
|
||||
Files: tls_client.c, tls_server.c, tls_session.c, tls_misc.c,
|
||||
tls_verify.c.
|
||||
|
||||
Open problems:
|
||||
|
||||
Look for systems with XPG basename() declared in <libgen.h>,
|
||||
|
@ -13,7 +13,7 @@ The main feature of interest is that IPv6 uses 128-bit IP addresses instead of
|
||||
the 32-bit addresses used by IPv4. It can therefore accommodate a much larger
|
||||
number of hosts and networks without ugly kluges such as NAT. A side benefit of
|
||||
the much larger address space is that it makes random network scanning
|
||||
unpractical.
|
||||
impractical.
|
||||
|
||||
Postfix uses the same SMTP protocol over IPv6 as it already uses over the older
|
||||
IPv4 network, and does AAAA record lookups in the DNS in addition to the older
|
||||
|
@ -20,6 +20,10 @@ When receiving mail, Postfix logs the client-provided username, authentication
|
||||
method, and sender address to the maillog file, and optionally grants mail
|
||||
access via the permit_sasl_authenticated UCE restriction.
|
||||
|
||||
When sending mail, Postfix can look up the server hostname or destination
|
||||
domain (the address right-hand part) in a table, and if a username/password is
|
||||
found, it will use that username and password to authenticate to the server.
|
||||
|
||||
This document covers the following topics:
|
||||
|
||||
* What SASL versions are supported
|
||||
@ -31,10 +35,6 @@ This document covers the following topics:
|
||||
* Enabling SASL authentication in the Postfix SMTP client
|
||||
* Credits
|
||||
|
||||
When sending mail, Postfix can look up the server hostname or destination
|
||||
domain (the address right-hand part) in a table, and if a username/password is
|
||||
found, it will use that username and password to authenticate to the server.
|
||||
|
||||
WWhhaatt SSAASSLL vveerrssiioonnss aarree ssuuppppoorrtteedd
|
||||
|
||||
Postfix+SASL 1.5.5 was seen working on RedHat 6.1 (pwcheck_method set to shadow
|
||||
|
@ -422,7 +422,7 @@ Example:
|
||||
...
|
||||
|
||||
The Postfix list manipulation routines give special treatment to whitespace and
|
||||
some other characters, making the use of certificate names unpractical. Instead
|
||||
some other characters, making the use of certificate names impractical. Instead
|
||||
we use the certificate fingerprints as they are difficult to fake but easy to
|
||||
use for lookup. Postfix lookup tables are in the form of (key, value) pairs.
|
||||
Since we only need the key, the value can be chosen freely, e.g. the name of
|
||||
|
@ -30,7 +30,7 @@ between these implementations. </p>
|
||||
addresses instead of the 32-bit addresses used by IPv4. It can
|
||||
therefore accommodate a much larger number of hosts and networks
|
||||
without ugly kluges such as NAT. A side benefit of the much larger
|
||||
address space is that it makes random network scanning unpractical.
|
||||
address space is that it makes random network scanning impractical.
|
||||
</p>
|
||||
|
||||
<p> Postfix uses the same SMTP protocol over IPv6 as it already
|
||||
|
@ -37,6 +37,11 @@ authentication method, and sender address to the maillog file, and
|
||||
optionally grants mail access via the <a href="postconf.5.html#permit_sasl_authenticated">permit_sasl_authenticated</a>
|
||||
UCE restriction. </p>
|
||||
|
||||
<p> When sending mail, Postfix can look up the server hostname or
|
||||
destination domain (the address right-hand part) in a table, and if a
|
||||
username/password is found, it will use that username and password
|
||||
to authenticate to the server. </p>
|
||||
|
||||
<p>This document covers the following topics: </p>
|
||||
|
||||
<ul>
|
||||
@ -63,11 +68,6 @@ Postfix SMTP client</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p> When sending mail, Postfix can look up the server hostname or
|
||||
destination domain (the address right-hand part) in a table, and if a
|
||||
username/password is found, it will use that username and password
|
||||
to authenticate to the server. </p>
|
||||
|
||||
<h2><a name="versions">What SASL versions are supported</a></h2>
|
||||
|
||||
<p> Postfix+SASL 1.5.5 was seen working on RedHat 6.1 (pwcheck_method
|
||||
|
@ -591,7 +591,7 @@ Postfix SMTP server access control: </p>
|
||||
<dt> <a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a> </dt> <dd> <p> Allow the remote SMTP
|
||||
client SMTP request if the client certificate passes verification,
|
||||
and if its fingerprint is listed in the list of client certificates
|
||||
(see relay_clientcerts discussion below). </p> </dd>
|
||||
(see <a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a> discussion below). </p> </dd>
|
||||
|
||||
<dt> <a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientcerts</a> </dt> <dd> <p> Allow the remote
|
||||
client SMTP request if the client certificate passes verification.
|
||||
@ -614,7 +614,7 @@ The <a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientce
|
||||
specially created email relay server. </p>
|
||||
|
||||
<p> It is however recommended to stay with the <a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a>
|
||||
feature and list all certificates via $relay_clientcerts, as
|
||||
feature and list all certificates via $<a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a>, as
|
||||
<a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientcerts</a> does not permit any control when a
|
||||
certificate must no longer be used (e.g. an employee leaving). </p>
|
||||
|
||||
@ -633,7 +633,7 @@ certificate must no longer be used (e.g. an employee leaving). </p>
|
||||
|
||||
<p> The Postfix list manipulation routines give special treatment
|
||||
to whitespace and some other characters, making the use of certificate
|
||||
names unpractical. Instead we use the certificate fingerprints as
|
||||
names impractical. Instead we use the certificate fingerprints as
|
||||
they are difficult to fake but easy to use for lookup. Postfix
|
||||
lookup tables are in the form of (key, value) pairs. Since we only
|
||||
need the key, the value can be chosen freely, e.g. the name of
|
||||
@ -644,7 +644,7 @@ the user or host.</p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
/etc/postfix/main.cf:
|
||||
relay_clientcerts = hash:/etc/postfix/relay_clientcerts
|
||||
<a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a> = hash:/etc/postfix/relay_clientcerts
|
||||
|
||||
/etc/postfix/relay_clientcerts:
|
||||
D7:04:2F:A7:0B:8C:A5:21:FA:31:77:E1:41:8A:EE:80 lutzpc.at.home
|
||||
|
@ -324,7 +324,7 @@ LDAP_TABLE(5) LDAP_TABLE(5)
|
||||
NOTE: DO NOT define this parameter for <a href="local.8.html">local(8)</a>
|
||||
aliases.
|
||||
|
||||
This feature is available in Postfix 2.1 and later.
|
||||
This feature is available in Postfix 1.0 and later.
|
||||
|
||||
<b>result_attribute (default: maildrop)</b>
|
||||
The attribute(s) Postfix will read from any direc-
|
||||
|
@ -305,7 +305,7 @@ for LDAP lookups.
|
||||
|
||||
NOTE: DO NOT define this parameter for local(8) aliases.
|
||||
|
||||
This feature is available in Postfix 2.1 and later.
|
||||
This feature is available in Postfix 1.0 and later.
|
||||
.IP "\fBresult_attribute (default: maildrop)\fR"
|
||||
The attribute(s) Postfix will read from any directory
|
||||
entries returned by the lookup, to be resolved to an email
|
||||
|
@ -30,7 +30,7 @@ between these implementations. </p>
|
||||
addresses instead of the 32-bit addresses used by IPv4. It can
|
||||
therefore accommodate a much larger number of hosts and networks
|
||||
without ugly kluges such as NAT. A side benefit of the much larger
|
||||
address space is that it makes random network scanning unpractical.
|
||||
address space is that it makes random network scanning impractical.
|
||||
</p>
|
||||
|
||||
<p> Postfix uses the same SMTP protocol over IPv6 as it already
|
||||
|
@ -37,6 +37,11 @@ authentication method, and sender address to the maillog file, and
|
||||
optionally grants mail access via the permit_sasl_authenticated
|
||||
UCE restriction. </p>
|
||||
|
||||
<p> When sending mail, Postfix can look up the server hostname or
|
||||
destination domain (the address right-hand part) in a table, and if a
|
||||
username/password is found, it will use that username and password
|
||||
to authenticate to the server. </p>
|
||||
|
||||
<p>This document covers the following topics: </p>
|
||||
|
||||
<ul>
|
||||
@ -63,11 +68,6 @@ Postfix SMTP client</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p> When sending mail, Postfix can look up the server hostname or
|
||||
destination domain (the address right-hand part) in a table, and if a
|
||||
username/password is found, it will use that username and password
|
||||
to authenticate to the server. </p>
|
||||
|
||||
<h2><a name="versions">What SASL versions are supported</a></h2>
|
||||
|
||||
<p> Postfix+SASL 1.5.5 was seen working on RedHat 6.1 (pwcheck_method
|
||||
|
@ -633,7 +633,7 @@ certificate must no longer be used (e.g. an employee leaving). </p>
|
||||
|
||||
<p> The Postfix list manipulation routines give special treatment
|
||||
to whitespace and some other characters, making the use of certificate
|
||||
names unpractical. Instead we use the certificate fingerprints as
|
||||
names impractical. Instead we use the certificate fingerprints as
|
||||
they are difficult to fake but easy to use for lookup. Postfix
|
||||
lookup tables are in the form of (key, value) pairs. Since we only
|
||||
need the key, the value can be chosen freely, e.g. the name of
|
||||
|
@ -293,7 +293,7 @@
|
||||
#
|
||||
# NOTE: DO NOT define this parameter for local(8) aliases.
|
||||
#
|
||||
# This feature is available in Postfix 2.1 and later.
|
||||
# This feature is available in Postfix 1.0 and later.
|
||||
# .IP "\fBresult_attribute (default: maildrop)\fR"
|
||||
# The attribute(s) Postfix will read from any directory
|
||||
# entries returned by the lookup, to be resolved to an email
|
||||
|
@ -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 "20050829"
|
||||
#define MAIL_RELEASE_DATE "20050920"
|
||||
#define MAIL_VERSION_NUMBER "2.3"
|
||||
|
||||
#ifdef SNAPSHOT
|
||||
|
@ -217,7 +217,6 @@ typedef struct SMTP_SESSION {
|
||||
int tls_enforce_tls; /* must do TLS */
|
||||
int tls_enforce_peername; /* cert must match */
|
||||
TLScontext_t *tls_context; /* TLS session state */
|
||||
tls_info_t tls_info; /* legacy */
|
||||
#endif
|
||||
|
||||
} SMTP_SESSION;
|
||||
|
@ -626,8 +626,7 @@ static int smtp_start_tls(SMTP_STATE *state, int misc_flags)
|
||||
var_smtp_starttls_tmout,
|
||||
session->tls_enforce_peername,
|
||||
session->host,
|
||||
lowercase(vstring_str(serverid)),
|
||||
&(session->tls_info));
|
||||
lowercase(vstring_str(serverid)));
|
||||
vstring_free(serverid);
|
||||
if (session->tls_context == 0)
|
||||
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
|
||||
@ -815,6 +814,7 @@ static void smtp_header_rewrite(void *context, int header_class,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* smtp_mime_fail - MIME problem */
|
||||
|
||||
static void smtp_mime_fail(SMTP_STATE *state, int mime_errs)
|
||||
|
@ -233,7 +233,6 @@ SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, const char *dest,
|
||||
session->tls_use_tls = session->tls_enforce_tls = 0;
|
||||
session->tls_enforce_peername = 0;
|
||||
session->tls_context = 0;
|
||||
session->tls_info = tls_info_zero;
|
||||
|
||||
/*
|
||||
* Override the main.cf TLS policy with an optional per-site policy.
|
||||
@ -282,8 +281,7 @@ void smtp_session_free(SMTP_SESSION *session)
|
||||
vstream_fflush(session->stream);
|
||||
if (session->tls_context)
|
||||
tls_client_stop(smtp_tls_ctx, session->stream,
|
||||
var_smtp_starttls_tmout, 0,
|
||||
&(session->tls_info));
|
||||
var_smtp_starttls_tmout, 0, session->tls_context);
|
||||
}
|
||||
#endif
|
||||
if (session->stream)
|
||||
|
@ -2142,15 +2142,17 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
|
||||
if (var_smtpd_tls_received_header && state->tls_context) {
|
||||
out_fprintf(out_stream, REC_TYPE_NORM,
|
||||
"\t(using %s with cipher %s (%d/%d bits))",
|
||||
state->tls_info.protocol, state->tls_info.cipher_name,
|
||||
state->tls_info.cipher_usebits,
|
||||
state->tls_info.cipher_algbits);
|
||||
if (state->tls_info.peer_CN) {
|
||||
peer_CN = VSTRING_STRDUP(state->tls_info.peer_CN);
|
||||
state->tls_context->protocol,
|
||||
state->tls_context->cipher_name,
|
||||
state->tls_context->cipher_usebits,
|
||||
state->tls_context->cipher_algbits);
|
||||
if (state->tls_context->peer_CN) {
|
||||
peer_CN = VSTRING_STRDUP(state->tls_context->peer_CN);
|
||||
comment_sanitize(peer_CN);
|
||||
issuer_CN = VSTRING_STRDUP(state->tls_info.issuer_CN);
|
||||
issuer_CN = VSTRING_STRDUP(state->tls_context->issuer_CN ?
|
||||
state->tls_context->issuer_CN : "");
|
||||
comment_sanitize(issuer_CN);
|
||||
if (state->tls_info.peer_verified)
|
||||
if (state->tls_context->peer_verified)
|
||||
out_fprintf(out_stream, REC_TYPE_NORM,
|
||||
"\t(Client CN \"%s\", Issuer \"%s\" (verified OK))",
|
||||
STR(peer_CN), STR(issuer_CN));
|
||||
@ -3036,8 +3038,7 @@ static void smtpd_start_tls(SMTPD_STATE *state)
|
||||
*/
|
||||
state->tls_context =
|
||||
tls_server_start(smtpd_tls_ctx, state->client,
|
||||
var_smtpd_starttls_tmout,
|
||||
state->name, state->addr, &(state->tls_info),
|
||||
var_smtpd_starttls_tmout, state->name, state->addr,
|
||||
(var_smtpd_tls_req_ccert && state->tls_enforce_tls));
|
||||
|
||||
/*
|
||||
@ -3121,7 +3122,7 @@ static void tls_reset(SMTPD_STATE *state)
|
||||
failure = 1;
|
||||
vstream_fflush(state->client); /* NOT: smtp_flush() */
|
||||
tls_server_stop(smtpd_tls_ctx, state->client, var_smtpd_starttls_tmout,
|
||||
failure, &(state->tls_info));
|
||||
failure, state->tls_context);
|
||||
state->tls_context = 0;
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +175,6 @@ typedef struct SMTPD_STATE {
|
||||
int tls_enforce_tls; /* must use TLS */
|
||||
int tls_auth_only; /* use SASL over TLS only */
|
||||
TLScontext_t *tls_context; /* TLS session state */
|
||||
tls_info_t tls_info; /* legacy */
|
||||
#endif
|
||||
|
||||
} SMTPD_STATE;
|
||||
|
@ -1216,13 +1216,17 @@ static int permit_tls_clientcerts(SMTPD_STATE *state, int permit_all_certs)
|
||||
char *low_name;
|
||||
const char *found;
|
||||
|
||||
if (state->tls_info.peer_verified && permit_all_certs) {
|
||||
if (!state->tls_context)
|
||||
return SMTPD_CHECK_DUNNO;
|
||||
|
||||
if (state->tls_context->peer_verified && permit_all_certs) {
|
||||
if (msg_verbose)
|
||||
msg_info("Relaying allowed for all verified client certificates");
|
||||
return (SMTPD_CHECK_OK);
|
||||
}
|
||||
if (state->tls_info.peer_verified && state->tls_info.peer_fingerprint) {
|
||||
low_name = lowercase(mystrdup(state->tls_info.peer_fingerprint));
|
||||
if (state->tls_context->peer_verified
|
||||
&& state->tls_context->peer_fingerprint) {
|
||||
low_name = lowercase(mystrdup(state->tls_context->peer_fingerprint));
|
||||
found = maps_find(relay_ccerts, low_name, DICT_FLAG_FIXED);
|
||||
myfree(low_name);
|
||||
if (found) {
|
||||
@ -1231,7 +1235,7 @@ static int permit_tls_clientcerts(SMTPD_STATE *state, int permit_all_certs)
|
||||
return (SMTPD_CHECK_OK);
|
||||
} else if (msg_verbose)
|
||||
msg_info("relay_clientcerts: No match for fingerprint '%s'",
|
||||
state->tls_info.peer_fingerprint);
|
||||
state->tls_context->peer_fingerprint);
|
||||
}
|
||||
return (SMTPD_CHECK_DUNNO);
|
||||
}
|
||||
@ -2496,9 +2500,13 @@ static int check_ccert_access(SMTPD_STATE *state, const char *table,
|
||||
char *myname = "check_ccert_access";
|
||||
int found;
|
||||
|
||||
if (state->tls_info.peer_verified && state->tls_info.peer_fingerprint) {
|
||||
if (!state->tls_context)
|
||||
return SMTPD_CHECK_DUNNO;
|
||||
|
||||
if (state->tls_context->peer_verified
|
||||
&& state->tls_context->peer_fingerprint) {
|
||||
if (msg_verbose)
|
||||
msg_info("%s: %s", myname, state->tls_info.peer_fingerprint);
|
||||
msg_info("%s: %s", myname, state->tls_context->peer_fingerprint);
|
||||
|
||||
/*
|
||||
* Regexp tables don't make sense for certificate fingerprints. That
|
||||
@ -2510,8 +2518,10 @@ static int check_ccert_access(SMTPD_STATE *state, const char *table,
|
||||
* client name and address are always syslogged as part of a "reject"
|
||||
* event.
|
||||
*/
|
||||
return (check_access(state, table, state->tls_info.peer_fingerprint,
|
||||
DICT_FLAG_NONE, &found, state->tls_info.peer_CN,
|
||||
return (check_access(state, table,
|
||||
state->tls_context->peer_fingerprint,
|
||||
DICT_FLAG_NONE, &found,
|
||||
state->tls_context->peer_CN,
|
||||
SMTPD_NAME_CCERT, def_acl));
|
||||
}
|
||||
return (SMTPD_CHECK_DUNNO);
|
||||
@ -3228,15 +3238,15 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
|
||||
state->sasl_sender : "",
|
||||
#endif
|
||||
#ifdef USE_TLS
|
||||
#define IF_VERIFIED(x) \
|
||||
((state->tls_context && \
|
||||
state->tls_context->peer_verified && ((x) != 0)) ? (x) : "")
|
||||
ATTR_TYPE_STR, MAIL_ATTR_CCERT_SUBJECT,
|
||||
state->tls_info.peer_verified ?
|
||||
state->tls_info.peer_CN : "",
|
||||
IF_VERIFIED(state->tls_context->peer_CN),
|
||||
ATTR_TYPE_STR, MAIL_ATTR_CCERT_ISSSUER,
|
||||
state->tls_info.peer_verified ?
|
||||
state->tls_info.issuer_CN : "",
|
||||
IF_VERIFIED(state->tls_context->issuer_CN),
|
||||
ATTR_TYPE_STR, MAIL_ATTR_CCERT_FINGERPRINT,
|
||||
state->tls_info.peer_verified ?
|
||||
state->tls_info.peer_fingerprint : "",
|
||||
IF_VERIFIED(state->tls_context->peer_fingerprint),
|
||||
#endif
|
||||
ATTR_TYPE_END,
|
||||
ATTR_FLAG_MISSING, /* Reply attributes. */
|
||||
|
@ -138,7 +138,6 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream,
|
||||
state->tls_enforce_tls = 0;
|
||||
state->tls_auth_only = 0;
|
||||
state->tls_context = 0;
|
||||
state->tls_info = tls_info_zero;
|
||||
#endif
|
||||
|
||||
#ifdef USE_SASL_AUTH
|
||||
|
@ -1,11 +1,11 @@
|
||||
SHELL = /bin/sh
|
||||
SRCS = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c \
|
||||
tls_prng_exch.c tls_stream.c tls_bio_ops.c tls_misc.c tls_dh.c \
|
||||
tls_rsa.c tls_verify.c tls_certkey.c tls_session.c tls_temp.c \
|
||||
tls_rsa.c tls_verify.c tls_certkey.c tls_session.c \
|
||||
tls_client.c tls_server.c tls_scache.c tls_mgr.c tls_seed.c
|
||||
OBJS = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o \
|
||||
tls_prng_exch.o tls_stream.o tls_bio_ops.o tls_misc.o tls_dh.o \
|
||||
tls_rsa.o tls_verify.o tls_certkey.o tls_session.o tls_temp.o \
|
||||
tls_rsa.o tls_verify.o tls_certkey.o tls_session.o \
|
||||
tls_client.o tls_server.o tls_scache.o tls_mgr.o tls_seed.o
|
||||
HDRS = tls.h tls_prng.h tls_scache.h tls_mgr.h
|
||||
TESTSRC =
|
||||
@ -222,12 +222,6 @@ tls_stream.o: ../../include/vstream.h
|
||||
tls_stream.o: ../../include/vstring.h
|
||||
tls_stream.o: tls.h
|
||||
tls_stream.o: tls_stream.c
|
||||
tls_temp.o: ../../include/sys_defs.h
|
||||
tls_temp.o: ../../include/vbuf.h
|
||||
tls_temp.o: ../../include/vstream.h
|
||||
tls_temp.o: ../../include/vstring.h
|
||||
tls_temp.o: tls.h
|
||||
tls_temp.o: tls_temp.c
|
||||
tls_verify.o: ../../include/msg.h
|
||||
tls_verify.o: ../../include/sys_defs.h
|
||||
tls_verify.o: ../../include/vbuf.h
|
||||
|
@ -35,15 +35,6 @@
|
||||
/*
|
||||
* TLS session context, also used by the VSTREAM call-back routines for SMTP
|
||||
* input/output, and by OpenSSL call-back routines for key verification.
|
||||
*
|
||||
* XXX Eliminate fixed-length buffers where possible.
|
||||
*
|
||||
* XXX Eliminate the tls_info structure; it is no longer needed now that the
|
||||
* TLScontext structure is exposed to the caller. If the caller's TLScontext
|
||||
* pointer is null, there is no TLS session. This change (plus other
|
||||
* changes) eliminated global variables that were shared between TLS client
|
||||
* and server code. Multiple clients and/or servers can now co-exist in the
|
||||
* same process.
|
||||
*/
|
||||
#define CCERT_BUFSIZ 256
|
||||
#define HOST_BUFSIZ 255 /* RFC 1035 */
|
||||
@ -53,63 +44,48 @@ typedef struct {
|
||||
BIO *internal_bio; /* postfix/TLS side of pair */
|
||||
BIO *network_bio; /* network side of pair */
|
||||
char *serverid; /* unique server identifier */
|
||||
char peer_subject[CCERT_BUFSIZ];
|
||||
char peer_issuer[CCERT_BUFSIZ];
|
||||
char peer_CN[CCERT_BUFSIZ];
|
||||
char issuer_CN[CCERT_BUFSIZ];
|
||||
unsigned char md[EVP_MAX_MD_SIZE];
|
||||
char fingerprint[EVP_MAX_MD_SIZE * 3];
|
||||
char *peer_CN; /* Peer Common Name */
|
||||
char *issuer_CN; /* Issuer Common Name */
|
||||
char *peer_fingerprint; /* ASCII fingerprint */
|
||||
char *peername;
|
||||
int enforce_verify_errors;
|
||||
int enforce_CN;
|
||||
int hostname_matched;
|
||||
int log_level;
|
||||
} TLScontext_t;
|
||||
|
||||
#define TLS_BIO_BUFSIZE 8192
|
||||
|
||||
typedef struct {
|
||||
int peer_verified;
|
||||
int hostname_matched;
|
||||
char *peer_subject;
|
||||
char *peer_issuer;
|
||||
char *peer_fingerprint;
|
||||
char *peer_CN;
|
||||
char *issuer_CN;
|
||||
const char *protocol;
|
||||
const char *cipher_name;
|
||||
int cipher_usebits;
|
||||
int cipher_algbits;
|
||||
} tls_info_t;
|
||||
int log_level;
|
||||
} TLScontext_t;
|
||||
|
||||
extern const tls_info_t tls_info_zero;
|
||||
#define TLS_BIO_BUFSIZE 8192
|
||||
|
||||
/*
|
||||
* tls_client.c
|
||||
*/
|
||||
extern SSL_CTX *tls_client_init(int);
|
||||
extern TLScontext_t *tls_client_start(SSL_CTX *, VSTREAM *, int, int,
|
||||
const char *, const char *,
|
||||
tls_info_t *);
|
||||
const char *, const char *);
|
||||
|
||||
#define tls_client_stop(ctx , stream, timeout, failure, tls_info) \
|
||||
tls_session_stop((ctx), (stream), (timeout), (failure), (tls_info))
|
||||
#define tls_client_stop(ctx , stream, timeout, failure, TLScontext) \
|
||||
tls_session_stop((ctx), (stream), (timeout), (failure), (TLScontext))
|
||||
|
||||
/*
|
||||
* tls_server.c
|
||||
*/
|
||||
extern SSL_CTX *tls_server_init(int, int);
|
||||
extern TLScontext_t *tls_server_start(SSL_CTX *, VSTREAM *, int,
|
||||
const char *, const char *,
|
||||
tls_info_t *, int);
|
||||
const char *, const char *, int);
|
||||
|
||||
#define tls_server_stop(ctx , stream, timeout, failure, tls_info) \
|
||||
tls_session_stop((ctx), (stream), (timeout), (failure), (tls_info))
|
||||
#define tls_server_stop(ctx , stream, timeout, failure, TLScontext) \
|
||||
tls_session_stop((ctx), (stream), (timeout), (failure), (TLScontext))
|
||||
|
||||
/*
|
||||
* tls_session.c
|
||||
*/
|
||||
extern void tls_session_stop(SSL_CTX *, VSTREAM *, int, int, tls_info_t *);
|
||||
extern void tls_session_stop(SSL_CTX *, VSTREAM *, int, int,
|
||||
TLScontext_t *);
|
||||
|
||||
#ifdef TLS_INTERNAL
|
||||
|
||||
@ -167,6 +143,8 @@ extern RSA *tls_tmp_rsa_cb(SSL *, int, int);
|
||||
/*
|
||||
* tls_verify.c
|
||||
*/
|
||||
extern char *tls_peer_CN(X509 *);
|
||||
extern char *tls_issuer_CN(X509 *);
|
||||
extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
|
||||
|
||||
/*
|
||||
|
@ -11,23 +11,48 @@
|
||||
/*
|
||||
/* TLScontext_t *tls_client_start(client_ctx, stream, timeout,
|
||||
/* enforce_peername, peername,
|
||||
/* serverid, tls_info)
|
||||
/* serverid)
|
||||
/* SSL_CTX *client_ctx;
|
||||
/* VSTREAM *stream;
|
||||
/* int timeout;
|
||||
/* int enforce_peername;
|
||||
/* const char *peername;
|
||||
/* const char *serverid;
|
||||
/* tls_info_t *tls_info;
|
||||
/*
|
||||
/* void tls_client_stop(client_ctx, stream, failure, tls_info)
|
||||
/* void tls_client_stop(client_ctx, stream, failure, TLScontext)
|
||||
/* SSL_CTX *client_ctx;
|
||||
/* VSTREAM *stream;
|
||||
/* int failure;
|
||||
/* tls_info_t *tls_info;
|
||||
/* TLScontext_t *TLScontext;
|
||||
/* DESCRIPTION
|
||||
/* This module is the interface between Postfix TLS clients
|
||||
/* and the OpenSSL library and TLS entropy and cache manager.
|
||||
/* This module is the interface between Postfix TLS clients,
|
||||
/* the OpenSSL library and the TLS entropy and cache manager.
|
||||
/*
|
||||
/* The SMTP client will attempt to verify the server hostname
|
||||
/* against the names listed in the server certificate. When
|
||||
/* a hostname match is required, the verification fails
|
||||
/* on certificate verification or hostname mis-match errors.
|
||||
/* When no hostname match is required, hostname verification
|
||||
/* failures are logged but they do not affect the TLS handshake
|
||||
/* or the SMTP session.
|
||||
/*
|
||||
/* The rules for peer name wild-card matching differ between
|
||||
/* RFC 2818 (HTTP over TLS) and RFC 2830 (LDAP over TLS), while
|
||||
/* RFC RFC3207 (SMTP over TLS) does not specify a rule at all.
|
||||
/* Postfix uses a restrictive match algorithm. One asterisk
|
||||
/* ('*') is allowed as the left-most component of a wild-card
|
||||
/* certificate name; it matches the left-most component of
|
||||
/* the peer hostname.
|
||||
/*
|
||||
/* Another area where RFCs aren't always explicit is the
|
||||
/* handling of dNSNames in peer certificates. RFC 3207 (SMTP
|
||||
/* over TLS) does not mention dNSNames. Postfix follows the
|
||||
/* strict rules in RFC 2818 (HTTP over TLS), section 3.1: The
|
||||
/* Subject Alternative Name/dNSName has precedence over
|
||||
/* CommonName. If at least one dNSName is provided, Postfix
|
||||
/* verifies those against the peer hostname and ignores the
|
||||
/* CommonName, otherwise Postfix verifies the CommonName
|
||||
/* against the peer hostname.
|
||||
/*
|
||||
/* tls_client_init() is called once when the SMTP client
|
||||
/* initializes.
|
||||
@ -35,12 +60,10 @@
|
||||
/* so that peer-specific behavior is not possible.
|
||||
/*
|
||||
/* tls_client_start() activates the TLS feature for the VSTREAM
|
||||
/* passed as argument. We expect that network buffers are flushed and the
|
||||
/* TLS handshake can begin immediately. Information about the peer
|
||||
/* is stored into the tls_info structure passed as argument.
|
||||
/* The serverid argument specifies a string that hopefully
|
||||
/* uniquely identifies a server. It is used as the client
|
||||
/* session cache lookup key.
|
||||
/* passed as argument. We expect that network buffers are flushed and
|
||||
/* the TLS handshake can begin immediately. The serverid argument
|
||||
/* specifies a string that hopefully uniquely identifies a server.
|
||||
/* It is used as the client session cache lookup key.
|
||||
/*
|
||||
/* tls_client_stop() sends the "close notify" alert via
|
||||
/* SSL_shutdown() to the peer and resets all connection specific
|
||||
@ -51,34 +74,32 @@
|
||||
/* If the failure flag is set, no SSL_shutdown() handshake is performed.
|
||||
/*
|
||||
/* Once the TLS connection is initiated, information about the TLS
|
||||
/* state is available via the tls_info structure:
|
||||
/* .IP tls_info->protocol
|
||||
/* state is available via the TLScontext structure:
|
||||
/* .IP TLScontext->protocol
|
||||
/* the protocol name (SSLv2, SSLv3, TLSv1),
|
||||
/* .IP tls_info->cipher_name
|
||||
/* .IP TLScontext->cipher_name
|
||||
/* the cipher name (e.g. RC4/MD5),
|
||||
/* .IP tls_info->cipher_usebits
|
||||
/* .IP TLScontext->cipher_usebits
|
||||
/* the number of bits actually used (e.g. 40),
|
||||
/* .IP tls_info->cipher_algbits
|
||||
/* .IP TLScontext->cipher_algbits
|
||||
/* the number of bits the algorithm is based on (e.g. 128).
|
||||
/* .PP
|
||||
/* The last two values may differ from each other when export-strength
|
||||
/* encryption is used.
|
||||
/*
|
||||
/* The status of the peer certificate verification is available in
|
||||
/* tls_info->peer_verified. It is set to 1 when the certificate could
|
||||
/* TLScontext->peer_verified. It is set to 1 when the certificate could
|
||||
/* be verified.
|
||||
/* If the peer offered a certificate, part of the certificate data are
|
||||
/* available as:
|
||||
/* .IP tls_info->peer_subject
|
||||
/* X509v3-oneline with the DN of the peer
|
||||
/* .IP tls_info->peer_CN
|
||||
/* extracted CommonName of the peer
|
||||
/* .IP tls_info->peer_issuer
|
||||
/* X509v3-oneline with the DN of the issuer
|
||||
/* .IP tls_info->issuer_CN
|
||||
/* extracted CommonName of the issuer
|
||||
/* .IP tls_info->peer_fingerprint
|
||||
/* fingerprint of the certificate
|
||||
/* .IP TLScontext->peer_CN
|
||||
/* Extracted CommonName of the peer, or zero-length string if the
|
||||
/* information could not be extracted.
|
||||
/* .IP TLScontext->issuer_CN
|
||||
/* extracted CommonName of the issuer, or zero-length string if the
|
||||
/* information could not be extracted.
|
||||
/* .PP
|
||||
/* Otherwise these fields are set to null pointers.
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
@ -130,11 +151,6 @@
|
||||
#define STR vstring_str
|
||||
#define LEN VSTRING_LEN
|
||||
|
||||
/*
|
||||
* To convert binary to fingerprint.
|
||||
*/
|
||||
static const char hexcodes[] = "0123456789ABCDEF";
|
||||
|
||||
/*
|
||||
* Do or don't we cache client sessions?
|
||||
*/
|
||||
@ -438,28 +454,28 @@ static int match_hostname(const char *pattern, const char *hostname)
|
||||
/* verify_extract_peer - verify peer name and extract peer information */
|
||||
|
||||
static void verify_extract_peer(const char *peername, X509 * peercert,
|
||||
TLScontext_t *TLScontext, tls_info_t *tls_info)
|
||||
TLScontext_t *TLScontext)
|
||||
{
|
||||
char buf[1024];
|
||||
int i;
|
||||
int r;
|
||||
int hostname_matched;
|
||||
int dNSName_found;
|
||||
int hostname_matched = 0;
|
||||
int dNSName_found = 0;
|
||||
int verify_peername;
|
||||
|
||||
STACK_OF(GENERAL_NAME) * gens;
|
||||
|
||||
tls_info->peer_verified =
|
||||
TLScontext->peer_verified =
|
||||
(SSL_get_verify_result(TLScontext->con) == X509_V_OK);
|
||||
|
||||
if (TLScontext->enforce_CN != 0 && tls_info->peer_verified != 0) {
|
||||
verify_peername =
|
||||
(TLScontext->enforce_CN != 0 && TLScontext->peer_verified != 0);
|
||||
|
||||
if (verify_peername) {
|
||||
|
||||
/*
|
||||
* Verify the name(s) in the peer certificate against the peer
|
||||
* hostname. Log peer hostname/certificate mis-matches. If a match is
|
||||
* required but fails, bail out with a verification error.
|
||||
* Verify the dNSName(s) in the peer certificate against the
|
||||
* peername.
|
||||
*/
|
||||
hostname_matched = dNSName_found = 0;
|
||||
|
||||
gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
|
||||
if (gens) {
|
||||
for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) {
|
||||
@ -474,61 +490,37 @@ static void verify_extract_peer(const char *peername, X509 * peercert,
|
||||
}
|
||||
sk_GENERAL_NAME_free(gens);
|
||||
}
|
||||
if (dNSName_found) {
|
||||
}
|
||||
if (dNSName_found) {
|
||||
if (!hostname_matched)
|
||||
msg_info("certificate peer name verification failed for "
|
||||
"%s: %d dNSNames in certificate found, "
|
||||
"but none match", peername, dNSName_found);
|
||||
}
|
||||
if ((TLScontext->peer_CN = tls_peer_CN(peercert)) == 0)
|
||||
TLScontext->peer_CN = mystrdup("");
|
||||
|
||||
if ((TLScontext->issuer_CN = tls_issuer_CN(peercert)) == 0)
|
||||
TLScontext->issuer_CN = mystrdup("");
|
||||
|
||||
if (!dNSName_found && verify_peername) {
|
||||
|
||||
/*
|
||||
* Verify the CommonName in the peer certificate against the
|
||||
* peername.
|
||||
*/
|
||||
if (TLScontext->peer_CN[0] != '\0') {
|
||||
hostname_matched = match_hostname(TLScontext->peer_CN, peername);
|
||||
if (!hostname_matched)
|
||||
msg_info("certificate peer name verification failed for "
|
||||
"%s: %d dNSNames in certificate found, "
|
||||
"but none matches", peername, dNSName_found);
|
||||
} else {
|
||||
buf[0] = '\0';
|
||||
if (!X509_NAME_get_text_by_NID(X509_get_subject_name(peercert),
|
||||
NID_commonName, buf,
|
||||
sizeof(buf))) {
|
||||
msg_info("certificate peer name verification failed for"
|
||||
" %s: cannot parse subject CommonName", peername);
|
||||
tls_print_errors();
|
||||
} else {
|
||||
hostname_matched = match_hostname(buf, peername);
|
||||
if (!hostname_matched)
|
||||
msg_info("certificate peer name verification failed "
|
||||
"for %s: CommonName mis-match: %s",
|
||||
peername, buf);
|
||||
}
|
||||
}
|
||||
|
||||
TLScontext->hostname_matched = hostname_matched;
|
||||
}
|
||||
tls_info->hostname_matched = TLScontext->hostname_matched;
|
||||
|
||||
TLScontext->peer_CN[0] = '\0';
|
||||
if (!X509_NAME_get_text_by_NID(X509_get_subject_name(peercert),
|
||||
NID_commonName, TLScontext->peer_CN,
|
||||
sizeof(TLScontext->peer_CN))) {
|
||||
msg_info("Could not parse server's subject CN");
|
||||
tls_print_errors();
|
||||
}
|
||||
tls_info->peer_CN = TLScontext->peer_CN;
|
||||
|
||||
TLScontext->issuer_CN[0] = '\0';
|
||||
if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peercert),
|
||||
NID_commonName, TLScontext->issuer_CN,
|
||||
sizeof(TLScontext->issuer_CN))) {
|
||||
msg_info("Could not parse server's issuer CN");
|
||||
tls_print_errors();
|
||||
}
|
||||
if (!TLScontext->issuer_CN[0]) {
|
||||
/* No issuer CN field, use Organization instead */
|
||||
if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peercert),
|
||||
NID_organizationName, TLScontext->issuer_CN,
|
||||
sizeof(TLScontext->issuer_CN))) {
|
||||
msg_info("Could not parse server's issuer Organization");
|
||||
tls_print_errors();
|
||||
msg_info("certificate peer name verification failed "
|
||||
"for %s: CommonName mis-match: %s",
|
||||
peername, TLScontext->peer_CN);
|
||||
}
|
||||
}
|
||||
tls_info->issuer_CN = TLScontext->issuer_CN;
|
||||
TLScontext->hostname_matched = hostname_matched;
|
||||
|
||||
if (var_smtp_tls_loglevel >= 1) {
|
||||
if (tls_info->peer_verified
|
||||
if (TLScontext->peer_verified
|
||||
&& (!TLScontext->enforce_CN || TLScontext->hostname_matched))
|
||||
msg_info("Verified: subject_CN=%s, issuer=%s",
|
||||
TLScontext->peer_CN, TLScontext->issuer_CN);
|
||||
@ -547,8 +539,7 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
|
||||
int timeout,
|
||||
int enforce_peername,
|
||||
const char *peername,
|
||||
const char *serverid,
|
||||
tls_info_t *tls_info)
|
||||
const char *serverid)
|
||||
{
|
||||
int sts;
|
||||
SSL_SESSION *session;
|
||||
@ -707,30 +698,30 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
|
||||
* from the certificate for later use.
|
||||
*/
|
||||
if ((peercert = SSL_get_peer_certificate(TLScontext->con)) != 0) {
|
||||
verify_extract_peer(peername, peercert, TLScontext, tls_info);
|
||||
verify_extract_peer(peername, peercert, TLScontext);
|
||||
X509_free(peercert);
|
||||
}
|
||||
if (enforce_peername && !TLScontext->hostname_matched) {
|
||||
msg_info("Server certificate could not be verified for %s:"
|
||||
" hostname mismatch", peername);
|
||||
tls_client_stop(client_ctx, stream, timeout, 0, tls_info);
|
||||
tls_client_stop(client_ctx, stream, timeout, 0, TLScontext);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, collect information about protocol and cipher for logging
|
||||
*/
|
||||
tls_info->protocol = SSL_get_version(TLScontext->con);
|
||||
TLScontext->protocol = SSL_get_version(TLScontext->con);
|
||||
cipher = SSL_get_current_cipher(TLScontext->con);
|
||||
tls_info->cipher_name = SSL_CIPHER_get_name(cipher);
|
||||
tls_info->cipher_usebits = SSL_CIPHER_get_bits(cipher,
|
||||
&(tls_info->cipher_algbits));
|
||||
TLScontext->cipher_name = SSL_CIPHER_get_name(cipher);
|
||||
TLScontext->cipher_usebits = SSL_CIPHER_get_bits(cipher,
|
||||
&(TLScontext->cipher_algbits));
|
||||
|
||||
if (var_smtp_tls_loglevel >= 1)
|
||||
msg_info("TLS connection established to %s: %s with cipher %s"
|
||||
" (%d/%d bits)", peername,
|
||||
tls_info->protocol, tls_info->cipher_name,
|
||||
tls_info->cipher_usebits, tls_info->cipher_algbits);
|
||||
TLScontext->protocol, TLScontext->cipher_name,
|
||||
TLScontext->cipher_usebits, TLScontext->cipher_algbits);
|
||||
|
||||
tls_int_seed();
|
||||
|
||||
|
@ -110,6 +110,8 @@ TLScontext_t *tls_alloc_context(int log_level, const char *peername)
|
||||
*
|
||||
* See the C language FAQ item 5.17, or if you have time to burn,
|
||||
* http://www.google.com/search?q=zero+bit+null+pointer
|
||||
*
|
||||
* However, it's OK to use memset() to zero integer values.
|
||||
*/
|
||||
TLScontext = (TLScontext_t *) mymalloc(sizeof(TLScontext_t));
|
||||
memset((char *) TLScontext, 0, sizeof(*TLScontext));
|
||||
@ -117,6 +119,11 @@ TLScontext_t *tls_alloc_context(int log_level, const char *peername)
|
||||
TLScontext->internal_bio = 0;
|
||||
TLScontext->network_bio = 0;
|
||||
TLScontext->serverid = 0;
|
||||
TLScontext->peer_CN = 0;
|
||||
TLScontext->issuer_CN = 0;
|
||||
TLScontext->peer_fingerprint = 0;
|
||||
TLScontext->protocol = 0;
|
||||
TLScontext->cipher_name = 0;
|
||||
TLScontext->log_level = log_level;
|
||||
TLScontext->peername = lowercase(mystrdup(peername));
|
||||
|
||||
@ -137,10 +144,19 @@ void tls_free_context(TLScontext_t *TLScontext)
|
||||
SSL_free(TLScontext->con);
|
||||
if (TLScontext->network_bio)
|
||||
BIO_free(TLScontext->network_bio);
|
||||
|
||||
if (TLScontext->peername)
|
||||
myfree(TLScontext->peername);
|
||||
if (TLScontext->serverid)
|
||||
myfree(TLScontext->serverid);
|
||||
|
||||
if (TLScontext->peer_CN)
|
||||
myfree(TLScontext->peer_CN);
|
||||
if (TLScontext->issuer_CN)
|
||||
myfree(TLScontext->issuer_CN);
|
||||
if (TLScontext->peer_fingerprint)
|
||||
myfree(TLScontext->peer_fingerprint);
|
||||
|
||||
myfree((char *) TLScontext);
|
||||
}
|
||||
|
||||
|
@ -11,24 +11,22 @@
|
||||
/* int askcert;
|
||||
/*
|
||||
/* TLScontext_t *tls_server_start(server_ctx, stream, timeout,
|
||||
/* peername, peeraddr,
|
||||
/* tls_info, requirecert)
|
||||
/* peername, peeraddr, requirecert)
|
||||
/* SSL_CTX *server_ctx;
|
||||
/* VSTREAM *stream;
|
||||
/* int timeout;
|
||||
/* const char *peername;
|
||||
/* const char *peeraddr;
|
||||
/* tls_info_t *tls_info;
|
||||
/* int requirecert;
|
||||
/*
|
||||
/* void tls_server_stop(server_ctx, stream, failure, tls_info)
|
||||
/* void tls_server_stop(server_ctx, stream, failure, TLScontext)
|
||||
/* SSL_CTX *server_ctx;
|
||||
/* VSTREAM *stream;
|
||||
/* int failure;
|
||||
/* tls_info_t *tls_info;
|
||||
/* TLScontext_t *TLScontext;
|
||||
/* DESCRIPTION
|
||||
/* This module is the interface between Postfix TLS servers
|
||||
/* and the OpenSSL library and TLS entropy and cache manager.
|
||||
/* This module is the interface between Postfix TLS servers,
|
||||
/* the OpenSSL library, and the TLS entropy and cache manager.
|
||||
/*
|
||||
/* tls_server_init() is called once when the SMTP server
|
||||
/* initializes.
|
||||
@ -36,9 +34,8 @@
|
||||
/* so that peer-specific behavior is not possible.
|
||||
/*
|
||||
/* tls_server_start() activates the TLS feature for the VSTREAM
|
||||
/* passed as argument. We assume that network buffers are flushed and the
|
||||
/* TLS handshake can begin immediately. Information about the peer
|
||||
/* is stored into the tls_info structure passed as argument.
|
||||
/* passed as argument. We assume that network buffers are flushed
|
||||
/* and the TLS handshake can begin immediately.
|
||||
/*
|
||||
/* tls_server_stop() sends the "close notify" alert via
|
||||
/* SSL_shutdown() to the peer and resets all connection specific
|
||||
@ -49,34 +46,35 @@
|
||||
/* If the failure flag is set, no SSL_shutdown() handshake is performed.
|
||||
/*
|
||||
/* Once the TLS connection is initiated, information about the TLS
|
||||
/* state is available via the tls_info structure:
|
||||
/* .IP tls_info->protocol
|
||||
/* state is available via the TLScontext structure:
|
||||
/* .IP TLScontext->protocol
|
||||
/* the protocol name (SSLv2, SSLv3, TLSv1),
|
||||
/* .IP tls_info->cipher_name
|
||||
/* .IP TLScontext->cipher_name
|
||||
/* the cipher name (e.g. RC4/MD5),
|
||||
/* .IP tls_info->cipher_usebits
|
||||
/* .IP TLScontext->cipher_usebits
|
||||
/* the number of bits actually used (e.g. 40),
|
||||
/* .IP tls_info->cipher_algbits
|
||||
/* .IP TLScontext->cipher_algbits
|
||||
/* the number of bits the algorithm is based on (e.g. 128).
|
||||
/* .PP
|
||||
/* The last two values may differ from each other when export-strength
|
||||
/* encryption is used.
|
||||
/*
|
||||
/* The status of the peer certificate verification is available in
|
||||
/* tls_info->peer_verified. It is set to 1 when the certificate could
|
||||
/* TLScontext->peer_verified. It is set to 1 when the certificate could
|
||||
/* be verified.
|
||||
/* If the peer offered a certificate, part of the certificate data are
|
||||
/* available as:
|
||||
/* .IP tls_info->peer_subject
|
||||
/* X509v3-oneline with the DN of the peer
|
||||
/* .IP tls_info->peer_CN
|
||||
/* extracted CommonName of the peer
|
||||
/* .IP tls_info->peer_issuer
|
||||
/* X509v3-oneline with the DN of the issuer
|
||||
/* .IP tls_info->issuer_CN
|
||||
/* extracted CommonName of the issuer
|
||||
/* .IP tls_info->peer_fingerprint
|
||||
/* fingerprint of the certificate
|
||||
/* .IP TLScontext->peer_CN
|
||||
/* Extracted CommonName of the peer, or zero-length string
|
||||
/* when information could not be extracted.
|
||||
/* .IP TLScontext->issuer_CN
|
||||
/* Extracted CommonName of the issuer, or zero-length string
|
||||
/* when information could not be extracted.
|
||||
/* .IP TLScontext->peer_fingerprint
|
||||
/* Fingerprint of the certificate, or null pointer when no
|
||||
/* certificate digest is available.
|
||||
/* .PP
|
||||
/* Otherwise these fields are set to null pointers.
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
@ -450,9 +448,7 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
|
||||
*/
|
||||
TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
|
||||
int timeout, const char *peername,
|
||||
const char *peeraddr,
|
||||
tls_info_t *tls_info,
|
||||
int requirecert)
|
||||
const char *peeraddr, int requirecert)
|
||||
{
|
||||
int sts;
|
||||
int j;
|
||||
@ -461,6 +457,8 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
|
||||
TLScontext_t *TLScontext;
|
||||
SSL_CIPHER *cipher;
|
||||
X509 *peer;
|
||||
unsigned char md[EVP_MAX_MD_SIZE];
|
||||
char buf[CCERT_BUFSIZ];
|
||||
|
||||
if (var_smtpd_tls_loglevel >= 1)
|
||||
msg_info("setting up TLS connection from %s[%s]", peername, peeraddr);
|
||||
@ -573,66 +571,38 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
|
||||
peer = SSL_get_peer_certificate(TLScontext->con);
|
||||
if (peer != NULL) {
|
||||
if (SSL_get_verify_result(TLScontext->con) == X509_V_OK)
|
||||
tls_info->peer_verified = 1;
|
||||
TLScontext->peer_verified = 1;
|
||||
|
||||
X509_NAME_oneline(X509_get_subject_name(peer),
|
||||
TLScontext->peer_subject,
|
||||
sizeof(TLScontext->peer_subject));
|
||||
if (var_smtpd_tls_loglevel >= 2)
|
||||
msg_info("subject=%s", TLScontext->peer_subject);
|
||||
tls_info->peer_subject = TLScontext->peer_subject;
|
||||
|
||||
X509_NAME_oneline(X509_get_issuer_name(peer),
|
||||
TLScontext->peer_issuer,
|
||||
sizeof(TLScontext->peer_issuer));
|
||||
if (var_smtpd_tls_loglevel >= 2)
|
||||
msg_info("issuer=%s", TLScontext->peer_issuer);
|
||||
tls_info->peer_issuer = TLScontext->peer_issuer;
|
||||
|
||||
if (X509_digest(peer, EVP_md5(), TLScontext->md, &n)) {
|
||||
if (var_smtpd_tls_loglevel >= 2) {
|
||||
X509_NAME_oneline(X509_get_subject_name(peer),
|
||||
buf, sizeof(buf));
|
||||
msg_info("subject=%s", buf);
|
||||
X509_NAME_oneline(X509_get_issuer_name(peer),
|
||||
buf, sizeof(buf));
|
||||
msg_info("issuer=%s", buf);
|
||||
}
|
||||
if (X509_digest(peer, EVP_md5(), md, &n) && n > 0) {
|
||||
TLScontext->peer_fingerprint = mymalloc(n * 3);
|
||||
for (j = 0; j < (int) n; j++) {
|
||||
TLScontext->fingerprint[j * 3] =
|
||||
hexcodes[(TLScontext->md[j] & 0xf0) >> 4U];
|
||||
TLScontext->fingerprint[(j * 3) + 1] =
|
||||
hexcodes[(TLScontext->md[j] & 0x0f)];
|
||||
TLScontext->peer_fingerprint[j * 3] =
|
||||
hexcodes[(md[j] & 0xf0) >> 4U];
|
||||
TLScontext->peer_fingerprint[(j * 3) + 1] =
|
||||
hexcodes[(md[j] & 0x0f)];
|
||||
if (j + 1 != (int) n)
|
||||
TLScontext->fingerprint[(j * 3) + 2] = ':';
|
||||
TLScontext->peer_fingerprint[(j * 3) + 2] = ':';
|
||||
else
|
||||
TLScontext->fingerprint[(j * 3) + 2] = '\0';
|
||||
TLScontext->peer_fingerprint[(j * 3) + 2] = '\0';
|
||||
}
|
||||
if (var_smtpd_tls_loglevel >= 1)
|
||||
msg_info("fingerprint=%s", TLScontext->fingerprint);
|
||||
tls_info->peer_fingerprint = TLScontext->fingerprint;
|
||||
msg_info("fingerprint=%s", TLScontext->peer_fingerprint);
|
||||
}
|
||||
TLScontext->peer_CN[0] = '\0';
|
||||
if (!X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
|
||||
NID_commonName, TLScontext->peer_CN,
|
||||
sizeof(TLScontext->peer_CN))) {
|
||||
msg_info("Could not parse client's subject CN");
|
||||
tls_print_errors();
|
||||
}
|
||||
tls_info->peer_CN = TLScontext->peer_CN;
|
||||
|
||||
TLScontext->issuer_CN[0] = '\0';
|
||||
if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
|
||||
NID_commonName, TLScontext->issuer_CN,
|
||||
sizeof(TLScontext->issuer_CN))) {
|
||||
msg_info("Could not parse client's issuer CN");
|
||||
tls_print_errors();
|
||||
}
|
||||
if (!TLScontext->issuer_CN[0]) {
|
||||
/* No issuer CN field, use Organization instead */
|
||||
if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
|
||||
NID_organizationName, TLScontext->issuer_CN,
|
||||
sizeof(TLScontext->issuer_CN))) {
|
||||
msg_info("Could not parse client's issuer Organization");
|
||||
tls_print_errors();
|
||||
}
|
||||
}
|
||||
tls_info->issuer_CN = TLScontext->issuer_CN;
|
||||
if ((TLScontext->peer_CN = tls_peer_CN(peer)) == 0)
|
||||
TLScontext->peer_CN = mystrdup("");
|
||||
if ((TLScontext->issuer_CN = tls_issuer_CN(peer)) == 0)
|
||||
TLScontext->issuer_CN = mystrdup("");
|
||||
|
||||
if (var_smtpd_tls_loglevel >= 1) {
|
||||
if (tls_info->peer_verified)
|
||||
if (TLScontext->peer_verified)
|
||||
msg_info("Verified: subject_CN=%s, issuer=%s",
|
||||
TLScontext->peer_CN, TLScontext->issuer_CN);
|
||||
else
|
||||
@ -647,7 +617,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
|
||||
* session peer was verified.
|
||||
*/
|
||||
if (requirecert) {
|
||||
if (!tls_info->peer_verified || !tls_info->peer_CN) {
|
||||
if (!TLScontext->peer_verified || !TLScontext->peer_CN) {
|
||||
msg_info("Re-used session without peer certificate removed");
|
||||
uncache_session(server_ctx, TLScontext);
|
||||
tls_free_context(TLScontext);
|
||||
@ -658,11 +628,11 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
|
||||
/*
|
||||
* Finally, collect information about protocol and cipher for logging
|
||||
*/
|
||||
tls_info->protocol = SSL_get_version(TLScontext->con);
|
||||
TLScontext->protocol = SSL_get_version(TLScontext->con);
|
||||
cipher = SSL_get_current_cipher(TLScontext->con);
|
||||
tls_info->cipher_name = SSL_CIPHER_get_name(cipher);
|
||||
tls_info->cipher_usebits = SSL_CIPHER_get_bits(cipher,
|
||||
&(tls_info->cipher_algbits));
|
||||
TLScontext->cipher_name = SSL_CIPHER_get_name(cipher);
|
||||
TLScontext->cipher_usebits = SSL_CIPHER_get_bits(cipher,
|
||||
&(TLScontext->cipher_algbits));
|
||||
|
||||
/*
|
||||
* The TLS engine is active. Switch to the tls_timed_read/write()
|
||||
@ -673,8 +643,8 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
|
||||
if (var_smtpd_tls_loglevel >= 1)
|
||||
msg_info("TLS connection established from %s[%s]: %s with cipher %s (%d/%d bits)",
|
||||
peername, peeraddr,
|
||||
tls_info->protocol, tls_info->cipher_name,
|
||||
tls_info->cipher_usebits, tls_info->cipher_algbits);
|
||||
TLScontext->protocol, TLScontext->cipher_name,
|
||||
TLScontext->cipher_usebits, TLScontext->cipher_algbits);
|
||||
tls_int_seed();
|
||||
|
||||
return (TLScontext);
|
||||
|
@ -6,12 +6,12 @@
|
||||
/* SYNOPSIS
|
||||
/* #include <tls.h>
|
||||
/*
|
||||
/* int tls_session_stop(ctx, stream, timeout, failure, tls_info)
|
||||
/* void tls_session_stop(ctx, stream, timeout, failure, TLScontext)
|
||||
/* SSL_CTX *ctx;
|
||||
/* VSTREAM *stream;
|
||||
/* int timeout;
|
||||
/* int failure;
|
||||
/* tls_info_t *tls_info;
|
||||
/* TLScontext_t *TLScontext;
|
||||
/*
|
||||
/* VSTRING *tls_session_passivate(session)
|
||||
/* SSL_SESSION *session;
|
||||
@ -75,16 +75,14 @@
|
||||
/* tls_session_stop - shut down the TLS connection and reset state */
|
||||
|
||||
void tls_session_stop(SSL_CTX *ctx, VSTREAM *stream, int timeout,
|
||||
int failure, tls_info_t *tls_info)
|
||||
int failure, TLScontext_t *TLScontext)
|
||||
{
|
||||
const char *myname = "tls_session_stop";
|
||||
TLScontext_t *TLScontext;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* Sanity check.
|
||||
*/
|
||||
TLScontext = (TLScontext_t *) vstream_context(stream);
|
||||
if (TLScontext == 0)
|
||||
msg_panic("%s: stream has no active TLS context", myname);
|
||||
|
||||
@ -103,7 +101,6 @@ void tls_session_stop(SSL_CTX *ctx, VSTREAM *stream, int timeout,
|
||||
}
|
||||
tls_free_context(TLScontext);
|
||||
tls_stream_stop(stream);
|
||||
*tls_info = tls_info_zero;
|
||||
}
|
||||
|
||||
/* tls_session_passivate - passivate SSL_SESSION object */
|
||||
|
@ -1,49 +0,0 @@
|
||||
/*++
|
||||
/* NAME
|
||||
/* tls_temp 3
|
||||
/* SUMMARY
|
||||
/* code that is to be replaced
|
||||
/* SYNOPSIS
|
||||
/* #define TLS_INTERNAL
|
||||
/* #include <tls.h>
|
||||
/* DESCRIPTION
|
||||
/* As the summary says.
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
/* This software is free. You can do with it whatever you want.
|
||||
/* The original author kindly requests that you acknowledge
|
||||
/* the use of his software.
|
||||
/* AUTHOR(S)
|
||||
/* Originally written by:
|
||||
/* Lutz Jaenicke
|
||||
/* BTU Cottbus
|
||||
/* Allgemeine Elektrotechnik
|
||||
/* Universitaetsplatz 3-4
|
||||
/* D-03044 Cottbus, Germany
|
||||
/*
|
||||
/* Updated by:
|
||||
/* Wietse Venema
|
||||
/* IBM T.J. Watson Research
|
||||
/* P.O. Box 704
|
||||
/* Yorktown Heights, NY 10598, USA
|
||||
/*--*/
|
||||
|
||||
/* System library. */
|
||||
|
||||
#include <sys_defs.h>
|
||||
|
||||
#ifdef USE_TLS
|
||||
|
||||
/* TLS library. */
|
||||
|
||||
#define TLS_INTERNAL
|
||||
#include <tls.h>
|
||||
|
||||
/* Application-specific. */
|
||||
|
||||
const tls_info_t tls_info_zero = {
|
||||
0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0
|
||||
};
|
||||
|
||||
#endif
|
@ -7,10 +7,26 @@
|
||||
/* #define TLS_INTERNAL
|
||||
/* #include <tls.h>
|
||||
/*
|
||||
/* char *tls_peer_CN(peercert)
|
||||
/* X509 *peercert;
|
||||
/*
|
||||
/* char *tls_issuer_CN(peercert)
|
||||
/* X509 *peercert;
|
||||
/*
|
||||
/* int tls_verify_certificate_callback(ok, ctx)
|
||||
/* int ok;
|
||||
/* X509_STORE_CTX *ctx;
|
||||
/* DESCRIPTION
|
||||
/* tls_peer_CN() returns the text CommonName for the peer
|
||||
/* certificate subject, or a null pointer if no CommonName was
|
||||
/* found. The result is allocated with mymalloc() and must be
|
||||
/* freed by the caller.
|
||||
/*
|
||||
/* tls_issuer_CN() returns the text CommonName for the peer
|
||||
/* certificate issuer, or a null pointer if no CommonName was
|
||||
/* found. The result is allocated with mymalloc() and must be
|
||||
/* freed by the caller.
|
||||
/*
|
||||
/* tls_verify_callback() is called several times (directly or
|
||||
/* indirectly) from crypto/x509/x509_vfy.c. It is called as
|
||||
/* a final check, and if it returns "0", the handshake is
|
||||
@ -32,32 +48,6 @@
|
||||
/* certificate verification failure will result in immediate
|
||||
/* termination (return 0).
|
||||
/*
|
||||
/* The SMTP client will attempt to verify the server hostname
|
||||
/* against the names listed in the server certificate. When
|
||||
/* a hostname match is required, the verification fails
|
||||
/* on certificate verification or hostname mis-match errors.
|
||||
/* When no hostname match is required, hostname verification
|
||||
/* failures are logged but they do not affect the TLS handshake
|
||||
/* or the SMTP session.
|
||||
/*
|
||||
/* The rules for peer name wild-card matching differ between
|
||||
/* RFC 2818 (HTTP over TLS) and RFC 2830 (LDAP over TLS), while
|
||||
/* RFC RFC3207 (SMTP over TLS) does not specify a rule at all.
|
||||
/* Postfix uses a restrictive match algorithm. One asterisk
|
||||
/* ('*') is allowed as the left-most component of a wild-card
|
||||
/* certificate name; it matches the left-most component of
|
||||
/* the peer hostname.
|
||||
/*
|
||||
/* Another area where RFCs aren't always explicit is the
|
||||
/* handling of dNSNames in peer certificates. RFC 3207 (SMTP
|
||||
/* over TLS) does not mention dNSNames. Postfix follows the
|
||||
/* strict rules in RFC 2818 (HTTP over TLS), section 3.1: The
|
||||
/* Subject Alternative Name/dNSName has precedence over
|
||||
/* CommonName. If at least one dNSName is provided, Postfix
|
||||
/* verifies those against the peer hostname and ignores the
|
||||
/* CommonName, otherwise Postfix verifies the CommonName
|
||||
/* against the peer hostname.
|
||||
/*
|
||||
/* The only error condition not handled inside the OpenSSL
|
||||
/* library is the case of a too-long certificate chain. We
|
||||
/* test for this condition only if "ok = 1", that is, if
|
||||
@ -72,6 +62,12 @@
|
||||
/* .IP ctx
|
||||
/* TLS client or server context. This also specifies the
|
||||
/* TLScontext with enforcement options.
|
||||
/* DIAGNOSTICS
|
||||
/* tls_peer_CN() and tls_issuer_CN() log a warning and return
|
||||
/* a null pointer when 1) the requested information is not
|
||||
/* available in the specified certificate, 2) the result
|
||||
/* exceeds a fixed limit, or 3) the result contains null
|
||||
/* characters.
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
@ -107,6 +103,7 @@
|
||||
/* Utility library. */
|
||||
|
||||
#include <msg.h>
|
||||
#include <mymalloc.h>
|
||||
|
||||
/* TLS library. */
|
||||
|
||||
@ -196,4 +193,72 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
|
||||
return (1);
|
||||
}
|
||||
|
||||
#ifndef DONT_GRIPE
|
||||
#define DONT_GRIPE 0
|
||||
#define DO_GRIPE 1
|
||||
#endif
|
||||
|
||||
/* tls_text_name - extract certificate property value by name */
|
||||
|
||||
static char *tls_text_name(X509_NAME *name, int nid, char *label, int gripe)
|
||||
{
|
||||
int len;
|
||||
char *text;
|
||||
|
||||
if ((len = X509_NAME_get_text_by_NID(name, nid, 0, 0)) < 0) {
|
||||
if (gripe != DONT_GRIPE) {
|
||||
msg_warn("peer certificate has no %s", label);
|
||||
tls_print_errors();
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the peer CN is used in peer verification, take care to detect
|
||||
* truncation due to excessive length or internal NULs.
|
||||
*/
|
||||
if (len >= CCERT_BUFSIZ) {
|
||||
msg_warn("peer %s too long: %d", label, (int) len);
|
||||
return (0);
|
||||
}
|
||||
text = mymalloc(len + 1);
|
||||
X509_NAME_get_text_by_NID(name, nid, text, len + 1);
|
||||
if (strlen(text) != len) {
|
||||
msg_warn("internal NUL in peer %s", label);
|
||||
myfree(text);
|
||||
text = 0;
|
||||
}
|
||||
return (text);
|
||||
}
|
||||
|
||||
/* tls_peer_CN - extract peer common name from certificate */
|
||||
|
||||
char *tls_peer_CN(X509 *peercert)
|
||||
{
|
||||
char *cn;
|
||||
|
||||
cn = tls_text_name(X509_get_subject_name(peercert),
|
||||
NID_commonName, "CN", DO_GRIPE);
|
||||
return (cn);
|
||||
}
|
||||
|
||||
/* tls_text_name - extract issuer common name from certificate */
|
||||
|
||||
char *tls_issuer_CN(X509 *peer)
|
||||
{
|
||||
X509_NAME *name;
|
||||
char *cn;
|
||||
|
||||
name = X509_get_issuer_name(peer);
|
||||
|
||||
/*
|
||||
* If no issuer CN field, use Organization instead. CA certs without a CN
|
||||
* are common, so we only complain if the organization is also missing.
|
||||
*/
|
||||
if (!(cn = tls_text_name(name, NID_commonName, "issuer CN", DONT_GRIPE)))
|
||||
cn = tls_text_name(name, NID_organizationName,
|
||||
"issuer Organization", DO_GRIPE);
|
||||
return (cn);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user