]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/tls.c
crypto: Add a tiny OpenSSL compat level
[bacula/bacula] / bacula / src / lib / tls.c
index e67f75818eee1b0de3b24698260b106d1d5ef869..c71a4ad525fe08b5f0b41d1217cafb54ded4eb5f 100644 (file)
@@ -1,29 +1,20 @@
 /*
-   Bacula® - The Network Backup Solution
-
-   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 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 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.
+   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
@@ -54,6 +45,8 @@
 
 #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"
 
@@ -68,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 */
 };
 
 /*
@@ -120,8 +115,24 @@ 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_FATAL, _("Error initializing SSL context"));
@@ -229,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;
 }
@@ -293,6 +304,7 @@ bool tls_postconnect_verify_host(JCR *jcr, TLS_CONNECTION *tls, const char *host
    bool auth_success = false;
    int extensions;
    int i, j;
+   const char *pval, *phost;
 
    int cnLastPos = -1;
    X509_NAME_ENTRY *neCN;
@@ -300,8 +312,9 @@ bool tls_postconnect_verify_host(JCR *jcr, TLS_CONNECTION *tls, const char *host
 
    /* Check if peer provided a certificate */
    if (!(cert = SSL_get_peer_certificate(ssl))) {
-      Qmsg1(jcr, 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;
    }
 
@@ -323,11 +336,7 @@ bool tls_postconnect_verify_host(JCR *jcr, TLS_CONNECTION *tls, const char *host
             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
 
             /* Get x509 extension method structure */
             if (!(method = X509V3_EXT_get(ext))) {
@@ -336,7 +345,6 @@ bool tls_postconnect_verify_host(JCR *jcr, TLS_CONNECTION *tls, const char *host
 
             ext_value_data = ext->value->data;
 
-#if (OPENSSL_VERSION_NUMBER > 0x00907000L)
             if (method->it) {
                /* New style ASN1 */
 
@@ -350,21 +358,27 @@ bool tls_postconnect_verify_host(JCR *jcr, TLS_CONNECTION *tls, const char *host
                extstr = method->d2i(NULL, &ext_value_data, ext->value->length);
             }
 
-#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);
                }
             }
          }
@@ -373,6 +387,7 @@ bool tls_postconnect_verify_host(JCR *jcr, 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) {
          /* Loop through all CNs */
          for (;;) {
@@ -382,17 +397,26 @@ bool tls_postconnect_verify_host(JCR *jcr, TLS_CONNECTION *tls, const char *host
             }
             neCN = X509_NAME_get_entry(subject, cnLastPos);
             asn1CN = X509_NAME_ENTRY_get_data(neCN);
-            if (strcasecmp((const char*)asn1CN->data, host) == 0) {
+            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;
 }
 
@@ -433,6 +457,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:
@@ -448,6 +475,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);
 }
@@ -457,14 +486,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();
@@ -472,8 +495,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 {
@@ -491,22 +515,12 @@ static inline bool openssl_bsock_session_start(BSOCK *bsock, bool server)
          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, NULL, &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, NULL, &tv);
+         fd_wait_data(bsock->m_fd, WAIT_WRITE, 10, 0);
          break;
       default:
          /* Socket Error Occurred */
@@ -525,6 +539,7 @@ cleanup:
    bsock->restore_blocking(flags);
    /* Clear timer */
    bsock->timer_start = 0;
+   bsock->set_killable(true);
 
    return stat;
 }
@@ -602,31 +617,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)) {
@@ -637,22 +653,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:
-         /* 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, NULL, &tv);
+         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 write */
-         select(fdmax, NULL, &fdset, NULL, &tv);
+         /* Block until we can read */
+         fd_wait_data(bsock->m_fd, WAIT_WRITE, 10, 0);
          break;
 
       case SSL_ERROR_ZERO_RETURN:
@@ -676,27 +697,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 */
@@ -720,12 +753,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;
 }