/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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 #include #include #include #include #if !defined(OPENSSL_NO_DH) # include #endif #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L # include #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 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))) 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 */