8e9a741832
Edited by @aaronmdjones: - Correct some data types and casts - Minor style fixups (e.g. we put * on the variable name not the type) - librb/src/openssl.c: - Defer call of BIO_free(3ssl) to the end of the conditional block to avoid having calls to it in multiple paths - Check the return value of SSL_CTX_set0_tmp_dh_pkey(3ssl) because if it fails then we must use EVP_PKEY_free(3ssl) to avoid a memory leak This could fail if, for example, the user supplied DSA parameters in the DH parameters file instead. - ircd/newconf.c: - Check whether OSSL_DECODER_CTX_new_for_pkey(3ssl) was able to parse the given CHALLANGE public key as a valid RSA public key, and then check whether OSSL_DECODER_from_bio(3ssl) actually loads it successfully - ircd/s_newconf.c: - Use EVP_PKEY_free(3ssl) instead of OPENSSL_free(3ssl) on EVP_PKEY pointers; this will avoid inadvertent memory leaks if the EVP_PKEY structure contains any dynamically-allocated child members - modules/m_challenge.c: - Unconditionally use EVP(3ssl) to generate the SHA-1 digest of the random challenge; this API has been around for a very long time and is available in all supported versions of OpenSSL - Add lots of error checking to all steps of the process Tested against 1.1.1 and 3.0; both with missing and provided DH parameters (which works as you'd expect; the server will not negotiate a DHE cipher without them), and CHALLENGE, including missing keys or keys of the wrong type (e.g. when you supply an EdDSA key instead of an RSA key). This does break compatibility with OpenSSL 1.1.0 and below, which are now all end-of-life and unsupported anyway. Closes #357
820 lines
18 KiB
C
820 lines
18 KiB
C
/*
|
|
* librb: a library used by ircd-ratbox and other things
|
|
* openssl.c: OpenSSL backend
|
|
*
|
|
* Copyright (C) 2007-2008 ircd-ratbox development team
|
|
* Copyright (C) 2007-2008 Aaron Sethman <androsyn@ratbox.org>
|
|
* Copyright (C) 2015-2016 Aaron Jones <aaronmdjones@gmail.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
|
* USA
|
|
*
|
|
*/
|
|
|
|
#include <librb_config.h>
|
|
#include <rb_lib.h>
|
|
|
|
#ifdef HAVE_OPENSSL
|
|
|
|
#include <commio-int.h>
|
|
#include <commio-ssl.h>
|
|
|
|
#include "openssl_ratbox.h"
|
|
|
|
typedef enum
|
|
{
|
|
RB_FD_TLS_DIRECTION_IN = 0,
|
|
RB_FD_TLS_DIRECTION_OUT = 1
|
|
} rb_fd_tls_direction;
|
|
|
|
#define SSL_P(x) ((SSL *)((x)->ssl))
|
|
|
|
|
|
|
|
static SSL_CTX *ssl_ctx = NULL;
|
|
|
|
struct ssl_connect
|
|
{
|
|
CNCB *callback;
|
|
void *data;
|
|
int timeout;
|
|
};
|
|
|
|
static const char *rb_ssl_strerror(unsigned long);
|
|
static void rb_ssl_connect_realcb(rb_fde_t *, int, struct ssl_connect *);
|
|
|
|
|
|
|
|
/*
|
|
* Internal OpenSSL-specific code
|
|
*/
|
|
|
|
static unsigned long
|
|
rb_ssl_last_err(void)
|
|
{
|
|
unsigned long err_saved, err = 0;
|
|
|
|
while((err_saved = ERR_get_error()) != 0)
|
|
err = err_saved;
|
|
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
rb_ssl_init_fd(rb_fde_t *const F, const rb_fd_tls_direction dir)
|
|
{
|
|
(void) rb_ssl_last_err();
|
|
|
|
F->ssl = SSL_new(ssl_ctx);
|
|
|
|
if(F->ssl == NULL)
|
|
{
|
|
rb_lib_log("%s: SSL_new: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
|
|
rb_close(F);
|
|
return;
|
|
}
|
|
|
|
switch(dir)
|
|
{
|
|
case RB_FD_TLS_DIRECTION_IN:
|
|
SSL_set_accept_state(SSL_P(F));
|
|
break;
|
|
case RB_FD_TLS_DIRECTION_OUT:
|
|
SSL_set_connect_state(SSL_P(F));
|
|
break;
|
|
}
|
|
|
|
SSL_set_fd(SSL_P(F), rb_get_fd(F));
|
|
}
|
|
|
|
static void
|
|
rb_ssl_accept_common(rb_fde_t *const F, void *const data __attribute__((unused)))
|
|
{
|
|
lrb_assert(F != NULL);
|
|
lrb_assert(F->accept != NULL);
|
|
lrb_assert(F->accept->callback != NULL);
|
|
lrb_assert(F->ssl != NULL);
|
|
|
|
(void) rb_ssl_last_err();
|
|
|
|
int ret = SSL_do_handshake(SSL_P(F));
|
|
int err = SSL_get_error(SSL_P(F), ret);
|
|
|
|
if(ret == 1)
|
|
{
|
|
F->handshake_count++;
|
|
|
|
rb_settimeout(F, 0, NULL, NULL);
|
|
rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
|
|
|
|
struct acceptdata *const ad = F->accept;
|
|
F->accept = NULL;
|
|
ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
|
|
rb_free(ad);
|
|
|
|
return;
|
|
}
|
|
if(ret == -1 && err == SSL_ERROR_WANT_READ)
|
|
{
|
|
rb_setselect(F, RB_SELECT_READ, rb_ssl_accept_common, NULL);
|
|
return;
|
|
}
|
|
if(ret == -1 && err == SSL_ERROR_WANT_WRITE)
|
|
{
|
|
rb_setselect(F, RB_SELECT_WRITE, rb_ssl_accept_common, NULL);
|
|
return;
|
|
}
|
|
|
|
errno = EIO;
|
|
F->ssl_errno = (unsigned long) err;
|
|
F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data);
|
|
}
|
|
|
|
static void
|
|
rb_ssl_connect_common(rb_fde_t *const F, void *const data)
|
|
{
|
|
lrb_assert(F != NULL);
|
|
lrb_assert(F->ssl != NULL);
|
|
|
|
(void) rb_ssl_last_err();
|
|
|
|
int ret = SSL_do_handshake(SSL_P(F));
|
|
int err = SSL_get_error(SSL_P(F), ret);
|
|
|
|
if(ret == 1)
|
|
{
|
|
F->handshake_count++;
|
|
|
|
rb_settimeout(F, 0, NULL, NULL);
|
|
rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
|
|
|
|
rb_ssl_connect_realcb(F, RB_OK, data);
|
|
|
|
return;
|
|
}
|
|
if(ret == -1 && err == SSL_ERROR_WANT_READ)
|
|
{
|
|
rb_setselect(F, RB_SELECT_READ, rb_ssl_connect_common, data);
|
|
return;
|
|
}
|
|
if(ret == -1 && err == SSL_ERROR_WANT_WRITE)
|
|
{
|
|
rb_setselect(F, RB_SELECT_WRITE, rb_ssl_connect_common, data);
|
|
return;
|
|
}
|
|
|
|
errno = EIO;
|
|
F->ssl_errno = (unsigned long) err;
|
|
rb_ssl_connect_realcb(F, RB_ERROR_SSL, data);
|
|
}
|
|
|
|
static const char *
|
|
rb_ssl_strerror(const unsigned long err)
|
|
{
|
|
static char errbuf[512];
|
|
|
|
ERR_error_string_n(err, errbuf, sizeof errbuf);
|
|
|
|
return errbuf;
|
|
}
|
|
|
|
static int
|
|
verify_accept_all_cb(const int preverify_ok __attribute__((unused)), X509_STORE_CTX *const x509_ctx __attribute__((unused)))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static ssize_t
|
|
rb_ssl_read_or_write(const int r_or_w, rb_fde_t *const F, void *const rbuf, const void *const wbuf, const size_t count)
|
|
{
|
|
ssize_t ret;
|
|
unsigned long err;
|
|
|
|
(void) rb_ssl_last_err();
|
|
|
|
if(r_or_w == 0)
|
|
ret = (ssize_t) SSL_read(SSL_P(F), rbuf, (int)count);
|
|
else
|
|
ret = (ssize_t) SSL_write(SSL_P(F), wbuf, (int)count);
|
|
|
|
if(ret < 0)
|
|
{
|
|
switch(SSL_get_error(SSL_P(F), ret))
|
|
{
|
|
case SSL_ERROR_WANT_READ:
|
|
errno = EAGAIN;
|
|
return RB_RW_SSL_NEED_READ;
|
|
case SSL_ERROR_WANT_WRITE:
|
|
errno = EAGAIN;
|
|
return RB_RW_SSL_NEED_WRITE;
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
return 0;
|
|
case SSL_ERROR_SYSCALL:
|
|
err = rb_ssl_last_err();
|
|
if(err == 0)
|
|
{
|
|
F->ssl_errno = 0;
|
|
return RB_RW_IO_ERROR;
|
|
}
|
|
break;
|
|
default:
|
|
err = rb_ssl_last_err();
|
|
break;
|
|
}
|
|
|
|
F->ssl_errno = err;
|
|
if(err > 0)
|
|
{
|
|
errno = EIO; /* not great but... */
|
|
return RB_RW_SSL_ERROR;
|
|
}
|
|
return RB_RW_IO_ERROR;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
make_certfp(X509 *const cert, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
|
|
{
|
|
unsigned int hashlen = 0;
|
|
const EVP_MD *md_type = NULL;
|
|
const ASN1_ITEM *item = NULL;
|
|
void *data = NULL;
|
|
|
|
switch(method)
|
|
{
|
|
case RB_SSL_CERTFP_METH_CERT_SHA1:
|
|
hashlen = RB_SSL_CERTFP_LEN_SHA1;
|
|
md_type = EVP_sha1();
|
|
item = ASN1_ITEM_rptr(X509);
|
|
data = cert;
|
|
break;
|
|
case RB_SSL_CERTFP_METH_CERT_SHA256:
|
|
hashlen = RB_SSL_CERTFP_LEN_SHA256;
|
|
md_type = EVP_sha256();
|
|
item = ASN1_ITEM_rptr(X509);
|
|
data = cert;
|
|
break;
|
|
case RB_SSL_CERTFP_METH_CERT_SHA512:
|
|
hashlen = RB_SSL_CERTFP_LEN_SHA512;
|
|
md_type = EVP_sha512();
|
|
item = ASN1_ITEM_rptr(X509);
|
|
data = cert;
|
|
break;
|
|
case RB_SSL_CERTFP_METH_SPKI_SHA256:
|
|
hashlen = RB_SSL_CERTFP_LEN_SHA256;
|
|
md_type = EVP_sha256();
|
|
item = ASN1_ITEM_rptr(X509_PUBKEY);
|
|
data = X509_get_X509_PUBKEY(cert);
|
|
break;
|
|
case RB_SSL_CERTFP_METH_SPKI_SHA512:
|
|
hashlen = RB_SSL_CERTFP_LEN_SHA512;
|
|
md_type = EVP_sha512();
|
|
item = ASN1_ITEM_rptr(X509_PUBKEY);
|
|
data = X509_get_X509_PUBKEY(cert);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
if(ASN1_item_digest(item, md_type, data, certfp, &hashlen) != 1)
|
|
{
|
|
rb_lib_log("%s: ASN1_item_digest: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
|
|
return 0;
|
|
}
|
|
|
|
return (int) hashlen;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* External OpenSSL-specific code
|
|
*/
|
|
|
|
void
|
|
rb_ssl_shutdown(rb_fde_t *const F)
|
|
{
|
|
if(F == NULL || F->ssl == NULL)
|
|
return;
|
|
|
|
(void) rb_ssl_last_err();
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
int ret = SSL_shutdown(SSL_P(F));
|
|
int err = SSL_get_error(SSL_P(F), ret);
|
|
|
|
if(ret >= 0 || (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE))
|
|
break;
|
|
}
|
|
|
|
SSL_free(SSL_P(F));
|
|
F->ssl = NULL;
|
|
}
|
|
|
|
int
|
|
rb_init_ssl(void)
|
|
{
|
|
#ifndef LRB_SSL_NO_EXPLICIT_INIT
|
|
(void) SSL_library_init();
|
|
SSL_load_error_strings();
|
|
#endif
|
|
|
|
rb_lib_log("%s: OpenSSL backend initialised", __func__);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
rb_setup_ssl_server(const char *const certfile, const char *keyfile,
|
|
const char *const dhfile, const char *cipherlist)
|
|
{
|
|
if(certfile == NULL)
|
|
{
|
|
rb_lib_log("%s: no certificate file specified", __func__);
|
|
return 0;
|
|
}
|
|
|
|
if(keyfile == NULL)
|
|
keyfile = certfile;
|
|
|
|
if(cipherlist == NULL)
|
|
cipherlist = rb_default_ciphers;
|
|
|
|
|
|
(void) rb_ssl_last_err();
|
|
|
|
#ifdef LRB_HAVE_TLS_METHOD_API
|
|
SSL_CTX *const ssl_ctx_new = SSL_CTX_new(TLS_method());
|
|
#else
|
|
SSL_CTX *const ssl_ctx_new = SSL_CTX_new(SSLv23_method());
|
|
#endif
|
|
|
|
if(ssl_ctx_new == NULL)
|
|
{
|
|
rb_lib_log("%s: SSL_CTX_new: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
|
|
return 0;
|
|
}
|
|
|
|
if(SSL_CTX_use_certificate_chain_file(ssl_ctx_new, certfile) != 1)
|
|
{
|
|
rb_lib_log("%s: SSL_CTX_use_certificate_chain_file ('%s'): %s", __func__, certfile,
|
|
rb_ssl_strerror(rb_ssl_last_err()));
|
|
|
|
SSL_CTX_free(ssl_ctx_new);
|
|
return 0;
|
|
}
|
|
|
|
if(SSL_CTX_use_PrivateKey_file(ssl_ctx_new, keyfile, SSL_FILETYPE_PEM) != 1)
|
|
{
|
|
rb_lib_log("%s: SSL_CTX_use_PrivateKey_file ('%s'): %s", __func__, keyfile,
|
|
rb_ssl_strerror(rb_ssl_last_err()));
|
|
|
|
SSL_CTX_free(ssl_ctx_new);
|
|
return 0;
|
|
}
|
|
|
|
if(dhfile == NULL)
|
|
{
|
|
rb_lib_log("%s: no DH parameters file specified", __func__);
|
|
}
|
|
else
|
|
{
|
|
BIO *const dhf = BIO_new_file(dhfile, "r");
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
|
|
EVP_PKEY *dhp = NULL;
|
|
#else
|
|
DH *dhp = NULL;
|
|
#endif
|
|
|
|
if(dhf == NULL)
|
|
{
|
|
rb_lib_log("%s: BIO_new_file ('%s'): %s", __func__, dhfile,
|
|
rb_ssl_strerror(rb_ssl_last_err()));
|
|
}
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
|
|
else if(PEM_read_bio_Parameters(dhf, &dhp) == NULL)
|
|
{
|
|
rb_lib_log("%s: PEM_read_bio_Parameters ('%s'): %s", __func__, dhfile,
|
|
rb_ssl_strerror(rb_ssl_last_err()));
|
|
}
|
|
#else
|
|
else if(PEM_read_bio_DHparams(dhf, &dhp, NULL, NULL) == NULL)
|
|
{
|
|
rb_lib_log("%s: PEM_read_bio_DHparams ('%s'): %s", __func__, dhfile,
|
|
rb_ssl_strerror(rb_ssl_last_err()));
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
|
|
if(SSL_CTX_set0_tmp_dh_pkey(ssl_ctx_new, dhp) != 1)
|
|
{
|
|
rb_lib_log("%s: SSL_CTX_set0_tmp_dh_pkey ('%s'): %s", __func__, dhfile,
|
|
rb_ssl_strerror(rb_ssl_last_err()));
|
|
EVP_PKEY_free(dhp);
|
|
}
|
|
#else
|
|
SSL_CTX_set_tmp_dh(ssl_ctx_new, dhp);
|
|
DH_free(dhp);
|
|
#endif
|
|
}
|
|
|
|
BIO_free(dhf);
|
|
}
|
|
|
|
int ret_old = SSL_CTX_set_cipher_list(ssl_ctx_new, cipherlist);
|
|
|
|
if (ret_old != 1)
|
|
{
|
|
(void) rb_lib_log("%s: no valid old-style ciphersuites found "
|
|
"in ssl_cipher_list; will use defaults",
|
|
__func__);
|
|
|
|
ret_old = SSL_CTX_set_cipher_list(ssl_ctx_new, rb_default_ciphers);
|
|
}
|
|
|
|
#ifndef LRB_HAVE_TLS13
|
|
int ret_new = 0;
|
|
#else
|
|
int ret_new = SSL_CTX_set_ciphersuites(ssl_ctx_new, cipherlist);
|
|
|
|
if (ret_new != 1)
|
|
{
|
|
(void) rb_lib_log("%s: no valid new-style ciphersuites found "
|
|
"in ssl_cipher_list; will use defaults",
|
|
__func__);
|
|
|
|
ret_new = SSL_CTX_set_ciphersuites(ssl_ctx_new, rb_default_ciphers);
|
|
}
|
|
#endif
|
|
|
|
if (ret_old != 1 && ret_new != 1)
|
|
{
|
|
rb_lib_log("%s: could not configure any ciphers", __func__);
|
|
SSL_CTX_free(ssl_ctx_new);
|
|
return 0;
|
|
}
|
|
|
|
|
|
SSL_CTX_set_session_cache_mode(ssl_ctx_new, SSL_SESS_CACHE_OFF);
|
|
SSL_CTX_set_verify(ssl_ctx_new, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_accept_all_cb);
|
|
|
|
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
|
|
(void) SSL_CTX_clear_options(ssl_ctx_new, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
|
|
#endif
|
|
|
|
#ifndef LRB_HAVE_TLS_METHOD_API
|
|
(void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
|
#endif
|
|
|
|
#ifdef SSL_OP_NO_TICKET
|
|
(void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_NO_TICKET);
|
|
#endif
|
|
|
|
#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
|
|
(void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
|
#endif
|
|
|
|
#ifdef SSL_OP_SINGLE_DH_USE
|
|
(void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_SINGLE_DH_USE);
|
|
#endif
|
|
|
|
#ifdef SSL_OP_SINGLE_ECDH_USE
|
|
(void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_SINGLE_ECDH_USE);
|
|
#endif
|
|
|
|
#ifdef LRB_HAVE_TLS_ECDH_AUTO
|
|
(void) SSL_CTX_set_ecdh_auto(ssl_ctx_new, 1);
|
|
#endif
|
|
|
|
#ifdef LRB_HAVE_TLS_SET_CURVES
|
|
(void) SSL_CTX_set1_curves_list(ssl_ctx_new, rb_default_curves);
|
|
#else
|
|
# if (OPENSSL_VERSION_NUMBER >= 0x10000000L) && !defined(OPENSSL_NO_ECDH) && defined(NID_secp384r1)
|
|
EC_KEY *const ec_key = EC_KEY_new_by_curve_name(NID_secp384r1);
|
|
if(ec_key != NULL)
|
|
{
|
|
SSL_CTX_set_tmp_ecdh(ssl_ctx_new, ec_key);
|
|
EC_KEY_free(ec_key);
|
|
}
|
|
else
|
|
rb_lib_log("%s: EC_KEY_new_by_curve_name failed; will not enable ECDHE- ciphers", __func__);
|
|
# else
|
|
rb_lib_log("%s: OpenSSL built without ECDH support; will not enable ECDHE- ciphers", __func__);
|
|
# endif
|
|
#endif
|
|
|
|
|
|
if(ssl_ctx)
|
|
SSL_CTX_free(ssl_ctx);
|
|
|
|
ssl_ctx = ssl_ctx_new;
|
|
|
|
|
|
rb_lib_log("%s: TLS configuration successful", __func__);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
rb_init_prng(const char *const path, prng_seed_t seed_type)
|
|
{
|
|
(void) rb_ssl_last_err();
|
|
|
|
if(seed_type == RB_PRNG_FILE && RAND_load_file(path, -1) < 0)
|
|
rb_lib_log("%s: RAND_load_file: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
|
|
|
|
if(RAND_status() != 1)
|
|
{
|
|
rb_lib_log("%s: RAND_status: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
|
|
return 0;
|
|
}
|
|
|
|
rb_lib_log("%s: PRNG initialised", __func__);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
rb_get_random(void *const buf, const size_t length)
|
|
{
|
|
(void) rb_ssl_last_err();
|
|
|
|
if(RAND_bytes(buf, (int) length) != 1)
|
|
{
|
|
rb_lib_log("%s: RAND_bytes: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
const char *
|
|
rb_get_ssl_strerror(rb_fde_t *const F)
|
|
{
|
|
return rb_ssl_strerror(F->ssl_errno);
|
|
}
|
|
|
|
int
|
|
rb_get_ssl_certfp(rb_fde_t *const F, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
|
|
{
|
|
if(F == NULL || F->ssl == NULL)
|
|
return 0;
|
|
|
|
X509 *const peer_cert = SSL_get_peer_certificate(SSL_P(F));
|
|
if(peer_cert == NULL)
|
|
return 0;
|
|
|
|
int len = 0;
|
|
|
|
switch(SSL_get_verify_result(SSL_P(F)))
|
|
{
|
|
case X509_V_OK:
|
|
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
|
|
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
|
|
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
|
|
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
|
|
case X509_V_ERR_CERT_UNTRUSTED:
|
|
len = make_certfp(peer_cert, certfp, method);
|
|
// fallthrough
|
|
default:
|
|
X509_free(peer_cert);
|
|
return len;
|
|
}
|
|
}
|
|
|
|
int
|
|
rb_get_ssl_certfp_file(const char *const filename, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
|
|
{
|
|
FILE *const fp = fopen(filename, "r");
|
|
if (fp == NULL)
|
|
return -1;
|
|
|
|
X509 *const cert = PEM_read_X509(fp, NULL, NULL, NULL);
|
|
if (cert == NULL)
|
|
{
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
|
|
int len = make_certfp(cert, certfp, method);
|
|
|
|
X509_free(cert);
|
|
fclose(fp);
|
|
|
|
return len;
|
|
}
|
|
|
|
void
|
|
rb_get_ssl_info(char *const buf, const size_t len)
|
|
{
|
|
#ifdef LRB_SSL_FULL_VERSION_INFO
|
|
if(LRB_SSL_VNUM_RUNTIME == LRB_SSL_VNUM_COMPILETIME)
|
|
(void) snprintf(buf, len, "OpenSSL: compiled 0x%lx, library %s",
|
|
LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_COMPILETIME);
|
|
else
|
|
(void) snprintf(buf, len, "OpenSSL: compiled (0x%lx, %s), library (0x%lx, %s)",
|
|
LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_COMPILETIME,
|
|
LRB_SSL_VNUM_RUNTIME, LRB_SSL_VTEXT_RUNTIME);
|
|
#else
|
|
(void) snprintf(buf, len, "OpenSSL: compiled 0x%lx, library %s",
|
|
LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_RUNTIME);
|
|
#endif
|
|
}
|
|
|
|
const char *
|
|
rb_ssl_get_cipher(rb_fde_t *const F)
|
|
{
|
|
if(F == NULL || F->ssl == NULL)
|
|
return NULL;
|
|
|
|
static char buf[512];
|
|
|
|
const char *const version = SSL_get_version(SSL_P(F));
|
|
const char *const cipher = SSL_get_cipher_name(SSL_P(F));
|
|
|
|
(void) snprintf(buf, sizeof buf, "%s, %s", version, cipher);
|
|
|
|
return buf;
|
|
}
|
|
|
|
ssize_t
|
|
rb_ssl_read(rb_fde_t *const F, void *const buf, const size_t count)
|
|
{
|
|
return rb_ssl_read_or_write(0, F, buf, NULL, count);
|
|
}
|
|
|
|
ssize_t
|
|
rb_ssl_write(rb_fde_t *const F, const void *const buf, const size_t count)
|
|
{
|
|
return rb_ssl_read_or_write(1, F, NULL, buf, count);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Internal library-agnostic code
|
|
*/
|
|
|
|
static void
|
|
rb_ssl_connect_realcb(rb_fde_t *const F, const int status, struct ssl_connect *const sconn)
|
|
{
|
|
lrb_assert(F->connect != NULL);
|
|
|
|
F->connect->callback = sconn->callback;
|
|
F->connect->data = sconn->data;
|
|
|
|
rb_connect_callback(F, status);
|
|
rb_free(sconn);
|
|
}
|
|
|
|
static void
|
|
rb_ssl_timeout_cb(rb_fde_t *const F, void *const data __attribute__((unused)))
|
|
{
|
|
lrb_assert(F->accept != NULL);
|
|
lrb_assert(F->accept->callback != NULL);
|
|
|
|
F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data);
|
|
}
|
|
|
|
static void
|
|
rb_ssl_tryconn_timeout_cb(rb_fde_t *const F, void *const data)
|
|
{
|
|
rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data);
|
|
}
|
|
|
|
static void
|
|
rb_ssl_tryconn(rb_fde_t *const F, const int status, void *const data)
|
|
{
|
|
lrb_assert(F != NULL);
|
|
|
|
struct ssl_connect *const sconn = data;
|
|
|
|
if(status != RB_OK)
|
|
{
|
|
rb_ssl_connect_realcb(F, status, sconn);
|
|
return;
|
|
}
|
|
|
|
F->type |= RB_FD_SSL;
|
|
|
|
rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
|
|
rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT);
|
|
rb_ssl_connect_common(F, sconn);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* External library-agnostic code
|
|
*/
|
|
|
|
int
|
|
rb_supports_ssl(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
unsigned int
|
|
rb_ssl_handshake_count(rb_fde_t *const F)
|
|
{
|
|
return F->handshake_count;
|
|
}
|
|
|
|
void
|
|
rb_ssl_clear_handshake_count(rb_fde_t *const F)
|
|
{
|
|
F->handshake_count = 0;
|
|
}
|
|
|
|
void
|
|
rb_ssl_start_accepted(rb_fde_t *const F, ACCB *const cb, void *const data, const int timeout)
|
|
{
|
|
F->type |= RB_FD_SSL;
|
|
|
|
F->accept = rb_malloc(sizeof(struct acceptdata));
|
|
F->accept->callback = cb;
|
|
F->accept->data = data;
|
|
F->accept->addrlen = 0;
|
|
(void) memset(&F->accept->S, 0x00, sizeof F->accept->S);
|
|
|
|
rb_settimeout(F, timeout, rb_ssl_timeout_cb, NULL);
|
|
rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_IN);
|
|
rb_ssl_accept_common(F, NULL);
|
|
}
|
|
|
|
void
|
|
rb_ssl_accept_setup(rb_fde_t *const srv_F, rb_fde_t *const cli_F, struct sockaddr *const st, const int addrlen)
|
|
{
|
|
cli_F->type |= RB_FD_SSL;
|
|
|
|
cli_F->accept = rb_malloc(sizeof(struct acceptdata));
|
|
cli_F->accept->callback = srv_F->accept->callback;
|
|
cli_F->accept->data = srv_F->accept->data;
|
|
cli_F->accept->addrlen = (rb_socklen_t) addrlen;
|
|
(void) memset(&cli_F->accept->S, 0x00, sizeof cli_F->accept->S);
|
|
(void) memcpy(&cli_F->accept->S, st, (size_t) addrlen);
|
|
|
|
rb_settimeout(cli_F, 10, rb_ssl_timeout_cb, NULL);
|
|
rb_ssl_init_fd(cli_F, RB_FD_TLS_DIRECTION_IN);
|
|
rb_ssl_accept_common(cli_F, NULL);
|
|
}
|
|
|
|
int
|
|
rb_ssl_listen(rb_fde_t *const F, const int backlog, const int defer_accept)
|
|
{
|
|
int result = rb_listen(F, backlog, defer_accept);
|
|
|
|
F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL;
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
rb_connect_tcp_ssl(rb_fde_t *const F, struct sockaddr *const dest, struct sockaddr *const clocal,
|
|
CNCB *const callback, void *const data, const int timeout)
|
|
{
|
|
if(F == NULL)
|
|
return;
|
|
|
|
struct ssl_connect *const sconn = rb_malloc(sizeof *sconn);
|
|
sconn->data = data;
|
|
sconn->callback = callback;
|
|
sconn->timeout = timeout;
|
|
|
|
rb_connect_tcp(F, dest, clocal, rb_ssl_tryconn, sconn, timeout);
|
|
}
|
|
|
|
void
|
|
rb_ssl_start_connected(rb_fde_t *const F, CNCB *const callback, void *const data, const int timeout)
|
|
{
|
|
if(F == NULL)
|
|
return;
|
|
|
|
struct ssl_connect *const sconn = rb_malloc(sizeof *sconn);
|
|
sconn->data = data;
|
|
sconn->callback = callback;
|
|
sconn->timeout = timeout;
|
|
|
|
F->connect = rb_malloc(sizeof(struct conndata));
|
|
F->connect->callback = callback;
|
|
F->connect->data = data;
|
|
F->type |= RB_FD_SSL;
|
|
|
|
rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
|
|
rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT);
|
|
rb_ssl_connect_common(F, sconn);
|
|
}
|
|
|
|
#endif /* HAVE_OPENSSL */
|