Thu Jan 15 09:50:45 CST 2009 Jarod Neuner <janeuner@networkharbor.com>

* 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
This commit is contained in:
Michael Jerris 2009-02-11 17:03:59 +00:00
parent 27a5a679be
commit 38dabb3635
9 changed files with 428 additions and 120 deletions

View File

@ -1 +1 @@
Wed Feb 11 11:03:24 CST 2009
Wed Feb 11 11:03:50 CST 2009

View File

@ -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 *);

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

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

View File

@ -56,7 +56,6 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if HAVE_SIGPIPE
#include <signal.h>
@ -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);

View File

@ -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);

View File

@ -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",