Commit 4af68bbd authored by Jarod Neuner's avatar Jarod Neuner
Browse files

Early TLS Handshake and Verification

tport_type_tls.c:
* tport_tls_accept():
  - Replaces tport_accept for incoming TLS connections.
* tport_tls_connect():
  - Replaces tport_base_connect() for outgoing TLS connections.

tport_tls.c:
* tls_t now use a memory home instead of malloc.
* removed tls_check_hosts()
* tls_connect():
  - Replaces tport_base_connect for TLS connection setup.
  - Completes TLS handshake and verifies peer certificates.
  - Destroys suspect TLS connections before sending/receiving payload.
  - Populates a su_strlst_t with subjects from the peer certificate.

tport.c:
* tport_is_verified()
  - true if peer certificate validated successfully
* tport_delivered_from_subjects()
  - Certificate subjects listed in the peer certificate.

darcs-hash:20081216221937-2152f-3d6b74d411b57c22230e4840fca133da48c86368.gz
parent f799e03c
...@@ -36,6 +36,9 @@ ...@@ -36,6 +36,9 @@
#ifndef SU_H #ifndef SU_H
#include <sofia-sip/su.h> #include <sofia-sip/su.h>
#endif #endif
#ifndef SU_STRLST_H
#include <sofia-sip/su_strlst.h>
#endif
#ifndef SU_WAIT_H #ifndef SU_WAIT_H
#include <sofia-sip/su_wait.h> #include <sofia-sip/su_wait.h>
#endif #endif
...@@ -267,6 +270,9 @@ TPORT_DLL int tport_is_tcp(tport_t const *self); ...@@ -267,6 +270,9 @@ TPORT_DLL int tport_is_tcp(tport_t const *self);
/** Test if transport has TLS. */ /** Test if transport has TLS. */
TPORT_DLL int tport_has_tls(tport_t const *tport); TPORT_DLL int tport_has_tls(tport_t const *tport);
/** Test if transport provided a verified certificate chain (TLS only) */
TPORT_DLL int tport_is_verified(tport_t const *tport);
/** Return true if transport is being updated. */ /** Return true if transport is being updated. */
TPORT_DLL int tport_is_updating(tport_t const *self); TPORT_DLL int tport_is_updating(tport_t const *self);
...@@ -332,6 +338,9 @@ TPORT_DLL tport_t *tport_delivered_by(tport_t const *tp, msg_t const *msg); ...@@ -332,6 +338,9 @@ TPORT_DLL tport_t *tport_delivered_by(tport_t const *tp, msg_t const *msg);
TPORT_DLL int tport_delivered_from(tport_t *tp, msg_t const *msg, TPORT_DLL int tport_delivered_from(tport_t *tp, msg_t const *msg,
tp_name_t name[1]); 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);
/** Check if transport named is already resolved */ /** Check if transport named is already resolved */
TPORT_DLL int tport_name_is_resolved(tp_name_t const *); TPORT_DLL int tport_name_is_resolved(tp_name_t const *);
......
...@@ -268,7 +268,12 @@ int tport_has_ip6(tport_t const *self) ...@@ -268,7 +268,12 @@ int tport_has_ip6(tport_t const *self)
int tport_has_tls(tport_t const *self) int tport_has_tls(tport_t const *self)
{ {
return self && self->tp_pri->pri_has_tls; return self && self->tp_pri->pri_has_tls;
}
/** Return true if transport certificate verified successfully */
int tport_is_verified(tport_t const *self)
{
return tport_has_tls(self) && self->tp_verified;
} }
/** Return true if transport is being updated. */ /** Return true if transport is being updated. */
...@@ -3040,6 +3045,17 @@ int tport_delivered_from(tport_t *tp, msg_t const *msg, tp_name_t name[1]) ...@@ -3040,6 +3045,17 @@ 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)
{
if (tp && msg && msg == tp->tp_master->mr_delivery->d_msg) {
tport_t *tp_sec = tp->tp_master->mr_delivery->d_tport;
return (tp_sec) ? tp_sec->tp_subjects : NULL;
}
else
return NULL;
}
/** Return UDVM used to decompress the message. */ /** Return UDVM used to decompress the message. */
int int
tport_delivered_with_comp(tport_t *tp, msg_t const *msg, tport_delivered_with_comp(tport_t *tp, msg_t const *msg,
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#endif #endif
#include <sofia-sip/su_uniqueid.h> #include <sofia-sip/su_uniqueid.h>
#include <sofia-sip/su_strlst.h>
#ifndef MSG_ADDR_H #ifndef MSG_ADDR_H
#include <sofia-sip/msg_addr.h> #include <sofia-sip/msg_addr.h>
...@@ -155,6 +156,7 @@ struct tport_s { ...@@ -155,6 +156,7 @@ struct tport_s {
unsigned tp_has_stun_server:1; unsigned tp_has_stun_server:1;
unsigned tp_trunc:1; unsigned tp_trunc:1;
unsigned tp_is_connected:1; /**< Connection is established */ unsigned tp_is_connected:1; /**< Connection is established */
unsigned tp_verified:1; /**< Certificate Chain was verified */
unsigned:0; unsigned:0;
tport_t *tp_left, *tp_right, *tp_dad; /**< Links in tport tree */ tport_t *tp_left, *tp_right, *tp_dad; /**< Links in tport tree */
...@@ -177,6 +179,12 @@ struct tport_s { ...@@ -177,6 +179,12 @@ struct tport_s {
* or peer name (if secondary). * or peer name (if secondary).
*/ */
su_strlst_t *tp_subjects; /**< Transport Subjects.
*
* Subject Name(s) provided by the
* peer in a TLS connection (if secondary).
*/
#define tp_protoname tp_name->tpn_proto #define tp_protoname tp_name->tpn_proto
#define tp_canon tp_name->tpn_canon #define tp_canon tp_name->tpn_canon
#define tp_host tp_name->tpn_host #define tp_host tp_name->tpn_host
......
...@@ -35,9 +35,11 @@ ...@@ -35,9 +35,11 @@
#include "config.h" #include "config.h"
#define OPENSSL_NO_KRB5 oh-no #define OPENSSL_NO_KRB5 oh-no
#define SU_WAKEUP_ARG_T struct tport_s
#include <sofia-sip/su_types.h> #include <sofia-sip/su_types.h>
#include <sofia-sip/su.h> #include <sofia-sip/su.h>
#include <sofia-sip/su_alloc.h>
#include <sofia-sip/su_wait.h> #include <sofia-sip/su_wait.h>
#include <openssl/lhash.h> #include <openssl/lhash.h>
...@@ -61,18 +63,21 @@ ...@@ -61,18 +63,21 @@
#endif #endif
#include "tport_tls.h" #include "tport_tls.h"
#include "tport_internal.h"
char const tls_version[] = OPENSSL_VERSION_TEXT; char const tls_version[] = OPENSSL_VERSION_TEXT;
enum { tls_master, tls_slave }; enum { tls_master = 0, tls_slave = 1};
struct tls_s { struct tls_s {
su_home_t home[1];
SSL_CTX *ctx; SSL_CTX *ctx;
SSL *con; SSL *con;
BIO *bio_con; BIO *bio_con;
int type; unsigned int type:1,
int verified; accept:1,
verify_outgoing:1,
verify_incoming:1,
verified:1;
/* Receiving */ /* Receiving */
int read_events; int read_events;
...@@ -85,7 +90,7 @@ struct tls_s { ...@@ -85,7 +90,7 @@ struct tls_s {
size_t write_buffer_len; size_t write_buffer_len;
/* Host names */ /* Host names */
char *hosts[TLS_MAX_HOSTS + 1]; su_strlst_t *subject;
}; };
enum { tls_buffer_size = 16384 }; enum { tls_buffer_size = 16384 };
...@@ -122,10 +127,11 @@ void tls_log_errors(unsigned level, char const *s, unsigned long e) ...@@ -122,10 +127,11 @@ void tls_log_errors(unsigned level, char const *s, unsigned long e)
static static
tls_t *tls_create(int type) tls_t *tls_create(int type)
{ {
tls_t *tls = calloc(1, sizeof(*tls)); tls_t *tls = su_home_new(sizeof(*tls));
memset(((void *)tls) + sizeof(su_home_t), 0, sizeof(*tls) - sizeof(su_home_t));
if (tls) if (tls)
tls->type = type; tls->type = type == tls_master ? tls_master : tls_slave;
return tls; return tls;
} }
...@@ -268,6 +274,8 @@ int tls_init_context(tls_t *tls, tls_issues_t const *ti) ...@@ -268,6 +274,8 @@ int tls_init_context(tls_t *tls, tls_issues_t const *ti)
ti->verify_peer == 1 ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, ti->verify_peer == 1 ? SSL_VERIFY_PEER : SSL_VERIFY_NONE,
tls_verify_cb); tls_verify_cb);
tls->verify_incoming = tls->verify_outgoing = ti->verify_peer ? 1 : 0;
if (!SSL_CTX_set_cipher_list(tls->ctx, ti->cipher)) { if (!SSL_CTX_set_cipher_list(tls->ctx, ti->cipher)) {
SU_DEBUG_1(("%s: error setting cipher list\n", "tls_init_context")); SU_DEBUG_1(("%s: error setting cipher list\n", "tls_init_context"));
tls_log_errors(1, "tls_init_context", 0); tls_log_errors(1, "tls_init_context", 0);
...@@ -280,14 +288,9 @@ int tls_init_context(tls_t *tls, tls_issues_t const *ti) ...@@ -280,14 +288,9 @@ int tls_init_context(tls_t *tls, tls_issues_t const *ti)
void tls_free(tls_t *tls) void tls_free(tls_t *tls)
{ {
int k;
if (!tls) if (!tls)
return; return;
if (tls->read_buffer)
free(tls->read_buffer), tls->read_buffer = NULL;
if (tls->con != NULL) if (tls->con != NULL)
SSL_shutdown(tls->con); SSL_shutdown(tls->con);
...@@ -297,12 +300,7 @@ void tls_free(tls_t *tls) ...@@ -297,12 +300,7 @@ void tls_free(tls_t *tls)
if (tls->bio_con != NULL) if (tls->bio_con != NULL)
BIO_free(tls->bio_con); BIO_free(tls->bio_con);
for (k = 0; k < TLS_MAX_HOSTS; k++) su_home_unref(tls->home);
if (tls->hosts[k]) {
free(tls->hosts[k]), tls->hosts[k] = NULL;
}
free(tls);
} }
int tls_get_socket(tls_t *tls) int tls_get_socket(tls_t *tls)
...@@ -369,9 +367,10 @@ tls_t *tls_clone(tls_t *master, int sock, int accept) ...@@ -369,9 +367,10 @@ tls_t *tls_clone(tls_t *master, int sock, int accept)
if (tls) { if (tls) {
tls->ctx = master->ctx; tls->ctx = master->ctx;
tls->accept = accept ? 1 : 0;
if (!(tls->read_buffer = malloc(tls_buffer_size))) if (!(tls->read_buffer = su_alloc(tls->home, tls_buffer_size)))
free(tls), tls = NULL; su_home_unref(tls->home), tls = NULL;
} }
if (!tls) if (!tls)
return tls; return tls;
...@@ -389,14 +388,9 @@ tls_t *tls_clone(tls_t *master, int sock, int accept) ...@@ -389,14 +388,9 @@ tls_t *tls_clone(tls_t *master, int sock, int accept)
} }
SSL_set_bio(tls->con, tls->bio_con, tls->bio_con); SSL_set_bio(tls->con, tls->bio_con, tls->bio_con);
if (accept)
SSL_set_accept_state(tls->con);
else
SSL_set_connect_state(tls->con);
SSL_set_mode(tls->con, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_set_mode(tls->con, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
su_setblocking(sock, 0); su_setblocking(sock, 0);
tls_read(tls); /* XXX - works only with non-blocking sockets */
return tls; return tls;
} }
...@@ -413,24 +407,12 @@ tls_t *tls_init_client(tls_t *master, int sock) ...@@ -413,24 +407,12 @@ tls_t *tls_init_client(tls_t *master, int sock)
return tls_clone(master, sock, accept = 0); return tls_clone(master, sock, accept = 0);
} }
static char *tls_strdup(char const *s)
{
if (s) {
size_t len = strlen(s) + 1;
char *d = malloc(len);
if (d)
memcpy(d, s, len);
return d;
}
return NULL;
}
static static
int tls_post_connection_check(tls_t *tls) int tls_post_connection_check(tls_t *tls)
{ {
X509 *cert; X509 *cert;
int extcount; int extcount;
int k, i, j, error; int i, j, error;
if (!tls) return -1; if (!tls) return -1;
...@@ -440,8 +422,8 @@ int tls_post_connection_check(tls_t *tls) ...@@ -440,8 +422,8 @@ int tls_post_connection_check(tls_t *tls)
extcount = X509_get_ext_count(cert); extcount = X509_get_ext_count(cert);
for (k = 0; k < TLS_MAX_HOSTS && tls->hosts[k]; k++) if (!tls->subject)
; tls->subject = su_strlst_create(tls->home);
/* Find matching subjectAltName.DNS */ /* Find matching subjectAltName.DNS */
for (i = 0; i < extcount; i++) { for (i = 0; i < extcount; i++) {
...@@ -464,23 +446,18 @@ int tls_post_connection_check(tls_t *tls) ...@@ -464,23 +446,18 @@ int tls_post_connection_check(tls_t *tls)
for (j = 0; j < sk_CONF_VALUE_num(values); j++) { for (j = 0; j < sk_CONF_VALUE_num(values); j++) {
value = sk_CONF_VALUE_value(values, j); value = sk_CONF_VALUE_value(values, j);
if (strcmp(value->name, "DNS") == 0) { if (strcmp(value->name, "DNS") == 0)
if (k < TLS_MAX_HOSTS) { su_strlst_dup_append(tls->subject, value->value);
tls->hosts[k] = tls_strdup(value->value);
k += tls->hosts[k] != NULL;
}
}
else if (strcmp(value->name, "URI") == 0) { else if (strcmp(value->name, "URI") == 0) {
char const *uri = strchr(value->value, ':'); char *uri = su_strlst_dup_append(tls->subject, value->value);
if (uri ++ && k < TLS_MAX_HOSTS) { char const *url = strchr(uri, ':');
tls->hosts[k] = tls_strdup(uri); if (url++)
k += tls->hosts[k] != NULL; su_strlst_append(tls->subject, url);
}
} }
} }
} }
if (k < TLS_MAX_HOSTS) { {
X509_NAME *subject; X509_NAME *subject;
char name[256]; char name[256];
...@@ -490,12 +467,12 @@ int tls_post_connection_check(tls_t *tls) ...@@ -490,12 +467,12 @@ int tls_post_connection_check(tls_t *tls)
name, sizeof name) > 0) { name, sizeof name) > 0) {
name[(sizeof name) - 1] = '\0'; name[(sizeof name) - 1] = '\0';
for (i = 0; tls->hosts[i]; i++) for (i = 0; i < su_strlst_len(tls->subject); i++)
if (strcasecmp(tls->hosts[i], name) == 0) if (strcasecmp(su_strlst_item(tls->subject, i), name) == 0)
break; break;
if (i == k) if (i == su_strlst_len(tls->subject))
tls->hosts[k++] = tls_strdup(name); su_strlst_dup_append(tls->subject, name);
} }
} }
} }
...@@ -507,33 +484,13 @@ int tls_post_connection_check(tls_t *tls) ...@@ -507,33 +484,13 @@ int tls_post_connection_check(tls_t *tls)
if (error == X509_V_OK) if (error == X509_V_OK)
tls->verified = 1; tls->verified = 1;
if (tls->accept && !tls->verify_incoming)
return X509_V_OK;
else if (!tls->accept && !tls->verify_outgoing)
return X509_V_OK;
return error; return error;
} }
int tls_check_hosts(tls_t *tls, char const *hosts[TLS_MAX_HOSTS])
{
int i, j;
if (tls == NULL) { errno = EINVAL; return -1; }
if (!tls->verified) { errno = EAGAIN; return -1; }
if (!hosts)
return 0;
for (i = 0; hosts[i]; i++) {
for (j = 0; tls->hosts[j]; j++) {
if (strcasecmp(hosts[i], tls->hosts[j]) == 0)
break;
}
if (tls->hosts[j] == NULL) {
errno = EACCES;
return -1;
}
}
return 0;
}
static static
int tls_error(tls_t *tls, int ret, char const *who, int tls_error(tls_t *tls, int ret, char const *who,
void *buf, int size) void *buf, int size)
...@@ -589,7 +546,7 @@ ssize_t tls_read(tls_t *tls) ...@@ -589,7 +546,7 @@ ssize_t tls_read(tls_t *tls)
if (0) if (0)
SU_DEBUG_1(("tls_read(%p) called on %s (events %u)\n", (void *)tls, SU_DEBUG_1(("tls_read(%p) called on %s (events %u)\n", (void *)tls,
tls->type == tls_slave ? "server" : "client", tls->accept ? "server" : "client",
tls->read_events)); tls->read_events));
if (tls->read_buffer_len) if (tls->read_buffer_len)
...@@ -601,19 +558,6 @@ ssize_t tls_read(tls_t *tls) ...@@ -601,19 +558,6 @@ ssize_t tls_read(tls_t *tls)
if (ret <= 0) if (ret <= 0)
return tls_error(tls, ret, "tls_read: SSL_read", NULL, 0); return tls_error(tls, ret, "tls_read: SSL_read", NULL, 0);
if (!tls->verified) {
int err = tls_post_connection_check(tls);
if (err != X509_V_OK &&
err != SSL_ERROR_SYSCALL &&
err != SSL_ERROR_WANT_WRITE &&
err != SSL_ERROR_WANT_READ) {
SU_DEBUG_1((
"%s: server certificate doesn't verify\n",
"tls_read"));
}
}
return (ssize_t)(tls->read_buffer_len = ret); return (ssize_t)(tls->read_buffer_len = ret);
} }
...@@ -694,13 +638,6 @@ ssize_t tls_write(tls_t *tls, void *buf, size_t size) ...@@ -694,13 +638,6 @@ ssize_t tls_write(tls_t *tls, void *buf, size_t size)
tls->write_events = 0; tls->write_events = 0;
if (!tls->verified) {
if (tls_post_connection_check(tls) != X509_V_OK) {
SU_DEBUG_1((
"tls_read: server certificate doesn't verify\n"));
}
}
ret = SSL_write(tls->con, buf, size); ret = SSL_write(tls->con, buf, size);
if (ret < 0) if (ret < 0)
return tls_error(tls, ret, "tls_write: SSL_write", buf, size); return tls_error(tls, ret, "tls_write: SSL_write", buf, size);
...@@ -748,3 +685,118 @@ int tls_events(tls_t const *tls, int mask) ...@@ -748,3 +685,118 @@ int tls_events(tls_t const *tls, int mask)
((mask & SU_WAIT_IN) ? tls->read_events : 0) | ((mask & SU_WAIT_IN) ? tls->read_events : 0) |
((mask & SU_WAIT_OUT) ? tls->write_events : 0); ((mask & SU_WAIT_OUT) ? tls->write_events : 0);
} }
int tls_connect(su_root_magic_t *magic, su_wait_t *w, tport_t *self)
{
tport_master_t *mr = self->tp_master;
tport_tls_t *tlstp = (tport_tls_t *)self;
tls_t *tls;
int events = su_wait_events(w, self->tp_socket);
int error;
SU_DEBUG_7(("%s(%p): events%s%s%s%s\n", __func__, (void *)self,
events & (SU_WAIT_CONNECT) ? " CONNECTING" : "",
events & SU_WAIT_IN ? " NEGOTIATING" : "",
events & SU_WAIT_ERR ? " ERROR" : "",
events & SU_WAIT_HUP ? " HANGUP" : ""));
#if HAVE_POLL
assert(w->fd == self->tp_socket);
#endif
if (events & SU_WAIT_ERR)
tport_error_event(self);
if (events & SU_WAIT_HUP && !self->tp_closed)
tport_hup_event(self);
if (self->tp_closed)
return 0;
error = su_soerror(self->tp_socket);
if (error) {
tport_error_report(self, error, NULL);
return 0;
}
if ((tls = tlstp->tlstp_context) == NULL) {
SU_DEBUG_3(("%s(%p): Error: no TLS context data for connected socket.\n",
__func__, tlstp));
tport_close(self);
tport_set_secondary_timer(self);
return 0;
}
if (self->tp_is_connected == 0) {
int ret, status;
ret = tls->accept ? SSL_accept(tls->con) : SSL_connect(tls->con);
status = SSL_get_error(tls->con, ret);
switch (status) {
case SSL_ERROR_WANT_READ:
/* OpenSSL is waiting for the peer to send handshake data */
self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP;
su_root_eventmask(mr->mr_root, self->tp_index,
self->tp_socket, self->tp_events);
return 0;
case SSL_ERROR_WANT_WRITE:
/* OpenSSL is waiting for the peer to receive handshake data */
self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP | SU_WAIT_OUT;
su_root_eventmask(mr->mr_root, self->tp_index,
self->tp_socket, self->tp_events);
return 0;
case SSL_ERROR_NONE:
/* TLS Handshake complete */
if ( tls_post_connection_check(tls) == X509_V_OK ) {
su_wait_t wait[1] = {SU_WAIT_INIT};
tport_master_t *mr = self->tp_master;
su_root_deregister(mr->mr_root, self->tp_index);
self->tp_index = -1;
self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP;
if ((su_wait_create(wait, self->tp_socket, self->tp_events) == -1) ||
((self->tp_index = su_root_register(mr->mr_root, wait, tport_wakeup,
self, 0)) == -1)) {
tport_close(self);
tport_set_secondary_timer(self);
return 0;
}
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 :