X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Flib%2Ftls.c;h=01d3f94e8113d8bccc972a754eb6abf696871faf;hb=f52b54ffe7b08ecd6f6df6a0e4b7a1a3f9396c4b;hp=26d8df332e98c7724290aa8ce1553134fa72d5e6;hpb=28d9ddfabbbcd02eb97483fff481453c6bb32f54;p=bacula%2Fbacula diff --git a/bacula/src/lib/tls.c b/bacula/src/lib/tls.c index 26d8df332e..01d3f94e81 100644 --- a/bacula/src/lib/tls.c +++ b/bacula/src/lib/tls.c @@ -1,37 +1,26 @@ /* - Bacula® - The Network Backup Solution - - Copyright (C) 2005-2007 Free Software Foundation Europe e.V. - - The main author of Bacula is Kern Sibbald, with contributions from - many others, a complete list can be found in the file AUTHORS. - This program is Free Software; you can redistribute it and/or - modify it under the terms of version two of the GNU General Public - License as published by the Free Software Foundation and included - in the file LICENSE. - - 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. - - Bacula® is a registered trademark of John Walker. - The licensor of Bacula is the Free Software Foundation Europe - (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, - Switzerland, email:ftf@fsfeurope.org. + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. */ /* * tls.c TLS support functions * * Author: Landon Fuller * - * Version $Id$ - * * This file was contributed to the Bacula project by Landon Fuller * and Three Rings Design, Inc. * @@ -51,12 +40,13 @@ #include "bacula.h" #include -extern time_t watchdog_time; #ifdef HAVE_TLS /* Is TLS enabled? */ #ifdef HAVE_OPENSSL /* How about OpenSSL? */ +#include "openssl-compat.h" + /* No anonymous ciphers, no <128 bit ciphers, no export ciphers, no MD5 ciphers */ #define TLS_DEFAULT_CIPHERS "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" @@ -71,6 +61,8 @@ struct TLS_Context { struct TLS_Connection { SSL *openssl; + pthread_mutex_t wlock; /* make openssl_bsock_readwrite() atomic when writing */ + pthread_mutex_t rwlock; /* only one SSL_read() or SSL_write() at a time */ }; /* @@ -90,9 +82,9 @@ static int openssl_verify_peer(int ok, X509_STORE_CTX *store) X509_NAME_oneline(X509_get_issuer_name(cert), issuer, 256); X509_NAME_oneline(X509_get_subject_name(cert), subject, 256); - Jmsg5(get_jcr_from_tid(), M_ERROR, 0, _("Error with certificate at depth: %d, issuer = %s," - " subject = %s, ERR=%d:%s\n"), depth, issuer, - subject, err, X509_verify_cert_error_string(err)); + Jmsg5(NULL, M_ERROR, 0, _("Error with certificate at depth: %d, issuer = %s," + " subject = %s, ERR=%d:%s\n"), depth, issuer, + subject, err, X509_verify_cert_error_string(err)); } @@ -123,11 +115,27 @@ TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir, ctx = (TLS_CONTEXT *)malloc(sizeof(TLS_CONTEXT)); - /* Allocate our OpenSSL TLSv1 Context */ - ctx->openssl = SSL_CTX_new(TLSv1_method()); + /* Allocate our OpenSSL TLS Context */ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + /* Allows SSLv3, TLSv1, TLSv1.1 and TLSv1.2 protocols */ + ctx->openssl = SSL_CTX_new(TLS_method()); + +#else + /* Allows most all protocols */ + ctx->openssl = SSL_CTX_new(SSLv23_method()); + +#endif + + /* Use SSL_OP_ALL to turn on all "rather harmless" workarounds that + * OpenSSL offers + */ + SSL_CTX_set_options(ctx->openssl, SSL_OP_ALL); + + /* Now disable old broken SSLv3 and SSLv2 protocols */ + SSL_CTX_set_options(ctx->openssl, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); if (!ctx->openssl) { - openssl_post_errors(M_ERROR, _("Error initializing SSL context")); + openssl_post_errors(M_FATAL, _("Error initializing SSL context")); goto err; } @@ -148,12 +156,12 @@ TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir, */ if (ca_certfile || ca_certdir) { if (!SSL_CTX_load_verify_locations(ctx->openssl, ca_certfile, ca_certdir)) { - openssl_post_errors(M_ERROR, _("Error loading certificate verification stores")); + openssl_post_errors(M_FATAL, _("Error loading certificate verification stores")); goto err; } } else if (verify_peer) { /* At least one CA is required for peer verification */ - Jmsg0(get_jcr_from_tid(), M_ERROR, 0, _("Either a certificate file or a directory must be" + Jmsg0(NULL, M_ERROR, 0, _("Either a certificate file or a directory must be" " specified as a verification store\n")); goto err; } @@ -164,7 +172,7 @@ TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir, */ if (certfile) { if (!SSL_CTX_use_certificate_chain_file(ctx->openssl, certfile)) { - openssl_post_errors(M_ERROR, _("Error loading certificate file")); + openssl_post_errors(M_FATAL, _("Error loading certificate file")); goto err; } } @@ -172,7 +180,7 @@ TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir, /* Load our private key. */ if (keyfile) { if (!SSL_CTX_use_PrivateKey_file(ctx->openssl, keyfile, SSL_FILETYPE_PEM)) { - openssl_post_errors(M_ERROR, _("Error loading private key")); + openssl_post_errors(M_FATAL, _("Error loading private key")); goto err; } } @@ -180,17 +188,17 @@ TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir, /* Load Diffie-Hellman Parameters. */ if (dhfile) { if (!(bio = BIO_new_file(dhfile, "r"))) { - openssl_post_errors(M_ERROR, _("Unable to open DH parameters file")); + openssl_post_errors(M_FATAL, _("Unable to open DH parameters file")); goto err; } dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); if (!dh) { - openssl_post_errors(M_ERROR, _("Unable to load DH parameters from specified file")); + openssl_post_errors(M_FATAL, _("Unable to load DH parameters from specified file")); goto err; } if (!SSL_CTX_set_tmp_dh(ctx->openssl, dh)) { - openssl_post_errors(M_ERROR, _("Failed to set TLS Diffie-Hellman parameters")); + openssl_post_errors(M_FATAL, _("Failed to set TLS Diffie-Hellman parameters")); DH_free(dh); goto err; } @@ -199,7 +207,7 @@ TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir, } if (SSL_CTX_set_cipher_list(ctx->openssl, TLS_DEFAULT_CIPHERS) != 1) { - Jmsg0(get_jcr_from_tid(), M_ERROR, 0, + Jmsg0(NULL, M_ERROR, 0, _("Error setting cipher list, no valid ciphers available\n")); goto err; } @@ -232,12 +240,12 @@ void free_tls_context(TLS_CONTEXT *ctx) free(ctx); } -bool get_tls_require(TLS_CONTEXT *ctx) +bool get_tls_require(TLS_CONTEXT *ctx) { return ctx->tls_require; } -bool get_tls_enable(TLS_CONTEXT *ctx) +bool get_tls_enable(TLS_CONTEXT *ctx) { return ctx->tls_enable; } @@ -249,7 +257,7 @@ bool get_tls_enable(TLS_CONTEXT *ctx) * Returns: true on success * false on failure */ -bool tls_postconnect_verify_cn(TLS_CONNECTION *tls, alist *verify_list) +bool tls_postconnect_verify_cn(JCR *jcr, TLS_CONNECTION *tls, alist *verify_list) { SSL *ssl = tls->openssl; X509 *cert; @@ -259,7 +267,7 @@ bool tls_postconnect_verify_cn(TLS_CONNECTION *tls, alist *verify_list) /* Check if peer provided a certificate */ if (!(cert = SSL_get_peer_certificate(ssl))) { - Jmsg0(get_jcr_from_tid(), M_ERROR, 0, _("Peer failed to present a TLS certificate\n")); + Qmsg0(jcr, M_ERROR, 0, _("Peer failed to present a TLS certificate\n")); return false; } @@ -288,21 +296,25 @@ bool tls_postconnect_verify_cn(TLS_CONNECTION *tls, alist *verify_list) * Returns: true on success * false on failure */ -bool tls_postconnect_verify_host(TLS_CONNECTION *tls, const char *host) +bool tls_postconnect_verify_host(JCR *jcr, TLS_CONNECTION *tls, const char *host) { SSL *ssl = tls->openssl; X509 *cert; X509_NAME *subject; bool auth_success = false; int extensions; - char data[256]; int i, j; + const char *pval, *phost; + int cnLastPos = -1; + X509_NAME_ENTRY *neCN; + ASN1_STRING *asn1CN; /* Check if peer provided a certificate */ if (!(cert = SSL_get_peer_certificate(ssl))) { - Jmsg1(get_jcr_from_tid(), M_ERROR, 0, + Qmsg1(jcr, M_ERROR, 0, _("Peer %s failed to present a TLS certificate\n"), host); + Dmsg1(250, _("Peer %s failed to present a TLS certificate\n"), host); return false; } @@ -316,52 +328,59 @@ bool tls_postconnect_verify_host(TLS_CONNECTION *tls, const char *host) extname = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext))); if (strcmp(extname, "subjectAltName") == 0) { +#ifdef HAVE_OPENSSLv1 + const X509V3_EXT_METHOD *method; +#else X509V3_EXT_METHOD *method; +#endif STACK_OF(CONF_VALUE) *val; CONF_VALUE *nval; void *extstr = NULL; -#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL) const unsigned char *ext_value_data; -#else - unsigned char *ext_value_data; -#endif + const ASN1_STRING *asn1_ext_val; /* Get x509 extension method structure */ if (!(method = X509V3_EXT_get(ext))) { break; } - ext_value_data = ext->value->data; + asn1_ext_val = X509_EXTENSION_get_data(ext); + ext_value_data = ASN1_STRING_get0_data(asn1_ext_val); -#if (OPENSSL_VERSION_NUMBER > 0x00907000L) if (method->it) { /* New style ASN1 */ /* Decode ASN1 item in data */ - extstr = ASN1_item_d2i(NULL, &ext_value_data, ext->value->length, + extstr = ASN1_item_d2i(NULL, &ext_value_data, ASN1_STRING_length(asn1_ext_val), ASN1_ITEM_ptr(method->it)); } else { /* Old style ASN1 */ /* Decode ASN1 item in data */ - extstr = method->d2i(NULL, &ext_value_data, ext->value->length); + extstr = method->d2i(NULL, &ext_value_data, ASN1_STRING_length(asn1_ext_val)); } -#else - extstr = method->d2i(NULL, &ext_value_data, ext->value->length); -#endif - /* Iterate through to find the dNSName field(s) */ val = method->i2v(method, extstr, NULL); /* dNSName shortname is "DNS" */ + Dmsg0(250, "Check DNS name\n"); for (j = 0; j < sk_CONF_VALUE_num(val); j++) { nval = sk_CONF_VALUE_value(val, j); if (strcmp(nval->name, "DNS") == 0) { - if (strcasecmp(nval->value, host) == 0) { + if (strncasecmp(nval->value, "*.", 2) == 0) { + Dmsg0(250, "Wildcard Certificate\n"); + pval = strstr(nval->value, "."); + phost = strstr(host, "."); + if (pval && phost && (strcasecmp(pval, phost) == 0)) { + auth_success = true; + goto success; + } + } else if (strcasecmp(nval->value, host) == 0) { auth_success = true; goto success; } + Dmsg2(250, "No DNS name match. Host=%s cert=%s\n", host, nval->value); } } } @@ -370,20 +389,36 @@ bool tls_postconnect_verify_host(TLS_CONNECTION *tls, const char *host) /* Try verifying against the subject name */ if (!auth_success) { + Dmsg0(250, "Check subject name name\n"); if ((subject = X509_get_subject_name(cert)) != NULL) { - if (X509_NAME_get_text_by_NID(subject, NID_commonName, data, sizeof(data)) > 0) { - /* NULL terminate data */ - data[255] = 0; - if (strcasecmp(data, host) == 0) { + /* Loop through all CNs */ + for (;;) { + cnLastPos = X509_NAME_get_index_by_NID(subject, NID_commonName, cnLastPos); + if (cnLastPos == -1) { + break; + } + neCN = X509_NAME_get_entry(subject, cnLastPos); + asn1CN = X509_NAME_ENTRY_get_data(neCN); + if (strncasecmp((const char*)asn1CN->data, "*.", 2) == 0) { + /* wildcard certificate */ + Dmsg0(250, "Wildcard Certificate\n"); + pval = strstr((const char*)asn1CN->data, "."); + phost = strstr(host, "."); + if (pval && phost && (strcasecmp(pval, phost) == 0)) { + auth_success = true; + goto success; + } + } else if (strcasecmp((const char*)asn1CN->data, host) == 0) { auth_success = true; + break; } + Dmsg2(250, "No subject name match. Host=%s cert=%s\n", host, (const char*)asn1CN->data); } } } success: X509_free(cert); - return auth_success; } @@ -404,7 +439,7 @@ TLS_CONNECTION *new_tls_connection(TLS_CONTEXT *ctx, int fd) bio = BIO_new(BIO_s_socket()); if (!bio) { /* Not likely, but never say never */ - openssl_post_errors(M_ERROR, _("Error creating file descriptor-based BIO")); + openssl_post_errors(M_FATAL, _("Error creating file descriptor-based BIO")); return NULL; /* Nothing allocated, nothing to clean up */ } BIO_set_fd(bio, fd, BIO_NOCLOSE); @@ -415,7 +450,7 @@ TLS_CONNECTION *new_tls_connection(TLS_CONTEXT *ctx, int fd) /* Create the SSL object and attach the socket BIO */ if ((tls->openssl = SSL_new(ctx->openssl)) == NULL) { /* Not likely, but never say never */ - openssl_post_errors(M_ERROR, _("Error creating new SSL object")); + openssl_post_errors(M_FATAL, _("Error creating new SSL object")); goto err; } @@ -424,6 +459,9 @@ TLS_CONNECTION *new_tls_connection(TLS_CONTEXT *ctx, int fd) /* Non-blocking partial writes */ SSL_set_mode(tls->openssl, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + pthread_mutex_init(&tls->wlock, NULL); + pthread_mutex_init(&tls->rwlock, NULL); + return tls; err: @@ -439,6 +477,8 @@ err: */ void free_tls_connection(TLS_CONNECTION *tls) { + pthread_mutex_destroy(&tls->rwlock); + pthread_mutex_destroy(&tls->wlock); SSL_free(tls->openssl); free(tls); } @@ -448,14 +488,8 @@ static inline bool openssl_bsock_session_start(BSOCK *bsock, bool server) { TLS_CONNECTION *tls = bsock->tls; int err; - int fdmax, flags; + int flags; int stat = true; - fd_set fdset; - struct timeval tv; - - /* Zero the fdset, we'll set our fd prior to each invocation of select() */ - FD_ZERO(&fdset); - fdmax = bsock->m_fd + 1; /* Ensure that socket is non-blocking */ flags = bsock->set_nonblocking(); @@ -463,8 +497,9 @@ static inline bool openssl_bsock_session_start(BSOCK *bsock, bool server) /* start timer */ bsock->timer_start = watchdog_time; bsock->clear_timed_out(); + bsock->set_killable(false); - for (;;) { + for (;;) { if (server) { err = SSL_accept(tls->openssl); } else { @@ -478,30 +513,20 @@ static inline bool openssl_bsock_session_start(BSOCK *bsock, bool server) goto cleanup; case SSL_ERROR_ZERO_RETURN: /* TLS connection was cleanly shut down */ - openssl_post_errors(M_ERROR, _("Connect failure")); + openssl_post_errors(bsock->get_jcr(), M_FATAL, _("Connect failure")); stat = false; goto cleanup; case SSL_ERROR_WANT_READ: - /* If we timeout of a select, this will be unset */ - FD_SET((unsigned) bsock->m_fd, &fdset); - /* Set our timeout */ - tv.tv_sec = 10; - tv.tv_usec = 0; /* Block until we can read */ - select(fdmax, &fdset, NULL, &fdset, &tv); + fd_wait_data(bsock->m_fd, WAIT_READ, 10, 0); break; case SSL_ERROR_WANT_WRITE: - /* If we timeout of a select, this will be unset */ - FD_SET((unsigned) bsock->m_fd, &fdset); - /* Set our timeout */ - tv.tv_sec = 10; - tv.tv_usec = 0; /* Block until we can write */ - select(fdmax, NULL, &fdset, &fdset, &tv); + fd_wait_data(bsock->m_fd, WAIT_WRITE, 10, 0); break; default: - /* Socket Error Occured */ - openssl_post_errors(M_ERROR, _("Connect failure")); + /* Socket Error Occurred */ + openssl_post_errors(bsock->get_jcr(), M_FATAL, _("Connect failure")); stat = false; goto cleanup; } @@ -516,6 +541,7 @@ cleanup: bsock->restore_blocking(flags); /* Clear timer */ bsock->timer_start = 0; + bsock->set_killable(true); return stat; } @@ -559,25 +585,32 @@ void tls_bsock_shutdown(BSOCK *bsock) */ int err; + btimer_t *tid; + /* Set socket blocking for shutdown */ bsock->set_blocking(); + tid = start_bsock_timer(bsock, 60 * 2); err = SSL_shutdown(bsock->tls->openssl); + stop_bsock_timer(tid); if (err == 0) { /* Complete shutdown */ + tid = start_bsock_timer(bsock, 60 * 2); err = SSL_shutdown(bsock->tls->openssl); + stop_bsock_timer(tid); } + switch (SSL_get_error(bsock->tls->openssl, err)) { case SSL_ERROR_NONE: break; case SSL_ERROR_ZERO_RETURN: /* TLS connection was shut down on us via a TLS protocol-level closure */ - openssl_post_errors(M_ERROR, _("TLS shutdown failure.")); + openssl_post_errors(bsock->get_jcr(), M_ERROR, _("TLS shutdown failure.")); break; default: /* Socket Error Occurred */ - openssl_post_errors(M_ERROR, _("TLS shutdown failure.")); + openssl_post_errors(bsock->get_jcr(), M_ERROR, _("TLS shutdown failure.")); break; } } @@ -586,31 +619,32 @@ void tls_bsock_shutdown(BSOCK *bsock) static inline int openssl_bsock_readwrite(BSOCK *bsock, char *ptr, int nbytes, bool write) { TLS_CONNECTION *tls = bsock->tls; - int fdmax, flags; - fd_set fdset; - struct timeval tv; + int flags; int nleft = 0; int nwritten = 0; - /* Zero the fdset, we'll set our fd prior to each invocation of select() */ - FD_ZERO(&fdset); - fdmax = bsock->m_fd + 1; - /* Ensure that socket is non-blocking */ flags = bsock->set_nonblocking(); /* start timer */ bsock->timer_start = watchdog_time; bsock->clear_timed_out(); + bsock->set_killable(false); nleft = nbytes; - while (nleft > 0) { + if (write) { + pthread_mutex_lock(&tls->wlock); + } + while (nleft > 0) { + + pthread_mutex_lock(&tls->rwlock); if (write) { nwritten = SSL_write(tls->openssl, ptr, nleft); } else { nwritten = SSL_read(tls->openssl, ptr, nleft); } + pthread_mutex_unlock(&tls->rwlock); /* Handle errors */ switch (SSL_get_error(tls->openssl, nwritten)) { @@ -621,14 +655,27 @@ static inline int openssl_bsock_readwrite(BSOCK *bsock, char *ptr, int nbytes, b } break; + case SSL_ERROR_SYSCALL: + if (nwritten == -1) { + if (errno == EINTR) { + continue; + } + if (errno == EAGAIN) { + bmicrosleep(0, 20000); /* try again in 20 ms */ + continue; + } + } + openssl_post_errors(bsock->get_jcr(), M_FATAL, _("TLS read/write failure.")); + goto cleanup; + case SSL_ERROR_WANT_READ: + /* Block until we can read */ + fd_wait_data(bsock->m_fd, WAIT_READ, 10, 0); + break; + case SSL_ERROR_WANT_WRITE: - /* If we timeout on a select, this will be unset */ - FD_SET((unsigned)bsock->m_fd, &fdset); - tv.tv_sec = 10; - tv.tv_usec = 0; - /* Block until we can read or write */ - select(fdmax, NULL, &fdset, &fdset, &tv); + /* Block until we can read */ + fd_wait_data(bsock->m_fd, WAIT_WRITE, 10, 0); break; case SSL_ERROR_ZERO_RETURN: @@ -636,7 +683,7 @@ static inline int openssl_bsock_readwrite(BSOCK *bsock, char *ptr, int nbytes, b /* Fall through wanted */ default: /* Socket Error Occured */ - openssl_post_errors(M_ERROR, _("TLS read/write failure.")); + openssl_post_errors(bsock->get_jcr(), M_FATAL, _("TLS read/write failure.")); goto cleanup; } @@ -652,27 +699,39 @@ static inline int openssl_bsock_readwrite(BSOCK *bsock, char *ptr, int nbytes, b } cleanup: + if (write) { + pthread_mutex_unlock(&tls->wlock); + } /* Restore saved flags */ bsock->restore_blocking(flags); /* Clear timer */ bsock->timer_start = 0; + bsock->set_killable(true); return nbytes - nleft; } -int tls_bsock_writen(BSOCK *bsock, char *ptr, int32_t nbytes) +int tls_bsock_writen(BSOCK *bsock, char *ptr, int32_t nbytes) { /* SSL_write(bsock->tls->openssl, ptr, nbytes) */ return openssl_bsock_readwrite(bsock, ptr, nbytes, true); } -int tls_bsock_readn(BSOCK *bsock, char *ptr, int32_t nbytes) +int tls_bsock_readn(BSOCK *bsock, char *ptr, int32_t nbytes) { /* SSL_read(bsock->tls->openssl, ptr, nbytes) */ return openssl_bsock_readwrite(bsock, ptr, nbytes, false); } +/* test if 4 bytes can be read without "blocking" */ +bool tls_bsock_probe(BSOCK *bsock) +{ + int32_t pktsiz; + return SSL_peek(bsock->tls->openssl, &pktsiz, sizeof(pktsiz))==sizeof(pktsiz); +} + + #else /* HAVE_OPENSSL */ # error No TLS implementation available. #endif /* !HAVE_OPENSSL */ @@ -696,12 +755,12 @@ void tls_bsock_shutdown(BSOCK *bsock) { } void free_tls_connection(TLS_CONNECTION *tls) { } -bool get_tls_require(TLS_CONTEXT *ctx) +bool get_tls_require(TLS_CONTEXT *ctx) { return false; } -bool get_tls_enable(TLS_CONTEXT *ctx) +bool get_tls_enable(TLS_CONTEXT *ctx) { return false; }