X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Flib%2Ftls.c;h=344fe79053fd7f0c648a7e680b7362e31f363e72;hb=2a7fda9473a7ac2669f59e9ff0dec1cba8b12ef1;hp=85aeb1dbcd4ae5c34f48ab7f279fa4ff85345aef;hpb=51694687ab0c72a2000b06aa450ab4e650214395;p=bacula%2Fbacula diff --git a/bacula/src/lib/tls.c b/bacula/src/lib/tls.c index 85aeb1dbcd..344fe79053 100644 --- a/bacula/src/lib/tls.c +++ b/bacula/src/lib/tls.c @@ -1,57 +1,54 @@ -/* - * 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. - * - * Three Rings Design, Inc. has been granted a perpetual, worldwide, - * non-exclusive, no-charge, royalty-free, irrevocable copyright - * license to reproduce, prepare derivative works of, publicly - * display, publicly perform, sublicense, and distribute the original - * work contributed by Three Rings Design, Inc. and its employees to - * the Bacula project in source or object form. - * - * If you wish to license contributions from Three Rings Design, Inc, - * under an alternate open source license please contact - * Landon Fuller . - */ /* Bacula® - The Network Backup Solution - Copyright (C) 2005-2007 Free Software Foundation Europe e.V. + Copyright (C) 2005-2010 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 plus additions - that are listed in the file LICENSE. + modify it under the terms of version three of the GNU Affero 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 + You should have received a copy of the GNU Affero 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. + Bacula® is a registered trademark of Kern Sibbald. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ +/* + * tls.c TLS support functions + * + * Author: Landon Fuller + * + * This file was contributed to the Bacula project by Landon Fuller + * and Three Rings Design, Inc. + * + * Three Rings Design, Inc. has been granted a perpetual, worldwide, + * non-exclusive, no-charge, royalty-free, irrevocable copyright + * license to reproduce, prepare derivative works of, publicly + * display, publicly perform, sublicense, and distribute the original + * work contributed by Three Rings Design, Inc. and its employees to + * the Bacula project in source or object form. + * + * If you wish to license contributions from Three Rings Design, Inc, + * under an alternate open source license please contact + * Landon Fuller . + */ #include "bacula.h" #include -extern time_t watchdog_time; #ifdef HAVE_TLS /* Is TLS enabled? */ @@ -90,9 +87,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); - Emsg5(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)); } @@ -127,7 +124,7 @@ TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir, ctx->openssl = SSL_CTX_new(TLSv1_method()); 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 +145,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 */ - Emsg0(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 +161,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 +169,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 +177,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 +196,8 @@ 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) { - Emsg0(M_ERROR, 0, _("Error setting cipher list, no valid ciphers available\n")); + Jmsg0(NULL, M_ERROR, 0, + _("Error setting cipher list, no valid ciphers available\n")); goto err; } @@ -248,7 +246,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; @@ -258,7 +256,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))) { - Emsg0(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; } @@ -287,20 +285,23 @@ 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; + int cnLastPos = -1; + X509_NAME_ENTRY *neCN; + ASN1_STRING *asn1CN; /* Check if peer provided a certificate */ if (!(cert = SSL_get_peer_certificate(ssl))) { - Emsg1(M_ERROR, 0, _("Peer %s failed to present a TLS certificate\n"), host); + Qmsg1(jcr, M_ERROR, 0, + _("Peer %s failed to present a TLS certificate\n"), host); return false; } @@ -314,7 +315,11 @@ 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; @@ -369,11 +374,17 @@ bool tls_postconnect_verify_host(TLS_CONNECTION *tls, const char *host) /* Try verifying against the subject name */ if (!auth_success) { 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 (strcasecmp((const char*)asn1CN->data, host) == 0) { auth_success = true; + break; } } } @@ -402,7 +413,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); @@ -413,7 +424,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; } @@ -429,7 +440,6 @@ err: BIO_free(bio); SSL_free(tls->openssl); free(tls); - return NULL; } @@ -457,11 +467,11 @@ static inline bool openssl_bsock_session_start(BSOCK *bsock, bool server) fdmax = bsock->m_fd + 1; /* Ensure that socket is non-blocking */ - flags = bnet_set_nonblocking(bsock); + flags = bsock->set_nonblocking(); /* start timer */ bsock->timer_start = watchdog_time; - bsock->m_timed_out = 0; + bsock->clear_timed_out(); for (;;) { if (server) { @@ -477,7 +487,7 @@ 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: @@ -487,7 +497,7 @@ static inline bool openssl_bsock_session_start(BSOCK *bsock, bool server) tv.tv_sec = 10; tv.tv_usec = 0; /* Block until we can read */ - select(fdmax, &fdset, NULL, &fdset, &tv); + select(fdmax, &fdset, NULL, NULL, &tv); break; case SSL_ERROR_WANT_WRITE: /* If we timeout of a select, this will be unset */ @@ -496,11 +506,11 @@ static inline bool openssl_bsock_session_start(BSOCK *bsock, bool server) tv.tv_sec = 10; tv.tv_usec = 0; /* Block until we can write */ - select(fdmax, NULL, &fdset, &fdset, &tv); + select(fdmax, NULL, &fdset, NULL, &tv); 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; } @@ -512,7 +522,7 @@ static inline bool openssl_bsock_session_start(BSOCK *bsock, bool server) cleanup: /* Restore saved flags */ - bnet_restore_blocking(bsock, flags); + bsock->restore_blocking(flags); /* Clear timer */ bsock->timer_start = 0; @@ -527,7 +537,7 @@ cleanup: bool tls_bsock_connect(BSOCK *bsock) { /* SSL_connect(bsock->tls) */ - return (openssl_bsock_session_start(bsock, false)); + return openssl_bsock_session_start(bsock, false); } /* @@ -538,7 +548,7 @@ bool tls_bsock_connect(BSOCK *bsock) bool tls_bsock_accept(BSOCK *bsock) { /* SSL_accept(bsock->tls) */ - return (openssl_bsock_session_start(bsock, true)); + return openssl_bsock_session_start(bsock, true); } /* @@ -551,42 +561,41 @@ void tls_bsock_shutdown(BSOCK *bsock) * The first time to initiate the shutdown handshake, and the second to * receive the peer's reply. * - * However, it is valid to close the SSL connection after the initial - * shutdown notification is sent to the peer, without waiting for the - * peer's reply, as long as you do not plan to re-use that particular - * SSL connection object. - * - * Because we do not re-use SSL connection objects, I do not bother - * calling SSL_shutdown a second time. - * * In addition, if the underlying socket is blocking, SSL_shutdown() * will not return until the current stage of the shutdown process has * completed or an error has occured. By setting the socket blocking * we can avoid the ugly for()/switch()/select() loop. */ int err; - int flags; + + btimer_t *tid; /* Set socket blocking for shutdown */ - flags = bsock->set_blocking(); + 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.")); - break; - default: - /* Socket Error Occured */ - openssl_post_errors(M_ERROR, _("TLS shutdown failure.")); - break; + 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(bsock->get_jcr(), M_ERROR, _("TLS shutdown failure.")); + break; + default: + /* Socket Error Occurred */ + openssl_post_errors(bsock->get_jcr(), M_ERROR, _("TLS shutdown failure.")); + break; } - - /* Restore saved flags */ - bsock->restore_blocking(flags); } /* Does all the manual labor for tls_bsock_readn() and tls_bsock_writen() */ @@ -608,12 +617,11 @@ static inline int openssl_bsock_readwrite(BSOCK *bsock, char *ptr, int nbytes, b /* start timer */ bsock->timer_start = watchdog_time; - bsock->m_timed_out = 0; + bsock->clear_timed_out(); nleft = nbytes; while (nleft > 0) { - if (write) { nwritten = SSL_write(tls->openssl, ptr, nleft); } else { @@ -628,29 +636,44 @@ static inline int openssl_bsock_readwrite(BSOCK *bsock, char *ptr, int nbytes, b ptr += nwritten; } break; - case SSL_ERROR_ZERO_RETURN: - /* TLS connection was cleanly shut down */ - openssl_post_errors(M_ERROR, _("TLS read/write failure.")); + + 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: - /* If we timeout of a select, this will be unset */ - FD_SET((unsigned) bsock->m_fd, &fdset); + /* 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 */ - select(fdmax, &fdset, NULL, &fdset, &tv); + select(fdmax, &fdset, NULL, NULL, &tv); break; + case SSL_ERROR_WANT_WRITE: - /* If we timeout of a select, this will be unset */ - FD_SET((unsigned) bsock->m_fd, &fdset); + /* 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 write */ - select(fdmax, NULL, &fdset, &fdset, &tv); + select(fdmax, NULL, &fdset, NULL, &tv); break; + + case SSL_ERROR_ZERO_RETURN: + /* TLS connection was cleanly shut down */ + /* 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; } @@ -671,26 +694,29 @@ cleanup: /* Clear timer */ bsock->timer_start = 0; - 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)); + 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)); + return openssl_bsock_readwrite(bsock, ptr, nbytes, false); } #else /* HAVE_OPENSSL */ # error No TLS implementation available. #endif /* !HAVE_OPENSSL */ -#else + +#else /* TLS NOT enabled, dummy routines substituted */ + /* Dummy routines */ TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir, @@ -705,15 +731,8 @@ void free_tls_context(TLS_CONTEXT *ctx) { } void tls_bsock_shutdown(BSOCK *bsock) { } -void free_tls_connection(TLS_CONNECTION *tls) -{ - if (tls) { - if (tls->openssl) { - SSL_free(tls->openssl); - } - free(tls); - } -} +void free_tls_connection(TLS_CONNECTION *tls) { } + bool get_tls_require(TLS_CONTEXT *ctx) { return false;