#include "config.h"
#define OPENSSL_NO_KRB5 oh-no
+#define SU_WAKEUP_ARG_T struct tport_s
#include <sofia-sip/su_types.h>
#include <sofia-sip/su.h>
+#include <sofia-sip/su_alloc.h>
#include <sofia-sip/su_wait.h>
#include <openssl/lhash.h>
#endif
#include "tport_tls.h"
-#include "tport_internal.h"
char const tls_version[] = OPENSSL_VERSION_TEXT;
-enum { tls_master, tls_slave };
+enum { tls_master = 0, tls_slave = 1};
struct tls_s {
+ su_home_t home[1];
SSL_CTX *ctx;
SSL *con;
BIO *bio_con;
- int type;
- int verified;
+ unsigned int type:1,
+ accept:1,
+ verify_outgoing:1,
+ verify_incoming:1,
+ verified:1;
/* Receiving */
int read_events;
size_t write_buffer_len;
/* Host names */
- char *hosts[TLS_MAX_HOSTS + 1];
+ su_strlst_t *subject;
};
enum { tls_buffer_size = 16384 };
static
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)
- tls->type = type;
+ tls->type = type == tls_master ? tls_master : tls_slave;
return tls;
}
ti->verify_peer == 1 ? SSL_VERIFY_PEER : SSL_VERIFY_NONE,
tls_verify_cb);
+ tls->verify_incoming = tls->verify_outgoing = ti->verify_peer ? 1 : 0;
+
if (!SSL_CTX_set_cipher_list(tls->ctx, ti->cipher)) {
SU_DEBUG_1(("%s: error setting cipher list\n", "tls_init_context"));
tls_log_errors(1, "tls_init_context", 0);
void tls_free(tls_t *tls)
{
- int k;
-
if (!tls)
return;
- if (tls->read_buffer)
- free(tls->read_buffer), tls->read_buffer = NULL;
-
if (tls->con != NULL)
SSL_shutdown(tls->con);
if (tls->bio_con != NULL)
BIO_free(tls->bio_con);
- for (k = 0; k < TLS_MAX_HOSTS; k++)
- if (tls->hosts[k]) {
- free(tls->hosts[k]), tls->hosts[k] = NULL;
- }
-
- free(tls);
+ su_home_unref(tls->home);
}
int tls_get_socket(tls_t *tls)
if (tls) {
tls->ctx = master->ctx;
+ tls->accept = accept ? 1 : 0;
- if (!(tls->read_buffer = malloc(tls_buffer_size)))
- free(tls), tls = NULL;
+ if (!(tls->read_buffer = su_alloc(tls->home, tls_buffer_size)))
+ su_home_unref(tls->home), tls = NULL;
}
if (!tls)
return tls;
}
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);
su_setblocking(sock, 0);
- tls_read(tls); /* XXX - works only with non-blocking sockets */
return tls;
}
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
int tls_post_connection_check(tls_t *tls)
{
X509 *cert;
int extcount;
- int k, i, j, error;
+ int i, j, error;
if (!tls) return -1;
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 */
for (i = 0; i < extcount; i++) {
for (j = 0; j < sk_CONF_VALUE_num(values); j++) {
value = sk_CONF_VALUE_value(values, j);
- if (strcmp(value->name, "DNS") == 0) {
- if (k < TLS_MAX_HOSTS) {
- tls->hosts[k] = tls_strdup(value->value);
- k += tls->hosts[k] != NULL;
- }
- }
+ if (strcmp(value->name, "DNS") == 0)
+ su_strlst_dup_append(tls->subject, value->value);
else if (strcmp(value->name, "URI") == 0) {
- char const *uri = strchr(value->value, ':');
- if (uri ++ && k < TLS_MAX_HOSTS) {
- tls->hosts[k] = tls_strdup(uri);
- k += tls->hosts[k] != NULL;
- }
+ char *uri = su_strlst_dup_append(tls->subject, value->value);
+ char const *url = strchr(uri, ':');
+ if (url++)
+ su_strlst_append(tls->subject, url);
}
}
}
- if (k < TLS_MAX_HOSTS) {
+ {
X509_NAME *subject;
char name[256];
name, sizeof name) > 0) {
name[(sizeof name) - 1] = '\0';
- for (i = 0; tls->hosts[i]; i++)
- if (strcasecmp(tls->hosts[i], name) == 0)
+ for (i = 0; i < su_strlst_len(tls->subject); i++)
+ if (strcasecmp(su_strlst_item(tls->subject, i), name) == 0)
break;
- if (i == k)
- tls->hosts[k++] = tls_strdup(name);
+ if (i == su_strlst_len(tls->subject))
+ su_strlst_dup_append(tls->subject, name);
}
}
}
if (error == X509_V_OK)
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;
}
-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
int tls_error(tls_t *tls, int ret, char const *who,
void *buf, int size)
if (0)
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));
if (tls->read_buffer_len)
if (ret <= 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);
}
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);
if (ret < 0)
return tls_error(tls, ret, "tls_write: SSL_write", buf, size);
((mask & SU_WAIT_IN) ? tls->read_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 :
+ su_strlst_dup(self->tp_home, tls->subject);
+
+ if (tport_has_queued(self))
+ tport_send_event(self);
+ else
+ tport_set_secondary_timer(self);
+
+ return 0;
+ }
+ break;
+
+ default:
+ {
+ char errbuf[64];
+ ERR_error_string_n(status, errbuf, 64);
+ SU_DEBUG_3(("%s(%p): TLS setup failed (%s)\n",
+ __func__, self, errbuf));
+ }
+ break;
+ }
+ }
+
+ /* TLS Handshake Failed or Peer Certificate did not Verify */
+ tport_close(self);
+ tport_set_secondary_timer(self);
+
+ return 0;
+}
+
#include "config.h"
+#define SU_WAKEUP_ARG_T struct tport_s
+
#include "tport_internal.h"
#include <stdlib.h>
static int tport_tls_recv(tport_t *self);
static ssize_t tport_tls_send(tport_t const *self, msg_t *msg,
msg_iovec_t iov[], size_t iovused);
-
-typedef struct
-{
- tport_primary_t tlspri_pri[1];
- tls_t *tlspri_master;
-} tport_tls_primary_t;
-
-typedef struct
-{
- tport_t tlstp_tp[1];
- tls_t *tlstp_context;
- char *tlstp_buffer; /**< 2k Buffer */
-} tport_tls_t;
+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);
+static void tport_tls_deliver(tport_t *self, msg_t *msg, su_time_t now);
tport_vtable_t const tport_tls_vtable =
{
sizeof (tport_tls_primary_t),
tport_tls_init_primary,
tport_tls_deinit_primary,
- tport_accept,
- NULL,
+ tport_tls_accept,
+ tport_tls_connect,
sizeof (tport_tls_t),
tport_tls_init_secondary,
tport_tls_deinit_secondary,
sizeof (tport_tls_primary_t),
tport_tls_init_client,
tport_tls_deinit_primary,
- tport_accept,
- NULL,
+ tport_tls_accept,
+ tport_tls_connect,
sizeof (tport_tls_t),
tport_tls_init_secondary,
tport_tls_deinit_secondary,
return size;
}
+
+static
+int tport_tls_accept(tport_primary_t *pri, int events)
+{
+ tport_t *self;
+ su_addrinfo_t ai[1];
+ su_sockaddr_t su[1];
+ socklen_t sulen = sizeof su;
+ su_socket_t s = INVALID_SOCKET, l = pri->pri_primary->tp_socket;
+ char const *reason = "accept";
+
+ if (events & SU_WAIT_ERR)
+ tport_error_event(pri->pri_primary);
+
+ if (!(events & SU_WAIT_ACCEPT))
+ return 0;
+
+ memcpy(ai, pri->pri_primary->tp_addrinfo, sizeof ai);
+ ai->ai_canonname = NULL;
+
+ s = accept(l, &su->su_sa, &sulen);
+
+ if (s < 0) {
+ tport_error_report(pri->pri_primary, su_errno(), NULL);
+ return 0;
+ }
+
+ ai->ai_addr = &su->su_sa, ai->ai_addrlen = sulen;
+
+ /* Alloc a new transport object, then register socket events with it */
+ if ((self = tport_alloc_secondary(pri, s, 1, &reason)) == NULL) {
+ SU_DEBUG_3(("%s(%p): incoming secondary on "TPN_FORMAT
+ " failed. reason = %s\n", __func__, pri,
+ TPN_ARGS(pri->pri_primary->tp_name), reason));
+ 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);
+
+ if (/* Name this transport */
+ tport_setname(self, pri->pri_protoname, ai, NULL) != -1
+ /* Register this secondary */
+ &&
+ tport_register_secondary(self, tls_connect, events) != -1) {
+
+ 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)));
+
+ /* Return succesfully */
+ return 0;
+ }
+
+ /* Failure: shutdown socket, */
+ tport_close(self);
+ tport_zap_secondary(self);
+ self = NULL;
+ }
+
+ return 0;
+}
+
+static
+tport_t *tport_tls_connect(tport_primary_t *pri,
+ su_addrinfo_t *ai,
+ tp_name_t const *tpn)
+{
+ tport_t *self = NULL;
+
+ su_socket_t s, server_socket;
+ int events = SU_WAIT_CONNECT | SU_WAIT_ERR;
+
+ int err;
+ unsigned errlevel = 3;
+ char buf[TPORT_HOSTPORTSIZE];
+ char const *what;
+
+ s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (s == INVALID_SOCKET)
+ goto sys_error;
+
+ what = "tport_alloc_secondary";
+ if ((self = tport_alloc_secondary(pri, s, 0, &what)) == NULL)
+ goto sys_error;
+
+ self->tp_conn_orient = 1;
+
+ if ((server_socket = pri->pri_primary->tp_socket) != INVALID_SOCKET) {
+ su_sockaddr_t susa;
+ socklen_t susalen = sizeof(susa);
+
+ if (getsockname(server_socket, &susa.su_sa, &susalen) < 0) {
+ SU_DEBUG_3(("%s(%p): getsockname(): %s\n",
+ __func__, (void *)self, su_strerror(su_errno())));
+ } else {
+ susa.su_port = 0;
+ if (bind(s, &susa.su_sa, susalen) < 0) {
+ SU_DEBUG_3(("%s(%p): bind(local-ip): %s\n",
+ __func__, (void *)self, su_strerror(su_errno())));
+ }
+ }
+ }
+
+ if (connect(s, ai->ai_addr, (socklen_t)(ai->ai_addrlen)) == SOCKET_ERROR) {
+ err = su_errno();
+ if (!su_is_blocking(err))
+ 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
+ goto sys_error;
+
+ SU_DEBUG_5(("%s(%p): connecting to " TPN_FORMAT "\n",
+ __func__, (void *)self, TPN_ARGS(self->tp_name)));
+
+ tport_set_secondary_timer(self);
+
+ return self;
+
+sys_error:
+ err = errno;
+ if (SU_LOG_LEVEL >= errlevel)
+ su_llog(tport_log, errlevel, "%s(%p): %s (pf=%d %s/%s): %s\n",
+ __func__, (void *)pri, what, ai->ai_family, tpn->tpn_proto,
+ tport_hostport(buf, sizeof(buf), (void *)ai->ai_addr, 2),
+ su_strerror(err));
+ tport_zap_secondary(self);
+ su_seterrno(err);
+ return NULL;
+}
+