Browse Source

Issue #63: libgearman patch to support SSL connections in Gearman PHP library

Ed Sabol 5 years ago
parent
commit
f1504398b7

+ 7 - 0
libgearman-1.0/client.h

@@ -171,6 +171,13 @@ int gearman_client_timeout(gearman_client_st *client);
 GEARMAN_API
 void gearman_client_set_timeout(gearman_client_st *client, int timeout);
 
+/**
+ * See gearman_universal_set_ssl() for details.
+ */
+GEARMAN_API
+void gearman_client_set_ssl(gearman_client_st *client, bool ssl,
+    const char *ca_file, const char *certificate, const char *key_file);
+
 /**
  * Get the application context for a client.
  *

+ 7 - 0
libgearman-1.0/worker.h

@@ -179,6 +179,13 @@ int gearman_worker_timeout(gearman_worker_st *worker);
 GEARMAN_API
 void gearman_worker_set_timeout(gearman_worker_st *worker, int timeout);
 
+/**
+ * See gearman_universal_set_ssl() for details.
+ */
+GEARMAN_API
+void gearman_worker_set_ssl(gearman_worker_st *worker, bool ssl,
+    const char *ca_file, const char *certificate, const char *key_file);
+
 /**
  * Get the application context for a worker.
  *

+ 6 - 3
libgearman-server/io.cc

@@ -152,7 +152,6 @@ static size_t _connection_read(gearman_server_con_st *con, void *data, size_t da
           break;
 
         case SSL_ERROR_SYSCALL:
-
           if (errno) // If errno is really set, then let our normal error logic handle.
           {
             read_size= SOCKET_ERROR;
@@ -163,7 +162,11 @@ static size_t _connection_read(gearman_server_con_st *con, void *data, size_t da
         case SSL_ERROR_SSL:
         default:
           { // All other errors
-            char errorString[SSL_ERROR_SIZE];
+            if (ERR_peek_last_error())
+            {
+              ssl_error = ERR_peek_last_error();
+            }
+            char errorString[SSL_ERROR_SIZE]= { 0 };
             ERR_error_string_n(ssl_error, errorString, sizeof(errorString));
             ret= GEARMAND_LOST_CONNECTION;
             gearmand_log_info(GEARMAN_DEFAULT_LOG_PARAM, "SSL failure(%s) errno:%d", errorString, errno);
@@ -343,7 +346,7 @@ static gearmand_error_t _connection_flush(gearman_server_con_st *con)
             case SSL_ERROR_SSL:
             default:
               {
-                char errorString[SSL_ERROR_SIZE];
+                char errorString[SSL_ERROR_SIZE]= { 0 };
                 ERR_error_string_n(ssl_error, errorString, sizeof(errorString));
                 _connection_close(connection);
                 return gearmand_log_gerror(GEARMAN_DEFAULT_LOG_PARAM, GEARMAND_LOST_CONNECTION, "SSL failure(%s)",

+ 18 - 6
libgearman-server/plugins/protocol/gear/protocol.cc

@@ -374,8 +374,8 @@ static gearmand_error_t _gear_con_add(gearman_server_con_st *connection)
     int accept_error;
     while ((accept_error= SSL_accept(connection->_ssl)) != SSL_SUCCESS)
     {
-      int wolfssl_error;
-      switch (wolfssl_error= SSL_get_error(connection->_ssl, accept_error))
+      int ssl_error;
+      switch (ssl_error= SSL_get_error(connection->_ssl, accept_error))
       {
         case SSL_ERROR_NONE:
           break;
@@ -393,10 +393,16 @@ static gearmand_error_t _gear_con_add(gearman_server_con_st *connection)
         case SSL_ERROR_SSL:
         case SSL_ERROR_ZERO_RETURN:
         default:
-          char wolfssl_error_buffer[SSL_ERROR_SIZE]= { 0 };
-          ERR_error_string_n(wolfssl_error, wolfssl_error_buffer, sizeof(wolfssl_error_buffer));
-          return gearmand_log_gerror(GEARMAN_DEFAULT_LOG_PARAM, GEARMAND_LOST_CONNECTION, "%s(%d)", 
-                                     wolfssl_error_buffer, wolfssl_error);
+          {
+            char ssl_error_buffer[SSL_ERROR_SIZE]= { 0 };
+            if (ERR_peek_last_error())
+            {
+              ssl_error = ERR_peek_last_error();
+            }
+            ERR_error_string_n(ssl_error, ssl_error_buffer, sizeof(ssl_error_buffer));
+            return gearmand_log_gerror(GEARMAN_DEFAULT_LOG_PARAM, GEARMAND_LOST_CONNECTION, "%s(%d)",
+                                       ssl_error_buffer, ssl_error);
+          }
       }
     }
     gearmand_log_debug(GEARMAN_DEFAULT_LOG_PARAM, "GearSSL connection made: %s:%s", connection->host(), connection->port());
@@ -512,6 +518,12 @@ gearmand_error_t Gear::start(gearmand_st *gearmand)
     }
     gearmand_log_info(GEARMAN_DEFAULT_LOG_PARAM, "Loading certificate key : %s", _ssl_key.c_str());
 
+    if (SSL_CTX_check_private_key(gearmand->ctx_ssl()) != SSL_SUCCESS)
+    {
+      gearmand_log_fatal(GEARMAN_DEFAULT_LOG_PARAM, "SSL_CTX_check_private_key() cannot check certificate %s", _ssl_key.c_str());
+    }
+    gearmand_log_info(GEARMAN_DEFAULT_LOG_PARAM, "Checking certificate key : %s", _ssl_key.c_str());
+
     assert(gearmand->ctx_ssl());
   }
 #endif

+ 9 - 0
libgearman/client.cc

@@ -546,6 +546,15 @@ void gearman_client_set_timeout(gearman_client_st *client_shell, int timeout)
   }
 }
 
+void gearman_client_set_ssl(gearman_client_st *client_shell, bool ssl,
+    const char *ca_file, const char *certificate, const char *key_file)
+{
+  if (client_shell && client_shell->impl())
+  {
+    gearman_universal_set_ssl(client_shell->impl()->universal, ssl, ca_file, certificate, key_file);
+  }
+}
+
 void *gearman_client_context(const gearman_client_st *client_shell)
 {
   if (client_shell and client_shell->impl())

+ 13 - 3
libgearman/connection.cc

@@ -680,10 +680,12 @@ gearman_return_t gearman_connection_st::enable_ssl()
     if (SSL_set_fd(_ssl, fd) != SSL_SUCCESS)
     {
       close_socket();
-      char errorString[SSL_ERROR_SIZE];
+      char errorString[SSL_ERROR_SIZE]= { 0 };
       ERR_error_string_n(SSL_get_error(_ssl, 0), errorString, sizeof(errorString));
       return gearman_error(universal, GEARMAN_COULD_NOT_CONNECT, errorString);
     }
+
+    SSL_set_connect_state(_ssl);
   }
 #endif
 
@@ -872,7 +874,11 @@ gearman_return_t gearman_connection_st::flush()
             case SSL_ERROR_SSL:
             default:
               {
-                char errorString[80];
+                if (ERR_peek_last_error())
+                {
+                  ssl_error = ERR_peek_last_error();
+                }
+                char errorString[SSL_ERROR_SIZE]= { 0 };
                 ERR_error_string_n(ssl_error, errorString, sizeof(errorString));
                 close_socket();
                 return gearman_universal_set_error(universal, GEARMAN_LOST_CONNECTION, GEARMAN_AT, "SSL failure(%s)", errorString);
@@ -1186,7 +1192,11 @@ size_t gearman_connection_st::recv_socket(void *data, size_t data_size, gearman_
         case SSL_ERROR_SSL:
         default:
           {
-            char errorString[80];
+            if (ERR_peek_last_error())
+            {
+              ssl_error = ERR_peek_last_error();
+            }
+            char errorString[SSL_ERROR_SIZE]= { 0 };
             ERR_error_string_n(ssl_error, errorString, sizeof(errorString));
             close_socket();
             return gearman_universal_set_error(universal, GEARMAN_LOST_CONNECTION, GEARMAN_AT, "SSL failure(%s)", errorString);

+ 66 - 1
libgearman/interface/universal.hpp

@@ -46,6 +46,8 @@
 #include "libgearman/assert.hpp" 
 #include "libgearman/ssl.h"
 
+#include <cstring>
+
 enum universal_options_t
 {
   GEARMAN_UNIVERSAL_NON_BLOCKING,
@@ -68,12 +70,18 @@ struct gearman_universal_st : public error_st
     bool non_blocking;
     bool no_new_data;
     bool _ssl;
+    struct gearman_vector_st *_ssl_ca_file;
+    struct gearman_vector_st *_ssl_certificate;
+    struct gearman_vector_st *_ssl_key;
 
     Options() :
       dont_track_packets{false},
       non_blocking{false},
       no_new_data{false},
-      _ssl{false}
+      _ssl{false},
+      _ssl_ca_file{NULL},
+      _ssl_certificate{NULL},
+      _ssl_key{NULL}
     { }
   } options;
   gearman_verbose_t verbose;
@@ -208,6 +216,11 @@ struct gearman_universal_st : public error_st
 
   const char* ssl_ca_file() const
   {
+    if (options._ssl_ca_file && options._ssl_ca_file->size())
+    {
+      return options._ssl_ca_file->c_str();
+    }
+
     if (getenv("GEARMAND_CA_CERTIFICATE"))
     {
       return getenv("GEARMAND_CA_CERTIFICATE");
@@ -216,8 +229,27 @@ struct gearman_universal_st : public error_st
     return GEARMAND_CA_CERTIFICATE;
   }
 
+  void ssl_ca_file(const char* ssl_ca_file_)
+  {
+    gearman_string_free(options._ssl_ca_file);
+    size_t ssl_ca_file_size_ = 0;
+    if (ssl_ca_file_ && (ssl_ca_file_size_ = strlen(ssl_ca_file_)))
+    {
+      options._ssl_ca_file = gearman_string_create(NULL, ssl_ca_file_, ssl_ca_file_size_);
+    }
+    else
+    {
+      options._ssl_ca_file = NULL;
+    }
+  }
+
   const char* ssl_certificate() const
   {
+    if (options._ssl_certificate && options._ssl_certificate->size())
+    {
+      return options._ssl_certificate->c_str();
+    }
+
     if (getenv("GEARMAN_CLIENT_PEM"))
     {
       return getenv("GEARMAN_CLIENT_PEM");
@@ -226,8 +258,27 @@ struct gearman_universal_st : public error_st
     return GEARMAN_CLIENT_PEM;
   }
 
+  void ssl_certificate(const char *ssl_certificate_)
+  {
+    gearman_string_free(options._ssl_certificate);
+    size_t ssl_certificate_size_ = 0;
+    if (ssl_certificate_ && (ssl_certificate_size_ = strlen(ssl_certificate_)))
+    {
+      options._ssl_certificate = gearman_string_create(NULL, ssl_certificate_, ssl_certificate_size_);
+    }
+    else
+    {
+      options._ssl_certificate = NULL;
+    }
+  }
+
   const char* ssl_key() const
   {
+    if (options._ssl_key && options._ssl_key->size())
+    {
+      return options._ssl_key->c_str();
+    }
+
     if (getenv("GEARMAN_CLIENT_KEY"))
     {
       return getenv("GEARMAN_CLIENT_KEY");
@@ -236,6 +287,20 @@ struct gearman_universal_st : public error_st
     return GEARMAN_CLIENT_KEY;
   }
 
+  void ssl_key(const char *ssl_key_)
+  {
+    gearman_string_free(options._ssl_key);
+    size_t ssl_key_size_ = 0;
+    if (ssl_key_ && (ssl_key_size_ = strlen(ssl_key_)))
+    {
+      options._ssl_key = gearman_string_create(NULL, ssl_key_, ssl_key_size_);
+    }
+    else
+    {
+      options._ssl_key = NULL;
+    }
+  }
+
 private:
   bool init_ssl();
 

+ 37 - 1
libgearman/universal.cc

@@ -183,6 +183,15 @@ void gearman_universal_set_timeout(gearman_universal_st &self, int timeout)
   self.timeout= timeout;
 }
 
+void gearman_universal_set_ssl(gearman_universal_st &self, bool ssl,
+    const char *ca_file, const char *certificate, const char *key_file)
+{
+  self.ssl(ssl);
+  self.ssl_ca_file(ca_file);
+  self.ssl_certificate(certificate);
+  self.ssl_key(key_file);
+}
+
 void gearman_set_log_fn(gearman_universal_st &self, gearman_log_fn *function,
                         void *context, gearman_verbose_t verbose)
 {
@@ -470,6 +479,27 @@ bool gearman_universal_st::init_ssl()
   if (ssl())
   {
 #if defined(HAVE_SSL) && HAVE_SSL
+    // Check these files exist or not to avoid coredump.
+    FILE *file = NULL;
+    if ((file = fopen(ssl_ca_file(), "r")) == NULL)
+    {
+      gearman_universal_set_error(*this, GEARMAN_INVALID_ARGUMENT, GEARMAN_AT, "Failed to open CA certificate %s (%d: %s)", ssl_ca_file(), errno, strerror(errno));
+      return false;
+    }
+    fclose(file);
+    if ((file = fopen(ssl_certificate(), "r")) == NULL)
+    {
+      gearman_universal_set_error(*this, GEARMAN_INVALID_ARGUMENT, GEARMAN_AT, "Failed to open certificate %s (%d: %s)", ssl_certificate(), errno, strerror(errno));
+      return false;
+    }
+    fclose(file);
+    if ((file = fopen(ssl_key(), "r")) == NULL)
+    {
+      gearman_universal_set_error(*this, GEARMAN_INVALID_ARGUMENT, GEARMAN_AT, "Failed to open certificate key %s (%d: %s)", ssl_key(), errno, strerror(errno));
+      return false;
+    }
+    fclose(file);
+
     SSL_load_error_strings();
     SSL_library_init();
 
@@ -479,7 +509,7 @@ bool gearman_universal_st::init_ssl()
     if ((_ctx_ssl= SSL_CTX_new(TLS_client_method())) == NULL)
 #endif
     {
-      gearman_universal_set_error(*this, GEARMAN_INVALID_ARGUMENT, GEARMAN_AT, "CyaTLSv1_client_method() failed");
+      gearman_universal_set_error(*this, GEARMAN_INVALID_ARGUMENT, GEARMAN_AT, "TLS_client_method() failed");
       return false;
     }
 
@@ -500,6 +530,12 @@ bool gearman_universal_st::init_ssl()
       gearman_universal_set_error(*this, GEARMAN_INVALID_ARGUMENT, GEARMAN_AT, "Failed to load certificate key %s", ssl_key());
       return false;
     }
+
+    if (SSL_CTX_check_private_key(_ctx_ssl) != SSL_SUCCESS)
+    {
+      gearman_universal_set_error(*this, GEARMAN_INVALID_ARGUMENT, GEARMAN_AT, "Failed to check private key");
+      return false;
+    }
 #endif // defined(HAVE_SSL) && HAVE_SSL
   }
 

+ 3 - 0
libgearman/universal.hpp

@@ -57,6 +57,9 @@ void gearman_universal_set_timeout(gearman_universal_st &self, int timeout);
 
 int gearman_universal_timeout(gearman_universal_st &self);
 
+void gearman_universal_set_ssl(gearman_universal_st &self, bool ssl,
+    const char *ca_file, const char *certificate, const char *key_file);
+
 void gearman_universal_set_namespace(gearman_universal_st &self, const char *namespace_key, size_t namespace_key_size);
 
 gearman_return_t cancel_job(gearman_universal_st& universal,

+ 2 - 2
libtest/client.cc

@@ -378,7 +378,7 @@ bool SimpleClient::message(const char* ptr, const size_t len)
             case SSL_ERROR_SSL:
             default:
               {
-                char errorString[SSL_ERROR_SIZE];
+                char errorString[SSL_ERROR_SIZE]= { 0 };
                 ERR_error_string_n(ssl_error, errorString, sizeof(errorString));
                 error(__FILE__, __LINE__, errorString);
                 close_socket();
@@ -500,7 +500,7 @@ bool SimpleClient::response(libtest::vchar_t& response_)
             case SSL_ERROR_SSL:
             default:
               {
-                char errorString[SSL_ERROR_SIZE];
+                char errorString[SSL_ERROR_SIZE]= { 0 };
                 ERR_error_string_n(readErr, errorString, sizeof(errorString));
                 error(__FILE__, __LINE__, errorString);
                 return false;

Some files were not shown because too many files changed in this diff