diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 5a3608b54..2a8c443b7 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -224,6 +224,8 @@ -TWAIT_STATUS_T -TWATCHDOG -TWATCH_FD +-TX509 +-TX509_NAME -TX509_STORE_CTX -Tregex_t -Tregmatch_t diff --git a/postfix/HISTORY b/postfix/HISTORY index afeb33460..8c9da4bb8 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -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 , diff --git a/postfix/README_FILES/IPV6_README b/postfix/README_FILES/IPV6_README index 742941c97..ee49675b7 100644 --- a/postfix/README_FILES/IPV6_README +++ b/postfix/README_FILES/IPV6_README @@ -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 diff --git a/postfix/README_FILES/SASL_README b/postfix/README_FILES/SASL_README index 54c65bfcb..2cc7da6af 100644 --- a/postfix/README_FILES/SASL_README +++ b/postfix/README_FILES/SASL_README @@ -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 diff --git a/postfix/README_FILES/TLS_README b/postfix/README_FILES/TLS_README index 1e1a75383..3c8fbb41b 100644 --- a/postfix/README_FILES/TLS_README +++ b/postfix/README_FILES/TLS_README @@ -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 diff --git a/postfix/html/IPV6_README.html b/postfix/html/IPV6_README.html index 4b7463630..cce966534 100644 --- a/postfix/html/IPV6_README.html +++ b/postfix/html/IPV6_README.html @@ -30,7 +30,7 @@ between these implementations.

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.

Postfix uses the same SMTP protocol over IPv6 as it already diff --git a/postfix/html/SASL_README.html b/postfix/html/SASL_README.html index e9f45f25b..f3e53efad 100644 --- a/postfix/html/SASL_README.html +++ b/postfix/html/SASL_README.html @@ -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.

+

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:

-

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.

-

What SASL versions are supported

Postfix+SASL 1.5.5 was seen working on RedHat 6.1 (pwcheck_method diff --git a/postfix/html/TLS_README.html b/postfix/html/TLS_README.html index 054bb40b2..307f40741 100644 --- a/postfix/html/TLS_README.html +++ b/postfix/html/TLS_README.html @@ -591,7 +591,7 @@ Postfix SMTP server access control:

permit_tls_clientcerts

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).

+(see relay_clientcerts discussion below).

permit_tls_all_clientcerts

Allow the remote client SMTP request if the client certificate passes verification. @@ -614,7 +614,7 @@ The permit_tls_all_clientce specially created email relay server.

It is however recommended to stay with the permit_tls_clientcerts -feature and list all certificates via $relay_clientcerts, as +feature and list all certificates via $relay_clientcerts, as permit_tls_all_clientcerts does not permit any control when a certificate must no longer be used (e.g. an employee leaving).

@@ -633,7 +633,7 @@ certificate must no longer be used (e.g. an employee leaving).

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.

 /etc/postfix/main.cf:
-    relay_clientcerts = hash:/etc/postfix/relay_clientcerts
+    relay_clientcerts = 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
diff --git a/postfix/html/ldap_table.5.html b/postfix/html/ldap_table.5.html
index e03a2ef57..254f5d0d1 100644
--- a/postfix/html/ldap_table.5.html
+++ b/postfix/html/ldap_table.5.html
@@ -324,7 +324,7 @@ LDAP_TABLE(5)                                                    LDAP_TABLE(5)
               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.
 
        result_attribute (default: maildrop)
               The  attribute(s) Postfix will read from any direc-
diff --git a/postfix/man/man5/ldap_table.5 b/postfix/man/man5/ldap_table.5
index 43654be47..9a0c79f9a 100644
--- a/postfix/man/man5/ldap_table.5
+++ b/postfix/man/man5/ldap_table.5
@@ -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
diff --git a/postfix/proto/IPV6_README.html b/postfix/proto/IPV6_README.html
index 10e5424b1..f5a8a9006 100644
--- a/postfix/proto/IPV6_README.html
+++ b/postfix/proto/IPV6_README.html
@@ -30,7 +30,7 @@ between these implementations. 

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.

