]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/tls.c
bat: Add pattern filter and make restore to start from brestore
[bacula/bacula] / bacula / src / lib / tls.c
index 85aeb1dbcd4ae5c34f48ab7f279fa4ff85345aef..344fe79053fd7f0c648a7e680b7362e31f363e72 100644 (file)
@@ -1,57 +1,54 @@
-/*
- * tls.c TLS support functions
- *
- * Author: Landon Fuller <landonf@threerings.net>
- *
- * 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 <landonf@threerings.net>.
- */
 /*
    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 <landonf@threerings.net>
+ *
+ * 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 <landonf@threerings.net>.
+ */
 
 
 #include "bacula.h"
 #include <assert.h>
 
-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;