blob: ee55406857ea74335c9ec66c1126ae044af0fcab [file] [log] [blame]
/*
* 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;
}