freeswitch/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c

785 lines
17 KiB
C

/*
* This file is part of the Sofia-SIP package
*
* Copyright (C) 2005 Nokia Corporation.
*
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
/**@CFILE tport_tls.c
* @brief TLS interface
*
* @author Mikko Haataja <ext-Mikko.A.Haataja@nokia.com>
* @author Pekka Pessi <ext-Pekka.Pessi@nokia.com>
*
* Copyright 2001, 2002 Nokia Research Center. All rights reserved.
*
*/
#include "config.h"
#define OPENSSL_NO_KRB5 oh-no
#include <openssl/lhash.h>
#include <openssl/bn.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/bio.h>
#include <openssl/opensslv.h>
#include <sofia-sip/su_types.h>
#include <sofia-sip/su.h>
#include <sofia-sip/su_wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if HAVE_SIGPIPE
#include <signal.h>
#endif
#include "tport_tls.h"
#include "tport_internal.h"
char const tls_version[] = OPENSSL_VERSION_TEXT;
enum { tls_master, tls_slave };
struct tls_s {
SSL_CTX *ctx;
SSL *con;
BIO *bio_con;
int type;
int verified;
/* Receiving */
int read_events;
void *read_buffer;
size_t read_buffer_len;
/* Sending */
int write_events;
void *write_buffer;
size_t write_buffer_len;
/* Host names */
char *hosts[TLS_MAX_HOSTS + 1];
};
enum { tls_buffer_size = 16384 };
static
int tls_print_errors(const char *str, size_t len, void *u)
{
SU_DEBUG_1((str));
return 0;
}
static
tls_t *tls_create(int type)
{
tls_t *tls = calloc(1, sizeof(*tls));
if (tls)
tls->type = type;
return tls;
}
static
void tls_set_default(tls_issues_t *i)
{
i->verify_depth = i->verify_depth == 0 ? 2 : i->verify_depth;
i->cert = i->cert ? i->cert : "agent.pem";
i->key = i->key ? i->key : i->cert;
i->randFile = i->randFile ? i->randFile : "tls_seed.dat";
i->CAfile = i->CAfile ? i->CAfile : "cafile.pem";
i->cipher = i->cipher ? i->cipher : "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
/* Default SIP cipher */
/* "RSA-WITH-AES-128-CBC-SHA"; */
/* RFC-2543-compatibility ciphersuite */
/* TLS_RSA_WITH_3DES_EDE_CBC_SHA; */
}
static
int tls_verify_cb(int ok, X509_STORE_CTX *store)
{
char data[256];
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);
#if nomore
509_NAME_oneline(X509_get_subject_name(cert), data, 256);
SU_DEBUG_1(("depth=%d %s\n",depth,data));
#endif
if (!ok)
{
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 1; /* Always return "ok" */
}
static
int tls_init_context(tls_t *tls, tls_issues_t const *ti)
{
static int initialized = 0;
if (!initialized) {
initialized = 1;
SSL_library_init();
SSL_load_error_strings();
if (ti->randFile &&
!RAND_load_file(ti->randFile, 1024 * 1024)) {
if (ti->configured > 1) {
SU_DEBUG_1(("%s: cannot open randFile %s\n",
"tls_init_context", ti->randFile));
ERR_print_errors_cb(&tls_print_errors, NULL);
}
/* errno = EIO; */
/* return -1; */
}
}
#if HAVE_SIGPIPE
/* Avoid possible SIGPIPE when sending close_notify */
signal(SIGPIPE, SIG_IGN);
#endif
if (tls->ctx == NULL) {
SSL_METHOD *meth;
/* meth = SSLv3_method(); */
/* meth = SSLv23_method(); */
if (ti->version)
meth = TLSv1_method();
else
meth = SSLv23_method();
tls->ctx = SSL_CTX_new(meth);
}
if (tls->ctx == NULL) {
ERR_print_errors_cb(&tls_print_errors, NULL);
errno = EIO;
return -1;
}
if (!SSL_CTX_use_certificate_file(tls->ctx,
ti->cert,
SSL_FILETYPE_PEM)) {
if (ti->configured > 0) {
SU_DEBUG_1(("%s: invalid certificate: %s\n",
"tls_init_context", ti->cert));
ERR_print_errors_cb(&tls_print_errors, NULL);
#if require_client_certificate
errno = EIO;
return -1;
#endif
}
}
if (!SSL_CTX_use_PrivateKey_file(tls->ctx,
ti->key,
SSL_FILETYPE_PEM)) {
if (ti->configured > 0) {
ERR_print_errors_cb(&tls_print_errors, NULL);
#if require_client_certificate
errno = EIO;
return -1;
#endif
}
}
if (!SSL_CTX_check_private_key(tls->ctx)) {
if (ti->configured > 0) {
SU_DEBUG_1(("Private key does not match the certificate public key\n"));
}
#if require_client_certificate
errno = EIO;
return -1;
#endif
}
if (!SSL_CTX_load_verify_locations(tls->ctx,
ti->CAfile,
ti->CApath)) {
if (ti->configured > 0)
ERR_print_errors_cb(&tls_print_errors, NULL);
errno = EIO;
return -1;
}
SSL_CTX_set_verify_depth(tls->ctx, ti->verify_depth);
SSL_CTX_set_verify(tls->ctx,
getenv("SSL_VERIFY_PEER") ? SSL_VERIFY_PEER : SSL_VERIFY_NONE
/* SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT */,
tls_verify_cb);
if (!SSL_CTX_set_cipher_list(tls->ctx, ti->cipher)) {
SU_DEBUG_1(("error setting cipher list\n"));
ERR_print_errors_cb(&tls_print_errors, NULL);
errno = EIO;
return -1;
}
return 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->ctx != NULL && tls->type != tls_slave)
SSL_CTX_free(tls->ctx);
if (tls->bio_con != NULL)
BIO_free(tls->bio_con);
for (k = 0; k < TLS_MAX_HOSTS; k++)
free(tls->hosts[k]), tls->hosts[k] = NULL;
free(tls);
}
int tls_get_socket(tls_t *tls)
{
int sock = -1;
if (tls != NULL && tls->bio_con != NULL)
BIO_get_fd(tls->bio_con, &sock);
return sock;
}
tls_t *tls_init_master(tls_issues_t *ti)
{
/* Default id in case RAND fails */
unsigned char sessionId[32] = "sofia/tls";
tls_t *tls;
#if HAVE_SIGPIPE
signal(SIGPIPE, SIG_IGN); /* Ignore spurios SIGPIPE from OpenSSL */
#endif
tls_set_default(ti);
if (!(tls = tls_create(tls_master)))
return NULL;
if (tls_init_context(tls, ti) < 0) {
int err = errno;
tls_free(tls);
errno = err;
return NULL;
}
RAND_pseudo_bytes(sessionId, sizeof(sessionId));
SSL_CTX_set_session_id_context(tls->ctx,
(void*) sessionId,
sizeof(sessionId));
if (ti->CAfile != NULL)
SSL_CTX_set_client_CA_list(tls->ctx,
SSL_load_client_CA_file(ti->CAfile));
#if 0
if (sock != -1) {
tls->bio_con = BIO_new_socket(sock, BIO_NOCLOSE);
if (tls->bio_con == NULL) {
SU_DEBUG_1(("tls_init_master: BIO_new_socket failed\n"));
ERR_print_errors_cb(&tls_print_errors, NULL);
tls_free(tls);
errno = EIO;
return NULL;
}
}
#endif
return tls;
}
#if 0
#include <poll.h>
static
int tls_accept(tls_t *tls)
{
int ret = SSL_accept(tls->con);
int verify_result;
if (ret <= 0) {
int err = SSL_get_error(tls->con, ret);
switch(err) {
case SSL_ERROR_WANT_READ:
return errno = EAGAIN, tls->read_events = SU_WAIT_IN, 0;
case SSL_ERROR_WANT_WRITE:
return errno = EAGAIN, tls->read_events = SU_WAIT_OUT, 0;
default:
SU_DEBUG_1(("SSL_connect failed: %d %s\n",
err,
ERR_error_string(err, NULL)));
ERR_print_errors_cb(&tls_print_errors, NULL);
return -1;
}
}
verify_result = SSL_get_verify_result(tls->con);
if (verify_result != X509_V_OK) {
SU_DEBUG_1((
"Client certificate doesn't verify: %s\n",
X509_verify_cert_error_string(verify_result)));
#if 0
tls_free(tls);
return NULL;
#endif
}
if (SSL_get_peer_certificate(tls->con) == NULL) {
SU_DEBUG_1(("Client didn't send certificate\n"));
#if 0
tls_free(tls);
return NULL;
#endif
}
return 1;
}
#endif
tls_t *tls_clone(tls_t *master, int sock, int accept)
{
tls_t *tls = tls_create(tls_slave);
if (tls) {
tls->ctx = master->ctx;
if (!(tls->read_buffer = malloc(tls_buffer_size)))
free(tls), tls = NULL;
}
if (!tls)
return tls;
assert(sock != -1);
tls->bio_con = BIO_new_socket(sock, BIO_NOCLOSE);
tls->con = SSL_new(tls->ctx);
if (tls->con == NULL) {
SU_DEBUG_1(("tls_clone: SSL_new failed\n"));
ERR_print_errors_cb(&tls_print_errors, NULL);
tls_free(tls);
errno = EIO;
return NULL;
}
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;
}
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 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;
if (!tls) return -1;
cert = SSL_get_peer_certificate(tls->con);
if (!cert)
return X509_V_OK;
extcount = X509_get_ext_count(cert);
for (k = 0; k < TLS_MAX_HOSTS && tls->hosts[k]; k++)
;
/* Find matching subjectAltName.DNS */
for (i = 0; i < extcount; i++) {
X509_EXTENSION *ext;
char const *name;
X509V3_EXT_METHOD *vp;
STACK_OF(CONF_VALUE) *values;
CONF_VALUE *value;
void *d2i;
ext = X509_get_ext(cert, i);
name = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
if (strcmp(name, "subjectAltName") != 0)
continue;
vp = X509V3_EXT_get(ext); if (!vp) continue;
d2i = X509V3_EXT_d2i(ext);
values = vp->i2v(vp, d2i, NULL);
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;
}
}
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;
}
}
}
}
if (k < TLS_MAX_HOSTS) {
X509_NAME *subject;
char name[256];
subject = X509_get_subject_name(cert);
if (subject) {
if (X509_NAME_get_text_by_NID(subject, NID_commonName,
name, sizeof name) > 0) {
name[(sizeof name) - 1] = '\0';
for (i = 0; tls->hosts[i]; i++)
if (strcasecmp(tls->hosts[i], name) == 0)
break;
if (i == k)
tls->hosts[k++] = tls_strdup(name);
}
}
}
X509_free(cert);
error = SSL_get_verify_result(tls->con);
if (error == X509_V_OK)
tls->verified = 1;
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, char const *operation,
void *buf, int size)
{
char errorbuf[128];
int events = 0;
int err = SSL_get_error(tls->con, ret);
switch (err) {
case SSL_ERROR_WANT_WRITE:
events = SU_WAIT_OUT;
break;
case SSL_ERROR_WANT_READ:
events = SU_WAIT_IN;
break;
case SSL_ERROR_ZERO_RETURN:
return 0;
case SSL_ERROR_SYSCALL:
if (SSL_get_shutdown(tls->con) & SSL_RECEIVED_SHUTDOWN)
return 0; /* EOS */
if (errno == 0)
return 0; /* EOS */
return -1;
default:
SU_DEBUG_1(("%s: %s failed (%d): %s\n",
who, operation, err, ERR_error_string(err, errorbuf)));
ERR_print_errors_cb(&tls_print_errors, NULL);
errno = EIO;
return -1;
}
if (buf) {
tls->write_events = events;
tls->write_buffer = buf, tls->write_buffer_len = size;
}
else {
tls->read_events = events;
}
errno = EAGAIN;
return -1;
}
ssize_t tls_read(tls_t *tls)
{
ssize_t ret;
if (tls == NULL) {
errno = EINVAL;
return -1;
}
if (0)
SU_DEBUG_1(("tls_read(%p) called on %s (events %u)\n", (void *)tls,
tls->type == tls_slave ? "server" : "client",
tls->read_events));
if (tls->read_buffer_len)
return (ssize_t)tls->read_buffer_len;
tls->read_events = SU_WAIT_IN;
ret = SSL_read(tls->con, tls->read_buffer, tls_buffer_size);
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);
}
void *tls_read_buffer(tls_t *tls, size_t N)
{
assert(N == tls->read_buffer_len);
tls->read_buffer_len = 0;
return tls->read_buffer;
}
int tls_pending(tls_t const *tls)
{
return tls && tls->con && SSL_pending(tls->con);
}
/** Check if data is available in TCP connection.
*
*
*
* @retval -1 upon an error
* @retval 0 end-of-stream
* @retval 1 nothing to read
* @retval 2 there is data to read
*/
int tls_want_read(tls_t *tls, int events)
{
if (tls && (events & tls->read_events)) {
int ret = tls_read(tls);
if (ret > 0)
return 2;
else if (ret == 0)
return 0;
else if (errno == EAGAIN)
return 3; /* ??? */
else
return -1;
}
return 1;
}
ssize_t tls_write(tls_t *tls, void *buf, size_t size)
{
ssize_t ret;
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"));
if (tls == NULL || buf == NULL) {
errno = EINVAL;
return -1;
}
if (tls->write_buffer) {
assert(buf == tls->write_buffer);
assert(size >= tls->write_buffer_len);
assert(tls->write_events == 0);
if (tls->write_events ||
buf != tls->write_buffer ||
size < tls->write_buffer_len) {
errno = EIO;
return -1;
}
ret = tls->write_buffer_len;
tls->write_buffer = NULL;
tls->write_buffer_len = 0;
return ret;
}
if (size == 0)
return 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);
if (ret < 0)
return tls_error(tls, ret, "tls_write", "SSL_write", buf, size);
return ret;
}
int tls_want_write(tls_t *tls, int events)
{
if (tls && (events & tls->write_events)) {
int ret;
void *buf = tls->write_buffer;
size_t size = tls->write_buffer_len;
tls->write_events = 0;
/* remove buf */
tls->write_buffer = NULL;
tls->write_buffer_len = 0;
ret = tls_write(tls, buf, size);
if (ret >= 0)
/* Restore buf */
return tls->write_buffer = buf, tls->write_buffer_len = ret;
else if (errno == EAGAIN)
return 0;
else
return -1;
}
return 0;
}
int tls_events(tls_t const *tls, int mask)
{
if (!tls)
return mask;
if (tls->type == tls_master)
return mask;
return
(mask & ~(SU_WAIT_IN|SU_WAIT_OUT)) |
((mask & SU_WAIT_IN) ? tls->read_events : 0) |
((mask & SU_WAIT_OUT) ? tls->write_events : 0);
}