Postfix uses the same SMTP protocol over IPv6 as it already diff --git a/postfix/proto/SASL_README.html b/postfix/proto/SASL_README.html index 9b019129f..8a6aa49c6 100644 --- a/postfix/proto/SASL_README.html +++ b/postfix/proto/SASL_README.html @@ -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.

+

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:

    @@ -63,11 +68,6 @@ Postfix SMTP client
-

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.

-

What SASL versions are supported

Postfix+SASL 1.5.5 was seen working on RedHat 6.1 (pwcheck_method diff --git a/postfix/proto/TLS_README.html b/postfix/proto/TLS_README.html index 23f51d28b..5e3243b0d 100644 --- a/postfix/proto/TLS_README.html +++ b/postfix/proto/TLS_README.html @@ -633,7 +633,7 @@ certificate must no longer be used (e.g. an employee leaving).

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 diff --git a/postfix/proto/ldap_table b/postfix/proto/ldap_table index 8faec6583..ec2674363 100644 --- a/postfix/proto/ldap_table +++ b/postfix/proto/ldap_table @@ -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 diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 6c87a3a04..72a3f5a01 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -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 diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index d924ce343..919cc8132 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -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; diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index b9da58558..a52fd6106 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -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) diff --git a/postfix/src/smtp/smtp_session.c b/postfix/src/smtp/smtp_session.c index f9d338708..e71defd9d 100644 --- a/postfix/src/smtp/smtp_session.c +++ b/postfix/src/smtp/smtp_session.c @@ -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) diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index c25b592d4..b9a4d1aab 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -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; } } diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index 6c3800895..80a0a5de4 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -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; diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 00e0dd60a..660e4efeb 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -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. */ diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c index d46716da4..bd443f0bd 100644 --- a/postfix/src/smtpd/smtpd_state.c +++ b/postfix/src/smtpd/smtpd_state.c @@ -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 diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in index 92d15c755..fb50f316a 100644 --- a/postfix/src/tls/Makefile.in +++ b/postfix/src/tls/Makefile.in @@ -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 diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h index 8674d0c3f..2fed1254b 100644 --- a/postfix/src/tls/tls.h +++ b/postfix/src/tls/tls.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 *); /* diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c index 263837129..979f4e8e2 100644 --- a/postfix/src/tls/tls_client.c +++ b/postfix/src/tls/tls_client.c @@ -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(); diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c index 7db5b4db9..b8d598039 100644 --- a/postfix/src/tls/tls_misc.c +++ b/postfix/src/tls/tls_misc.c @@ -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); } diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c index 2df0edcb1..6bfb6cd84 100644 --- a/postfix/src/tls/tls_server.c +++ b/postfix/src/tls/tls_server.c @@ -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); diff --git a/postfix/src/tls/tls_session.c b/postfix/src/tls/tls_session.c index 21d939ee0..b30b72761 100644 --- a/postfix/src/tls/tls_session.c +++ b/postfix/src/tls/tls_session.c @@ -6,12 +6,12 @@ /* SYNOPSIS /* #include /* -/* 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 */ diff --git a/postfix/src/tls/tls_temp.c b/postfix/src/tls/tls_temp.c deleted file mode 100644 index a75d8ef62..000000000 --- a/postfix/src/tls/tls_temp.c +++ /dev/null @@ -1,49 +0,0 @@ -/*++ -/* NAME -/* tls_temp 3 -/* SUMMARY -/* code that is to be replaced -/* SYNOPSIS -/* #define TLS_INTERNAL -/* #include -/* 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 - -#ifdef USE_TLS - -/* TLS library. */ - -#define TLS_INTERNAL -#include - -/* Application-specific. */ - -const tls_info_t tls_info_zero = { - 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0 -}; - -#endif diff --git a/postfix/src/tls/tls_verify.c b/postfix/src/tls/tls_verify.c index 508ba7f3c..2580ff21c 100644 --- a/postfix/src/tls/tls_verify.c +++ b/postfix/src/tls/tls_verify.c @@ -7,10 +7,26 @@ /* #define TLS_INTERNAL /* #include /* +/* 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 +#include /* 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