| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <errno.h> |
| #include <netdb.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <openssl/ssl.h> |
| #include <pthread.h> |
| |
| #include <glib-object.h> |
| #include <glib.h> |
| |
| #include <thrift/c_glib/thrift.h> |
| #include <thrift/c_glib/transport/thrift_transport.h> |
| #include <thrift/c_glib/transport/thrift_socket.h> |
| #include <thrift/c_glib/transport/thrift_ssl_socket.h> |
| |
| |
| #if defined(WIN32) |
| #define MUTEX_TYPE HANDLE |
| #define MUTEX_SETUP(x) (x) = CreateMutex(NULL, FALSE, NULL) |
| #define MUTEX_CLEANUP(x) CloseHandle(x) |
| #define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE) |
| #define MUTEX_UNLOCK(x) ReleaseMutex(x) |
| #else |
| #define MUTEX_TYPE pthread_mutex_t |
| #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) |
| #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) |
| #define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) |
| #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) |
| #endif |
| |
| #define OPENSSL_VERSION_NO_THREAD_ID 0x10000000L |
| |
| |
| /* object properties */ |
| enum _ThriftSSLSocketProperties |
| { |
| PROP_THRIFT_SSL_SOCKET_CONTEXT = 3, |
| PROP_THRIFT_SSL_SELF_SIGNED |
| }; |
| |
| /* To hold a global state management of openssl for all instances */ |
| static gboolean thrift_ssl_socket_openssl_initialized=FALSE; |
| /* This array will store all of the mutexes available to OpenSSL. */ |
| static MUTEX_TYPE *thrift_ssl_socket_global_mutex_buf=NULL; |
| |
| |
| /** |
| * OpenSSL uniq id function. |
| * |
| * @return thread id |
| */ |
| static unsigned long thrift_ssl_socket_static_id_function(void) |
| { |
| #if defined(WIN32) |
| return GetCurrentThreadId(); |
| #else |
| return ((unsigned long) pthread_self()); |
| #endif |
| } |
| |
| static void thrift_ssl_socket_static_locking_callback(int mode, int n, const char* unk, int id) { |
| if (mode & CRYPTO_LOCK) |
| MUTEX_LOCK(thrift_ssl_socket_global_mutex_buf[n]); |
| else |
| MUTEX_UNLOCK(thrift_ssl_socket_global_mutex_buf[n]); |
| } |
| |
| static int thrift_ssl_socket_static_thread_setup(void) |
| { |
| int i; |
| |
| thrift_ssl_socket_global_mutex_buf = malloc(CRYPTO_num_locks() * sizeof(MUTEX_TYPE)); |
| if (!thrift_ssl_socket_global_mutex_buf) |
| return 0; |
| for (i = 0; i < CRYPTO_num_locks( ); i++) |
| MUTEX_SETUP(thrift_ssl_socket_global_mutex_buf[i]); |
| CRYPTO_set_id_callback(thrift_ssl_socket_static_id_function); |
| CRYPTO_set_locking_callback(thrift_ssl_socket_static_locking_callback); |
| return 1; |
| } |
| |
| static int thrift_ssl_socket_static_thread_cleanup(void) |
| { |
| int i; |
| if (!thrift_ssl_socket_global_mutex_buf) |
| return 0; |
| CRYPTO_set_id_callback(NULL); |
| CRYPTO_set_locking_callback(NULL); |
| for (i = 0; i < CRYPTO_num_locks( ); i++) |
| MUTEX_CLEANUP(thrift_ssl_socket_global_mutex_buf[i]); |
| free(thrift_ssl_socket_global_mutex_buf); |
| thrift_ssl_socket_global_mutex_buf = NULL; |
| return 1; |
| } |
| |
| /* |
| static void* thrift_ssl_socket_dyn_lock_create_callback(const char* unk, int id) { |
| g_print("We should create a lock\n"); |
| return NULL; |
| } |
| |
| static void thrift_ssl_socket_dyn_lock_callback(int mode, void* lock, const char* unk, int id) { |
| if (lock != NULL) { |
| if (mode & CRYPTO_LOCK) { |
| g_printf("We should lock thread %d\n"); |
| } else { |
| g_printf("We should unlock thread %d\n"); |
| } |
| } |
| } |
| |
| static void thrift_ssl_socket_dyn_lock_destroy_callback(void* lock, const char* unk, int id) { |
| g_printf("We must destroy the lock\n"); |
| } |
| */ |
| |
| |
| G_DEFINE_TYPE(ThriftSSLSocket, thrift_ssl_socket, THRIFT_TYPE_SOCKET) |
| |
| |
| |
| /** |
| * When there's a thread context attached, we pass the SSL socket context so it |
| * can check if the error is outside SSL, on I/O for example |
| * @param socket |
| * @param error_msg |
| * @param thrift_error_no |
| * @param ssl_error |
| * @param error |
| */ |
| static |
| void thrift_ssl_socket_get_ssl_error(ThriftSSLSocket *socket, const guchar *error_msg, guint thrift_error_no, int ssl_error, GError **error) |
| { |
| unsigned long error_code; |
| char buffer[1024]; |
| int buffer_size=1024; |
| gboolean first_error = TRUE; |
| int ssl_error_type = SSL_get_error(socket->ssl, ssl_error); |
| if(ssl_error_type>0){ |
| switch(ssl_error_type){ |
| case SSL_ERROR_SSL: |
| buffer_size-=snprintf(buffer, buffer_size, "SSL %s: ", error_msg); |
| while ((error_code = ERR_get_error()) != 0 && buffer_size>1) { |
| const char* reason = ERR_reason_error_string(error_code); |
| if(reason!=NULL){ |
| if(!first_error) { |
| buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "\n\t"); |
| first_error=FALSE; |
| } |
| buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX(%s) -> %s", error_code, reason, SSL_state_string(socket->ssl)); |
| } |
| } |
| break; |
| case SSL_ERROR_SYSCALL: |
| buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg); |
| buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", errno, strerror(errno)); |
| break; |
| case SSL_ERROR_WANT_READ: |
| buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg); |
| buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", ssl_error_type, "Error while reading from underlaying layer"); |
| break; |
| case SSL_ERROR_WANT_WRITE: |
| buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg); |
| buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", ssl_error_type, "Error while writting to underlaying layer"); |
| break; |
| |
| } |
| g_set_error (error, THRIFT_TRANSPORT_ERROR, |
| thrift_error_no, "%s", buffer); |
| } |
| } |
| |
| /** |
| * For global SSL errors |
| * @param error_msg |
| * @param thrift_error_no |
| * @param error |
| */ |
| static |
| void thrift_ssl_socket_get_error(const guchar *error_msg, guint thrift_error_no, GError **error) |
| { |
| unsigned long error_code; |
| while ((error_code = ERR_get_error()) != 0) { |
| const char* reason = ERR_reason_error_string(error_code); |
| if (reason == NULL) { |
| g_set_error (error, THRIFT_TRANSPORT_ERROR, |
| thrift_error_no, |
| "SSL error %lX: %s", error_code, error_msg); |
| }else{ |
| g_set_error (error, THRIFT_TRANSPORT_ERROR, |
| thrift_error_no, |
| "SSL error %lX %s: %s", error_code,reason, error_msg); |
| } |
| } |
| } |
| |
| |
| |
| /* implements thrift_transport_is_open */ |
| gboolean |
| thrift_ssl_socket_is_open (ThriftTransport *transport) |
| { |
| return thrift_socket_is_open(transport); |
| } |
| |
| /* overrides thrift_transport_peek */ |
| gboolean |
| thrift_ssl_socket_peek (ThriftTransport *transport, GError **error) |
| { |
| gboolean retval = FALSE; |
| ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); |
| if (thrift_ssl_socket_is_open (transport)) |
| { |
| int rc; |
| gchar byte; |
| rc = SSL_peek(ssl_socket->ssl, &byte, 1); |
| if (rc < 0) { |
| thrift_ssl_socket_get_ssl_error(ssl_socket, "Check socket data", |
| THRIFT_SSL_SOCKET_ERROR_SSL, rc, error); |
| } |
| if (rc == 0) { |
| ERR_clear_error(); |
| } |
| retval = (rc > 0); |
| } |
| return retval; |
| } |
| |
| /* implements thrift_transport_open */ |
| gboolean |
| thrift_ssl_socket_open (ThriftTransport *transport, GError **error) |
| { |
| ERR_clear_error(); |
| |
| if (!thrift_socket_open(transport, error)) { |
| return FALSE; |
| } |
| |
| if (!THRIFT_SSL_SOCKET_GET_CLASS(transport)->handle_handshake(transport, error)) { |
| thrift_ssl_socket_close(transport, NULL); |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| /* implements thrift_transport_close */ |
| gboolean |
| thrift_ssl_socket_close (ThriftTransport *transport, GError **error) |
| { |
| gboolean retval = FALSE; |
| ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET(transport); |
| if(ssl_socket!=NULL && ssl_socket->ssl) { |
| int rc = SSL_shutdown(ssl_socket->ssl); |
| /* if (rc < 0) { |
| int errno_copy = THRIFT_SSL_SOCKET_ERROR_SSL; |
| }*/ |
| SSL_free(ssl_socket->ssl); |
| ssl_socket->ssl = NULL; |
| ERR_remove_state(0); |
| } |
| return thrift_socket_close(transport, error); |
| } |
| |
| /* implements thrift_transport_read */ |
| gint32 |
| thrift_ssl_socket_read (ThriftTransport *transport, gpointer buf, |
| guint32 len, GError **error) |
| { |
| guint maxRecvRetries_ = 10; |
| ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); |
| guint bytes = 0; |
| guint retries = 0; |
| ThriftSocket *socket = THRIFT_SOCKET (transport); |
| g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET && ssl_socket->ssl!=NULL, FALSE); |
| |
| for (retries=0; retries < maxRecvRetries_; retries++) { |
| bytes = SSL_read(ssl_socket->ssl, buf, len); |
| if (bytes >= 0) |
| break; |
| int errno_copy = THRIFT_GET_SOCKET_ERROR; |
| if (SSL_get_error(ssl_socket->ssl, bytes) == SSL_ERROR_SYSCALL) { |
| if (ERR_get_error() == 0 && errno_copy == THRIFT_EINTR) { |
| continue; |
| } |
| }else{ |
| thrift_ssl_socket_get_ssl_error(ssl_socket, "Receive error", |
| THRIFT_SSL_SOCKET_ERROR_SSL, bytes, error); |
| |
| } |
| return -1; |
| } |
| return bytes; |
| } |
| |
| /* implements thrift_transport_read_end |
| * called when write is complete. nothing to do on our end. */ |
| gboolean |
| thrift_ssl_socket_read_end (ThriftTransport *transport, GError **error) |
| { |
| /* satisfy -Wall */ |
| THRIFT_UNUSED_VAR (transport); |
| THRIFT_UNUSED_VAR (error); |
| return TRUE; |
| } |
| |
| /* implements thrift_transport_write */ |
| gboolean |
| thrift_ssl_socket_write (ThriftTransport *transport, const gpointer buf, |
| const guint32 len, GError **error) |
| { |
| ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); |
| gint ret = 0; |
| guint sent = 0; |
| ThriftSocket *socket = THRIFT_SOCKET (transport); |
| g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET && ssl_socket->ssl!=NULL, FALSE); |
| |
| while (sent < len) |
| { |
| ret = SSL_write (ssl_socket->ssl, (guint8 *)buf + sent, len - sent); |
| if (ret < 0) |
| { |
| thrift_ssl_socket_get_ssl_error(ssl_socket, "Send error", |
| THRIFT_SSL_SOCKET_ERROR_SSL, ret, error); |
| return FALSE; |
| } |
| sent += ret; |
| } |
| |
| return sent==len; |
| } |
| |
| /* implements thrift_transport_write_end |
| * called when write is complete. nothing to do on our end. */ |
| gboolean |
| thrift_ssl_socket_write_end (ThriftTransport *transport, GError **error) |
| { |
| /* satisfy -Wall */ |
| THRIFT_UNUSED_VAR (transport); |
| THRIFT_UNUSED_VAR (error); |
| return TRUE; |
| } |
| |
| /* implements thrift_transport_flush |
| * flush pending data. since we are not buffered, this is a no-op */ |
| gboolean |
| thrift_ssl_socket_flush (ThriftTransport *transport, GError **error) |
| { |
| ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); |
| gint ret = 0; |
| guint sent = 0; |
| |
| ThriftSocket *socket = THRIFT_SOCKET (transport); |
| g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET && ssl_socket->ssl!=NULL, FALSE); |
| |
| BIO* bio = SSL_get_wbio(ssl_socket->ssl); |
| if (bio == NULL) { |
| g_set_error (error, THRIFT_TRANSPORT_ERROR, |
| THRIFT_TRANSPORT_ERROR_SEND, |
| "failed to flush, wbio returned null"); |
| return FALSE; |
| } |
| if (BIO_flush(bio) != 1) { |
| g_set_error (error, THRIFT_TRANSPORT_ERROR, |
| THRIFT_TRANSPORT_ERROR_SEND, |
| "failed to flush it returned error"); |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| |
| gboolean |
| thrift_ssl_socket_handle_handshake(ThriftTransport * transport, GError **error) |
| { |
| ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); |
| ThriftSocket *socket = THRIFT_SOCKET (transport); |
| g_return_val_if_fail (thrift_transport_is_open (transport), FALSE); |
| |
| if(THRIFT_SSL_SOCKET_GET_CLASS(ssl_socket)->create_ssl_context(transport, error)){ |
| /*Context created*/ |
| SSL_set_fd(ssl_socket->ssl, socket->sd); |
| int rc; |
| if(ssl_socket->server){ |
| rc = SSL_accept(ssl_socket->ssl); |
| }else{ |
| rc = SSL_connect(ssl_socket->ssl); |
| } |
| if (rc <= 0) { |
| thrift_ssl_socket_get_ssl_error(ssl_socket, "Error while connect/bind", THRIFT_SSL_SOCKET_ERROR_CONNECT_BIND, rc, error); |
| return FALSE; |
| } |
| }else |
| return FALSE; |
| |
| return thrift_ssl_socket_authorize(transport, error); |
| } |
| |
| gboolean |
| thrift_ssl_socket_create_ssl_context(ThriftTransport * transport, GError **error) |
| { |
| ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (transport); |
| |
| if(socket->ctx!=NULL){ |
| if(socket->ssl!=NULL) { |
| return TRUE; |
| } |
| |
| socket->ssl = SSL_new(socket->ctx); |
| if (socket->ssl == NULL) { |
| g_set_error (error, THRIFT_TRANSPORT_ERROR, |
| THRIFT_SSL_SOCKET_ERROR_TRANSPORT, |
| "Unable to create default SSL context"); |
| return FALSE; |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| |
| gboolean thrift_ssl_load_cert_from_file(ThriftSSLSocket *ssl_socket, const char *file_name) |
| { |
| char error_buffer[255]; |
| if (!thrift_ssl_socket_openssl_initialized) { |
| g_error("OpenSSL is not initialized yet"); |
| return FALSE; |
| } |
| int rc = SSL_CTX_load_verify_locations(ssl_socket->ctx, file_name, NULL); |
| if (rc != 1) { /*verify authentication result*/ |
| ERR_error_string_n(ERR_get_error(), error_buffer, 254); |
| g_warning("Load of certificates failed: %s!", error_buffer); |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| |
| gboolean thrift_ssl_load_cert_from_buffer(ThriftSSLSocket *ssl_socket, const char chain_certs[]) |
| { |
| gboolean retval = FALSE; |
| /* Load chain of certs*/ |
| X509 *cacert=NULL; |
| BIO *mem = BIO_new_mem_buf(chain_certs,strlen(chain_certs)); |
| X509_STORE *cert_store = SSL_CTX_get_cert_store(ssl_socket->ctx); |
| |
| if(cert_store!=NULL){ |
| int index = 0; |
| while ((cacert = PEM_read_bio_X509(mem, NULL, 0, NULL))!=NULL) { |
| if(cacert) { |
| X509_STORE_add_cert(cert_store, cacert); |
| X509_free(cacert); |
| cacert=NULL; |
| } /* Free immediately */ |
| index++; |
| } |
| retval=TRUE; |
| } |
| BIO_free(mem); |
| return retval; |
| } |
| |
| gboolean |
| thrift_ssl_socket_authorize(ThriftTransport * transport, GError **error) |
| { |
| ThriftSocket *socket = THRIFT_SOCKET (transport); |
| ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); |
| ThriftSSLSocketClass *cls = THRIFT_SSL_SOCKET_GET_CLASS(ssl_socket); |
| gboolean authorization_result = FALSE; |
| |
| if(cls!=NULL && ssl_socket->ssl!=NULL){ |
| int rc = SSL_get_verify_result(ssl_socket->ssl); |
| if (rc != X509_V_OK) { /* verify authentication result */ |
| if (rc == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && ssl_socket->allow_selfsigned) { |
| g_debug("The certificate is a self-signed certificate and configuration allows it"); |
| } else { |
| g_set_error (error, |
| THRIFT_TRANSPORT_ERROR, |
| THRIFT_SSL_SOCKET_ERROR_SSL_CERT_VALIDATION_FAILED, |
| "The certificate verification failed: %s (%d)", X509_verify_cert_error_string(rc), rc); |
| return FALSE; |
| } |
| } |
| |
| X509* cert = SSL_get_peer_certificate(ssl_socket->ssl); |
| if (cert == NULL) { |
| if (SSL_get_verify_mode(ssl_socket->ssl) & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { |
| g_set_error (error, |
| THRIFT_TRANSPORT_ERROR, |
| THRIFT_SSL_SOCKET_ERROR_SSL_CERT_VALIDATION_FAILED, |
| "No certificate present. Are you connecting SSL server?"); |
| return FALSE; |
| } |
| g_debug("No certificate required"); |
| return TRUE; |
| } |
| |
| /* certificate is present, since we don't support access manager we are done */ |
| if (cls->authorize_peer == NULL) { |
| X509_free(cert); |
| g_debug("Certificate presented but we're not checking it"); |
| return TRUE; |
| } else { |
| /* both certificate and access manager are present */ |
| struct sockaddr_storage sa; |
| socklen_t saLength = sizeof(struct sockaddr_storage); |
| if (getpeername(socket->sd, (struct sockaddr*)&sa, &saLength) != 0) { |
| sa.ss_family = AF_UNSPEC; |
| } |
| authorization_result = cls->authorize_peer(transport, cert, &sa, error); |
| } |
| if(cert != NULL) { |
| X509_free(cert); |
| } |
| } |
| |
| return authorization_result; |
| } |
| |
| |
| /* initializes the instance */ |
| static void |
| thrift_ssl_socket_init (ThriftSSLSocket *socket) |
| { |
| GError *error = NULL; |
| socket->ssl = NULL; |
| socket->ctx = thrift_ssl_socket_context_initialize(SSLTLS, &error); |
| if(socket->ctx == NULL) { |
| g_info("The SSL context was not automatically initialized with protocol %d", SSLTLS); |
| if(error!=NULL){ |
| g_info("Reported reason %s", error->message); |
| g_error_free (error); |
| } |
| } |
| socket->server = FALSE; |
| socket->allow_selfsigned = FALSE; |
| |
| } |
| |
| /* destructor */ |
| static void |
| thrift_ssl_socket_finalize (GObject *object) |
| { |
| ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (object); |
| GError *error=NULL; |
| if(socket!=NULL){ |
| g_debug("Instance %p destroyed", (void *)socket); |
| if(socket->ssl != NULL) |
| { |
| thrift_ssl_socket_close(THRIFT_TRANSPORT(object), &error); |
| socket->ssl=NULL; |
| } |
| |
| if(socket->ctx!=NULL){ |
| g_debug("Freeing the context for the instance"); |
| SSL_CTX_free(socket->ctx); |
| socket->ctx=NULL; |
| } |
| } |
| |
| if (G_OBJECT_CLASS (thrift_ssl_socket_parent_class)->finalize) |
| (*G_OBJECT_CLASS (thrift_ssl_socket_parent_class)->finalize) (object); |
| } |
| |
| /* property accessor */ |
| void |
| thrift_ssl_socket_get_property (GObject *object, guint property_id, |
| GValue *value, GParamSpec *pspec) |
| { |
| ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (object); |
| THRIFT_UNUSED_VAR (pspec); |
| |
| switch (property_id) |
| { |
| case PROP_THRIFT_SSL_SOCKET_CONTEXT: |
| g_value_set_pointer (value, socket->ctx); |
| break; |
| } |
| } |
| |
| /* property mutator */ |
| void |
| thrift_ssl_socket_set_property (GObject *object, guint property_id, |
| const GValue *value, GParamSpec *pspec) |
| { |
| ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (object); |
| |
| THRIFT_UNUSED_VAR (pspec); |
| switch (property_id) |
| { |
| case PROP_THRIFT_SSL_SOCKET_CONTEXT: |
| if(socket->ctx!=NULL){ |
| g_debug("Freeing the context since we are setting a new one"); |
| SSL_CTX_free(socket->ctx); |
| } |
| socket->ctx = g_value_get_pointer(value); /* We copy the context */ |
| break; |
| |
| case PROP_THRIFT_SSL_SELF_SIGNED: |
| socket->allow_selfsigned = g_value_get_boolean(value); |
| break; |
| default: |
| g_warning("Trying to set property %i that doesn't exists!", property_id); |
| /* thrift_socket_set_property(object, property_id, value, pspec); */ |
| break; |
| } |
| } |
| |
| void |
| thrift_ssl_socket_initialize_openssl(void) |
| { |
| if(thrift_ssl_socket_openssl_initialized){ |
| return; |
| } |
| thrift_ssl_socket_openssl_initialized=TRUE; |
| SSL_library_init(); |
| ERR_load_crypto_strings(); |
| SSL_load_error_strings(); |
| ERR_load_BIO_strings(); |
| |
| /* Setup locking */ |
| g_debug("We setup %d threads locks", thrift_ssl_socket_static_thread_setup()); |
| |
| /* dynamic locking |
| CRYPTO_set_dynlock_create_callback(thrift_ssl_socket_dyn_lock_create_callback); |
| CRYPTO_set_dynlock_lock_callback(thrift_ssl_socket_dyn_lock_callback); |
| CRYPTO_set_dynlock_destroy_callback(thrift_ssl_socket_dyn_lock_destroy_callback); |
| */ |
| } |
| |
| |
| void thrift_ssl_socket_finalize_openssl(void) |
| { |
| if (!thrift_ssl_socket_openssl_initialized) { |
| return; |
| } |
| thrift_ssl_socket_openssl_initialized = FALSE; |
| |
| g_debug("We cleared %d threads locks", thrift_ssl_socket_static_thread_cleanup()); |
| /* Not supported |
| CRYPTO_set_locking_callback(NULL); |
| CRYPTO_set_dynlock_create_callback(NULL); |
| CRYPTO_set_dynlock_lock_callback(NULL); |
| CRYPTO_set_dynlock_destroy_callback(NULL); |
| */ |
| ERR_free_strings(); |
| EVP_cleanup(); |
| CRYPTO_cleanup_all_ex_data(); |
| ERR_remove_state(0); |
| } |
| |
| |
| /* initializes the class */ |
| static void |
| thrift_ssl_socket_class_init (ThriftSSLSocketClass *cls) |
| { |
| ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls); |
| GObjectClass *gobject_class = G_OBJECT_CLASS (cls); |
| GParamSpec *param_spec = NULL; |
| |
| g_debug("Initialization of ThriftSSLSocketClass"); |
| /* setup accessors and mutators */ |
| gobject_class->get_property = thrift_ssl_socket_get_property; |
| gobject_class->set_property = thrift_ssl_socket_set_property; |
| param_spec = g_param_spec_pointer ("ssl_context", |
| "SSLContext", |
| "Set the SSL context for handshake with the remote host", |
| G_PARAM_READWRITE); |
| g_object_class_install_property (gobject_class, PROP_THRIFT_SSL_SOCKET_CONTEXT, |
| param_spec); |
| param_spec = g_param_spec_boolean ("ssl_accept_selfsigned", |
| "Accept Self Signed", |
| "Whether or not accept self signed certificate", |
| FALSE, |
| G_PARAM_READWRITE); |
| g_object_class_install_property (gobject_class, PROP_THRIFT_SSL_SELF_SIGNED, |
| param_spec); |
| /* Class methods */ |
| cls->handle_handshake = thrift_ssl_socket_handle_handshake; |
| cls->create_ssl_context = thrift_ssl_socket_create_ssl_context; |
| |
| /* Override */ |
| gobject_class->finalize = thrift_ssl_socket_finalize; |
| ttc->is_open = thrift_ssl_socket_is_open; |
| ttc->peek = thrift_ssl_socket_peek; |
| ttc->open = thrift_ssl_socket_open; |
| ttc->close = thrift_ssl_socket_close; |
| ttc->read = thrift_ssl_socket_read; |
| ttc->read_end = thrift_ssl_socket_read_end; |
| ttc->write = thrift_ssl_socket_write; |
| ttc->write_end = thrift_ssl_socket_write_end; |
| ttc->flush = thrift_ssl_socket_flush; |
| } |
| |
| |
| /* |
| * Public API |
| */ |
| ThriftSSLSocket* |
| thrift_ssl_socket_new(ThriftSSLSocketProtocol ssl_protocol, GError **error) |
| { |
| ThriftSSLSocket *thriftSSLSocket = NULL; |
| SSL_CTX *ssl_context = NULL; |
| /* Create the context */ |
| if((ssl_context=thrift_ssl_socket_context_initialize(ssl_protocol, error))==NULL){ |
| g_warning("We cannot initialize context for protocol %d", ssl_protocol); |
| return thriftSSLSocket; |
| } |
| |
| /* FIXME if the protocol is different? */ |
| thriftSSLSocket = g_object_new (THRIFT_TYPE_SSL_SOCKET, "ssl_context", ssl_context, NULL); |
| return thriftSSLSocket; |
| } |
| |
| ThriftSSLSocket* |
| thrift_ssl_socket_new_with_host(ThriftSSLSocketProtocol ssl_protocol, gchar *hostname, guint port, GError **error) |
| { |
| ThriftSSLSocket *thriftSSLSocket = NULL; |
| SSL_CTX *ssl_context = NULL; |
| /* Create the context */ |
| if((ssl_context=thrift_ssl_socket_context_initialize(ssl_protocol, error))==NULL){ |
| /* FIXME Do error control */ |
| return thriftSSLSocket; |
| } |
| /* FIXME if the protocol is different? */ |
| thriftSSLSocket = g_object_new (THRIFT_TYPE_SSL_SOCKET, "ssl_context", ssl_context, "hostname", hostname, "port", port, NULL); |
| return thriftSSLSocket; |
| } |
| |
| void thrift_ssl_socket_set_manager(ThriftSSLSocket *ssl_socket, AUTHORIZATION_MANAGER_CALLBACK callback) |
| { |
| ThriftSSLSocketClass *sslSocketClass = THRIFT_SSL_SOCKET_GET_CLASS (ssl_socket); |
| if(sslSocketClass){ |
| sslSocketClass->authorize_peer = callback; |
| } |
| } |
| |
| |
| SSL_CTX* |
| thrift_ssl_socket_context_initialize(ThriftSSLSocketProtocol ssl_protocol, GError **error) |
| { |
| SSL_CTX* context = NULL; |
| switch(ssl_protocol){ |
| case SSLTLS: |
| context = SSL_CTX_new(SSLv23_method()); |
| break; |
| #ifndef OPENSSL_NO_SSL3 |
| case SSLv3: |
| context = SSL_CTX_new(SSLv3_method()); |
| break; |
| #endif |
| case TLSv1_0: |
| context = SSL_CTX_new(TLSv1_method()); |
| break; |
| case TLSv1_1: |
| context = SSL_CTX_new(TLSv1_1_method()); |
| break; |
| case TLSv1_2: |
| context = SSL_CTX_new(TLSv1_2_method()); |
| break; |
| default: |
| g_set_error (error, THRIFT_TRANSPORT_ERROR, |
| THRIFT_SSL_SOCKET_ERROR_CIPHER_NOT_AVAILABLE, |
| "The SSL protocol is unknown for %d", ssl_protocol); |
| return NULL; |
| break; |
| } |
| |
| if (context == NULL) { |
| thrift_ssl_socket_get_error("No cipher overlay", THRIFT_SSL_SOCKET_ERROR_CIPHER_NOT_AVAILABLE, error); |
| return NULL; |
| } |
| SSL_CTX_set_mode(context, SSL_MODE_AUTO_RETRY); |
| |
| /* Disable horribly insecure SSLv2 and SSLv3 protocols but allow a handshake |
| with older clients so they get a graceful denial. */ |
| if (ssl_protocol == SSLTLS) { |
| SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); |
| SSL_CTX_set_options(context, SSL_OP_NO_SSLv3); /* THRIFT-3164 */ |
| } |
| |
| return context; |
| } |
| |