X-Git-Url: https://jfr.im/git/irc/rqf/shadowircd.git/blobdiff_plain/cee7d8576df097061e13113381db8f9b1610343e..6f187f63b510ade944b8b3704727eeff3f0d31ca:/libratbox/src/openssl.c diff --git a/libratbox/src/openssl.c b/libratbox/src/openssl.c index 6d5562a..1c7c0ec 100644 --- a/libratbox/src/openssl.c +++ b/libratbox/src/openssl.c @@ -20,7 +20,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * - * $Id: commio.c 24808 2008-01-02 08:17:05Z androsyn $ */ #include @@ -37,14 +36,16 @@ static SSL_CTX *ssl_server_ctx; static SSL_CTX *ssl_client_ctx; +static int libratbox_index = -1; -static unsigned long get_last_err(void) +static unsigned long +get_last_err(void) { unsigned long t_err, err = 0; err = ERR_get_error(); if(err == 0) return 0; - + while((t_err = ERR_get_error()) > 0) err = t_err; @@ -52,14 +53,14 @@ static unsigned long get_last_err(void) } void -rb_ssl_shutdown(rb_fde_t * F) +rb_ssl_shutdown(rb_fde_t *F) { int i; if(F == NULL || F->ssl == NULL) return; SSL_set_shutdown((SSL *) F->ssl, SSL_RECEIVED_SHUTDOWN); - for (i = 0; i < 4; i++) + for(i = 0; i < 4; i++) { if(SSL_shutdown((SSL *) F->ssl)) break; @@ -68,19 +69,52 @@ rb_ssl_shutdown(rb_fde_t * F) SSL_free((SSL *) F->ssl); } +unsigned int +rb_ssl_handshake_count(rb_fde_t *F) +{ + return F->handshake_count; +} + +void +rb_ssl_clear_handshake_count(rb_fde_t *F) +{ + F->handshake_count = 0; +} + static void -rb_ssl_timeout(rb_fde_t * fd, void *notused) +rb_ssl_timeout(rb_fde_t *F, void *notused) { - rb_close(fd); + lrb_assert(F->accept != NULL); + F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data); } static void -rb_ssl_tryaccept(rb_fde_t * F, void *data) +rb_ssl_info_callback(SSL * ssl, int where, int ret) +{ + if(where & SSL_CB_HANDSHAKE_START) + { + rb_fde_t *F = SSL_get_ex_data(ssl, libratbox_index); + if(F == NULL) + return; + F->handshake_count++; + } +} + +static void +rb_setup_ssl_cb(rb_fde_t *F) +{ + SSL_set_ex_data(F->ssl, libratbox_index, (char *)F); + SSL_set_info_callback((SSL *) F->ssl, (void (*)(const SSL *,int,int))rb_ssl_info_callback); +} + +static void +rb_ssl_tryaccept(rb_fde_t *F, void *data) { int ssl_err; lrb_assert(F->accept != NULL); - int flags = RB_SELECT_READ; + int flags; + struct acceptdata *ad; if(!SSL_is_init_finished((SSL *) F->ssl)) { @@ -88,18 +122,18 @@ rb_ssl_tryaccept(rb_fde_t * F, void *data) { switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err)) { - case SSL_ERROR_SYSCALL: - if(rb_ignore_errno(errno)) case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: - { - if(ssl_err == SSL_ERROR_WANT_WRITE) - flags |= RB_SELECT_WRITE; - F->ssl_errno = get_last_err(); - rb_setselect(F, flags, - rb_ssl_tryaccept, NULL); - return; - } + if(ssl_err == SSL_ERROR_WANT_WRITE) + flags = RB_SELECT_WRITE; + else + flags = RB_SELECT_READ; + F->ssl_errno = get_last_err(); + rb_setselect(F, flags, rb_ssl_tryaccept, NULL); + break; + case SSL_ERROR_SYSCALL: + F->accept->callback(F, RB_ERROR, NULL, 0, F->accept->data); + break; default: F->ssl_errno = get_last_err(); F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data); @@ -110,29 +144,19 @@ rb_ssl_tryaccept(rb_fde_t * F, void *data) } rb_settimeout(F, 0, NULL, NULL); rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL); - - F->accept->callback(F, RB_OK, (struct sockaddr *) &F->accept->S, F->accept->addrlen, - F->accept->data); - rb_free(F->accept); + + ad = F->accept; F->accept = NULL; + ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data); + rb_free(ad); } -void -rb_ssl_start_accepted(rb_fde_t * new_F, ACCB * cb, void *data, int timeout) + +static void +rb_ssl_accept_common(rb_fde_t *new_F) { int ssl_err; - - new_F->type |= RB_FD_SSL; - new_F->ssl = SSL_new(ssl_server_ctx); - new_F->accept = rb_malloc(sizeof(struct acceptdata)); - - new_F->accept->callback = cb; - new_F->accept->data = data; - rb_settimeout(new_F, timeout, rb_ssl_timeout, NULL); - - new_F->accept->addrlen = 0; - SSL_set_fd((SSL *) new_F->ssl, rb_get_fd(new_F)); if((ssl_err = SSL_accept((SSL *) new_F->ssl)) <= 0) { switch (ssl_err = SSL_get_error((SSL *) new_F->ssl, ssl_err)) @@ -159,16 +183,29 @@ rb_ssl_start_accepted(rb_fde_t * new_F, ACCB * cb, void *data, int timeout) } } +void +rb_ssl_start_accepted(rb_fde_t *new_F, ACCB * cb, void *data, int timeout) +{ + new_F->type |= RB_FD_SSL; + new_F->ssl = SSL_new(ssl_server_ctx); + new_F->accept = rb_malloc(sizeof(struct acceptdata)); + + new_F->accept->callback = cb; + new_F->accept->data = data; + rb_settimeout(new_F, timeout, rb_ssl_timeout, NULL); + + new_F->accept->addrlen = 0; + SSL_set_fd((SSL *) new_F->ssl, rb_get_fd(new_F)); + rb_setup_ssl_cb(new_F); + rb_ssl_accept_common(new_F); +} + void -rb_ssl_accept_setup(rb_fde_t * F, int new_fd, struct sockaddr *st, int addrlen) +rb_ssl_accept_setup(rb_fde_t *F, rb_fde_t *new_F, struct sockaddr *st, int addrlen) { - rb_fde_t *new_F; - int ssl_err; - - new_F = rb_find_fd(new_fd); new_F->type |= RB_FD_SSL; new_F->ssl = SSL_new(ssl_server_ctx); new_F->accept = rb_malloc(sizeof(struct acceptdata)); @@ -179,44 +216,22 @@ rb_ssl_accept_setup(rb_fde_t * F, int new_fd, struct sockaddr *st, int addrlen) memcpy(&new_F->accept->S, st, addrlen); new_F->accept->addrlen = addrlen; - SSL_set_fd((SSL *) new_F->ssl, new_fd); - if((ssl_err = SSL_accept((SSL *) new_F->ssl)) <= 0) - { - switch (ssl_err = SSL_get_error((SSL *) new_F->ssl, ssl_err)) - { - case SSL_ERROR_SYSCALL: - if(rb_ignore_errno(errno)) - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - { - F->ssl_errno = get_last_err(); - rb_setselect(new_F, RB_SELECT_READ | RB_SELECT_WRITE, - rb_ssl_tryaccept, NULL); - return; - } - default: - F->ssl_errno = get_last_err(); - F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data); - return; - } - } - else - { - rb_ssl_tryaccept(new_F, NULL); - } + SSL_set_fd((SSL *) new_F->ssl, rb_get_fd(new_F)); + rb_setup_ssl_cb(new_F); + rb_ssl_accept_common(new_F); } static ssize_t -rb_ssl_read_or_write(int r_or_w, rb_fde_t * F, void *rbuf, const void *wbuf, size_t count) +rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size_t count) { ssize_t ret; unsigned long err; SSL *ssl = F->ssl; if(r_or_w == 0) - ret = (ssize_t)SSL_read(ssl, rbuf, (int) count); + ret = (ssize_t) SSL_read(ssl, rbuf, (int)count); else - ret = (ssize_t)SSL_write(ssl, wbuf, (int) count); + ret = (ssize_t) SSL_write(ssl, wbuf, (int)count); if(ret < 0) { @@ -254,39 +269,57 @@ rb_ssl_read_or_write(int r_or_w, rb_fde_t * F, void *rbuf, const void *wbuf, siz } ssize_t -rb_ssl_read(rb_fde_t * F, void *buf, size_t count) +rb_ssl_read(rb_fde_t *F, void *buf, size_t count) { return rb_ssl_read_or_write(0, F, buf, NULL, count); } ssize_t -rb_ssl_write(rb_fde_t * F, const void *buf, size_t count) +rb_ssl_write(rb_fde_t *F, const void *buf, size_t count) { return rb_ssl_read_or_write(1, F, NULL, buf, count); } +static int +verify_accept_all_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + return 1; +} + +static const char * +get_ssl_error(unsigned long err) +{ + static char buf[512]; + + ERR_error_string_n(err, buf, sizeof buf); + return buf; +} + int rb_init_ssl(void) { int ret = 1; + char libratbox_data[] = "libratbox data"; SSL_load_error_strings(); SSL_library_init(); + libratbox_index = SSL_get_ex_new_index(0, libratbox_data, NULL, NULL, NULL); ssl_server_ctx = SSL_CTX_new(SSLv23_server_method()); if(ssl_server_ctx == NULL) { rb_lib_log("rb_init_openssl: Unable to initialize OpenSSL server context: %s", - ERR_error_string(ERR_get_error(), NULL)); + get_ssl_error(ERR_get_error())); ret = 0; } /* Disable SSLv2, make the client use our settings */ SSL_CTX_set_options(ssl_server_ctx, SSL_OP_NO_SSLv2 | SSL_OP_CIPHER_SERVER_PREFERENCE); - + SSL_CTX_set_verify(ssl_server_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_accept_all_cb); + ssl_client_ctx = SSL_CTX_new(TLSv1_client_method()); if(ssl_client_ctx == NULL) { rb_lib_log("rb_init_openssl: Unable to initialize OpenSSL client context: %s", - ERR_error_string(ERR_get_error(), NULL)); + get_ssl_error(ERR_get_error())); ret = 0; } return ret; @@ -296,7 +329,6 @@ rb_init_ssl(void) int rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile) { - FILE *param; DH *dh; unsigned long err; if(cert == NULL) @@ -304,11 +336,11 @@ rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile) rb_lib_log("rb_setup_ssl_server: No certificate file"); return 0; } - if(!SSL_CTX_use_certificate_file(ssl_server_ctx, cert, SSL_FILETYPE_PEM)) + if(!SSL_CTX_use_certificate_chain_file(ssl_server_ctx, cert)) { err = ERR_get_error(); rb_lib_log("rb_setup_ssl_server: Error loading certificate file [%s]: %s", cert, - ERR_error_string(err, NULL)); + get_ssl_error(err)); return 0; } @@ -323,35 +355,41 @@ rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile) { err = ERR_get_error(); rb_lib_log("rb_setup_ssl_server: Error loading keyfile [%s]: %s", keyfile, - ERR_error_string(err, NULL)); + get_ssl_error(err)); return 0; } if(dhfile != NULL) { /* DH parameters aren't necessary, but they are nice..if they didn't pass one..that is their problem */ - param = fopen(dhfile, "r"); - if(param != NULL) + BIO *bio = BIO_new_file(dhfile, "r"); + if(bio != NULL) { - dh = PEM_read_DHparams(param, NULL, NULL, NULL); + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); if(dh == NULL) { err = ERR_get_error(); rb_lib_log ("rb_setup_ssl_server: Error loading DH params file [%s]: %s", - param, ERR_error_string(err, NULL)); - fclose(param); + dhfile, get_ssl_error(err)); + BIO_free(bio); return 0; } + BIO_free(bio); SSL_CTX_set_tmp_dh(ssl_server_ctx, dh); - fclose(param); + } + else + { + err = ERR_get_error(); + rb_lib_log("rb_setup_ssl_server: Error loading DH params file [%s]: %s", + dhfile, get_ssl_error(err)); } } return 1; } int -rb_ssl_listen(rb_fde_t * F, int backlog) +rb_ssl_listen(rb_fde_t *F, int backlog) { F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL; return listen(F->fd, backlog); @@ -365,7 +403,7 @@ struct ssl_connect }; static void -rb_ssl_connect_realcb(rb_fde_t * F, int status, struct ssl_connect *sconn) +rb_ssl_connect_realcb(rb_fde_t *F, int status, struct ssl_connect *sconn) { F->connect->callback = sconn->callback; F->connect->data = sconn->data; @@ -374,13 +412,13 @@ rb_ssl_connect_realcb(rb_fde_t * F, int status, struct ssl_connect *sconn) } static void -rb_ssl_tryconn_timeout_cb(rb_fde_t * F, void *data) +rb_ssl_tryconn_timeout_cb(rb_fde_t *F, void *data) { rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data); } static void -rb_ssl_tryconn_cb(rb_fde_t * F, void *data) +rb_ssl_tryconn_cb(rb_fde_t *F, void *data) { struct ssl_connect *sconn = data; int ssl_err; @@ -414,7 +452,7 @@ rb_ssl_tryconn_cb(rb_fde_t * F, void *data) } static void -rb_ssl_tryconn(rb_fde_t * F, int status, void *data) +rb_ssl_tryconn(rb_fde_t *F, int status, void *data) { struct ssl_connect *sconn = data; int ssl_err; @@ -427,7 +465,7 @@ rb_ssl_tryconn(rb_fde_t * F, int status, void *data) F->type |= RB_FD_SSL; F->ssl = SSL_new(ssl_client_ctx); SSL_set_fd((SSL *) F->ssl, F->fd); - + rb_setup_ssl_cb(F); rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0) { @@ -456,7 +494,7 @@ rb_ssl_tryconn(rb_fde_t * F, int status, void *data) } void -rb_connect_tcp_ssl(rb_fde_t * F, struct sockaddr *dest, +rb_connect_tcp_ssl(rb_fde_t *F, struct sockaddr *dest, struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int timeout) { struct ssl_connect *sconn; @@ -472,7 +510,7 @@ rb_connect_tcp_ssl(rb_fde_t * F, struct sockaddr *dest, } void -rb_ssl_start_connected(rb_fde_t * F, CNCB * callback, void *data, int timeout) +rb_ssl_start_connected(rb_fde_t *F, CNCB * callback, void *data, int timeout) { struct ssl_connect *sconn; int ssl_err; @@ -488,8 +526,9 @@ rb_ssl_start_connected(rb_fde_t * F, CNCB * callback, void *data, int timeout) F->connect->data = data; F->type |= RB_FD_SSL; F->ssl = SSL_new(ssl_client_ctx); - + SSL_set_fd((SSL *) F->ssl, F->fd); + rb_setup_ssl_cb(F); rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0) { @@ -522,7 +561,7 @@ rb_init_prng(const char *path, prng_seed_t seed_type) { if(seed_type == RB_PRNG_DEFAULT) { -#ifdef WIN32 +#ifdef _WIN32 RAND_screen(); #endif return RAND_status(); @@ -540,7 +579,7 @@ rb_init_prng(const char *path, prng_seed_t seed_type) if(RAND_load_file(path, -1) == -1) return -1; break; -#ifdef WIN32 +#ifdef _WIN32 case RB_PRNGWIN32: RAND_screen(); break; @@ -555,24 +594,57 @@ rb_init_prng(const char *path, prng_seed_t seed_type) int rb_get_random(void *buf, size_t length) { - if(RAND_status()) + int ret; + + if((ret = RAND_bytes(buf, length)) == 0) { - if(RAND_bytes(buf, length) > 0) - return 1; + /* remove the error from the queue */ + ERR_get_error(); } - else - { - if(RAND_pseudo_bytes(buf, length) >= 0) - return 1; - } - return 0; + return ret; } +int +rb_get_pseudo_random(void *buf, size_t length) +{ + int ret; + ret = RAND_pseudo_bytes(buf, length); + if(ret < 0) + return 0; + return 1; +} const char * -rb_get_ssl_strerror(rb_fde_t * F) +rb_get_ssl_strerror(rb_fde_t *F) { - return ERR_error_string(F->ssl_errno, NULL); + return get_ssl_error(F->ssl_errno); +} + +int +rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN]) +{ + X509 *cert; + int res; + + if (F->ssl == NULL) + return 0; + + cert = SSL_get_peer_certificate((SSL *) F->ssl); + if(cert != NULL) + { + res = SSL_get_verify_result((SSL *) F->ssl); + if(res == X509_V_OK || + res == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN || + res == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE || + res == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) + { + memcpy(certfp, cert->sha1_hash, RB_SSL_CERTFP_LEN); + return 1; + } + X509_free(cert); + } + + return 0; } int @@ -581,4 +653,12 @@ rb_supports_ssl(void) return 1; } +void +rb_get_ssl_info(char *buf, size_t len) +{ + rb_snprintf(buf, len, "Using SSL: %s compiled: 0x%lx, library 0x%lx", + SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER, SSLeay()); +} + + #endif /* HAVE_OPESSL */