From 38dabb3635d15f51450fc91521a625a38e823c69 Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Wed, 11 Feb 2009 17:03:59 +0000 Subject: [PATCH] Thu Jan 15 09:50:45 CST 2009 Jarod Neuner * TLS Subject Checking in tport sofia-sip/tport.h: * tport_delivered_from_subjects() returns type (su_strlst_t const *) * Export tport_subject_search() sofia-sip/tport_tag.h + tport_tag.c: * Remove TPTAG_TLS_VERIFY_PEER() - Depreciated. Use TPTAG_TLS_VERIFY_POLICY instead. - Binary Compatibility is preserved. * Add TPTAG_TLS_VERIFY_POLICY() - tport can verify incoming and/or outgoing connections, using: 1) Certificate Signatures only - or - 2) Certificate Signatures and Certificate Subjects * Add TPTAG_TLS_VERIFY_DEPTH() - Restrict certificate chain verification to a set length. * Add TPTAG_TLS_VERIFY_DATE() - Disable notBefore/notAfter checking (application: embedded devices) * Add TPTAG_TLS_VERIFY_SUBJECTS() - Incoming connections must present client certificates with subjects that match an item in this list. - Intended Use: Proxy Authentication * Replaced TPTAG_TRUSTED() with TPTAG_X509_SUBJECT() - Commented out for future use. - Intended Use: SIP User Identities in Server Certificates. * Add appropriate doxygen documentation. tport.c * Add tport_subject_search() - Subject can be a hostname, IP Address, or a URI. - Valid subject examples include: example.com alice@example.com sip:alice@example.com sips:alice@example.com * tport_by_addrinfo() matches tpn_canon against the subject list of reusable TLS connections. tport_tls.h: * Add tls_init_secondary() * Remove tls_init_slave() & tls_init_client() tport_tls.c: * tls_verify_cb() supports TPTAG_TLS_VERIFY_DATE() * tls_post_connection_check() verifies certificate subjects. * tls_init_secondary() - Replaces tls_init_slave(), tls_init_client(), and tls_clone(). tport_type_tls.c: * Removed erroneous reference to tport_tls_deliver() * Fix a memory leak caused by duplicate calls to tls_clone(). * Populate the (tport_t *)->tp_subjects field with peer certificate data for new secondary connections. git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@11830 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- libs/sofia-sip/.update | 2 +- .../libsofia-sip-ua/tport/sofia-sip/tport.h | 6 +- .../tport/sofia-sip/tport_tag.h | 51 +++- libs/sofia-sip/libsofia-sip-ua/tport/tport.c | 68 +++++- .../libsofia-sip-ua/tport/tport_internal.h | 6 +- .../libsofia-sip-ua/tport/tport_tag.c | 137 ++++++++++- .../libsofia-sip-ua/tport/tport_tls.c | 220 +++++++++++++----- .../libsofia-sip-ua/tport/tport_tls.h | 9 +- .../libsofia-sip-ua/tport/tport_type_tls.c | 49 ++-- 9 files changed, 428 insertions(+), 120 deletions(-) diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index f30fffd9d1..03da062bc1 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Wed Feb 11 11:03:24 CST 2009 +Wed Feb 11 11:03:50 CST 2009 diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h b/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h index 51f62ba3fc..f440927b23 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h @@ -339,7 +339,11 @@ TPORT_DLL int tport_delivered_from(tport_t *tp, msg_t const *msg, tp_name_t name[1]); /** Return TLS Subjects provided by the source transport */ -TPORT_DLL su_strlst_t *tport_delivered_from_subjects(tport_t *tp, msg_t const *msg); +TPORT_DLL su_strlst_t const *tport_delivered_from_subjects(tport_t *tp, + msg_t const *msg); + +/** Check if the given subject string is found in su_strlst_t */ +TPORT_DLL int tport_subject_search(char const *, su_strlst_t const *); /** Check if transport named is already resolved */ TPORT_DLL int tport_name_is_resolved(tp_name_t const *); diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport_tag.h b/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport_tag.h index 69a4e9a237..891d26b168 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport_tag.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport_tag.h @@ -186,18 +186,59 @@ TPORT_DLL extern tag_typedef_t tptag_tls_version; TPORT_DLL extern tag_typedef_t tptag_tls_version_ref; #define TPTAG_TLS_VERSION_REF(x) tptag_tls_version_ref, tag_uint_vr(&(x)) +enum tport_tls_verify_policy { + TPTLS_VERIFY_NONE = 0x0, + TPTLS_VERIFY_INCOMING = 0x1, + TPTLS_VERIFY_IN = 0x1, + TPTLS_VERIFY_OUTGOING = 0x2, + TPTLS_VERIFY_OUT = 0x2, + TPTLS_VERIFY_ALL = 0x3, + TPTLS_VERIFY_SUBJECTS_IN = 0x5, /* 0x4 | TPTLS_VERIFY_INCOMING */ + TPTLS_VERIFY_SUBJECTS_OUT = 0xA, /* 0x8 | TPTLS_VERIFY_OUTGOING */ + TPTLS_VERIFY_SUBJECTS_ALL = 0xF, +}; + +TPORT_DLL extern tag_typedef_t tptag_tls_verify_policy; +#define TPTAG_TLS_VERIFY_POLICY(x) tptag_tls_verify_policy, tag_uint_v((x)) + +TPORT_DLL extern tag_typedef_t tptag_tls_verify_policy_ref; +#define TPTAG_TLS_VERIFY_POLICY_REF(x) tptag_tls_verify_policy_ref, tag_uint_vr(&(x)) + +TPORT_DLL extern tag_typedef_t tptag_tls_verify_depth; +#define TPTAG_TLS_VERIFY_DEPTH(x) tptag_tls_verify_depth, tag_uint_v((x)) + +TPORT_DLL extern tag_typedef_t tptag_tls_verify_depth_ref; +#define TPTAG_TLS_VERIFY_DEPTH_REF(x) \ + tptag_tls_verify_depth_ref, tag_uint_vr(&(x)) + +TPORT_DLL extern tag_typedef_t tptag_tls_verify_date; +#define TPTAG_TLS_VERIFY_DATE(x) tptag_tls_verify_date, tag_uint_v((x)) + +TPORT_DLL extern tag_typedef_t tptag_tls_verify_date_ref; +#define TPTAG_TLS_VERIFY_DATE_REF(x) \ + tptag_tls_verify_date_ref, tag_uint_vr(&(x)) + +TPORT_DLL extern tag_typedef_t tptag_tls_verify_subjects; +#define TPTAG_TLS_VERIFY_SUBJECTS(x) tptag_tls_verify_subjects, tag_cptr_v((x)) + +TPORT_DLL extern tag_typedef_t tptag_tls_verify_subjects_ref; +#define TPTAG_TLS_VERIFY_SUBJECTS_REF(x) \ + tptag_tls_verify_subjects_ref, tag_cptr_vr(&(x), (x)) + +/* TPTAG_TLS_VERIFY_PEER is depreciated - Use TPTAG_TLS_VERIFY_POLICY */ TPORT_DLL extern tag_typedef_t tptag_tls_verify_peer; -#define TPTAG_TLS_VERIFY_PEER(x) tptag_tls_verify_peer, tag_uint_v((x)) +#define TPTAG_TLS_VERIFY_PEER(x) TPTAG_TLS_VERIFY_POLICY( (x) ? \ + TPTLS_VERIFY_ALL : TPTLS_VERIFY_NONE) TPORT_DLL extern tag_typedef_t tptag_tls_verify_peer_ref; #define TPTAG_TLS_VERIFY_PEER_REF(x) tptag_tls_verify_peer_ref, tag_uint_vr(&(x)) #if 0 -TPORT_DLL extern tag_typedef_t tptag_trusted; -#define TPTAG_TRUSTED(x) tptag_trusted, tag_bool_v((x)) +TPORT_DLL extern tag_typedef_t tport_x509_subject; +#define TPTAG_X509_SUBJECT(x) tptag_x509_subject, tag_str_v((x)) -TPORT_DLL extern tag_typedef_t tptag_trusted_ref; -#define TPTAG_TRUSTED_REF(x) tptag_trusted_ref, tag_bool_vr(&(x)) +TPORT_DLL extern tag_typedef_t tptag_x509_subject_ref; +#define TPTAG_X509_SUBJECT_REF(x) tptag_x509_subject_ref, tag_str_vr(&(x)) #endif TPORT_DLL extern tag_typedef_t tptag_debug_drop; diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport.c index 3186fe5bc1..c731ff9e0a 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport.c @@ -273,7 +273,7 @@ int tport_has_tls(tport_t const *self) /** Return true if transport certificate verified successfully */ int tport_is_verified(tport_t const *self) { - return tport_has_tls(self) && self->tp_verified; + return tport_has_tls(self) && self->tp_is_connected && self->tp_verified; } /** Return true if transport is being updated. */ @@ -1465,8 +1465,8 @@ int tport_bind_set(tport_master_t *mr, * * @TAGS * TPTAG_SERVER(), TPTAG_PUBLIC(), TPTAG_IDENT(), TPTAG_HTTP_CONNECT(), - * TPTAG_CERTIFICATE(), TPTAG_TLS_VERSION(), TPTAG_TLS_VERIFY_PEER, and tags used with - * tport_set_params(), especially TPTAG_QUEUESIZE(). + * TPTAG_CERTIFICATE(), TPTAG_TLS_VERSION(), TPTAG_TLS_VERIFY_POLICY, and + * tags used with tport_set_params(), especially TPTAG_QUEUESIZE(). */ int tport_tbind(tport_t *self, tp_name_t const *tpn, @@ -3045,7 +3045,7 @@ int tport_delivered_from(tport_t *tp, msg_t const *msg, tp_name_t name[1]) } /** Return TLS Subjects provided by the source transport */ -su_strlst_t *tport_delivered_from_subjects(tport_t *tp, msg_t const *msg) +su_strlst_t const *tport_delivered_from_subjects(tport_t *tp, msg_t const *msg) { if (tp && msg && msg == tp->tp_master->mr_delivery->d_msg) { tport_t *tp_sec = tp->tp_master->mr_delivery->d_tport; @@ -3069,6 +3069,57 @@ tport_delivered_with_comp(tport_t *tp, msg_t const *msg, return 0; } +/** Search for subject in list of TLS Certificate subjects */ +int +tport_subject_search(char const *subject, su_strlst_t const *lst) +{ + int idx, ilen; + const char *subjuri; + + if (!subject || su_strmatch(tpn_any, subject)) + return 1; + + if (!lst) + return 0; + + /* Check if subject is a URI */ + if (su_casenmatch(subject,"sip:",4) || su_casenmatch(subject,"sips:",5)) + subjuri = subject + su_strncspn(subject,5,":") + 1; + else + subjuri = NULL; + + ilen = su_strlst_len(lst); + + for (idx = 0; idx < ilen; idx++) { + const char *lsturi, *lststr; + + lststr = su_strlst_item(lst, idx); + + /* check if lststr is a URI (sips URI is an unacceptable cert subject) */ + if (su_casenmatch(lststr,"sip:",4)) + lsturi = lststr + su_strncspn(lststr,4,":") + 1; + else + lsturi = NULL; + + + /* Match two SIP Server Identities */ + if (host_cmp(subjuri ? subjuri : subject, lsturi ? lsturi : lststr) == 0) + return 1; +#if 0 + /* XXX - IETF drafts forbid wildcard certs */ + if (!subjuri && !lsturi && su_strnmatch("*.", lststr, 2)) { + size_t urioffset = su_strncspn(subject, 64, "."); + if (urioffset) { + if (su_casematch(subject + urioffset, lststr+1)) + return 1; + } + } +#endif + } + + return 0; +} + /** Allocate message for N bytes, * return message buffer as a iovec */ @@ -3152,7 +3203,7 @@ int tport_recv_error_report(tport_t *self) * * @TAGS * TPTAG_MTU(), TPTAG_REUSE(), TPTAG_CLOSE_AFTER(), TPTAG_SDWN_AFTER(), - * TPTAG_FRESH(), TPTAG_COMPARTMENT(). + * TPTAG_FRESH(), TPTAG_COMPARTMENT(), TPTAG_X509_SUBJECT() */ tport_t *tport_tsend(tport_t *self, msg_t *msg, @@ -4581,6 +4632,13 @@ tport_t *tport_by_addrinfo(tport_primary_t const *pri, if (tport_is_shutdown(sub)) continue; + if (tport_has_tls(sub) && !su_casematch(tpn->tpn_canon, sub->tp_name->tpn_canon)) { + if (!tport_is_verified(sub)) + continue; + if (!tport_subject_search(tpn->tpn_canon, sub->tp_subjects)) + continue; + } + if (comp != sub->tp_name->tpn_comp) continue; diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h b/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h index c4cf4a22e6..b56dd2d9bc 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h @@ -181,8 +181,10 @@ struct tport_s { su_strlst_t *tp_subjects; /**< Transport Subjects. * - * Subject Name(s) provided by the - * peer in a TLS connection (if secondary). + * Subject Name(s) provided by the peer + * in a TLS connection (if secondary). + * or matched against incoming + * connections (if primary). */ #define tp_protoname tp_name->tpn_proto diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c index 2acb1f5d8e..2b8064c625 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tag.c @@ -281,21 +281,138 @@ tag_typedef_t tptag_compartment = PTRTAG_TYPEDEF(compartment); tag_typedef_t tptag_tls_version = UINTTAG_TYPEDEF(tls_version); /**@def TPTAG_TLS_VERIFY_PEER(x) - * - * The verification of certificates can be controlled: - * 0: no verify certificates; - * 1: on server mode, the certificate returned by client is checked - * if fail the TLS/SSL handshake is immediately terminated; - * 1: on client mode, the server certificate is verified - * if fail the TLS/SSL handshake is immediately terminated; - * - * Use with tport_tbind(), nua_create(), nta_agent_create(), - * nta_agent_add_tport(), nth_engine_create(), or initial nth_site_create(). + * @par Depreciated: + * Alias for TPTAG_TLS_VERIFY_POLICY(TPTLS_VERIFY_IN|TPTLS_VERIFY_OUT) * * @NEW_1_12_10. */ tag_typedef_t tptag_tls_verify_peer = UINTTAG_TYPEDEF(tls_verify_peer); +/**@def TPTAG_TLS_VERIFY_POLICY(x) + * + * The verification of certificates can be controlled: + * @par Values: + * - #TPTLS_VERIFY_NONE: + * Do not verify Peer Certificates. + * - #TPTLS_VERIFY_IN: + * Drop incoming connections which fail signature verification + * against trusted certificate authorities. Peers must provide a + * certificate during the initial TLS Handshake. + * - #TPTLS_VERIFY_OUT: + * Drop outgoing connections which fail signature verification + * against trusted certificate authorities. + * - #TPTLS_VERIFY_ALL: + * Alias for (TPTLS_VERIFY_IN|TPTLS_VERIFY_OUT) + * - #TPTLS_VERIFY_SUBJECTS_IN: + * Match the certificate subject on incoming connections against + * a provided list. If no match is found, the connection is + * rejected. If no list is provided, subject checking is bypassed. + * Note: Implies #TPTLS_VERIFY_IN. + * - #TPTLS_VERIFY_SUBJECTS_OUT: + * Match the certificate subject on outgoing connections against + * a provided list. If no match is found, the connection is + * rejected. + * Note: Implies #TPTLS_VERIFY_OUT. + * - #TPTLS_VERIFY_SUBJECTS_ALL: + * Alias for (TPTLS_VERIFY_SUBJECTS_IN|TPTLS_VERIFY_SUBJECTS_OUT) + * + * @par Used with + * tport_tbind(), nua_create(), nta_agent_create(), nta_agent_add_tport(), + * nth_engine_create(), initial nth_site_create(), + * TPTAG_TLS_VERIFY_SUBJECTS(), TPTAG_TLS_VERIFY_DEPTH(). + * + * @NEW_1_12_11. + */ +tag_typedef_t tptag_tls_verify_policy = UINTTAG_TYPEDEF(tls_verify_policy); + +/**@def TPTAG_TLS_VERIFY_DEPTH(x) + * + * Define the maximum length of a valid certificate chain. + * + * @par Default + * 2 + * + * @par Used with + * tport_tbind(), nua_create(), nta_agent_create(), nta_agent_add_tport(), + * nth_engine_create(), or initial nth_site_create(). + * + * @par Parameter Type: + * unsigned int + * + * @NEW_1_12_11. + */ +tag_typedef_t tptag_tls_verify_depth = UINTTAG_TYPEDEF(tls_verify_depth); + +/**@def TPTAG_TLS_VERIFY_DATE(x) + * + * Enable/Disable verification of notBefore and notAfter parameters of + * X.509 Certificates. + * + * @par Default + * Enabled + * + * @par Values + * - 0 - Disable date verification. + * - Non-Zero - Enable date verification. + * + * @par Used with + * tport_tbind(), nua_create(), nta_agent_create(), nta_agent_add_tport(), + * nth_engine_create(), or initial nth_site_create(). + * + * @par Parameter Type: + * unsigned int + * + * @par Note + * This tag should be only used on devices which lack accurate timekeeping. + * + * @NEW_1_12_11. + */ +tag_typedef_t tptag_tls_verify_date = UINTTAG_TYPEDEF(tls_verify_date); + +/**@def TPTAG_TLS_VERIFY_SUBJECTS(x) + * + * Incoming TLS connections must provide a trusted X.509 certificate. + * The character strings provided with this tag are matched against + * the subjects from the trusted certificate. If a match is not found, + * the connection is automatically rejected. + * + * @par Used with + * tport_tbind(), nua_create(), nta_agent_create(), nta_agent_add_tport(), + * nth_engine_create(), initial nth_site_create(), + * TPTLS_VERIFY_SUBJECTS_IN + * + * @par Parameter Type: + * void const * (actually su_strlst_t const *) + * + * @par Values + * - SIP Identity - sip:example.com or sip:username@example.com + * - DNS - sip.example.com + * - IP Address - Both IPv4 and IPv6 Supported + * + * @NEW_1_12_11. + */ +tag_typedef_t tptag_tls_verify_subjects = PTRTAG_TYPEDEF(tls_verify_subjects); + +#if 0 +/**@def TPTAG_X509_SUBJECT(x) + * + * Requires that a message be sent over a TLS transport with trusted X.509 + * certificate. The character string provided must match against a subject + * from the trusted certificate. + * + * @par Used with + * tport_tsend(), TPTLS_VERIFY_SUBJECTS_OUT + * + * @par Parameter Type: + * char const * + * + * @par Values + * - Refer to TPTAG_TLS_VERIFY_SUBJECTS() + * + * @note Not Implemented. + */ +#endif + /**@def TPTAG_QUEUESIZE(x) * * Specify the number of messages that can be queued per connection. diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c index d98b61543e..52aa4d803f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c @@ -56,7 +56,6 @@ #include #include #include -#include #if HAVE_SIGPIPE #include @@ -65,6 +64,7 @@ #include "tport_tls.h" char const tls_version[] = OPENSSL_VERSION_TEXT; +int tls_ex_data_idx = -1; /* see SSL_get_ex_new_index(3ssl) */ enum { tls_master = 0, tls_slave = 1}; @@ -75,9 +75,12 @@ struct tls_s { BIO *bio_con; unsigned int type:1, accept:1, - verify_outgoing:1, verify_incoming:1, - verified:1; + verify_outgoing:1, + verify_subj_in:1, + verify_subj_out:1, + verify_date:1, + x509_verified:1; /* Receiving */ int read_events; @@ -90,7 +93,7 @@ struct tls_s { size_t write_buffer_len; /* Host names */ - su_strlst_t *subject; + su_strlst_t *subjects; }; enum { tls_buffer_size = 16384 }; @@ -162,13 +165,44 @@ int tls_verify_cb(int ok, X509_STORE_CTX *store) X509 *cert = X509_STORE_CTX_get_current_cert(store); int depth = X509_STORE_CTX_get_error_depth(store); int err = X509_STORE_CTX_get_error(store); + int sslidx = SSL_get_ex_data_X509_STORE_CTX_idx(); + SSL *ssl = X509_STORE_CTX_get_ex_data(store, sslidx); + tls_t *tls = SSL_get_ex_data(ssl, tls_ex_data_idx); + + assert(tls); + +#define TLS_VERIFY_CB_CLEAR_ERROR(OK,ERR,STORE) \ + do {\ + OK = 1;\ + ERR = X509_V_OK;\ + X509_STORE_CTX_set_error(STORE,ERR);\ + } while (0) + + if (tls->accept && !tls->verify_incoming) + TLS_VERIFY_CB_CLEAR_ERROR(ok, err, store); + else if (!tls->accept && !tls->verify_outgoing) + TLS_VERIFY_CB_CLEAR_ERROR(ok, err, store); + else switch (err) { + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_CRL_NOT_YET_VALID: + case X509_V_ERR_CRL_HAS_EXPIRED: + if (!tls->verify_date) + TLS_VERIFY_CB_CLEAR_ERROR(ok, err, store); + + default: + break; + } + + if (!ok) { + SU_DEBUG_3(("-Error with certificate at depth: %i\n", depth)); + X509_NAME_oneline(X509_get_issuer_name(cert), data, 256); + SU_DEBUG_3((" issuer = %s\n", data)); + X509_NAME_oneline(X509_get_subject_name(cert), data, 256); + SU_DEBUG_3((" subject = %s\n", data)); + SU_DEBUG_3((" err %i:%s\n", err, X509_verify_cert_error_string(err))); + } - SU_DEBUG_1(("-Error with certificate at depth: %i\n", depth)); - X509_NAME_oneline(X509_get_issuer_name(cert), data, 256); - SU_DEBUG_1((" issuer = %s\n", data)); - X509_NAME_oneline(X509_get_subject_name(cert), data, 256); - SU_DEBUG_1((" subject = %s\n", data)); - SU_DEBUG_1((" err %i:%s\n", err, X509_verify_cert_error_string(err))); } return ok; @@ -178,11 +212,14 @@ static int tls_init_context(tls_t *tls, tls_issues_t const *ti) { static int initialized = 0; + int verify; if (!initialized) { initialized = 1; SSL_library_init(); SSL_load_error_strings(); + tls_ex_data_idx = SSL_get_ex_new_index(0, \ + "sofia-sip private data", NULL, NULL, NULL); if (ti->randFile && !RAND_load_file(ti->randFile, 1024 * 1024)) { @@ -267,13 +304,20 @@ int tls_init_context(tls_t *tls, tls_issues_t const *ti) return -1; } + /* corresponds to (enum tport_tls_verify_policy) */ + tls->verify_incoming = (ti->policy & 0x1) ? 1 : 0; + tls->verify_outgoing = (ti->policy & 0x2) ? 1 : 0; + tls->verify_subj_in = (ti->policy & 0x4) ? tls->verify_incoming : 0; + tls->verify_subj_out = (ti->policy & 0x8) ? tls->verify_outgoing : 0; + tls->verify_date = (ti->verify_date) ? 1 : 0; + + if (tls->verify_incoming) + verify = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + else + verify = SSL_VERIFY_NONE; + SSL_CTX_set_verify_depth(tls->ctx, ti->verify_depth); - - SSL_CTX_set_verify(tls->ctx, - ti->verify_peer == 1 ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, - tls_verify_cb); - - tls->verify_incoming = tls->verify_outgoing = ti->verify_peer ? 1 : 0; + SSL_CTX_set_verify(tls->ctx, verify, tls_verify_cb); if (!SSL_CTX_set_cipher_list(tls->ctx, ti->cipher)) { SU_DEBUG_1(("%s: error setting cipher list\n", "tls_init_context")); @@ -360,13 +404,20 @@ tls_t *tls_init_master(tls_issues_t *ti) return tls; } -tls_t *tls_clone(tls_t *master, int sock, int accept) +tls_t *tls_init_secondary(tls_t *master, int sock, int accept) { tls_t *tls = tls_create(tls_slave); if (tls) { tls->ctx = master->ctx; + tls->type = master->type; tls->accept = accept ? 1 : 0; + tls->verify_outgoing = master->verify_outgoing; + tls->verify_incoming = master->verify_incoming; + tls->verify_subj_out = master->verify_subj_out; + tls->verify_subj_in = master->verify_subj_in; + tls->verify_date = master->verify_date; + tls->x509_verified = master->x509_verified; if (!(tls->read_buffer = su_alloc(tls->home, tls_buffer_size))) su_home_unref(tls->home), tls = NULL; @@ -380,7 +431,7 @@ tls_t *tls_clone(tls_t *master, int sock, int accept) tls->con = SSL_new(tls->ctx); if (tls->con == NULL) { - tls_log_errors(1, "tls_clone", 0); + tls_log_errors(1, "tls_init_secondary", 0); tls_free(tls); errno = EIO; return NULL; @@ -388,26 +439,15 @@ tls_t *tls_clone(tls_t *master, int sock, int accept) SSL_set_bio(tls->con, tls->bio_con, tls->bio_con); SSL_set_mode(tls->con, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_set_ex_data(tls->con, tls_ex_data_idx, tls); su_setblocking(sock, 0); return tls; } -tls_t *tls_init_slave(tls_t *master, int sock) -{ - int accept; - return tls_clone(master, sock, accept = 1); -} - -tls_t *tls_init_client(tls_t *master, int sock) -{ - int accept; - return tls_clone(master, sock, accept = 0); -} - -static -int tls_post_connection_check(tls_t *tls) +su_inline +int tls_post_connection_check(tport_t *self, tls_t *tls) { X509 *cert; int extcount; @@ -416,14 +456,23 @@ int tls_post_connection_check(tls_t *tls) if (!tls) return -1; cert = SSL_get_peer_certificate(tls->con); - if (!cert) - return X509_V_OK; + if (!cert) { + SU_DEBUG_7(("%s(%p): Peer did not provide X.509 Certificate.\n", + __func__, self)); + if (self->tp_accepted && tls->verify_incoming) + return X509_V_ERR_CERT_UNTRUSTED; + else if (!self->tp_accepted && tls->verify_outgoing) + return X509_V_ERR_CERT_UNTRUSTED; + else + return X509_V_OK; + } + + tls->subjects = su_strlst_create(tls->home); + if (!tls->subjects) + return X509_V_ERR_OUT_OF_MEM; extcount = X509_get_ext_count(cert); - if (!tls->subject) - tls->subject = su_strlst_create(tls->home); - /* Find matching subjectAltName.DNS */ for (i = 0; i < extcount; i++) { X509_EXTENSION *ext; @@ -446,13 +495,11 @@ int tls_post_connection_check(tls_t *tls) for (j = 0; j < sk_CONF_VALUE_num(values); j++) { value = sk_CONF_VALUE_value(values, j); if (strcmp(value->name, "DNS") == 0) - su_strlst_dup_append(tls->subject, value->value); - else if (strcmp(value->name, "URI") == 0) { - char *uri = su_strlst_dup_append(tls->subject, value->value); - char const *url = strchr(uri, ':'); - if (url++) - su_strlst_append(tls->subject, url); - } + su_strlst_dup_append(tls->subjects, value->value); + if (strcmp(value->name, "IP") == 0) + su_strlst_dup_append(tls->subjects, value->value); + else if (strcmp(value->name, "URI") == 0) + su_strlst_dup_append(tls->subjects, value->value); } } @@ -465,15 +512,15 @@ int tls_post_connection_check(tls_t *tls) if (subject) { if (X509_NAME_get_text_by_NID(subject, NID_commonName, name, sizeof name) > 0) { - usize_t k, N = su_strlst_len(tls->subject); + usize_t k, N = su_strlst_len(tls->subjects); name[(sizeof name) - 1] = '\0'; for (k = 0; k < N; k++) - if (strcasecmp(su_strlst_item(tls->subject, k), name) == 0) + if (su_casematch(su_strlst_item(tls->subjects, k), name) == 0) break; - if (k == N) - su_strlst_dup_append(tls->subject, name); + if (k >= N) + su_strlst_dup_append(tls->subjects, name); } } } @@ -482,13 +529,64 @@ int tls_post_connection_check(tls_t *tls) error = SSL_get_verify_result(tls->con); - if (error == X509_V_OK) - tls->verified = 1; + if (cert && error == X509_V_OK) + tls->x509_verified = 1; + + if (tport_log->log_level >= 7) { + int i, len = su_strlst_len(tls->subjects); + for (i=0; i < len; i++) + SU_DEBUG_7(("%s(%p): Peer Certificate Subject %i: %s\n", \ + __func__, self, i, su_strlst_item(tls->subjects, i))); + if (i == 0) + SU_DEBUG_7(("%s(%p): Peer Certificate provided no usable subjects.\n", + __func__, self)); + } + + /* Verify incoming connections */ + if (self->tp_accepted) { + if (!tls->verify_incoming) + return X509_V_OK; + + if (!tls->x509_verified) + return error; + + if (tls->verify_subj_in) { + su_strlst_t const *subjects = self->tp_pri->pri_primary->tp_subjects; + int i, items; + + items = subjects ? su_strlst_len(subjects) : 0; + if (items == 0) + return X509_V_OK; + + for (i=0; i < items; i++) { + if (tport_subject_search(su_strlst_item(subjects, i), tls->subjects)) + return X509_V_OK; + } + SU_DEBUG_3(("%s(%p): Peer Subject Mismatch (incoming connection)\n", \ + __func__, self)); + + return X509_V_ERR_CERT_UNTRUSTED; + } + } + /* Verify outgoing connections */ + else { + char const *subject = self->tp_canon; + if (!tls->verify_outgoing) + return X509_V_OK; + + if (!tls->x509_verified || !subject) + return error; + + if (tls->verify_subj_out) { + if (tport_subject_search(subject, tls->subjects)) + return X509_V_OK; /* Subject match found in verified certificate chain */ + SU_DEBUG_3(("%s(%p): Peer Subject Mismatch (%s)\n", \ + __func__, self, subject)); + + return X509_V_ERR_CERT_UNTRUSTED; + } + } - if (tls->accept && !tls->verify_incoming) - return X509_V_OK; - else if (!tls->accept && !tls->verify_outgoing) - return X509_V_OK; return error; } @@ -547,7 +645,7 @@ ssize_t tls_read(tls_t *tls) if (0) SU_DEBUG_1(("tls_read(%p) called on %s (events %u)\n", (void *)tls, - tls->accept ? "server" : "client", + tls->type ? "master" : "slave", tls->read_events)); if (tls->read_buffer_len) @@ -607,7 +705,7 @@ ssize_t tls_write(tls_t *tls, void *buf, size_t size) if (0) SU_DEBUG_1(("tls_write(%p, %p, "MOD_ZU") called on %s\n", (void *)tls, buf, size, - tls && tls->type == tls_slave ? "server" : "client")); + tls && tls->type == tls_slave ? "master" : "slave")); if (tls == NULL || buf == NULL) { errno = EINVAL; @@ -731,7 +829,7 @@ int tls_connect(su_root_magic_t *magic, su_wait_t *w, tport_t *self) if (self->tp_is_connected == 0) { int ret, status; - ret = tls->accept ? SSL_accept(tls->con) : SSL_connect(tls->con); + ret = self->tp_accepted ? SSL_accept(tls->con) : SSL_connect(tls->con); status = SSL_get_error(tls->con, ret); switch (status) { @@ -751,7 +849,8 @@ int tls_connect(su_root_magic_t *magic, su_wait_t *w, tport_t *self) case SSL_ERROR_NONE: /* TLS Handshake complete */ - if ( tls_post_connection_check(tls) == X509_V_OK ) { + status = tls_post_connection_check(self, tls); + if ( status == X509_V_OK ) { su_wait_t wait[1] = {SU_WAIT_INIT}; tport_master_t *mr = self->tp_master; @@ -770,9 +869,8 @@ int tls_connect(su_root_magic_t *magic, su_wait_t *w, tport_t *self) tls->read_events = SU_WAIT_IN; tls->write_events = 0; self->tp_is_connected = 1; - self->tp_verified = tls->verified; - self->tp_subjects = tls->subject == NULL ? NULL : - su_strlst_dup(self->tp_home, tls->subject); + self->tp_verified = tls->x509_verified; + self->tp_subjects = tls->subjects; if (tport_has_queued(self)) tport_send_event(self); diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.h b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.h index caa8fe0398..416143153b 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.h @@ -50,9 +50,9 @@ typedef struct tls_s tls_t; extern char const tls_version[]; typedef struct tls_issues_s { - int verify_peer; /* 0: no verify certificate, * - * 1: if fail the TLS/SSL handshake is terminated. */ - int verify_depth; /* if 0, then do nothing */ + unsigned policy; /* refer to tport_tag.h, tport_tls_verify_policy */ + unsigned verify_depth;/* if 0, revert to default (2) */ + unsigned verify_date; /* if 0, notBefore and notAfter dates are ignored */ int configured; /* If non-zero, complain about certificate errors */ char *cert; /* CERT file name. File format is PEM */ char *key; /* Private key file. PEM format */ @@ -78,8 +78,7 @@ typedef struct tport_tls_primary_s { } tport_tls_primary_t; tls_t *tls_init_master(tls_issues_t *tls_issues); -tls_t *tls_init_slave(tls_t *tls_master, int sock); -tls_t *tls_init_client(tls_t *tls_master, int sock); +tls_t *tls_init_secondary(tls_t *tls_master, int sock, int accept); void tls_free(tls_t *tls); int tls_get_socket(tls_t *tls); ssize_t tls_read(tls_t *tls); diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c index face6045f4..247a5f4a9e 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c @@ -95,9 +95,6 @@ static ssize_t tport_tls_send(tport_t const *self, msg_t *msg, static int tport_tls_accept(tport_primary_t *pri, int events); static tport_t *tport_tls_connect(tport_primary_t *pri, su_addrinfo_t *ai, tp_name_t const *tpn); -#if notyet -static void tport_tls_deliver(tport_t *self, msg_t *msg, su_time_t now); -#endif tport_vtable_t const tport_tls_vtable = { @@ -171,6 +168,10 @@ static int tport_tls_init_master(tport_primary_t *pri, char const *path = NULL; unsigned tls_version = 1; unsigned tls_verify = 0; + unsigned tls_policy = TPTLS_VERIFY_NONE; + unsigned tls_depth = 0; + unsigned tls_date = 1; + su_strlst_t const *tls_subjects = NULL; su_home_t autohome[SU_HOME_AUTO_SIZE(1024)]; tls_issues_t ti = {0}; @@ -183,6 +184,10 @@ static int tport_tls_init_master(tport_primary_t *pri, TPTAG_CERTIFICATE_REF(path), TPTAG_TLS_VERSION_REF(tls_version), TPTAG_TLS_VERIFY_PEER_REF(tls_verify), + TPTAG_TLS_VERIFY_POLICY_REF(tls_policy), + TPTAG_TLS_VERIFY_DEPTH_REF(tls_depth), + TPTAG_TLS_VERIFY_DATE_REF(tls_date), + TPTAG_TLS_VERIFY_SUBJECTS_REF(tls_subjects), TAG_END()); if (!path) { @@ -193,8 +198,9 @@ static int tport_tls_init_master(tport_primary_t *pri, } if (path) { - ti.verify_peer = tls_verify; - ti.verify_depth = 2; + ti.policy = tls_policy | (tls_verify ? TPTLS_VERIFY_ALL : 0); + ti.verify_depth = tls_depth; + ti.verify_date = tls_date; ti.configured = path != tbf; ti.randFile = su_sprintf(autohome, "%s/%s", path, "tls_seed.dat"); ti.key = su_sprintf(autohome, "%s/%s", path, "agent.pem"); @@ -225,6 +231,8 @@ static int tport_tls_init_master(tport_primary_t *pri, return *return_culprit = "tls_init_master", -1; } + if (tls_subjects) + pri->pri_primary->tp_subjects = su_strlst_dup(pri->pri_home, tls_subjects); pri->pri_has_tls = 1; return 0; @@ -247,11 +255,9 @@ static int tport_tls_init_secondary(tport_t *self, int socket, int accepted, if (tport_tcp_init_secondary(self, socket, accepted, return_reason) < 0) return -1; - if (accepted) { - tlstp->tlstp_context = tls_init_slave(master, socket); - if (!tlstp->tlstp_context) - return *return_reason = "tls_init_slave", -1; - } + tlstp->tlstp_context = tls_init_secondary(master, socket, accepted); + if (!tlstp->tlstp_context) + return *return_reason = "tls_init_slave", -1; return 0; } @@ -439,20 +445,12 @@ ssize_t tport_tls_send(tport_t const *self, msg_iovec_t iov[], size_t iovlen) { - tport_tls_primary_t *tlspri = (tport_tls_primary_t *)self->tp_pri; tport_tls_t *tlstp = (tport_tls_t *)self; enum { TLSBUFSIZE = 2048 }; size_t i, j, n, m, size = 0; ssize_t nerror; int oldmask, mask; - if (tlstp->tlstp_context == NULL) { - tls_t *master = tlspri->tlspri_master; - tlstp->tlstp_context = tls_init_client(master, self->tp_socket); - if (!tlstp->tlstp_context) - return -1; - } - oldmask = tls_events(tlstp->tlstp_context, self->tp_events); #if 0 @@ -560,8 +558,6 @@ int tport_tls_accept(tport_primary_t *pri, int events) return 0; } else { - tport_tls_t *tlstp = (tport_tls_t *)self; - tport_tls_primary_t *tlspri = (tport_tls_primary_t *)self->tp_pri; int events = SU_WAIT_IN|SU_WAIT_ERR|SU_WAIT_HUP; SU_CANONIZE_SOCKADDR(su); @@ -575,8 +571,6 @@ int tport_tls_accept(tport_primary_t *pri, int events) self->tp_conn_orient = 1; self->tp_is_connected = 0; - tlstp->tlstp_context = tls_init_slave(tlspri->tlspri_master, s); - SU_DEBUG_5(("%s(%p): new connection from " TPN_FORMAT "\n", __func__, (void *)self, TPN_ARGS(self->tp_name))); @@ -640,14 +634,9 @@ tport_t *tport_tls_connect(tport_primary_t *pri, goto sys_error; } - if (tport_setname(self, tpn->tpn_proto, ai, tpn->tpn_canon) != -1 - && - tport_register_secondary(self, tls_connect, events) != -1) { - tport_tls_t *tlstp = (tport_tls_t *)self; - tport_tls_primary_t *tlspri = (tport_tls_primary_t *)self->tp_pri; - tlstp->tlstp_context = tls_init_client(tlspri->tlspri_master, s); - } - else + if (tport_setname(self, tpn->tpn_proto, ai, tpn->tpn_canon) == -1) + goto sys_error; + else if (tport_register_secondary(self, tls_connect, events) == -1) goto sys_error; SU_DEBUG_5(("%s(%p): connecting to " TPN_FORMAT "\n",