-TWAIT_STATUS_T
-TWATCHDOG
-TWATCH_FD
+-TX509
+-TX509_NAME
-TX509_STORE_CTX
-Tregex_t
-Tregmatch_t
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>,
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
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
* 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.
-
W\bWh\bha\bat\bt S\bSA\bAS\bSL\bL v\bve\ber\brs\bsi\bio\bon\bns\bs a\bar\bre\be s\bsu\bup\bpp\bpo\bor\brt\bte\bed\bd
Postfix+SASL 1.5.5 was seen working on RedHat 6.1 (pwcheck_method set to shadow
...
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
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
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>
</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
<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.
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>
<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
<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
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-
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
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
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>
</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
<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
#
# 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
* 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
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;
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,
}
}
}
+
/* smtp_mime_fail - MIME problem */
static void smtp_mime_fail(SMTP_STATE *state, int mime_errs)
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.
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)
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));
*/
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));
/*
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;
}
}
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;
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) {
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);
}
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
* 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);
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. */
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
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 =
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
/*
* 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 */
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
/*
* 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 *);
/*
/*
/* 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.
/* 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
/* 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
#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?
*/
/* 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) {
}
sk_GENERAL_NAME_free(gens);
}
- if (dNSName_found) {
- 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();
+ 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);
}
- tls_info->peer_CN = TLScontext->peer_CN;
+ if ((TLScontext->peer_CN = tls_peer_CN(peercert)) == 0)
+ TLScontext->peer_CN = mystrdup("");
- 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();
+ 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: 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);
int timeout,
int enforce_peername,
const char *peername,
- const char *serverid,
- tls_info_t *tls_info)
+ const char *serverid)
{
int sts;
SSL_SESSION *session;
* 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();
*
* 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));
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));
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);
}
/* 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.
/* 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
/* 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
*/
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;
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);
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;
-
- 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)) {
+ TLScontext->peer_verified = 1;
+
+ 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;
- }
- 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();
- }
+ msg_info("fingerprint=%s", TLScontext->peer_fingerprint);
}
- 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
* 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);
/*
* 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()
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);
/* 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;
/* 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);
}
tls_free_context(TLScontext);
tls_stream_stop(stream);
- *tls_info = tls_info_zero;
}
/* tls_session_passivate - passivate SSL_SESSION object */
+++ /dev/null
-/*++
-/* 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
/* #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
/* 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
/* .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
/* Utility library. */
#include <msg.h>
+#include <mymalloc.h>
/* TLS library. */
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