pjproject/pjlib/src/pj/ssl_sock_ossl.c

2576 lines
79 KiB
C

/*
* Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pj/ssl_sock.h>
#include <pj/activesock.h>
#include <pj/compat/socket.h>
#include <pj/assert.h>
#include <pj/errno.h>
#include <pj/file_access.h>
#include <pj/list.h>
#include <pj/lock.h>
#include <pj/log.h>
#include <pj/math.h>
#include <pj/os.h>
#include <pj/pool.h>
#include <pj/string.h>
#include <pj/timer.h>
/* Only build when PJ_HAS_SSL_SOCK is enabled and when the backend is
* OpenSSL.
*/
#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \
(PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_OPENSSL)
#include "ssl_sock_imp_common.h"
#define THIS_FILE "ssl_sock_ossl.c"
/*
* Include OpenSSL headers
*/
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>
#if !defined(OPENSSL_NO_DH)
# include <openssl/dh.h>
#endif
#include <openssl/rand.h>
#include <openssl/opensslconf.h>
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
# include <openssl/decoder.h>
#endif
/* Specify whether server supports session reuse using session ID. */
#define SERVER_SUPPORT_SESSION_REUSE 1
/* Specify whether server should disable session tickets. */
#define SERVER_DISABLE_SESSION_TICKETS 1
/* Each server application must set its own session id context,
* which is used to distinguish the contexts and is stored in
* exported sessions.
*/
#ifndef SERVER_SESSION_ID_CONTEXT
# define SERVER_SESSION_ID_CONTEXT 999
#endif
/* Server session timeout duration. Default is 300 sec. */
#define SERVER_SESSION_TIMEOUT 300
#if defined(LIBRESSL_VERSION_NUMBER)
# define USING_LIBRESSL 1
#else
# define USING_LIBRESSL 0
#endif
#if defined(OPENSSL_IS_BORINGSSL)
# define USING_BORINGSSL 1
# define TLSEXT_nid_unknown 0x1000000
#undef SSL_CTRL_SET_ECDH_AUTO
# define SSL_CTRL_SET_ECDH_AUTO 94
#else
# define USING_BORINGSSL 0
#endif
#if !USING_LIBRESSL && !defined(OPENSSL_NO_EC) \
&& OPENSSL_VERSION_NUMBER >= 0x1000200fL
# include <openssl/obj_mac.h>
static const unsigned nid_cid_map[] = {
NID_sect163k1, /* sect163k1 (1) */
NID_sect163r1, /* sect163r1 (2) */
NID_sect163r2, /* sect163r2 (3) */
NID_sect193r1, /* sect193r1 (4) */
NID_sect193r2, /* sect193r2 (5) */
NID_sect233k1, /* sect233k1 (6) */
NID_sect233r1, /* sect233r1 (7) */
NID_sect239k1, /* sect239k1 (8) */
NID_sect283k1, /* sect283k1 (9) */
NID_sect283r1, /* sect283r1 (10) */
NID_sect409k1, /* sect409k1 (11) */
NID_sect409r1, /* sect409r1 (12) */
NID_sect571k1, /* sect571k1 (13) */
NID_sect571r1, /* sect571r1 (14) */
NID_secp160k1, /* secp160k1 (15) */
NID_secp160r1, /* secp160r1 (16) */
NID_secp160r2, /* secp160r2 (17) */
NID_secp192k1, /* secp192k1 (18) */
NID_X9_62_prime192v1, /* secp192r1 (19) */
NID_secp224k1, /* secp224k1 (20) */
NID_secp224r1, /* secp224r1 (21) */
NID_secp256k1, /* secp256k1 (22) */
NID_X9_62_prime256v1, /* secp256r1 (23) */
NID_secp384r1, /* secp384r1 (24) */
NID_secp521r1, /* secp521r1 (25) */
NID_brainpoolP256r1, /* brainpoolP256r1 (26) */
NID_brainpoolP384r1, /* brainpoolP384r1 (27) */
NID_brainpoolP512r1 /* brainpoolP512r1 (28) */
};
static unsigned get_cid_from_nid(unsigned nid)
{
unsigned i, cid = 0;
for (i=0; i<PJ_ARRAY_SIZE(nid_cid_map); ++i) {
if (nid == nid_cid_map[i]) {
cid = i+1;
break;
}
}
return cid;
}
static unsigned get_nid_from_cid(unsigned cid)
{
if ((cid == 0) || (cid > PJ_ARRAY_SIZE(nid_cid_map)))
return 0;
return nid_cid_map[cid-1];
}
#endif
static void update_certs_info(pj_ssl_sock_t* ssock,
X509_STORE_CTX* ctx,
pj_ssl_cert_info *local_cert_info,
pj_ssl_cert_info *remote_cert_info,
pj_bool_t is_verify);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
# define OPENSSL_NO_SSL2 /* seems to be removed in 1.1.0 */
# ifndef M_ASN1_STRING_data
# define M_ASN1_STRING_data(x) ASN1_STRING_get0_data(x)
# define M_ASN1_STRING_length(x) ASN1_STRING_length(x)
# endif
# if defined(OPENSSL_API_COMPAT) && OPENSSL_API_COMPAT >= 0x10100000L || \
defined(OPENSSL_NO_DEPRECATED)
# define X509_get_notBefore(x) X509_get0_notBefore(x)
# define X509_get_notAfter(x) X509_get0_notAfter(x)
# if OPENSSL_VERSION_NUMBER >= 0x30000000L
# if defined(OPENSSL_API_COMPAT) && OPENSSL_API_COMPAT >= 0x30000000L || \
defined(OPENSSL_NO_DEPRECATED)
# define SSL_get_peer_certificate(x) SSL_get1_peer_certificate(x)
# endif
# endif
# endif
#else
# define SSL_CIPHER_get_id(c) (c)->id
# define SSL_set_session(ssl, s) (ssl)->session = (s)
# define X509_STORE_CTX_get0_cert(ctx) ((ctx)->cert)
#endif
#ifdef _MSC_VER
# if OPENSSL_VERSION_NUMBER >= 0x10100000L
# pragma comment(lib, "libcrypto")
# pragma comment(lib, "libssl")
# pragma comment(lib, "crypt32")
# else
# pragma comment(lib, "libeay32")
# pragma comment(lib, "ssleay32")
# endif
#endif
#if defined(PJ_WIN32) && PJ_WIN32 != 0 || \
defined(PJ_WIN64) && PJ_WIN64 != 0
# ifdef _MSC_VER
# define strerror_r(err,buf,len) strerror_s(buf,len,err)
# else
# define strerror_r(err,buf,len) pj_ansi_strxcpy(buf,strerror(err),len)
# endif
#endif
/* Suppress compile warning of OpenSSL deprecation (OpenSSL is deprecated
* since MacOSX 10.7).
*/
#if defined(PJ_DARWINOS) && PJ_DARWINOS==1
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
/*
* Secure socket structure definition.
*/
typedef struct ossl_sock_t
{
pj_ssl_sock_t base;
SSL_CTX *ossl_ctx;
pj_bool_t own_ctx;
SSL *ossl_ssl;
BIO *ossl_rbio;
BIO *ossl_wbio;
} ossl_sock_t;
#include "ssl_sock_imp_common.c"
/**
* Mapping from OpenSSL error codes to pjlib error space.
*/
#define PJ_SSL_ERRNO_START (PJ_ERRNO_START_USER + \
PJ_ERRNO_SPACE_SIZE*6)
#define PJ_SSL_ERRNO_SPACE_SIZE PJ_ERRNO_SPACE_SIZE
/* Expected maximum value of reason component in OpenSSL error code */
#define MAX_OSSL_ERR_REASON 1200
static char *SSLErrorString (int err)
{
switch (err) {
case SSL_ERROR_NONE:
return "SSL_ERROR_NONE";
case SSL_ERROR_ZERO_RETURN:
return "SSL_ERROR_ZERO_RETURN";
case SSL_ERROR_WANT_READ:
return "SSL_ERROR_WANT_READ";
case SSL_ERROR_WANT_WRITE:
return "SSL_ERROR_WANT_WRITE";
case SSL_ERROR_WANT_CONNECT:
return "SSL_ERROR_WANT_CONNECT";
case SSL_ERROR_WANT_ACCEPT:
return "SSL_ERROR_WANT_ACCEPT";
case SSL_ERROR_WANT_X509_LOOKUP:
return "SSL_ERROR_WANT_X509_LOOKUP";
case SSL_ERROR_SYSCALL:
return "SSL_ERROR_SYSCALL";
case SSL_ERROR_SSL:
return "SSL_ERROR_SSL";
default:
return "SSL_ERROR_UNKNOWN";
}
}
# if OPENSSL_VERSION_NUMBER >= 0x30000000L
#define ERROR_LOG(msg, err, ssock) \
{ \
char err_str[PJ_ERR_MSG_SIZE]; \
char buf[PJ_INET6_ADDRSTRLEN + 10]; \
ERR_error_string_n(err, err_str, sizeof(err_str)); \
PJ_LOG(2,("SSL", "%s (%s): Level: %d err: <%lu> <%s> len: %d peer: %s", \
msg, action, level, err, err_str, len, \
(ssock && pj_sockaddr_has_addr(&ssock->rem_addr)? \
pj_sockaddr_print(&ssock->rem_addr, buf, sizeof(buf), 3):"???")));\
}
# else
#define ERROR_LOG(msg, err, ssock) \
{ \
char buf[PJ_INET6_ADDRSTRLEN+10]; \
PJ_LOG(2,("SSL", "%s (%s): Level: %d err: <%lu> <%s-%s-%s> len: %d " \
"peer: %s", \
msg, action, level, err, \
(ERR_lib_error_string(err)? ERR_lib_error_string(err): "???"), \
(ERR_func_error_string(err)? ERR_func_error_string(err):"???"),\
(ERR_reason_error_string(err)? \
ERR_reason_error_string(err): "???"), len, \
(ssock && pj_sockaddr_has_addr(&ssock->rem_addr)? \
pj_sockaddr_print(&ssock->rem_addr, buf, sizeof(buf), 3):"???")));\
}
# endif
static void SSLLogErrors(char * action, int ret, int ssl_err, int len,
pj_ssl_sock_t *ssock)
{
char *ssl_err_str = SSLErrorString(ssl_err);
if (!action) {
action = "UNKNOWN";
}
switch (ssl_err) {
case SSL_ERROR_SYSCALL:
{
unsigned long err2 = ERR_get_error();
if (err2) {
int level = 0;
while (err2) {
ERROR_LOG("SSL_ERROR_SYSCALL", err2, ssock);
level++;
err2 = ERR_get_error();
}
} else if (ret == 0) {
/* An EOF was observed that violates the protocol */
/* The TLS/SSL handshake was not successful but was shut down
* controlled and by the specifications of the TLS/SSL protocol.
*/
} else if (ret == -1) {
/* BIO error - look for more info in errno... */
char errStr[250] = "";
strerror_r(errno, errStr, sizeof(errStr));
/* for now - continue logging these if they occur.... */
PJ_LOG(4,("SSL", "BIO error, SSL_ERROR_SYSCALL (%s): "
"errno: <%d> <%s> len: %d",
action, errno, errStr, len));
} else {
/* ret!=0 & ret!=-1 & nothing on error stack - is this valid??? */
PJ_LOG(2,("SSL", "SSL_ERROR_SYSCALL (%s) ret: %d len: %d",
action, ret, len));
}
break;
}
case SSL_ERROR_SSL:
{
unsigned long err2 = ERR_get_error();
int level = 0;
while (err2) {
unsigned long next_err;
ERROR_LOG("SSL_ERROR_SSL", err2, ssock);
level++;
do {
next_err = ERR_get_error();
} while (next_err == err2);
err2 = next_err;
}
break;
}
default:
PJ_LOG(2,("SSL", "%d [%s] (%s) ret: %d len: %d",
ssl_err, ssl_err_str, action, ret, len));
break;
}
}
static pj_status_t GET_STATUS_FROM_SSL_ERR(unsigned long err)
{
pj_status_t status;
/* OpenSSL error range is much wider than PJLIB errno space, so
* if it exceeds the space, only the error reason will be kept.
* Note that the last native error will be kept as is and can be
* retrieved via SSL socket info.
*/
status = ERR_GET_LIB(err)*MAX_OSSL_ERR_REASON + ERR_GET_REASON(err);
if (status > PJ_SSL_ERRNO_SPACE_SIZE)
status = ERR_GET_REASON(err);
status += PJ_SSL_ERRNO_START;
return status;
}
/* err contains ERR_get_error() status */
static pj_status_t STATUS_FROM_SSL_ERR(char *action, pj_ssl_sock_t *ssock,
unsigned long err)
{
int level = 0;
int len = 0; //dummy
ERROR_LOG("STATUS_FROM_SSL_ERR", err, ssock);
level++;
/* General SSL error, dig more from OpenSSL error queue */
if (err == SSL_ERROR_SSL) {
err = ERR_get_error();
ERROR_LOG("STATUS_FROM_SSL_ERR", err, ssock);
}
if (ssock)
ssock->last_err = err;
return GET_STATUS_FROM_SSL_ERR(err);
}
/* err contains SSL_get_error() status */
static pj_status_t STATUS_FROM_SSL_ERR2(char *action, pj_ssl_sock_t *ssock,
int ret, int err, int len)
{
unsigned long ssl_err = err;
if (err == SSL_ERROR_SSL) {
ssl_err = ERR_peek_error();
}
/* Dig for more from OpenSSL error queue */
SSLLogErrors(action, ret, err, len, ssock);
if (ssock)
ssock->last_err = ssl_err;
return GET_STATUS_FROM_SSL_ERR(ssl_err);
}
static pj_status_t GET_SSL_STATUS(pj_ssl_sock_t *ssock)
{
return STATUS_FROM_SSL_ERR("status", ssock, ERR_get_error());
}
/*
* Get error string of OpenSSL.
*/
static pj_str_t ssl_strerror(pj_status_t status,
char *buf, pj_size_t bufsize)
{
pj_str_t errstr;
unsigned long ssl_err = status;
if (ssl_err) {
unsigned long l, r;
ssl_err -= PJ_SSL_ERRNO_START;
l = ssl_err / MAX_OSSL_ERR_REASON;
r = ssl_err % MAX_OSSL_ERR_REASON;
#if USING_BORINGSSL
ssl_err = ERR_PACK(l, r);
#else
ssl_err = ERR_PACK(l, 0, r);
#endif
}
#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
{
const char *tmp = NULL;
tmp = ERR_reason_error_string(ssl_err);
if (tmp) {
pj_ansi_strxcpy(buf, tmp, bufsize);
errstr = pj_str(buf);
return errstr;
}
}
#endif /* PJ_HAS_ERROR_STRING */
errstr.ptr = buf;
errstr.slen = pj_ansi_snprintf(buf, bufsize,
"Unknown OpenSSL error %lu",
ssl_err);
if (errstr.slen < 1 || errstr.slen >= (int)bufsize)
errstr.slen = bufsize - 1;
return errstr;
}
/* Additional ciphers recognized by SSL_set_cipher_list()
but not returned from SSL_get_ciphers().
NOTE: ids are designed to not conflict with those from
SSL_get_cipher() which get masked to the lower 24
bits before use.
*/
static const struct ssl_ciphers_t ADDITIONAL_CIPHERS[] = {
{0xFF000000, "DEFAULT"},
{0xFF000001, "@SECLEVEL=0"},
{0xFF000002, "@SECLEVEL=1"},
{0xFF000003, "@SECLEVEL=2"},
{0xFF000004, "@SECLEVEL=3"},
{0xFF000005, "@SECLEVEL=4"},
{0xFF000006, "@SECLEVEL=5"}
};
static const unsigned int ADDITIONAL_CIPHER_COUNT =
sizeof (ADDITIONAL_CIPHERS) / sizeof (ADDITIONAL_CIPHERS[0]);
/*
*******************************************************************
* I/O functions.
*******************************************************************
*/
static pj_bool_t io_empty(pj_ssl_sock_t *ssock, circ_buf_t *cb)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
PJ_UNUSED_ARG(cb);
return !BIO_pending(ossock->ossl_wbio);
}
static pj_size_t io_size(pj_ssl_sock_t *ssock, circ_buf_t *cb)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
char *data;
PJ_UNUSED_ARG(cb);
return BIO_get_mem_data(ossock->ossl_wbio, &data);
}
static void io_read(pj_ssl_sock_t *ssock, circ_buf_t *cb,
pj_uint8_t *dst, pj_size_t len)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
char *data;
PJ_UNUSED_ARG(cb);
BIO_get_mem_data(ossock->ossl_wbio, &data);
pj_memcpy(dst, data, len);
/* Reset write BIO */
(void)BIO_reset(ossock->ossl_wbio);
}
static pj_status_t io_write(pj_ssl_sock_t *ssock, circ_buf_t *cb,
const pj_uint8_t *src, pj_size_t len)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
int nwritten;
nwritten = BIO_write(ossock->ossl_rbio, src, (int)len);
return (nwritten < (int)len)? GET_SSL_STATUS(cb->owner): PJ_SUCCESS;
}
/*
*******************************************************************
*/
/* OpenSSL library initialization counter */
static int openssl_init_count;
/* OpenSSL application data index */
static int sslsock_idx;
#if defined(PJ_SSL_SOCK_OSSL_USE_THREAD_CB) && \
PJ_SSL_SOCK_OSSL_USE_THREAD_CB != 0 && OPENSSL_VERSION_NUMBER < 0x10100000L
/* Thread lock pool.*/
static pj_caching_pool cp;
static pj_pool_t *lock_pool;
/* OpenSSL locking list. */
static pj_lock_t **ossl_locks;
/* OpenSSL number locks. */
static unsigned ossl_num_locks;
#if OPENSSL_VERSION_NUMBER >= 0x10000000
static void ossl_set_thread_id(CRYPTO_THREADID *id)
{
CRYPTO_THREADID_set_numeric(id,
(unsigned long)pj_thread_get_os_handle(pj_thread_this()));
}
#else
static unsigned long ossl_thread_id(void)
{
return ((unsigned long)pj_thread_get_os_handle(pj_thread_this()));
}
#endif
static void ossl_lock(int mode, int id, const char *file, int line)
{
PJ_UNUSED_ARG(file);
PJ_UNUSED_ARG(line);
if (openssl_init_count == 0)
return;
if (mode & CRYPTO_LOCK) {
if (ossl_locks[id]) {
//PJ_LOG(6, (THIS_FILE, "Lock File (%s) Line(%d)", file, line));
pj_lock_acquire(ossl_locks[id]);
}
} else {
if (ossl_locks[id]) {
//PJ_LOG(6, (THIS_FILE, "Unlock File (%s) Line(%d)", file, line));
pj_lock_release(ossl_locks[id]);
}
}
}
static void release_thread_cb(void)
{
unsigned i = 0;
#if OPENSSL_VERSION_NUMBER >= 0x10000000
CRYPTO_THREADID_set_callback(NULL);
#else
CRYPTO_set_id_callback(NULL);
#endif
CRYPTO_set_locking_callback(NULL);
for (; i < ossl_num_locks; ++i) {
if (ossl_locks[i]) {
pj_lock_destroy(ossl_locks[i]);
ossl_locks[i] = NULL;
}
}
if (lock_pool) {
pj_pool_release(lock_pool);
lock_pool = NULL;
pj_caching_pool_destroy(&cp);
}
ossl_locks = NULL;
ossl_num_locks = 0;
}
static pj_status_t init_ossl_lock()
{
pj_status_t status = PJ_SUCCESS;
pj_caching_pool_init(&cp, NULL, 0);
lock_pool = pj_pool_create(&cp.factory,
"ossl-lock",
64,
64,
NULL);
if (!lock_pool) {
status = PJ_ENOMEM;
PJ_PERROR(1, (THIS_FILE, status,"Fail creating OpenSSL lock pool"));
pj_caching_pool_destroy(&cp);
return status;
}
ossl_num_locks = CRYPTO_num_locks();
ossl_locks = (pj_lock_t **)pj_pool_calloc(lock_pool,
ossl_num_locks,
sizeof(pj_lock_t*));
if (ossl_locks) {
unsigned i = 0;
for (; (i < ossl_num_locks) && (status == PJ_SUCCESS); ++i) {
status = pj_lock_create_simple_mutex(lock_pool, "ossl_lock%p",
&ossl_locks[i]);
}
if (status != PJ_SUCCESS) {
PJ_PERROR(1, (THIS_FILE, status,
"Fail creating mutex for OpenSSL lock"));
release_thread_cb();
return status;
}
#if OPENSSL_VERSION_NUMBER >= 0x10000000
CRYPTO_THREADID_set_callback(ossl_set_thread_id);
#else
CRYPTO_set_id_callback(ossl_thread_id);
#endif
CRYPTO_set_locking_callback(ossl_lock);
status = pj_atexit(&release_thread_cb);
if (status != PJ_SUCCESS) {
PJ_PERROR(1, (THIS_FILE, status, "Warning! Unable to set OpenSSL "
"lock thread callback unrelease method."));
}
} else {
status = PJ_ENOMEM;
PJ_PERROR(1, (THIS_FILE, status,"Fail creating OpenSSL locks"));
release_thread_cb();
}
return status;
}
#endif
/* Initialize OpenSSL */
static pj_status_t init_openssl(void)
{
pj_status_t status;
if (openssl_init_count)
return PJ_SUCCESS;
openssl_init_count = 1;
PJ_LOG(4, (THIS_FILE, "OpenSSL version : %ld", OPENSSL_VERSION_NUMBER));
/* Register error subsystem */
status = pj_register_strerror(PJ_SSL_ERRNO_START,
PJ_SSL_ERRNO_SPACE_SIZE,
&ssl_strerror);
pj_assert(status == PJ_SUCCESS);
/* Init OpenSSL lib */
#if USING_LIBRESSL || OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_library_init();
SSL_load_error_strings();
#else
OPENSSL_init_ssl(0, NULL);
#endif
#if OPENSSL_VERSION_NUMBER < 0x009080ffL
/* This is now synonym of SSL_library_init() */
OpenSSL_add_all_algorithms();
#endif
/* Init available ciphers */
if (ssl_cipher_num == 0 || ssl_curves_num == 0) {
SSL_METHOD *meth = NULL;
SSL_CTX *ctx;
SSL *ssl;
STACK_OF(SSL_CIPHER) *sk_cipher;
SSL_SESSION *ssl_sess;
unsigned i, n;
int nid;
const char *cname;
#if (USING_LIBRESSL && LIBRESSL_VERSION_NUMBER < 0x2020100fL)\
|| OPENSSL_VERSION_NUMBER < 0x10100000L
meth = (SSL_METHOD*)SSLv23_server_method();
if (!meth)
meth = (SSL_METHOD*)TLSv1_server_method();
#ifndef OPENSSL_NO_SSL3_METHOD
if (!meth)
meth = (SSL_METHOD*)SSLv3_server_method();
#endif
#ifndef OPENSSL_NO_SSL2
if (!meth)
meth = (SSL_METHOD*)SSLv2_server_method();
#endif
#else
/* Specific version methods are deprecated in 1.1.0 */
meth = (SSL_METHOD*)TLS_method();
#endif
pj_assert(meth);
ctx=SSL_CTX_new(meth);
SSL_CTX_set_cipher_list(ctx, "ALL:COMPLEMENTOFALL");
ssl = SSL_new(ctx);
sk_cipher = SSL_get_ciphers(ssl);
n = sk_SSL_CIPHER_num(sk_cipher);
if (n > PJ_ARRAY_SIZE(ssl_ciphers) - ADDITIONAL_CIPHER_COUNT)
n = PJ_ARRAY_SIZE(ssl_ciphers) - ADDITIONAL_CIPHER_COUNT;
for (i = 0; i < n; ++i) {
const SSL_CIPHER *c;
c = sk_SSL_CIPHER_value(sk_cipher,i);
ssl_ciphers[i].id = (pj_ssl_cipher)
(pj_uint32_t)SSL_CIPHER_get_id(c) &
0x00FFFFFF;
ssl_ciphers[i].name = SSL_CIPHER_get_name(c);
}
/* Add cipher aliases not returned from SSL_get_ciphers() */
for (i = 0; i < ADDITIONAL_CIPHER_COUNT; ++i) {
ssl_ciphers[n++] = ADDITIONAL_CIPHERS[i];
}
ssl_cipher_num = n;
#if USING_BORINGSSL
ssl_sess = SSL_SESSION_new(ctx);
#else
ssl_sess = SSL_SESSION_new();
#endif
SSL_set_session(ssl, ssl_sess);
#if !USING_LIBRESSL && !defined(OPENSSL_NO_EC) \
&& OPENSSL_VERSION_NUMBER >= 0x1000200fL
#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
ssl_curves_num = EC_get_builtin_curves(NULL, 0);
#else
#if USING_BORINGSSL
ssl_curves_num = SSL_get_curve_id(ssl);
#else
ssl_curves_num = SSL_get_shared_curve(ssl,-1);
#endif
if (ssl_curves_num > PJ_ARRAY_SIZE(ssl_curves))
ssl_curves_num = PJ_ARRAY_SIZE(ssl_curves);
#endif
if( ssl_curves_num > 0 ) {
#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
EC_builtin_curve * curves = NULL;
curves = OPENSSL_malloc((int)sizeof(*curves) * ssl_curves_num);
if (!EC_get_builtin_curves(curves, ssl_curves_num)) {
OPENSSL_free(curves);
curves = NULL;
ssl_curves_num = 0;
}
n = ssl_curves_num;
ssl_curves_num = 0;
for (i = 0; i < n; i++) {
nid = curves[i].nid;
if ( 0 != get_cid_from_nid(nid) ) {
cname = OBJ_nid2sn(nid);
if (!cname)
cname = OBJ_nid2sn(nid);
if (cname) {
ssl_curves[ssl_curves_num].id = get_cid_from_nid(nid);
ssl_curves[ssl_curves_num].name = cname;
ssl_curves_num++;
if (ssl_curves_num >= PJ_SSL_SOCK_MAX_CURVES )
break;
}
}
}
if(curves)
OPENSSL_free(curves);
#else
for (i = 0; i < ssl_curves_num; i++) {
#if USING_BORINGSSL
nid = SSL_get_curve_id(ssl);
#else
nid = SSL_get_shared_curve(ssl, i);
#endif
if (nid & TLSEXT_nid_unknown) {
cname = "curve unknown";
nid &= 0xFFFF;
} else {
cname = EC_curve_nid2nist(nid);
if (!cname)
cname = OBJ_nid2sn(nid);
}
ssl_curves[i].id = get_cid_from_nid(nid);
ssl_curves[i].name = cname;
}
#endif
}
#else
PJ_UNUSED_ARG(nid);
PJ_UNUSED_ARG(cname);
ssl_curves_num = 0;
#endif
SSL_free(ssl);
/* On OpenSSL 1.1.1, omitting SSL_SESSION_free() will cause
* memory leak (e.g: as reported by Address Sanitizer). But using
* SSL_SESSION_free() may cause crash (due to double free?) on 1.0.x.
* As OpenSSL docs specifies to not calling SSL_SESSION_free() after
* SSL_free(), perhaps it is safer to obey this, the leak amount seems
* to be relatively small (<500 bytes) and should occur once only in
* the library lifetime.
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
SSL_SESSION_free(ssl_sess);
#endif
*/
SSL_CTX_free(ctx);
}
/* Create OpenSSL application data index for SSL socket */
sslsock_idx = SSL_get_ex_new_index(0, "SSL socket", NULL, NULL, NULL);
if (sslsock_idx == -1) {
status = STATUS_FROM_SSL_ERR2("Init", NULL, -1, ERR_get_error(), 0);
PJ_LOG(1,(THIS_FILE,
"Fatal error: failed to get application data index for "
"SSL socket"));
return status;
}
#if defined(PJ_SSL_SOCK_OSSL_USE_THREAD_CB) && \
PJ_SSL_SOCK_OSSL_USE_THREAD_CB != 0 && OPENSSL_VERSION_NUMBER < 0x10100000L
status = init_ossl_lock();
if (status != PJ_SUCCESS)
return status;
#endif
return status;
}
/* Shutdown OpenSSL */
static void shutdown_openssl(void)
{
PJ_UNUSED_ARG(openssl_init_count);
}
/* SSL password callback. */
static int password_cb(char *buf, int num, int rwflag, void *user_data)
{
pj_ssl_cert_t *cert = (pj_ssl_cert_t*) user_data;
PJ_UNUSED_ARG(rwflag);
if(num < cert->privkey_pass.slen)
return 0;
pj_memcpy(buf, cert->privkey_pass.ptr, cert->privkey_pass.slen);
return (int)cert->privkey_pass.slen;
}
/* SSL certificate verification result callback.
* Note that this callback seems to be always called from library worker
* thread, e.g: active socket on_read_complete callback, which should have
* already been equipped with race condition avoidance mechanism (should not
* be destroyed while callback is being invoked).
*/
static int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
pj_ssl_sock_t *ssock = NULL;
SSL *ossl_ssl = NULL;
int err;
/* Get SSL instance */
ossl_ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
SSL_get_ex_data_X509_STORE_CTX_idx());
if (!ossl_ssl) {
PJ_LOG(1,(THIS_FILE,
"SSL verification callback failed to get SSL instance"));
goto on_return;
}
/* Get SSL socket instance */
ssock = SSL_get_ex_data(ossl_ssl, sslsock_idx);
if (!ssock) {
/* SSL socket may have been destroyed */
PJ_LOG(1,(THIS_FILE,
"SSL verification callback failed to get SSL socket "
"instance (sslsock_idx=%d).", sslsock_idx));
goto on_return;
}
/* Store verification status */
err = X509_STORE_CTX_get_error(x509_ctx);
switch (err) {
case X509_V_OK:
break;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
ssock->verify_status |= PJ_SSL_CERT_EISSUER_NOT_FOUND;
break;
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT;
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_CERT_HAS_EXPIRED:
ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD;
break;
case X509_V_ERR_UNABLE_TO_GET_CRL:
case X509_V_ERR_CRL_NOT_YET_VALID:
case X509_V_ERR_CRL_HAS_EXPIRED:
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
ssock->verify_status |= PJ_SSL_CERT_ECRL_FAILURE;
break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
case X509_V_ERR_CERT_UNTRUSTED:
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED;
break;
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
case X509_V_ERR_AKID_SKID_MISMATCH:
case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH;
break;
case X509_V_ERR_CERT_REVOKED:
ssock->verify_status |= PJ_SSL_CERT_EREVOKED;
break;
case X509_V_ERR_INVALID_PURPOSE:
case X509_V_ERR_CERT_REJECTED:
case X509_V_ERR_INVALID_CA:
ssock->verify_status |= PJ_SSL_CERT_EINVALID_PURPOSE;
break;
case X509_V_ERR_CERT_CHAIN_TOO_LONG: /* not really used */
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
ssock->verify_status |= PJ_SSL_CERT_ECHAIN_TOO_LONG;
break;
/* Unknown errors */
case X509_V_ERR_OUT_OF_MEM:
default:
ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
break;
}
/* Invoke app's verification callback */
if (ssock->param.cb.on_verify_cb) {
update_certs_info(ssock, x509_ctx, &ssock->local_cert_info,
&ssock->remote_cert_info, PJ_TRUE);
preverify_ok = (*ssock->param.cb.on_verify_cb)(ssock,
ssock->is_server);
goto on_return;
}
/* When verification is not requested just return ok here, however
* application can still get the verification status.
*/
if (PJ_FALSE == ssock->param.verify_peer)
preverify_ok = 1;
on_return:
return preverify_ok;
}
/* Setting SSL sock cipher list */
static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock);
/* Setting SSL sock curves list */
static pj_status_t set_curves_list(pj_ssl_sock_t *ssock);
/* Setting sigalgs list */
static pj_status_t set_sigalgs(pj_ssl_sock_t *ssock);
/* Setting entropy for rng */
static void set_entropy(pj_ssl_sock_t *ssock);
static pj_ssl_sock_t *ssl_alloc(pj_pool_t *pool)
{
return (pj_ssl_sock_t *)PJ_POOL_ZALLOC_T(pool, ossl_sock_t);
}
#if !USING_BORINGSSL
static int xname_cmp(const X509_NAME * const *a, const X509_NAME * const *b) {
return X509_NAME_cmp(*a, *b);
}
#else
static int xname_cmp(const X509_NAME **a, const X509_NAME **b) {
return X509_NAME_cmp(*a, *b);
}
#endif
#if !defined(OPENSSL_NO_DH)
static void set_option(const pj_ssl_sock_t* ssock, SSL_CTX* ctx) {
unsigned long options = SSL_OP_CIPHER_SERVER_PREFERENCE |
#if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L
SSL_OP_SINGLE_ECDH_USE |
#endif
SSL_OP_SINGLE_DH_USE;
options = SSL_CTX_set_options(ctx, options);
PJ_LOG(4, (ssock->pool->obj_name, "SSL DH "
"initialized, PFS cipher-suites enabled"));
}
static void set_dh_use_option(BIO *bio, const pj_ssl_sock_t* ssock,
const pj_str_t *pass, SSL_CTX* ctx)
{
#if OPENSSL_VERSION_NUMBER < 0x30000000L
DH* dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
if (dh != NULL) {
if (SSL_CTX_set_tmp_dh(ctx, dh)) {
set_option(ssock, ctx);
}
DH_free(dh);
}
PJ_UNUSED_ARG(pass);
#else
OSSL_DECODER_CTX* dctx;
EVP_PKEY* dh_pkey = NULL;
const char* format = "PEM";
const char* structure = NULL;
const char* keytype = NULL;
dctx = OSSL_DECODER_CTX_new_for_pkey(&dh_pkey, format, structure, keytype,
0, NULL, NULL);
if (dctx != NULL) {
if (pass->slen) {
OSSL_DECODER_CTX_set_passphrase(dctx,
(const unsigned char*)pass->ptr,
pass->slen);
}
if (OSSL_DECODER_from_bio(dctx, bio)) {
if (SSL_CTX_set0_tmp_dh_pkey(ctx, dh_pkey)) {
set_option(ssock, ctx);
}
}
OSSL_DECODER_CTX_free(dctx);
}
#endif
}
#endif
/* Initialize OpenSSL context for the ssock */
static pj_status_t init_ossl_ctx(pj_ssl_sock_t *ssock)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
SSL_CTX *ctx = NULL;
SSL_METHOD *ssl_method = NULL;
pj_uint32_t ssl_opt = 0;
pj_ssl_cert_t *cert = ssock->cert;
int rc;
pj_status_t status;
if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT)
ssock->param.proto = PJ_SSL_SOCK_PROTO_SSL23;
/* Determine SSL method to use */
/* Specific version methods are deprecated since 1.1.0 */
#if (USING_LIBRESSL && LIBRESSL_VERSION_NUMBER < 0x2020100fL)\
|| OPENSSL_VERSION_NUMBER < 0x10100000L
switch (ssock->param.proto) {
case PJ_SSL_SOCK_PROTO_TLS1:
ssl_method = (SSL_METHOD*)TLSv1_method();
break;
#ifndef OPENSSL_NO_SSL2
case PJ_SSL_SOCK_PROTO_SSL2:
ssl_method = (SSL_METHOD*)SSLv2_method();
break;
#endif
#ifndef OPENSSL_NO_SSL3_METHOD
case PJ_SSL_SOCK_PROTO_SSL3:
ssl_method = (SSL_METHOD*)SSLv3_method();
#endif
break;
}
#endif
if (!ssl_method) {
#if (USING_LIBRESSL && LIBRESSL_VERSION_NUMBER < 0x2020100fL)\
|| OPENSSL_VERSION_NUMBER < 0x10100000L
ssl_method = (SSL_METHOD*)SSLv23_method();
#else
ssl_method = (SSL_METHOD*)TLS_method();
#endif
#ifdef SSL_OP_NO_SSLv2
/** Check if SSLv2 is enabled */
ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL2)==0)?
SSL_OP_NO_SSLv2:0;
#endif
#ifdef SSL_OP_NO_SSLv3
/** Check if SSLv3 is enabled */
ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL3)==0)?
SSL_OP_NO_SSLv3:0;
#endif
#ifdef SSL_OP_NO_TLSv1
/** Check if TLSv1 is enabled */
ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1)==0)?
SSL_OP_NO_TLSv1:0;
#endif
#ifdef SSL_OP_NO_TLSv1_1
/** Check if TLSv1_1 is enabled */
ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_1)==0)?
SSL_OP_NO_TLSv1_1:0;
#endif
#ifdef SSL_OP_NO_TLSv1_2
/** Check if TLSv1_2 is enabled */
ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2)==0)?
SSL_OP_NO_TLSv1_2:0;
#endif
#ifdef SSL_OP_NO_TLSv1_3
/** Check if TLSv1_3 is enabled */
ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3)==0)?
SSL_OP_NO_TLSv1_3:0;
#endif
}
ossock->ossl_ctx = ctx = SSL_CTX_new(ssl_method);
if (ctx == NULL) {
return GET_SSL_STATUS(ssock);
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (ssock->param.proto <= PJ_SSL_SOCK_PROTO_TLS1_1) {
/* TLS 1.0, TLS 1.1 no longer working at the default security
* level of 1 and instead requires security level 0. */
SSL_CTX_set_security_level(ossock->ossl_ctx, 0);
}
#endif
if (ssock->is_server) {
unsigned int sid_ctx = SERVER_SESSION_ID_CONTEXT;
#if SERVER_DISABLE_SESSION_TICKETS
/* Disable session tickets for TLSv1.2 and below. */
ssl_opt |= SSL_OP_NO_TICKET;
#ifdef SSL_CTX_set_num_tickets
/* Set the number of TLSv1.3 session tickets issued to 0. */
SSL_CTX_set_num_tickets(ctx, 0);
#endif
#endif
SSL_CTX_set_timeout(ctx, SERVER_SESSION_TIMEOUT);
if (!SSL_CTX_set_session_id_context(ctx,
(const unsigned char *)&sid_ctx, sizeof(sid_ctx)))
{
PJ_LOG(1, (THIS_FILE, "Warning! Unable to set server session id "
"context. Session reuse will not work."));
}
}
#ifdef SSL_OP_NO_RENEGOTIATION
if (!ssock->param.enable_renegotiation) {
ssl_opt |= SSL_OP_NO_RENEGOTIATION;
}
#endif
if (ssl_opt)
SSL_CTX_set_options(ctx, ssl_opt);
/* Set cipher list */
status = set_cipher_list(ssock);
if (status != PJ_SUCCESS) {
SSL_CTX_free(ctx);
ossock->ossl_ctx = NULL;
return status;
}
/* Apply credentials */
if (cert) {
/* Load CA list if one is specified. */
if (cert->CA_file.slen || cert->CA_path.slen) {
rc = SSL_CTX_load_verify_locations(
ctx,
cert->CA_file.slen == 0 ? NULL : cert->CA_file.ptr,
cert->CA_path.slen == 0 ? NULL : cert->CA_path.ptr);
if (rc != 1) {
status = GET_SSL_STATUS(ssock);
if (cert->CA_file.slen) {
PJ_PERROR(1,(ssock->pool->obj_name, status,
"Error loading CA list file '%s'",
cert->CA_file.ptr));
}
if (cert->CA_path.slen) {
PJ_PERROR(1,(ssock->pool->obj_name, status,
"Error loading CA path '%s'",
cert->CA_path.ptr));
}
SSL_CTX_free(ctx);
ossock->ossl_ctx = NULL;
return status;
} else {
PJ_LOG(4,(ssock->pool->obj_name,
"CA certificates loaded from '%s%s%s'",
cert->CA_file.ptr,
((cert->CA_file.slen && cert->CA_path.slen)?
" + ":""),
cert->CA_path.ptr));
}
}
/* Set password callback */
if (cert->privkey_pass.slen) {
SSL_CTX_set_default_passwd_cb(ctx, password_cb);
SSL_CTX_set_default_passwd_cb_userdata(ctx, cert);
}
/* Load certificate if one is specified */
if (cert->cert_file.slen) {
/* Load certificate chain from file into ctx */
rc = SSL_CTX_use_certificate_chain_file(ctx, cert->cert_file.ptr);
if(rc != 1) {
status = GET_SSL_STATUS(ssock);
PJ_PERROR(1,(ssock->pool->obj_name, status,
"Error loading certificate chain file '%s'",
cert->cert_file.ptr));
SSL_CTX_free(ctx);
ossock->ossl_ctx = NULL;
return status;
} else {
PJ_LOG(4,(ssock->pool->obj_name,
"Certificate chain loaded from '%s'",
cert->cert_file.ptr));
}
}
/* Load private key if one is specified */
if (cert->privkey_file.slen) {
/* Adds the first private key found in file to ctx */
rc = SSL_CTX_use_PrivateKey_file(ctx, cert->privkey_file.ptr,
SSL_FILETYPE_PEM);
if(rc != 1) {
status = GET_SSL_STATUS(ssock);
PJ_PERROR(1,(ssock->pool->obj_name, status,
"Error adding private key from '%s'",
cert->privkey_file.ptr));
SSL_CTX_free(ctx);
ossock->ossl_ctx = NULL;
return status;
} else {
PJ_LOG(4,(ssock->pool->obj_name,
"Private key loaded from '%s'",
cert->privkey_file.ptr));
}
#if !defined(OPENSSL_NO_DH)
if (ssock->is_server) {
BIO *bio = BIO_new_file(cert->privkey_file.ptr, "r");
if (bio != NULL) {
set_dh_use_option(bio, ssock, &cert->privkey_pass, ctx);
BIO_free(bio);
}
}
#endif
}
/* Load from buffer. */
if (cert->cert_buf.slen) {
BIO *cbio;
X509 *xcert = NULL;
cbio = BIO_new_mem_buf((void*)cert->cert_buf.ptr,
cert->cert_buf.slen);
if (cbio != NULL) {
xcert = PEM_read_bio_X509(cbio, NULL, 0, NULL);
if (xcert != NULL) {
rc = SSL_CTX_use_certificate(ctx, xcert);
if (rc != 1) {
status = GET_SSL_STATUS(ssock);
PJ_PERROR(1,(ssock->pool->obj_name, status,
"Error loading chain certificate from buffer"));
X509_free(xcert);
BIO_free(cbio);
SSL_CTX_free(ctx);
ossock->ossl_ctx = NULL;
return status;
} else {
PJ_LOG(4,(ssock->pool->obj_name,
"Certificate chain loaded from buffer"));
}
X509_free(xcert);
}
BIO_free(cbio);
}
}
if (cert->CA_buf.slen) {
BIO *cbio = BIO_new_mem_buf((void*)cert->CA_buf.ptr,
cert->CA_buf.slen);
X509_STORE *cts = SSL_CTX_get_cert_store(ctx);
if (cbio && cts) {
STACK_OF(X509_INFO) *inf = PEM_X509_INFO_read_bio(cbio, NULL,
NULL, NULL);
if (inf != NULL) {
int i = 0, cnt = 0;
for (; i < sk_X509_INFO_num(inf); i++) {
X509_INFO *itmp = sk_X509_INFO_value(inf, i);
if (!itmp->x509)
continue;
rc = X509_STORE_add_cert(cts, itmp->x509);
if (rc == 1) {
++cnt;
} else {
#if PJ_LOG_MAX_LEVEL >= 4
char buf[256];
PJ_LOG(4,(ssock->pool->obj_name,
"Error adding CA cert: %s",
X509_NAME_oneline(
X509_get_subject_name(itmp->x509),
buf, sizeof(buf))));
#endif
}
}
PJ_LOG(4,(ssock->pool->obj_name,
"CA certificates loaded from buffer (cnt=%d)",
cnt));
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
BIO_free(cbio);
}
}
if (cert->privkey_buf.slen) {
BIO *kbio;
EVP_PKEY *pkey = NULL;
kbio = BIO_new_mem_buf((void*)cert->privkey_buf.ptr,
cert->privkey_buf.slen);
if (kbio != NULL) {
pkey = PEM_read_bio_PrivateKey(kbio, NULL, &password_cb,
cert);
if (pkey) {
rc = SSL_CTX_use_PrivateKey(ctx, pkey);
if (rc != 1) {
status = GET_SSL_STATUS(ssock);
PJ_PERROR(1,(ssock->pool->obj_name, status,
"Error adding private key from buffer"));
EVP_PKEY_free(pkey);
BIO_free(kbio);
SSL_CTX_free(ctx);
ossock->ossl_ctx = NULL;
return status;
} else {
PJ_LOG(4,(ssock->pool->obj_name,
"Private key loaded from buffer"));
}
EVP_PKEY_free(pkey);
} else {
PJ_LOG(1,(ssock->pool->obj_name,
"Error reading private key from buffer"));
}
if (ssock->is_server) {
set_dh_use_option(kbio, ssock, &cert->privkey_pass, ctx);
}
BIO_free(kbio);
}
}
}
if (ssock->is_server) {
char *p = NULL;
/* If certificate file name contains "_rsa.", let's check if there are
* ecc and dsa certificates too.
*/
if (cert && cert->cert_file.slen) {
const pj_str_t RSA = {"_rsa.", 5};
p = pj_strstr(&cert->cert_file, &RSA);
if (p) p++; /* Skip underscore */
}
if (p) {
/* Certificate type string length must be exactly 3 */
enum { CERT_TYPE_LEN = 3 };
const char* cert_types[] = { "ecc", "dsa" };
char *cf = cert->cert_file.ptr;
int i;
/* Check and load ECC & DSA certificates & private keys */
for (i = 0; i < (int)PJ_ARRAY_SIZE(cert_types); ++i) {
int err;
pj_memcpy(p, cert_types[i], CERT_TYPE_LEN);
if (!pj_file_exists(cf))
continue;
err = SSL_CTX_use_certificate_chain_file(ctx, cf);
if (err == 1)
err = SSL_CTX_use_PrivateKey_file(ctx, cf,
SSL_FILETYPE_PEM);
if (err == 1) {
PJ_LOG(4,(ssock->pool->obj_name,
"Additional certificate '%s' loaded.", cf));
} else {
PJ_PERROR(1,(ssock->pool->obj_name, GET_SSL_STATUS(ssock),
"Error loading certificate file '%s'", cf));
ERR_clear_error();
}
}
/* Put back original name */
pj_memcpy(p, "rsa", CERT_TYPE_LEN);
}
#if USING_BORINGSSL
if (SSL_CTX_set_mode(ctx, SSL_CTRL_SET_ECDH_AUTO)) {
#else
#ifndef SSL_CTRL_SET_ECDH_AUTO
#define SSL_CTRL_SET_ECDH_AUTO 94
#endif
/* SSL_CTX_set_ecdh_auto(ctx,on) requires OpenSSL 1.0.2 which wraps: */
if (SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL)) {
#endif
PJ_LOG(4,(ssock->pool->obj_name, "SSL ECDH initialized "
"(automatic), faster PFS ciphers enabled"));
#if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L && \
OPENSSL_VERSION_NUMBER < 0x10100000L
} else {
/* enables AES-128 ciphers, to get AES-256 use NID_secp384r1 */
EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (ecdh != NULL) {
if (SSL_CTX_set_tmp_ecdh(ctx, ecdh)) {
PJ_LOG(4,(ssock->pool->obj_name, "SSL ECDH initialized "
"(secp256r1), faster PFS cipher-suites enabled"));
}
EC_KEY_free(ecdh);
}
#endif
}
} else {
X509_STORE *pkix_validation_store = SSL_CTX_get_cert_store(ctx);
if (NULL != pkix_validation_store) {
#if defined(X509_V_FLAG_TRUSTED_FIRST)
X509_STORE_set_flags(pkix_validation_store,
X509_V_FLAG_TRUSTED_FIRST);
#endif
#if defined(X509_V_FLAG_PARTIAL_CHAIN)
X509_STORE_set_flags(pkix_validation_store,
X509_V_FLAG_PARTIAL_CHAIN);
#endif
}
}
/* Add certificate authorities for clients from CA.
* Needed for certificate request during handshake.
*/
if (cert && ssock->is_server) {
STACK_OF(X509_NAME) *ca_dn = NULL;
if (cert->CA_file.slen > 0) {
ca_dn = SSL_load_client_CA_file(cert->CA_file.ptr);
} else if (cert->CA_buf.slen > 0) {
X509 *x = NULL;
X509_NAME *xn = NULL;
STACK_OF(X509_NAME) *sk = NULL;
BIO *new_bio = BIO_new_mem_buf((void*)cert->CA_buf.ptr,
cert->CA_buf.slen);
sk = sk_X509_NAME_new(xname_cmp);
if (sk != NULL && new_bio != NULL) {
for (;;) {
if (PEM_read_bio_X509(new_bio, &x, NULL, NULL) == NULL)
break;
if ((xn = X509_get_subject_name(x)) == NULL)
break;
if ((xn = X509_NAME_dup(xn)) == NULL )
break;
#if !USING_BORINGSSL
if (sk_X509_NAME_find(sk, xn) >= 0) {
#else
if (sk_X509_NAME_find(sk, NULL, xn) >= 0) {
#endif
X509_NAME_free(xn);
} else {
sk_X509_NAME_push(sk, xn);
}
X509_free(x);
x = NULL;
}
}
if (sk != NULL)
ca_dn = sk;
if (new_bio != NULL)
BIO_free(new_bio);
}
if (ca_dn != NULL) {
SSL_CTX_set_client_CA_list(ctx, ca_dn);
PJ_LOG(4,(ssock->pool->obj_name,
"CA certificates loaded from %s",
(cert->CA_file.slen?cert->CA_file.ptr:"buffer")));
} else {
PJ_LOG(1,(ssock->pool->obj_name,
"Error reading CA certificates from %s",
(cert->CA_file.slen?cert->CA_file.ptr:"buffer")));
}
}
/* Early sensitive data cleanup after OpenSSL context setup. However,
* this cannot be done for listener sockets, as the data will still
* be needed by accepted sockets.
*/
if (cert && (!ssock->is_server || ssock->parent)) {
pj_ssl_cert_wipe_keys(cert);
}
return PJ_SUCCESS;
}
/* Create and initialize new SSL context and instance */
static pj_status_t ssl_create(pj_ssl_sock_t *ssock)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
int mode;
pj_status_t status;
pj_assert(ssock);
/* Make sure OpenSSL library has been initialized */
init_openssl();
set_entropy(ssock);
if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT)
ssock->param.proto = PJ_SSL_SOCK_PROTO_SSL23;
/* Create SSL context */
if (SERVER_SUPPORT_SESSION_REUSE && ssock->is_server) {
SSL_CTX *server_ctx = ((ossl_sock_t *)ssock->parent)->ossl_ctx;
if (!server_ctx) {
status = init_ossl_ctx(ssock->parent);
if (status != PJ_SUCCESS)
return status;
server_ctx = ((ossl_sock_t *)ssock->parent)->ossl_ctx;
((ossl_sock_t *)ssock->parent)->own_ctx = PJ_TRUE;
}
ossock->ossl_ctx = server_ctx;
} else {
status = init_ossl_ctx(ssock);
if (status != PJ_SUCCESS)
return status;
}
/* Create SSL instance */
ossock->ossl_ssl = SSL_new(ossock->ossl_ctx);
if (ossock->ossl_ssl == NULL) {
return GET_SSL_STATUS(ssock);
}
/* Set SSL sock as application data of SSL instance */
SSL_set_ex_data(ossock->ossl_ssl, sslsock_idx, ssock);
/* SSL verification options */
mode = SSL_VERIFY_PEER;
if (ssock->is_server && ssock->param.require_client_cert)
mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
SSL_set_verify(ossock->ossl_ssl, mode, &verify_cb);
/* Set curve list */
status = set_curves_list(ssock);
if (status != PJ_SUCCESS)
return status;
/* Set sigalg list */
status = set_sigalgs(ssock);
if (status != PJ_SUCCESS)
return status;
/* Setup SSL BIOs */
ossock->ossl_rbio = BIO_new(BIO_s_mem());
ossock->ossl_wbio = BIO_new(BIO_s_mem());
(void)BIO_set_close(ossock->ossl_rbio, BIO_CLOSE);
(void)BIO_set_close(ossock->ossl_wbio, BIO_CLOSE);
SSL_set_bio(ossock->ossl_ssl, ossock->ossl_rbio, ossock->ossl_wbio);
return PJ_SUCCESS;
}
/* Destroy SSL context and instance */
static void ssl_destroy(pj_ssl_sock_t *ssock)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
/* Destroy SSL instance */
if (ossock->ossl_ssl) {
SSL_free(ossock->ossl_ssl); /* this will also close BIOs */
ossock->ossl_ssl = NULL;
}
/* Destroy SSL context */
if (ossock->ossl_ctx) {
if (ssock->is_server) {
if (!SERVER_SUPPORT_SESSION_REUSE || ossock->own_ctx)
SSL_CTX_free(ossock->ossl_ctx);
} else {
SSL_CTX_free(ossock->ossl_ctx);
}
ossock->ossl_ctx = NULL;
}
/* Potentially shutdown OpenSSL library if this is the last
* context exists.
*/
shutdown_openssl();
}
/* Reset SSL socket state */
static void ssl_reset_sock_state(pj_ssl_sock_t *ssock)
{
int post_unlock_flush_circ_buf = 0;
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
/* Must lock around SSL calls, particularly SSL_shutdown
* as it can modify the write BIOs and destructively
* interfere with any ssl_write() calls in progress
* above in a multithreaded environment */
pj_lock_acquire(ssock->write_mutex);
/* Detach from SSL instance */
if (ossock->ossl_ssl) {
SSL_set_ex_data(ossock->ossl_ssl, sslsock_idx, NULL);
}
/**
* Avoid calling SSL_shutdown() if handshake wasn't completed.
* OpenSSL 1.0.2f complains if SSL_shutdown() is called during an
* SSL handshake, while previous versions always return 0.
* Call SSL_shutdown() when there is a timeout handshake failure or
* the last error is not SSL_ERROR_SYSCALL and not SSL_ERROR_SSL.
*/
if (ossock->ossl_ssl && SSL_in_init(ossock->ossl_ssl) == 0) {
if (ssock->handshake_status == PJ_ETIMEDOUT ||
(ssock->last_err != SSL_ERROR_SYSCALL &&
ssock->last_err != SSL_ERROR_SSL))
{
int ret = SSL_shutdown(ossock->ossl_ssl);
if (ret == 0) {
/* SSL_shutdown will potentially trigger a bunch of
* data to dump to the socket */
post_unlock_flush_circ_buf = 1;
}
}
}
ssock->ssl_state = SSL_STATE_NULL;
pj_lock_release(ssock->write_mutex);
if (post_unlock_flush_circ_buf) {
/* Flush data to send close notify. */
flush_circ_buf_output(ssock, &ssock->shutdown_op_key, 0, 0);
}
ssl_close_sockets(ssock);
/* Upon error, OpenSSL may leave any error description in the thread
* error queue, which sometime may cause next call to SSL API returning
* false error alarm, e.g: in Linux, SSL_CTX_use_certificate_chain_file()
* returning false error after a handshake error (in different SSL_CTX!).
* For now, just clear thread error queue here.
*/
ERR_clear_error();
}
static void ssl_ciphers_populate()
{
if (ssl_cipher_num == 0 || ssl_curves_num == 0) {
init_openssl();
shutdown_openssl();
}
}
static pj_ssl_cipher ssl_get_cipher(pj_ssl_sock_t *ssock)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
const SSL_CIPHER *cipher;
/* Current cipher */
cipher = SSL_get_current_cipher(ossock->ossl_ssl);
if (cipher) {
return (SSL_CIPHER_get_id(cipher) & 0x00FFFFFF);
} else {
return PJ_TLS_UNKNOWN_CIPHER;
}
}
/* Generate cipher list with user preference order in OpenSSL format */
static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
pj_pool_t *tmp_pool = NULL;
char *buf = NULL;
enum { BUF_SIZE = 8192 };
pj_str_t cipher_list;
unsigned i, j;
int ret;
if (ssock->param.ciphers_num == 0) {
ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, PJ_SSL_SOCK_OSSL_CIPHERS);
if (ret < 1) {
return GET_SSL_STATUS(ssock);
}
return PJ_SUCCESS;
}
/* Create temporary pool. */
tmp_pool = pj_pool_create(ssock->pool->factory, "ciphpool", BUF_SIZE,
BUF_SIZE/2 , NULL);
if (!tmp_pool)
return PJ_ENOMEM;
buf = (char *)pj_pool_zalloc(tmp_pool, BUF_SIZE);
pj_strset(&cipher_list, buf, 0);
/* Generate user specified cipher list in OpenSSL format */
for (i = 0; i < ssock->param.ciphers_num; ++i) {
for (j = 0; j < ssl_cipher_num; ++j) {
if (ssock->param.ciphers[i] == ssl_ciphers[j].id)
{
const char *c_name = ssl_ciphers[j].name;
/* Check buffer size */
if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 >
BUF_SIZE)
{
pj_assert(!"Insufficient temporary buffer for cipher");
return PJ_ETOOMANY;
}
/* Add colon separator */
if (cipher_list.slen)
pj_strcat2(&cipher_list, ":");
/* Add the cipher */
pj_strcat2(&cipher_list, c_name);
break;
}
}
}
/* Put NULL termination in the generated cipher list */
cipher_list.ptr[cipher_list.slen] = '\0';
/* Finally, set chosen cipher list */
ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, buf);
if (ret < 1) {
pj_pool_release(tmp_pool);
return GET_SSL_STATUS(ssock);
}
pj_pool_release(tmp_pool);
return PJ_SUCCESS;
}
static pj_status_t set_curves_list(pj_ssl_sock_t *ssock)
{
#if !USING_LIBRESSL && !defined(OPENSSL_NO_EC) \
&& OPENSSL_VERSION_NUMBER >= 0x1000200fL
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
int ret;
int curves[PJ_SSL_SOCK_MAX_CURVES];
unsigned cnt;
if (ssock->param.curves_num == 0)
return PJ_SUCCESS;
for (cnt = 0; cnt < ssock->param.curves_num; cnt++) {
curves[cnt] = get_nid_from_cid(ssock->param.curves[cnt]);
}
if( SSL_is_server(ossock->ossl_ssl) ) {
ret = SSL_set1_curves(ossock->ossl_ssl, curves,
ssock->param.curves_num);
if (ret < 1)
return GET_SSL_STATUS(ssock);
} else {
ret = SSL_CTX_set1_curves(ossock->ossl_ctx, curves,
ssock->param.curves_num);
if (ret < 1)
return GET_SSL_STATUS(ssock);
}
#else
PJ_UNUSED_ARG(ssock);
#endif
return PJ_SUCCESS;
}
static pj_status_t set_sigalgs(pj_ssl_sock_t *ssock)
{
#if !USING_LIBRESSL && OPENSSL_VERSION_NUMBER >= 0x1000200fL
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
int ret;
if (ssock->param.sigalgs.ptr && ssock->param.sigalgs.slen) {
#if !USING_BORINGSSL
if (ssock->is_server) {
ret = SSL_set1_client_sigalgs_list(ossock->ossl_ssl,
ssock->param.sigalgs.ptr);
} else {
ret = SSL_set1_sigalgs_list(ossock->ossl_ssl,
ssock->param.sigalgs.ptr);
}
#else
ret = SSL_set1_sigalgs_list(ossock->ossl_ssl,
ssock->param.sigalgs.ptr);
#endif
if (ret < 1)
return GET_SSL_STATUS(ssock);
}
#else
PJ_UNUSED_ARG(ssock);
#endif
return PJ_SUCCESS;
}
static void set_entropy(pj_ssl_sock_t *ssock)
{
int ret = 0;
switch (ssock->param.entropy_type) {
#ifndef OPENSSL_NO_EGD
case PJ_SSL_ENTROPY_EGD:
ret = RAND_egd(ssock->param.entropy_path.ptr);
break;
#endif
case PJ_SSL_ENTROPY_RANDOM:
ret = RAND_load_file("/dev/random",255);
break;
case PJ_SSL_ENTROPY_URANDOM:
ret = RAND_load_file("/dev/urandom",255);
break;
case PJ_SSL_ENTROPY_FILE:
ret = RAND_load_file(ssock->param.entropy_path.ptr,255);
break;
case PJ_SSL_ENTROPY_NONE:
default:
break;
}
if (ret < 0) {
PJ_LOG(3, (ssock->pool->obj_name,
"SSL failed to reseed with entropy type %d "
"[native err=%d]",
ssock->param.entropy_type, ret));
}
}
/* Parse OpenSSL ASN1_TIME to pj_time_val and GMT info */
static pj_bool_t parse_ossl_asn1_time(pj_time_val *tv, pj_bool_t *gmt,
const ASN1_TIME *tm)
{
unsigned long parts[7] = {0};
char *p, *end;
unsigned len;
pj_bool_t utc;
pj_parsed_time pt;
int i;
utc = tm->type == V_ASN1_UTCTIME;
p = (char*)tm->data;
len = tm->length;
end = p + len - 1;
/* GMT */
*gmt = (*end == 'Z');
/* parse parts */
for (i = 0; i < 7 && p < end; ++i) {
pj_str_t st;
if (i==0 && !utc) {
/* 4 digits year part for non-UTC time format */
st.slen = 4;
} else if (i==6) {
/* fraction of seconds */
if (*p == '.') ++p;
st.slen = end - p + 1;
} else {
/* other parts always 2 digits length */
st.slen = 2;
}
st.ptr = p;
parts[i] = pj_strtoul(&st);
p += st.slen;
}
/* encode parts to pj_time_val */
pt.year = parts[0];
if (utc)
pt.year += (pt.year < 50)? 2000:1900;
pt.mon = parts[1] - 1;
pt.day = parts[2];
pt.hour = parts[3];
pt.min = parts[4];
pt.sec = parts[5];
pt.msec = parts[6];
pj_time_encode(&pt, tv);
return PJ_TRUE;
}
/* Get Common Name field string from a general name string */
static void get_cn_from_gen_name(const pj_str_t *gen_name, pj_str_t *cn)
{
pj_str_t CN_sign = {"/CN=", 4};
char *p, *q;
pj_bzero(cn, sizeof(pj_str_t));
if (!gen_name->slen)
return;
p = pj_strstr(gen_name, &CN_sign);
if (!p)
return;
p += 4; /* shift pointer to value part */
pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr));
q = pj_strchr(cn, '/');
if (q)
cn->slen = q - p;
}
/* Get certificate info from OpenSSL X509, in case the certificate info
* hal already populated, this function will check if the contents need
* to be updated by inspecting the issuer and the serial number.
*/
static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci, X509 *x,
pj_bool_t get_pem)
{
pj_bool_t update_needed;
char buf[512];
pj_uint8_t serial_no[64] = {0}; /* should be >= sizeof(ci->serial_no) */
const pj_uint8_t *q;
unsigned len;
GENERAL_NAMES *names = NULL;
pj_assert(pool && ci && x);
/* Get issuer */
X509_NAME_oneline(X509_get_issuer_name(x), buf, sizeof(buf));
/* Get serial no */
q = (const pj_uint8_t*) M_ASN1_STRING_data(X509_get_serialNumber(x));
len = M_ASN1_STRING_length(X509_get_serialNumber(x));
if (len > sizeof(ci->serial_no))
len = sizeof(ci->serial_no);
pj_memcpy(serial_no + sizeof(ci->serial_no) - len, q, len);
/* Check if the contents need to be updated. */
update_needed = pj_strcmp2(&ci->issuer.info, buf) ||
pj_memcmp(ci->serial_no, serial_no, sizeof(ci->serial_no));
if (!update_needed)
return;
/* Update cert info */
pj_bzero(ci, sizeof(pj_ssl_cert_info));
/* Version */
ci->version = X509_get_version(x) + 1;
/* Issuer */
pj_strdup2(pool, &ci->issuer.info, buf);
get_cn_from_gen_name(&ci->issuer.info, &ci->issuer.cn);
/* Serial number */
pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no));
/* Subject */
pj_strdup2(pool, &ci->subject.info,
X509_NAME_oneline(X509_get_subject_name(x),
buf, sizeof(buf)));
get_cn_from_gen_name(&ci->subject.info, &ci->subject.cn);
/* Validity */
parse_ossl_asn1_time(&ci->validity.start, &ci->validity.gmt,
X509_get_notBefore(x));
parse_ossl_asn1_time(&ci->validity.end, &ci->validity.gmt,
X509_get_notAfter(x));
/* Subject Alternative Name extension */
if (ci->version >= 3) {
names = (GENERAL_NAMES*) X509_get_ext_d2i(x, NID_subject_alt_name,
NULL, NULL);
}
if (names) {
unsigned i, cnt;
cnt = sk_GENERAL_NAME_num(names);
ci->subj_alt_name.entry = pj_pool_calloc(pool, cnt,
sizeof(*ci->subj_alt_name.entry));
for (i = 0; i < cnt; ++i) {
unsigned char *p = 0;
pj_ssl_cert_name_type type = PJ_SSL_CERT_NAME_UNKNOWN;
const GENERAL_NAME *name;
name = sk_GENERAL_NAME_value(names, i);
switch (name->type) {
case GEN_EMAIL:
len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
type = PJ_SSL_CERT_NAME_RFC822;
break;
case GEN_DNS:
len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
type = PJ_SSL_CERT_NAME_DNS;
break;
case GEN_URI:
len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
type = PJ_SSL_CERT_NAME_URI;
break;
case GEN_IPADD:
p = (unsigned char*)M_ASN1_STRING_data(name->d.ip);
len = M_ASN1_STRING_length(name->d.ip);
type = PJ_SSL_CERT_NAME_IP;
break;
default:
break;
}
if (p && len && type != PJ_SSL_CERT_NAME_UNKNOWN) {
ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type;
if (type == PJ_SSL_CERT_NAME_IP) {
int af = pj_AF_INET();
if (len == sizeof(pj_in6_addr)) af = pj_AF_INET6();
pj_inet_ntop2(af, p, buf, sizeof(buf));
pj_strdup2(pool,
&ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
buf);
} else {
pj_strdup2(pool,
&ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
(char*)p);
OPENSSL_free(p);
}
ci->subj_alt_name.cnt++;
}
}
GENERAL_NAMES_free(names);
names = NULL;
}
if (get_pem) {
/* Update raw Certificate info in PEM format. */
BIO *bio;
BUF_MEM *ptr;
bio = BIO_new(BIO_s_mem());
if (!PEM_write_bio_X509(bio, x)) {
PJ_LOG(3,(THIS_FILE, "Error retrieving raw certificate info"));
ci->raw.ptr = NULL;
ci->raw.slen = 0;
} else {
BIO_write(bio, "\0", 1);
BIO_get_mem_ptr(bio, &ptr);
pj_strdup2(pool, &ci->raw, ptr->data);
}
BIO_free(bio);
}
}
/* Update remote certificates chain info. This function should be
* called after handshake or renegotiation successfully completed.
*/
static void ssl_update_remote_cert_chain_info(pj_pool_t *pool,
pj_ssl_cert_info *ci,
STACK_OF(X509) *chain,
pj_bool_t get_pem)
{
int i;
/* For now, get_pem has to be PJ_TRUE */
pj_assert(get_pem);
PJ_UNUSED_ARG(get_pem);
ci->raw_chain.cert_raw = (pj_str_t *)pj_pool_calloc(pool,
sk_X509_num(chain),
sizeof(pj_str_t));
ci->raw_chain.cnt = sk_X509_num(chain);
for (i = 0; i < sk_X509_num(chain); i++) {
BIO *bio;
BUF_MEM *ptr;
X509 *x = sk_X509_value(chain, i);
bio = BIO_new(BIO_s_mem());
if (!PEM_write_bio_X509(bio, x)) {
PJ_LOG(3, (THIS_FILE, "Error retrieving raw certificate info"));
ci->raw_chain.cert_raw[i].ptr = NULL;
ci->raw_chain.cert_raw[i].slen = 0;
} else {
BIO_write(bio, "\0", 1);
BIO_get_mem_ptr(bio, &ptr);
pj_strdup2(pool, &ci->raw_chain.cert_raw[i], ptr->data );
}
BIO_free(bio);
}
}
/* Update local & remote certificates info. This function should be
* called after handshake or renegotiation successfully completed.
*/
static void ssl_update_certs_info(pj_ssl_sock_t *ssock)
{
pj_assert(ssock->ssl_state == SSL_STATE_ESTABLISHED);
update_certs_info(ssock, NULL, &ssock->local_cert_info,
&ssock->remote_cert_info, PJ_FALSE);
}
static void update_certs_info(pj_ssl_sock_t* ssock,
X509_STORE_CTX* ctx,
pj_ssl_cert_info *local_cert_info,
pj_ssl_cert_info *remote_cert_info,
pj_bool_t is_verify)
{
ossl_sock_t* ossock = (ossl_sock_t*)ssock;
X509* x;
STACK_OF(X509)* chain;
/* Active local certificate */
x = SSL_get_certificate(ossock->ossl_ssl);
if (x) {
get_cert_info(ssock->pool, local_cert_info, x, PJ_FALSE);
/* Don't free local's X509! */
} else {
pj_bzero(local_cert_info, sizeof(pj_ssl_cert_info));
}
/* Active remote certificate */
if (is_verify) {
x = X509_STORE_CTX_get0_cert(ctx);
} else {
x = SSL_get_peer_certificate(ossock->ossl_ssl);
}
if (x) {
get_cert_info(ssock->pool, remote_cert_info, x, PJ_TRUE);
if (!is_verify) {
/* Free peer's X509 */
X509_free(x);
}
} else {
pj_bzero(remote_cert_info, sizeof(pj_ssl_cert_info));
}
if (is_verify) {
chain = X509_STORE_CTX_get1_chain(ctx);
} else {
chain = SSL_get_peer_cert_chain(ossock->ossl_ssl);
}
if (chain) {
pj_pool_reset(ssock->info_pool);
ssl_update_remote_cert_chain_info(ssock->info_pool,
remote_cert_info,
chain, PJ_TRUE);
/* Only free the chain returned by X509_STORE_CTX_get1_chain().
* The reference count of each cert returned by
* SSL_get_peer_cert_chain() is not incremented.
*/
if (is_verify) {
sk_X509_pop_free(chain, X509_free);
}
} else {
remote_cert_info->raw_chain.cnt = 0;
}
}
/* Flush write BIO to network socket. Note that any access to write BIO
* MUST be serialized, so mutex protection must cover any call to OpenSSL
* API (that possibly generate data for write BIO) along with the call to
* this function (flushing all data in write BIO generated by above
* OpenSSL API call).
*/
static pj_status_t flush_circ_buf_output(pj_ssl_sock_t *ssock,
pj_ioqueue_op_key_t *send_key,
pj_size_t orig_len, unsigned flags);
static void ssl_set_state(pj_ssl_sock_t *ssock, pj_bool_t is_server)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
if (is_server) {
SSL_set_accept_state(ossock->ossl_ssl);
} else {
SSL_set_connect_state(ossock->ossl_ssl);
}
}
/* Server Name Indication server callback */
static int sni_cb(SSL *ssl, int *al, void *arg)
{
pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)arg;
const char *sname;
PJ_UNUSED_ARG(al);
sname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (!sname || pj_stricmp2(&ssock->param.server_name, sname)) {
PJ_LOG(4, (THIS_FILE, "Client SNI rejected: %s", sname));
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
return SSL_TLSEXT_ERR_OK;
}
static void ssl_set_peer_name(pj_ssl_sock_t *ssock)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
/* Set server name to connect */
if (ssock->param.server_name.slen &&
get_ip_addr_ver(&ssock->param.server_name) == 0)
{
if (ssock->is_server) {
#if defined(SSL_CTX_set_tlsext_servername_callback) && \
defined(SSL_CTX_set_tlsext_servername_arg)
SSL_CTX_set_tlsext_servername_callback(ossock->ossl_ctx, &sni_cb);
SSL_CTX_set_tlsext_servername_arg(ossock->ossl_ctx, ssock);
#endif
} else {
#ifdef SSL_set_tlsext_host_name
/* Server name is null terminated already */
if (!SSL_set_tlsext_host_name(ossock->ossl_ssl,
ssock->param.server_name.ptr))
{
char err_str[PJ_ERR_MSG_SIZE];
ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str));
PJ_LOG(3,(ssock->pool->obj_name, "SSL_set_tlsext_host_name() "
"failed: %s", err_str));
}
#endif
}
}
}
/* Asynchronouse handshake */
static pj_status_t ssl_do_handshake(pj_ssl_sock_t *ssock)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
pj_status_t status;
int err;
/* Perform SSL handshake */
pj_lock_acquire(ssock->write_mutex);
/* Clear the error queue prior to any I/O functions, as per openssl docs */
ERR_clear_error();
err = SSL_do_handshake(ossock->ossl_ssl);
pj_lock_release(ssock->write_mutex);
/* SSL_do_handshake() may put some pending data into SSL write BIO,
* flush it if any.
*/
status = flush_circ_buf_output(ssock, &ssock->handshake_op_key, 0, 0);
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
return status;
}
if (err < 0) {
int err2 = SSL_get_error(ossock->ossl_ssl, err);
if (err2 != SSL_ERROR_NONE && err2 != SSL_ERROR_WANT_READ)
{
/* Handshake fails */
status = STATUS_FROM_SSL_ERR2("Handshake", ssock, err, err2, 0);
return status;
}
}
/* Check if handshake has been completed */
if (SSL_is_init_finished(ossock->ossl_ssl)) {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
if (ssock->is_server && ssock->ssl_state != SSL_STATE_ESTABLISHED) {
enum {BUF_SIZE = 64};
unsigned int len = 0, i;
const unsigned char *sctx, *sid;
char buf[BUF_SIZE+1];
SSL_SESSION *sess;
sess = SSL_get_session(ossock->ossl_ssl);
#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
PJ_LOG(5, (THIS_FILE, "Session info: reused=%d, resumable=%d, "
"timeout=%ld",
(int)SSL_session_reused(ossock->ossl_ssl),
SSL_SESSION_is_resumable(sess),
SSL_SESSION_get_timeout(sess)));
#else
PJ_LOG(5, (THIS_FILE, "Session info: reused=%d, resumable=%d, "
"timeout=%ld",
(int)SSL_session_reused(ossock->ossl_ssl),
-1,
SSL_SESSION_get_timeout(sess)));
#endif
sid = SSL_SESSION_get_id(sess, &len);
len *= 2;
if (len >= BUF_SIZE) len = BUF_SIZE;
for (i = 0; i < len; i+=2)
pj_ansi_snprintf(buf+i, sizeof(buf)-i, "%02X", sid[i/2]);
buf[len] = '\0';
PJ_LOG(5, (THIS_FILE, "Session id: %s", buf));
sctx = SSL_SESSION_get0_id_context(sess, &len);
if (len >= BUF_SIZE) len = BUF_SIZE;
for (i = 0; i < len; i++)
pj_ansi_snprintf(buf + i, sizeof(buf)-i, "%d", sctx[i]);
buf[len] = '\0';
PJ_LOG(5, (THIS_FILE, "Session id context: %s", buf));
}
#endif
ssock->ssl_state = SSL_STATE_ESTABLISHED;
return PJ_SUCCESS;
}
return PJ_EPENDING;
}
static pj_status_t ssl_read(pj_ssl_sock_t *ssock, void *data, int *size)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
int size_ = *size;
int len = size_;
/* SSL_read() may write some data to write buffer when re-negotiation
* is on progress, so let's protect it with write mutex.
*/
pj_lock_acquire(ssock->write_mutex);
/* Clear the error queue prior to any I/O functions, as per openssl docs */
ERR_clear_error();
*size = size_ = SSL_read(ossock->ossl_ssl, data, size_);
if (size_ <= 0) {
pj_status_t status;
int err = SSL_get_error(ossock->ossl_ssl, size_);
/* SSL might just return SSL_ERROR_WANT_READ in
* re-negotiation.
*/
if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) {
if (err == SSL_ERROR_SYSCALL && size_ == -1 &&
ERR_peek_error() == 0 && errno == 0)
{
status = STATUS_FROM_SSL_ERR2("Read", ssock, size_,
err, len);
PJ_LOG(4,("SSL", "SSL_read() = -1, with "
"SSL_ERROR_SYSCALL, no SSL error, "
"and errno = 0 - skip BIO error"));
/* Ignore these errors */
} else {
/* Reset SSL socket state, then return PJ_FALSE */
status = STATUS_FROM_SSL_ERR2("Read", ssock, size_,
err, len);
pj_lock_release(ssock->write_mutex);
/* Unfortunately we can't hold the lock here to reset all the state.
* We probably should though.
*/
ssl_reset_sock_state(ssock);
return status;
}
}
pj_lock_release(ssock->write_mutex);
/* Need renegotiation */
return PJ_EEOF;
}
pj_lock_release(ssock->write_mutex);
return PJ_SUCCESS;
}
/* Write plain data to SSL and flush write BIO. */
static pj_status_t ssl_write(pj_ssl_sock_t *ssock, const void *data,
pj_ssize_t size, int *nwritten)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
pj_status_t status = PJ_SUCCESS;
/* Clear the error queue prior to any I/O functions, as per openssl docs */
ERR_clear_error();
*nwritten = SSL_write(ossock->ossl_ssl, data, (int)size);
if (*nwritten <= 0) {
/* SSL failed to process the data, it may just that re-negotiation
* is on progress.
*/
int err;
err = SSL_get_error(ossock->ossl_ssl, *nwritten);
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_NONE) {
status = PJ_EEOF;
} else {
/* Some problem occured */
status = STATUS_FROM_SSL_ERR2("Write", ssock, *nwritten,
err, size);
}
} else if (*nwritten < size) {
/* nwritten < size, shouldn't happen, unless write BIO cannot hold
* the whole secured data, perhaps because of insufficient memory.
*/
status = PJ_ENOMEM;
}
return status;
}
static pj_status_t ssl_renegotiate(pj_ssl_sock_t *ssock)
{
ossl_sock_t *ossock = (ossl_sock_t *)ssock;
pj_status_t status = PJ_SUCCESS;
int ret;
if (SSL_renegotiate_pending(ossock->ossl_ssl))
return PJ_EPENDING;
/* Clear the error queue prior to any I/O functions, as per openssl docs */
ERR_clear_error();
ret = SSL_renegotiate(ossock->ossl_ssl);
if (ret <= 0) {
status = GET_SSL_STATUS(ssock);
}
return status;
}
/* Put back deprecation warning setting */
#if defined(PJ_DARWINOS) && PJ_DARWINOS==1
# pragma GCC diagnostic pop
#endif
#endif /* PJ_HAS_SSL_SOCK */