12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082 |
- /*
- * This file is part of the Sofia-SIP package
- *
- * Copyright (C) 2005 Nokia Corporation.
- *
- * Contact: Pekka Pessi <pekka.pessi@nokia.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
- /**@CFILE tport_tls.c
- * @brief TLS interface
- *
- * @author Mikko Haataja <ext-Mikko.A.Haataja@nokia.com>
- * @author Pekka Pessi <ext-Pekka.Pessi@nokia.com>
- * @author Jarod Neuner <janeuner@networkharbor.com>
- *
- */
- #include "config.h"
- #define OPENSSL_NO_KRB5 oh-no
- #define SU_WAKEUP_ARG_T struct tport_s
- #include <sofia-sip/su_types.h>
- #include <sofia-sip/su.h>
- #include <sofia-sip/su_alloc.h>
- #include <sofia-sip/su_wait.h>
- #include <sofia-sip/su_string.h>
- #include <openssl/lhash.h>
- #include <openssl/bn.h>
- #include <openssl/x509.h>
- #include <openssl/x509v3.h>
- #include <openssl/ssl.h>
- #include <openssl/err.h>
- #include <openssl/pem.h>
- #include <openssl/rand.h>
- #include <openssl/bio.h>
- #include <openssl/opensslv.h>
- #include <assert.h>
- #include <stdio.h>
- #include <stdlib.h>
- #if HAVE_FUNC
- #elif HAVE_FUNCTION
- #define __func__ __FUNCTION__
- #else
- static char const __func__[] = "tport_tls";
- #endif
- #if HAVE_SIGPIPE
- #include <signal.h>
- #endif
- #if SU_HAVE_PTHREADS
- #include <pthread.h>
- #if __sun
- #undef PTHREAD_ONCE_INIT
- #define PTHREAD_ONCE_INIT {{ 0, 0, 0, PTHREAD_ONCE_NOTDONE }}
- #endif
- static pthread_once_t once = PTHREAD_ONCE_INIT;
- #define ONCE_INIT(f) pthread_once(&once, f)
- #else
- static int once;
- #define ONCE_INIT(f) (!once ? (once = 1), f() : (void)0)
- #endif
- #include "tport_tls.h"
- char const tls_version[] = OPENSSL_VERSION_TEXT;
- static int tls_ex_data_idx = -1; /* see SSL_get_ex_new_index(3ssl) */
- static void
- tls_init_once(void)
- {
- SSL_library_init();
- SSL_load_error_strings();
- tls_ex_data_idx = SSL_get_ex_new_index(0, "sofia-sip private data", NULL, NULL, NULL);
- }
- enum { tls_master = 0, tls_slave = 1};
- struct tls_s {
- su_home_t home[1];
- SSL_CTX *ctx;
- SSL *con;
- BIO *bio_con;
- unsigned int type:1,
- accept:1,
- verify_incoming:1,
- verify_outgoing:1,
- verify_subj_in:1,
- verify_subj_out:1,
- verify_date:1,
- x509_verified:1;
- /* Receiving */
- int read_events;
- void *read_buffer;
- size_t read_buffer_len;
- /* Sending */
- int write_events;
- void *write_buffer;
- size_t write_buffer_len;
- /* Host names */
- su_strlst_t *subjects;
- };
- enum { tls_buffer_size = 16384 };
- /** Log TLS error(s).
- *
- * Log the TLS error specified by the error code @a e and all the errors in
- * the queue. The error code @a e implies no error, and it is not logged.
- */
- void tls_log_errors(unsigned level, char const *s, unsigned long e)
- {
- if (e == 0)
- e = ERR_get_error();
- if (!tport_log->log_init)
- su_log_init(tport_log);
- if (s == NULL) s = "tls";
- for (; e != 0; e = ERR_get_error()) {
- if (level <= tport_log->log_level) {
- const char *error = ERR_lib_error_string(e);
- const char *func = ERR_func_error_string(e);
- const char *reason = ERR_reason_error_string(e);
- su_llog(tport_log, level, "%s: %08lx:%s:%s:%s\n",
- s, e, error, func, reason);
- }
- }
- }
- /*
- * This callback hands back the password to be used during decryption.
- *
- * buf : the function will write the password into this buffer
- * size : the size of "buf"
- * rwflag : indicates whether the callback is being used for reading/
- * decryption (0) or writing/encryption (1)
- * userdata : pointer tls_issues_t where the passphrase is stored
- */
- static int passwd_cb(char *buf, int size, int rwflag, void *userdata)
- {
- if (rwflag == 0) { // reading/decryption
- tls_issues_t *tlsi = (tls_issues_t *)userdata;
- strncpy(buf, tlsi->passphrase, size);
- buf[size - 1] = '\0';
- return strlen(tlsi->passphrase);
- }
- return 0;
- }
- static
- tls_t *tls_create(int type)
- {
- tls_t *tls = su_home_new(sizeof(*tls));
- if (tls)
- tls->type = type == tls_master ? tls_master : tls_slave;
- return tls;
- }
- static
- void tls_set_default(tls_issues_t *i)
- {
- i->verify_depth = i->verify_depth == 0 ? 2 : i->verify_depth;
- i->cert = i->cert ? i->cert : "agent.pem";
- i->key = i->key ? i->key : i->cert;
- i->randFile = i->randFile ? i->randFile : "tls_seed.dat";
- i->CAfile = i->CAfile ? i->CAfile : "cafile.pem";
- i->ciphers = i->ciphers ? i->ciphers : "!eNULL:!aNULL:!EXP:!LOW:!MD5:ALL:@STRENGTH";
- /* Default SIP cipher */
- /* "RSA-WITH-AES-128-CBC-SHA"; */
- /* RFC-2543-compatibility ciphersuite */
- /* TLS_RSA_WITH_3DES_EDE_CBC_SHA; */
- }
- static
- int tls_verify_cb(int ok, X509_STORE_CTX *store)
- {
- if (!ok)
- {
- char data[256];
- X509 *cert = X509_STORE_CTX_get_current_cert(store);
- int depth = X509_STORE_CTX_get_error_depth(store);
- int err = X509_STORE_CTX_get_error(store);
- int sslidx = SSL_get_ex_data_X509_STORE_CTX_idx();
- SSL *ssl = X509_STORE_CTX_get_ex_data(store, sslidx);
- tls_t *tls = SSL_get_ex_data(ssl, tls_ex_data_idx);
- assert(tls);
- #define TLS_VERIFY_CB_CLEAR_ERROR(OK,ERR,STORE) \
- do {\
- OK = 1;\
- ERR = X509_V_OK;\
- X509_STORE_CTX_set_error(STORE,ERR);\
- } while (0)
- if (tls->accept && !tls->verify_incoming)
- TLS_VERIFY_CB_CLEAR_ERROR(ok, err, store);
- else if (!tls->accept && !tls->verify_outgoing)
- TLS_VERIFY_CB_CLEAR_ERROR(ok, err, store);
- else switch (err) {
- case X509_V_ERR_CERT_NOT_YET_VALID:
- case X509_V_ERR_CERT_HAS_EXPIRED:
- case X509_V_ERR_CRL_NOT_YET_VALID:
- case X509_V_ERR_CRL_HAS_EXPIRED:
- if (!tls->verify_date)
- TLS_VERIFY_CB_CLEAR_ERROR(ok, err, store);
- default:
- break;
- }
- if (!ok) {
- SU_DEBUG_3(("-Error with certificate at depth: %i\n", depth));
- X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
- SU_DEBUG_3((" issuer = %s\n", data));
- X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
- SU_DEBUG_3((" subject = %s\n", data));
- SU_DEBUG_3((" err %i:%s\n", err, X509_verify_cert_error_string(err)));
- }
- }
- return ok;
- }
- void tls_init(void) {
- ONCE_INIT(tls_init_once);
- }
- #ifndef OPENSSL_NO_EC
- static
- int tls_init_ecdh_curve(tls_t *tls)
- {
- #if OPENSSL_VERSION_NUMBER < 0x10002000
- EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- if (!ecdh) {
- tls_log_errors(1, "Couldn't create specified curve", 0);
- errno = EIO;
- return -1;
- }
- SSL_CTX_set_options(tls->ctx, SSL_OP_SINGLE_ECDH_USE);
- SSL_CTX_set_tmp_ecdh(tls->ctx, ecdh);
- EC_KEY_free(ecdh);
- return 0;
- #elif OPENSSL_VERSION_NUMBER < 0x10100000
- if (!SSL_CTX_set_ecdh_auto(tls->ctx, 1)) {
- return -1;
- }
- SSL_CTX_set_options(tls->ctx, SSL_OP_SINGLE_ECDH_USE);
- return 0;
- #else
- return 0;
- #endif
- }
- #endif
- static
- int tls_init_context(tls_t *tls, tls_issues_t const *ti)
- {
- int verify;
- static int random_loaded;
- ONCE_INIT(tls_init_once);
- if (!random_loaded) {
- random_loaded = 1;
- if (ti->randFile &&
- !RAND_load_file(ti->randFile, 1024 * 1024)) {
- if (ti->configured > 1) {
- SU_DEBUG_3(("%s: cannot open randFile %s\n",
- "tls_init_context", ti->randFile));
- tls_log_errors(3, "tls_init_context", 0);
- }
- /* errno = EIO; */
- /* return -1; */
- }
- }
- #if HAVE_SIGPIPE
- /* Avoid possible SIGPIPE when sending close_notify */
- signal(SIGPIPE, SIG_IGN);
- #endif
- if (tls->ctx == NULL)
- if (!(tls->ctx = SSL_CTX_new((SSL_METHOD*)SSLv23_method()))) {
- tls_log_errors(1, "SSL_CTX_new() failed", 0);
- errno = EIO;
- return -1;
- }
- if (!(ti->version & TPTLS_VERSION_SSLv2))
- SSL_CTX_set_options(tls->ctx, SSL_OP_NO_SSLv2);
- if (!(ti->version & TPTLS_VERSION_SSLv3))
- SSL_CTX_set_options(tls->ctx, SSL_OP_NO_SSLv3);
- if (!(ti->version & TPTLS_VERSION_TLSv1))
- SSL_CTX_set_options(tls->ctx, SSL_OP_NO_TLSv1);
- if (!(ti->version & TPTLS_VERSION_TLSv1_1))
- SSL_CTX_set_options(tls->ctx, SSL_OP_NO_TLSv1_1);
- if (!(ti->version & TPTLS_VERSION_TLSv1_2))
- SSL_CTX_set_options(tls->ctx, SSL_OP_NO_TLSv1_2);
- SSL_CTX_sess_set_remove_cb(tls->ctx, NULL);
- SSL_CTX_set_timeout(tls->ctx, ti->timeout);
- /* CRIME (CVE-2012-4929) mitigation */
- SSL_CTX_set_options(tls->ctx, SSL_OP_NO_COMPRESSION);
- /* Set callback if we have a passphrase */
- if (ti->passphrase != NULL) {
- SSL_CTX_set_default_passwd_cb(tls->ctx, passwd_cb);
- SSL_CTX_set_default_passwd_cb_userdata(tls->ctx, (void *)ti);
- }
- if (!SSL_CTX_use_certificate_file(tls->ctx,
- ti->cert,
- SSL_FILETYPE_PEM)) {
- if (ti->configured > 0) {
- SU_DEBUG_1(("%s: invalid local certificate: %s\n",
- "tls_init_context", ti->cert));
- tls_log_errors(3, "tls_init_context", 0);
- #if require_client_certificate
- errno = EIO;
- return -1;
- #endif
- }
- }
- if (!SSL_CTX_use_PrivateKey_file(tls->ctx,
- ti->key,
- SSL_FILETYPE_PEM)) {
- if (ti->configured > 0) {
- SU_DEBUG_1(("%s: invalid private key: %s\n",
- "tls_init_context", ti->key));
- tls_log_errors(3, "tls_init_context(key)", 0);
- #if require_client_certificate
- errno = EIO;
- return -1;
- #endif
- }
- }
- if (!SSL_CTX_check_private_key(tls->ctx)) {
- if (ti->configured > 0) {
- SU_DEBUG_1(("%s: private key does not match the certificate public key\n",
- "tls_init_context"));
- }
- #if require_client_certificate
- errno = EIO;
- return -1;
- #endif
- #ifndef OPENSSL_NO_DH
- } else {
- BIO *bio = BIO_new_file(ti->key, "r");
- if (bio != NULL) {
- DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
- if (dh != NULL) {
- if (!SSL_CTX_set_tmp_dh(tls->ctx, dh)) {
- SU_DEBUG_1(("%s: invalid DH parameters (PFS) because %s: %s\n",
- "tls_init_context",
- ERR_reason_error_string(ERR_get_error()),
- ti->key));
- } else {
- long options = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_DH_USE;
- SSL_CTX_set_options(tls->ctx, options);
- SU_DEBUG_3(("%s\n", "tls: initialized DHE"));
- }
- DH_free(dh);
- }
- BIO_free(bio);
- }
- #endif
- }
- if (!SSL_CTX_load_verify_locations(tls->ctx,
- ti->CAfile,
- ti->CApath)) {
- SU_DEBUG_1(("%s: error loading CA list: %s\n",
- "tls_init_context", ti->CAfile));
- if (ti->configured > 0)
- tls_log_errors(3, "tls_init_context(CA)", 0);
- errno = EIO;
- return -1;
- }
- /* corresponds to (enum tport_tls_verify_policy) */
- tls->verify_incoming = (ti->policy & 0x1) ? 1 : 0;
- tls->verify_outgoing = (ti->policy & 0x2) ? 1 : 0;
- tls->verify_subj_in = (ti->policy & 0x4) ? tls->verify_incoming : 0;
- tls->verify_subj_out = (ti->policy & 0x8) ? tls->verify_outgoing : 0;
- tls->verify_date = (ti->verify_date) ? 1 : 0;
- if (tls->verify_incoming)
- verify = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- else
- verify = SSL_VERIFY_NONE;
- SSL_CTX_set_verify_depth(tls->ctx, ti->verify_depth);
- SSL_CTX_set_verify(tls->ctx, verify, tls_verify_cb);
- #ifndef OPENSSL_NO_EC
- #if OPENSSL_VERSION_NUMBER < 0x10100000L
- if (tls_init_ecdh_curve(tls) == 0) {
- SU_DEBUG_3(("%s\n", "tls: initialized ECDH"));
- } else {
- SU_DEBUG_3(("%s\n", "tls: failed to initialize ECDH"));
- }
- #else
- if (tls->accept == 0) {
- SU_DEBUG_3(("%s\n", "tls: initialized ECDH"));
- } else if (tls_init_ecdh_curve(tls) == 0) {
- SU_DEBUG_3(("%s\n", "tls: initialized ECDH"));
- } else {
- SU_DEBUG_3(("%s\n", "tls: failed to initialize ECDH"));
- }
- #endif
- #endif
- if (!SSL_CTX_set_cipher_list(tls->ctx, ti->ciphers)) {
- SU_DEBUG_1(("%s: error setting cipher list\n", "tls_init_context"));
- tls_log_errors(3, "tls_init_context", 0);
- errno = EIO;
- return -1;
- }
- return 0;
- }
- void tls_free(tls_t *tls)
- {
- int ret;
- if (!tls)
- return;
- if (tls->con != NULL) {
- do {
- ret = SSL_shutdown(tls->con);
- if (ret == -1) {
- /* The return value -1 means that the connection wasn't actually established */
- /* so it should be safe to not call shutdown again. We need to clear the eror */
- /* queue for other connections though. */
- tls_log_errors(3, "tls_free", 0);
- ret = 1;
- }
- } while (ret != 1);
- SSL_free(tls->con), tls->con = NULL;
- }
- if (tls->ctx != NULL && tls->type != tls_slave) {
- SSL_CTX_free(tls->ctx);
- }
- su_home_unref(tls->home);
- }
- int tls_get_socket(tls_t *tls)
- {
- int sock = -1;
- if (tls != NULL && tls->bio_con != NULL)
- BIO_get_fd(tls->bio_con, &sock);
- return sock;
- }
- tls_t *tls_init_master(tls_issues_t *ti)
- {
- /* Default id in case RAND fails */
- unsigned char sessionId[32] = "sofia/tls";
- tls_t *tls;
- #if HAVE_SIGPIPE
- signal(SIGPIPE, SIG_IGN); /* Ignore spurios SIGPIPE from OpenSSL */
- #endif
- tls_set_default(ti);
- if (!(tls = tls_create(tls_master)))
- return NULL;
- if (tls_init_context(tls, ti) < 0) {
- int err = errno;
- tls_free(tls);
- errno = err;
- return NULL;
- }
- RAND_bytes(sessionId, sizeof(sessionId));
- if (!SSL_CTX_set_session_id_context(tls->ctx,
- (void*) sessionId,
- sizeof(sessionId))) {
- tls_log_errors(3, "tls_init_master", 0);
- }
- if (ti->CAfile != NULL) {
- SSL_CTX_set_client_CA_list(tls->ctx,
- SSL_load_client_CA_file(ti->CAfile));
- #if OPENSSL_VERSION_NUMBER >= 0x10100000
- if (SSL_CTX_get_client_CA_list(tls->ctx) == NULL)
- #else
- if (tls->ctx->client_CA == NULL)
- #endif
- tls_log_errors(3, "tls_init_master", 0);
- }
- #if 0
- if (sock != -1) {
- tls->bio_con = BIO_new_socket(sock, BIO_NOCLOSE);
- if (tls->bio_con == NULL) {
- tls_log_errors(1, "tls_init_master", 0);
- tls_free(tls);
- errno = EIO;
- return NULL;
- }
- }
- #endif
- return tls;
- }
- tls_t *tls_init_secondary(tls_t *master, int sock, int accept)
- {
- tls_t *tls = tls_create(tls_slave);
- if (tls) {
- tls->ctx = master->ctx;
- tls->accept = accept ? 1 : 0;
- tls->verify_outgoing = master->verify_outgoing;
- tls->verify_incoming = master->verify_incoming;
- tls->verify_subj_out = master->verify_subj_out;
- tls->verify_subj_in = master->verify_subj_in;
- tls->verify_date = master->verify_date;
- tls->x509_verified = master->x509_verified;
- if (!(tls->read_buffer = su_alloc(tls->home, tls_buffer_size)))
- su_home_unref(tls->home), tls = NULL;
- }
- if (!tls)
- return tls;
- assert(sock != -1);
- tls->bio_con = BIO_new_socket(sock, BIO_NOCLOSE);
- tls->con = SSL_new(tls->ctx);
- if (tls->con == NULL) {
- tls_log_errors(1, "tls_init_secondary", 0);
- tls_free(tls);
- errno = EIO;
- return NULL;
- }
- SSL_set_bio(tls->con, tls->bio_con, tls->bio_con);
- SSL_set_mode(tls->con, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- SSL_set_ex_data(tls->con, tls_ex_data_idx, tls);
- su_setblocking(sock, 0);
- return tls;
- }
- su_inline
- int tls_post_connection_check(tport_t *self, tls_t *tls)
- {
- X509 *cert;
- const SSL_CIPHER *cipher;
- char cipher_description[256];
- int cipher_bits, alg_bits;
- int extcount;
- int i, j, error;
- if (!tls) return -1;
- if (!(cipher = SSL_get_current_cipher(tls->con))) {
- tls_log_errors(3, "tls_post_connection_check", 0);
- SU_DEBUG_7(("%s(%p): %s\n", __func__, (void*)self,
- "OpenSSL failed to return an SSL_CIPHER object to us."));
- return SSL_ERROR_SSL;
- }
- SU_DEBUG_9(("%s(%p): TLS cipher chosen (name): %s\n", __func__, (void*)self,
- SSL_CIPHER_get_name(cipher)));
- SU_DEBUG_9(("%s(%p): TLS cipher chosen (version): %s\n", __func__, (void*)self,
- SSL_CIPHER_get_version(cipher)));
- cipher_bits = SSL_CIPHER_get_bits(cipher, &alg_bits);
- SU_DEBUG_9(("%s(%p): TLS cipher chosen (bits/alg_bits): %d/%d\n", __func__, (void*)self,
- cipher_bits, alg_bits));
- SSL_CIPHER_description(cipher, cipher_description, sizeof(cipher_description));
- SU_DEBUG_9(("%s(%p): TLS cipher chosen (description): %s\n", __func__, (void*)self,
- cipher_description));
- cert = SSL_get_peer_certificate(tls->con);
- if (!cert) {
- SU_DEBUG_7(("%s(%p): Peer did not provide X.509 Certificate.\n",
- __func__, (void *) self));
- if (self->tp_accepted && tls->verify_incoming)
- return X509_V_ERR_CERT_UNTRUSTED;
- else if (!self->tp_accepted && tls->verify_outgoing)
- return X509_V_ERR_CERT_UNTRUSTED;
- else
- return X509_V_OK;
- }
- tls->subjects = su_strlst_create(tls->home);
- if (!tls->subjects)
- return X509_V_ERR_OUT_OF_MEM;
- extcount = X509_get_ext_count(cert);
- /* Find matching subjectAltName.DNS */
- for (i = 0; i < extcount; i++) {
- X509_EXTENSION *ext;
- char const *name;
- #if OPENSSL_VERSION_NUMBER > 0x10000000L
- const X509V3_EXT_METHOD *vp;
- #else
- X509V3_EXT_METHOD *vp;
- #endif
- STACK_OF(CONF_VALUE) *values;
- CONF_VALUE *value;
- void *d2i;
- ext = X509_get_ext(cert, i);
- name = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
- if (strcmp(name, "subjectAltName") != 0)
- continue;
- vp = X509V3_EXT_get(ext); if (!vp) continue;
- d2i = X509V3_EXT_d2i(ext);
- values = vp->i2v(vp, d2i, NULL);
- for (j = 0; j < sk_CONF_VALUE_num(values); j++) {
- value = sk_CONF_VALUE_value(values, j);
- if (strcmp(value->name, "DNS") == 0)
- su_strlst_dup_append(tls->subjects, value->value);
- if (strcmp(value->name, "IP") == 0)
- su_strlst_dup_append(tls->subjects, value->value);
- else if (strcmp(value->name, "URI") == 0)
- su_strlst_dup_append(tls->subjects, value->value);
- }
- }
- {
- X509_NAME *subject;
- char name[256];
- subject = X509_get_subject_name(cert);
- if (subject) {
- if (X509_NAME_get_text_by_NID(subject, NID_commonName,
- name, sizeof name) > 0) {
- usize_t k, N = su_strlst_len(tls->subjects);
- name[(sizeof name) - 1] = '\0';
- for (k = 0; k < N; k++)
- if (su_casematch(su_strlst_item(tls->subjects, k), name) == 0)
- break;
- if (k >= N)
- su_strlst_dup_append(tls->subjects, name);
- }
- }
- }
- X509_free(cert);
- error = SSL_get_verify_result(tls->con);
- if (cert && error == X509_V_OK)
- tls->x509_verified = 1;
- if (tport_log->log_level >= 7) {
- int i, len = su_strlst_len(tls->subjects);
- for (i=0; i < len; i++)
- SU_DEBUG_7(("%s(%p): Peer Certificate Subject %i: %s\n", \
- __func__, (void *)self, i, su_strlst_item(tls->subjects, i)));
- if (i == 0)
- SU_DEBUG_7(("%s(%p): Peer Certificate provided no usable subjects.\n",
- __func__, (void *)self));
- }
- /* Verify incoming connections */
- if (self->tp_accepted) {
- if (!tls->verify_incoming)
- return X509_V_OK;
- if (!tls->x509_verified)
- return error;
- if (tls->verify_subj_in) {
- su_strlst_t const *subjects = self->tp_pri->pri_primary->tp_subjects;
- int i, items;
- items = subjects ? su_strlst_len(subjects) : 0;
- if (items == 0)
- return X509_V_OK;
- for (i=0; i < items; i++) {
- if (tport_subject_search(su_strlst_item(subjects, i), tls->subjects))
- return X509_V_OK;
- }
- SU_DEBUG_3(("%s(%p): Peer Subject Mismatch (incoming connection)\n", \
- __func__, (void *)self));
- return X509_V_ERR_CERT_UNTRUSTED;
- }
- }
- /* Verify outgoing connections */
- else {
- char const *subject = self->tp_canon;
- if (!tls->verify_outgoing)
- return X509_V_OK;
- if (!tls->x509_verified || !subject)
- return error;
- if (tls->verify_subj_out) {
- if (tport_subject_search(subject, tls->subjects))
- return X509_V_OK; /* Subject match found in verified certificate chain */
- SU_DEBUG_3(("%s(%p): Peer Subject Mismatch (%s)\n", \
- __func__, (void *)self, subject));
- return X509_V_ERR_CERT_UNTRUSTED;
- }
- }
- return error;
- }
- static
- int tls_error(tls_t *tls, int ret, char const *who,
- void *buf, int size)
- {
- int events = 0;
- int err = SSL_get_error(tls->con, ret);
- switch (err) {
- case SSL_ERROR_WANT_WRITE:
- events = SU_WAIT_OUT;
- break;
- case SSL_ERROR_WANT_READ:
- events = SU_WAIT_IN;
- break;
- case SSL_ERROR_ZERO_RETURN:
- return 0;
- case SSL_ERROR_SYSCALL:
- ERR_clear_error();
- if (SSL_get_shutdown(tls->con) & SSL_RECEIVED_SHUTDOWN)
- return 0; /* EOS */
- if (errno == 0)
- return 0; /* EOS */
- return -1;
- default:
- tls_log_errors(1, who, err);
- errno = EIO;
- return -1;
- }
- if (buf) {
- tls->write_events = events;
- tls->write_buffer = buf, tls->write_buffer_len = size;
- }
- else {
- tls->read_events = events;
- }
- errno = EAGAIN;
- return -1;
- }
- ssize_t tls_read(tls_t *tls)
- {
- ssize_t ret;
- if (tls == NULL) {
- errno = EINVAL;
- return -1;
- }
- if (0)
- SU_DEBUG_1(("tls_read(%p) called on %s (events %u)\n", (void *)tls,
- tls->type ? "master" : "slave",
- tls->read_events));
- if (tls->read_buffer_len)
- return (ssize_t)tls->read_buffer_len;
- tls->read_events = SU_WAIT_IN;
- ret = SSL_read(tls->con, tls->read_buffer, tls_buffer_size);
- if (ret <= 0)
- return tls_error(tls, ret, "tls_read: SSL_read", NULL, 0);
- return (ssize_t)(tls->read_buffer_len = ret);
- }
- void *tls_read_buffer(tls_t *tls, size_t N)
- {
- assert(N == tls->read_buffer_len);
- tls->read_buffer_len = 0;
- return tls->read_buffer;
- }
- int tls_pending(tls_t const *tls)
- {
- return tls && tls->con && SSL_pending(tls->con);
- }
- /** Check if data is available in TCP connection.
- *
- *
- *
- * @retval -1 upon an error
- * @retval 0 end-of-stream
- * @retval 1 nothing to read
- * @retval 2 there is data to read
- */
- int tls_want_read(tls_t *tls, int events)
- {
- if (tls && (events & tls->read_events)) {
- int ret = tls_read(tls);
- if (ret > 0)
- return 2;
- else if (ret == 0)
- return 0;
- else if (errno == EAGAIN)
- return 3; /* ??? */
- else
- return -1;
- }
- return 1;
- }
- ssize_t tls_write(tls_t *tls, void *buf, size_t size)
- {
- ssize_t ret;
- if (0)
- SU_DEBUG_1(("tls_write(%p, %p, "MOD_ZU") called on %s\n",
- (void *)tls, buf, size,
- tls && tls->type == tls_slave ? "master" : "slave"));
- if (tls == NULL || buf == NULL) {
- errno = EINVAL;
- return -1;
- }
- if (tls->write_buffer) {
- assert(buf == tls->write_buffer);
- assert(size >= tls->write_buffer_len);
- assert(tls->write_events == 0);
- if (tls->write_events ||
- buf != tls->write_buffer ||
- size < tls->write_buffer_len) {
- errno = EIO;
- return -1;
- }
- ret = tls->write_buffer_len;
- tls->write_buffer = NULL;
- tls->write_buffer_len = 0;
- return ret;
- }
- if (size == 0)
- return 0;
- tls->write_events = 0;
- ret = SSL_write(tls->con, buf, size);
- if (ret <= 0)
- return tls_error(tls, ret, "tls_write: SSL_write", buf, size);
- return ret;
- }
- int tls_want_write(tls_t *tls, int events)
- {
- if (tls && (events & tls->write_events)) {
- int ret;
- void *buf = tls->write_buffer;
- size_t size = tls->write_buffer_len;
- tls->write_events = 0;
- /* remove buf */
- tls->write_buffer = NULL;
- tls->write_buffer_len = 0;
- ret = tls_write(tls, buf, size);
- if (ret >= 0)
- /* Restore buf */
- return tls->write_buffer = buf, tls->write_buffer_len = ret;
- else if (errno == EAGAIN)
- return 0;
- else
- return -1;
- }
- return 0;
- }
- int tls_events(tls_t const *tls, int mask)
- {
- if (!tls)
- return mask;
- if (tls->type == tls_master)
- return mask;
- return
- (mask & ~(SU_WAIT_IN|SU_WAIT_OUT)) |
- ((mask & SU_WAIT_IN) ? tls->read_events : 0) |
- ((mask & SU_WAIT_OUT) ? tls->write_events : 0);
- }
- int tls_connect(su_root_magic_t *magic, su_wait_t *w, tport_t *self)
- {
- tport_master_t *mr = self->tp_master;
- tport_tls_t *tlstp = (tport_tls_t *)self;
- tls_t *tls;
- int events = su_wait_events(w, self->tp_socket);
- int error;
- SU_DEBUG_7(("%s(%p): events%s%s%s%s\n", __func__, (void *)self,
- events & (SU_WAIT_CONNECT) ? " CONNECTING" : "",
- events & SU_WAIT_IN ? " NEGOTIATING" : "",
- events & SU_WAIT_ERR ? " ERROR" : "",
- events & SU_WAIT_HUP ? " HANGUP" : ""));
- #if HAVE_POLL
- assert(w->fd == self->tp_socket);
- #endif
- if (events & SU_WAIT_ERR)
- tport_error_event(self);
- if (events & SU_WAIT_HUP && !self->tp_closed)
- tport_hup_event(self);
- if (self->tp_closed) {
- SU_DEBUG_9(("%s(%p): tport was closed during connect. Returning, but set secondary timer first.\n",
- __func__, (void *)self));
- tport_set_secondary_timer(self);
- return 0;
- }
- error = su_soerror(self->tp_socket);
- if (error) {
- tport_error_report(self, error, NULL);
- SU_DEBUG_9(("%s(%p): socket error during connect. Returning, but set secondary timer first.\n",
- __func__, (void *)self));
- tport_set_secondary_timer(self);
- return 0;
- }
- if ((tls = tlstp->tlstp_context) == NULL) {
- SU_DEBUG_3(("%s(%p): Error: no TLS context data for connected socket.\n",
- __func__, (void *)tlstp));
- tport_close(self);
- tport_set_secondary_timer(self);
- return 0;
- }
- if (self->tp_is_connected == 0) {
- int ret, status;
- ret = self->tp_accepted ? SSL_accept(tls->con) : SSL_connect(tls->con);
- status = SSL_get_error(tls->con, ret);
- switch (status) {
- case SSL_ERROR_WANT_READ:
- /* OpenSSL is waiting for the peer to send handshake data */
- self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP;
- su_root_eventmask(mr->mr_root, self->tp_index,
- self->tp_socket, self->tp_events);
- return 0;
- case SSL_ERROR_WANT_WRITE:
- /* OpenSSL is waiting for the peer to receive handshake data */
- self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP | SU_WAIT_OUT;
- su_root_eventmask(mr->mr_root, self->tp_index,
- self->tp_socket, self->tp_events);
- return 0;
- case SSL_ERROR_NONE:
- /* TLS Handshake complete */
- status = tls_post_connection_check(self, tls);
- if ( status == X509_V_OK ) {
- su_wait_t wait[1] = {SU_WAIT_INIT};
- tport_master_t *mr = self->tp_master;
- int su_wait_create_ret;
- su_root_deregister(mr->mr_root, self->tp_index);
- self->tp_index = -1;
- self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP;
- if (((su_wait_create_ret = su_wait_create(wait, self->tp_socket, self->tp_events)) == -1) ||
- ((self->tp_index = su_root_register(mr->mr_root, wait, tport_wakeup,
- self, 0)) == -1)) {
- if (su_wait_create_ret == 0) {
- su_wait_destroy(wait);
- }
- tls_log_errors(3, "TLS post handshake error", status);
- tport_close(self);
- tport_set_secondary_timer(self);
- return 0;
- }
- tls->read_events = SU_WAIT_IN;
- tls->write_events = 0;
- self->tp_is_connected = 1;
- self->tp_verified = tls->x509_verified;
- self->tp_subjects = tls->subjects;
- if (tport_has_queued(self))
- tport_send_event(self);
- else
- tport_set_secondary_timer(self);
- return 0;
- }
- break;
- default:
- tls_log_errors(3, "TLS setup failed", status);
- break;
- }
- }
- /* TLS Handshake Failed or Peer Certificate did not Verify */
- tport_close(self);
- tport_set_secondary_timer(self);
- return 0;
- }
|