diff --git a/libgearman-1.0/client.h b/libgearman-1.0/client.h index 381b87656..3dbff55c0 100644 --- a/libgearman-1.0/client.h +++ b/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. * diff --git a/libgearman-1.0/worker.h b/libgearman-1.0/worker.h index 712fc2ebc..cb8b9962d 100644 --- a/libgearman-1.0/worker.h +++ b/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. * diff --git a/libgearman-server/io.cc b/libgearman-server/io.cc index 82649f526..5e00e8308 100644 --- a/libgearman-server/io.cc +++ b/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,7 @@ 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]; + 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 +342,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)", @@ -797,7 +796,7 @@ gearmand_error_t gearman_io_recv(gearman_server_con_st *con, bool recv_data) connection->recv_buffer_ptr= connection->recv_buffer; size_t recv_size= _connection_read(con, connection->recv_buffer + connection->recv_buffer_size, - GEARMAND_RECV_BUFFER_SIZE - connection->recv_buffer_size, ret); + GEARMAND_RECV_BUFFER_SIZE - connection->recv_buffer_size, ret); if (gearmand_failed(ret)) { // GEARMAND_LOST_CONNECTION is not worth a warning, clients/workers just diff --git a/libgearman-server/plugins/protocol/gear/protocol.cc b/libgearman-server/plugins/protocol/gear/protocol.cc index 4c0d1beed..8a718f5f0 100644 --- a/libgearman-server/plugins/protocol/gear/protocol.cc +++ b/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,12 @@ 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 }; + 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 +514,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 diff --git a/libgearman/client.cc b/libgearman/client.cc index fc4f02d67..19581fc26 100644 --- a/libgearman/client.cc +++ b/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()) diff --git a/libgearman/connection.cc b/libgearman/connection.cc index facd560b4..18e3f00fb 100644 --- a/libgearman/connection.cc +++ b/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,7 @@ gearman_return_t gearman_connection_st::flush() case SSL_ERROR_SSL: default: { - char errorString[80]; + 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 +1188,7 @@ size_t gearman_connection_st::recv_socket(void *data, size_t data_size, gearman_ case SSL_ERROR_SSL: default: { - char errorString[80]; + 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); diff --git a/libgearman/interface/universal.hpp b/libgearman/interface/universal.hpp index 1575db5be..56a34dcfd 100644 --- a/libgearman/interface/universal.hpp +++ b/libgearman/interface/universal.hpp @@ -46,6 +46,8 @@ #include "libgearman/assert.hpp" #include "libgearman/ssl.h" +#include + 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(); diff --git a/libgearman/universal.cc b/libgearman/universal.cc index 3426d9d06..dd3760010 100644 --- a/libgearman/universal.cc +++ b/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 } diff --git a/libgearman/universal.hpp b/libgearman/universal.hpp index 671ddfe42..a97b2fe73 100644 --- a/libgearman/universal.hpp +++ b/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, diff --git a/libtest/client.cc b/libtest/client.cc index 8dca6ed55..0ff6a729b 100644 --- a/libtest/client.cc +++ b/libtest/client.cc @@ -382,7 +382,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(); @@ -504,7 +504,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; diff --git a/util/instance.cc b/util/instance.cc index 7e1c51014..acce53102 100644 --- a/util/instance.cc +++ b/util/instance.cc @@ -158,7 +158,15 @@ bool Instance::init_ssl() _last_error= message.str(); return false; } -#endif // defined(HAVE_WOLFSSL) && HAVE_WOLFSSL + + if (SSL_CTX_check_private_key(_ctx_ssl) != SSL_SUCCESS) + { + std::stringstream message; + message << "Error checking private key"; + _last_error = message.str(); + return false; + } +#endif // defined(HAVE_SSL) && HAVE_SSL return true; } @@ -280,6 +288,8 @@ bool Instance::run() _last_error= "SSL_set_fd() failed"; return false; } + + SSL_set_connect_state(_ssl); } #endif @@ -323,9 +333,9 @@ bool Instance::run() case SSL_ERROR_SSL: default: { - char wolfssl_error_buffer[SSL_ERROR_SIZE]= { 0 }; - ERR_error_string_n(ssl_error, wolfssl_error_buffer, sizeof(wolfssl_error_buffer)); - _last_error= wolfssl_error_buffer; + char ssl_error_buffer[SSL_ERROR_SIZE]= { 0 }; + ERR_error_string_n(ssl_error, ssl_error_buffer, sizeof(ssl_error_buffer)); + _last_error= ssl_error_buffer; errno= ECONNRESET; write_size= SOCKET_ERROR; @@ -407,9 +417,9 @@ bool Instance::run() case SSL_ERROR_SSL: default: { - char wolfssl_error_buffer[SSL_ERROR_SIZE]= { 0 }; - ERR_error_string_n(ssl_error, wolfssl_error_buffer, sizeof(wolfssl_error_buffer)); - _last_error= wolfssl_error_buffer; + char ssl_error_buffer[SSL_ERROR_SIZE]= { 0 }; + ERR_error_string_n(ssl_error, ssl_error_buffer, sizeof(ssl_error_buffer)); + _last_error= ssl_error_buffer; read_size= SOCKET_ERROR; break; }