blob: ae258b5dc00bb6fe19aca0a8251be7cbd6b5fae3 [file] [log] [blame]
/*
* Copyright (C) Tildeslash Ltd. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3.
*
* 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 Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
*
* You must obey the GNU Affero General Public License in all respects
* for all of the code used other than OpenSSL.
*/
#include "config.h"
#ifdef HAVE_OPENSSL
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/bio.h>
#include "monit.h"
#include "net.h"
#include "ssl.h"
/* -------------------------------------------------------------- Prototypes */
#define SSLERROR ERR_error_string(ERR_get_error(),NULL)
static int unsigned long ssl_thread_id();
static void ssl_mutex_lock(int, int n, const char *, int );
static int verify_init(ssl_server_connection *);
static int verify_callback(int, X509_STORE_CTX *);
static int check_preverify(X509_STORE_CTX *);
static void cleanup_ssl_socket(ssl_connection *);
static void cleanup_ssl_server_socket(ssl_server_connection *);
static int handle_error(int, ssl_connection *);
static int update_ssl_cert_data(ssl_connection *);
static ssl_server_connection *new_ssl_server_connection(char *, char *);
static int start_ssl();
static int ssl_initialized = FALSE;
static pthread_mutex_t ssl_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t *ssl_mutex_table;
/* ------------------------------------------------------------- Definitions */
/**
* Number of random bytes to obtain
*/
#define RANDOM_BYTES 1024
/**
* The PRIMARY random device selected for seeding the PRNG. We use a
* non-blocking pseudo random device, to generate pseudo entropy.
*/
#define URANDOM_DEVICE "/dev/urandom"
/**
* If a non-blocking device is not found on the system a blocking
* entropy producer is tried instead.
*/
#define RANDOM_DEVICE "/dev/random"
/* The list of all ciphers suites in order of strength except those containing
anonymous DH ciphers, low bit-size ciphers, export-crippled ciphers or the
MD5 hash algorithm */
#define CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"
/**
* SSL Socket methods.
*
* @file
*/
/* ------------------------------------------------------------------ Public */
/**
* Embeds a socket in a ssl connection.
* @param socket the socket to be used.
* @return The ssl connection or NULL if an error occured.
*/
int embed_ssl_socket(ssl_connection *ssl, int socket) {
int ssl_error;
time_t ssl_time;
if (!ssl)
return FALSE;
if (!ssl_initialized)
start_ssl();
if (socket >= 0) {
ssl->socket = socket;
} else {
LogError("%s: Socket error!\n", prog);
goto sslerror;
}
if ((ssl->handler = SSL_new (ssl->ctx)) == NULL) {
LogError("%s: Cannot initialize the SSL handler -- %s\n", prog, SSLERROR);
goto sslerror;
}
if (SSL_CTX_set_cipher_list(ssl->ctx, CIPHER_LIST) != 1) {
LogError("%s: Error setting cipher list '" CIPHER_LIST "' (no valid ciphers)", prog);
goto sslerror;
}
set_noblock(ssl->socket);
if ((ssl->socket_bio = BIO_new_socket(ssl->socket, BIO_NOCLOSE)) == NULL) {
LogError("%s: Cannot generate IO buffer -- %s\n", prog, SSLERROR);
goto sslerror;
}
SSL_set_bio(ssl->handler, ssl->socket_bio, ssl->socket_bio);
ssl_time = time(NULL);
while ((ssl_error = SSL_connect (ssl->handler)) < 0) {
if ((time(NULL) - ssl_time) > SSL_TIMEOUT) {
LogError("%s: SSL service timeout!\n", prog);
goto sslerror;
}
if (!handle_error(ssl_error, ssl))
goto sslerror;
if (!BIO_should_retry(ssl->socket_bio))
goto sslerror;
}
ssl->cipher = (char *) SSL_get_cipher(ssl->handler);
if (! update_ssl_cert_data(ssl)) {
LogError("%s: Cannot get the SSL server certificate!\n", prog);
goto sslerror;
}
return TRUE;
sslerror:
cleanup_ssl_socket(ssl);
return FALSE;
}
/**
* Compare certificate with given md5 sum
* @param ssl reference to ssl connection
* @param md5sum string of the md5sum to test against
* @return TRUE, if sums do not match FALSE
*/
int check_ssl_md5sum(ssl_connection *ssl, char *md5sum) {
unsigned int i = 0;
ASSERT(md5sum);
while ((i < ssl->cert_md5_len) && (md5sum[2*i] != '\0') && (md5sum[2*i+1] != '\0')) {
unsigned char c = (md5sum[2*i] > 57 ? md5sum[2*i] - 87 : md5sum[2*i] - 48) * 0x10+ (md5sum[2*i+1] > 57 ? md5sum[2*i+1] - 87 : md5sum[2*i+1] - 48);
if (c != ssl->cert_md5[i])
return FALSE;
i++;
}
return TRUE;
}
/**
* Closes a ssl connection (ssl socket + net socket)
* @param ssl ssl connection
* @return TRUE, or FALSE if an error has occured.
*/
int close_ssl_socket(ssl_connection *ssl) {
int rv;
if (!ssl)
return FALSE;
if (! (rv = SSL_shutdown(ssl->handler))) {
shutdown(ssl->socket, 1);
rv = SSL_shutdown(ssl->handler);
}
close_socket(ssl->socket);
cleanup_ssl_socket(ssl);
return (rv > 0) ? TRUE : FALSE;
}
/**
* Garbage collection for non-reusable parts a ssl connection
* @param ssl ssl connection
*/
void delete_ssl_socket(ssl_connection *ssl) {
if (!ssl)
return;
cleanup_ssl_socket(ssl);
if (ssl->ctx && !ssl->accepted)
SSL_CTX_free(ssl->ctx);
ssl->ctx = NULL;
FREE(ssl);
}
/**
* Initializes a ssl connection for server use.
* @param pemfilename Filename for the key/cert file
* @return An ssl connection, or NULL if an error occured.
*/
ssl_server_connection *init_ssl_server(char *pemfile, char *clientpemfile) {
SSL_METHOD *server_method = NULL;
ssl_server_connection *ssl_server;
ASSERT(pemfile);
if (!ssl_initialized)
start_ssl();
ssl_server = new_ssl_server_connection(pemfile, clientpemfile);
#ifdef OPENSSL_FIPS
if (FIPS_mode())
server_method = TLSv1_server_method();
else
#endif
server_method = SSLv23_server_method();
if (!(ssl_server->method = server_method)) {
LogError("%s: Cannot initialize the SSL method -- %s\n", prog, SSLERROR);
goto sslerror;
}
if (!(ssl_server->ctx = SSL_CTX_new(ssl_server->method))) {
LogError("%s: Cannot initialize SSL server certificate handler -- %s\n", prog, SSLERROR);
goto sslerror;
}
if (SSL_CTX_use_certificate_chain_file(ssl_server->ctx, pemfile) != 1) {
LogError("%s: Cannot initialize SSL server certificate -- %s\n", prog, SSLERROR);
goto sslerror;
}
if (SSL_CTX_use_PrivateKey_file(ssl_server->ctx, pemfile, SSL_FILETYPE_PEM) != 1) {
LogError("%s: Cannot initialize SSL server private key -- %s\n", prog, SSLERROR);
goto sslerror;
}
if (SSL_CTX_check_private_key(ssl_server->ctx) != 1) {
LogError("%s: The private key doesn't match the certificate public key -- %s\n", prog, SSLERROR);
goto sslerror;
}
/* Disable session cache */
SSL_CTX_set_session_cache_mode(ssl_server->ctx, SSL_SESS_CACHE_OFF);
/*
* We need this to force transmission of client certs
*/
if (!verify_init(ssl_server)) {
LogError("%s: Verification engine was not properly initialized -- %s\n", prog, SSLERROR);
goto sslerror;
}
if (ssl_server->clientpemfile) {
STACK_OF(X509_NAME) *stack = SSL_CTX_get_client_CA_list(ssl_server->ctx);
LogInfo("%s: Found %d client certificates\n", prog, sk_X509_NAME_num(stack));
}
return ssl_server;
sslerror:
delete_ssl_server_socket(ssl_server);
return NULL;
}
/**
* Deletes a SSL server connection.
* @param ssl_server data for ssl server connection
*/
void delete_ssl_server_socket(ssl_server_connection *ssl_server) {
if (!ssl_server)
return;
cleanup_ssl_server_socket(ssl_server);
if (ssl_server->ctx)
SSL_CTX_free(ssl_server->ctx);
FREE(ssl_server);
}
/**
* Inserts an SSL connection in the connection list of a server.
* @param ssl_server data for ssl server connection
* @return new SSL connection for the connection, or NULL if failed
*/
ssl_connection *insert_accepted_ssl_socket(ssl_server_connection *ssl_server) {
ssl_connection *ssl;
ASSERT(ssl_server);
if (!ssl_initialized)
start_ssl();
NEW(ssl);
ssl->method = NULL;
ssl->handler = NULL;
ssl->cert = NULL;
ssl->cipher = NULL;
ssl->socket = 0;
ssl->next = NULL;
ssl->accepted = FALSE;
ssl->cert_md5= NULL;
ssl->cert_md5_len = 0;
ssl->clientpemfile = NULL;
if (ssl_server->clientpemfile != NULL)
ssl->clientpemfile = Str_dup(ssl_server->clientpemfile);
LOCK(ssl_mutex);
ssl->prev = NULL;
ssl->next = ssl_server->ssl_conn_list;
if ( ssl->next != NULL )
ssl->next->prev = ssl;
END_LOCK;
ssl_server->ssl_conn_list = ssl;
ssl->ctx = ssl_server->ctx;
ssl->accepted = TRUE;
return ssl;
}
/**
* Closes an accepted SSL server connection and deletes it form the
* connection list.
* @param ssl_server data for ssl server connection
* @param ssl data the connection to be deleted
*/
void close_accepted_ssl_socket(ssl_server_connection *ssl_server, ssl_connection *ssl) {
if (!ssl || !ssl_server)
return;
close_socket(ssl->socket);
LOCK(ssl_mutex);
if (ssl->prev == NULL)
ssl_server->ssl_conn_list = ssl->next;
else
ssl->prev->next = ssl->next;
END_LOCK;
delete_ssl_socket(ssl);
}
/**
* Embeds an accepted server socket in an existing ssl connection.
* @param ssl ssl connection
* @param socket the socket to be used.
* @return TRUE, or FALSE if an error has occured.
*/
int embed_accepted_ssl_socket(ssl_connection *ssl, int socket) {
int ssl_error;
time_t ssl_time;
ASSERT(ssl);
ssl->socket = socket;
if (!ssl_initialized)
start_ssl();
if (!(ssl->handler = SSL_new(ssl->ctx))) {
LogError("%s: Cannot initialize the SSL handler -- %s\n", prog, SSLERROR);
return FALSE;
}
if (socket < 0) {
LogError("%s: Socket error!\n", prog);
return FALSE;
}
set_noblock(ssl->socket);
if (!(ssl->socket_bio = BIO_new_socket(ssl->socket, BIO_NOCLOSE))) {
LogError("%s: Cannot generate IO buffer -- %s\n", prog, SSLERROR);
return FALSE;
}
SSL_set_bio(ssl->handler, ssl->socket_bio, ssl->socket_bio);
ssl_time = time(NULL);
while ((ssl_error = SSL_accept(ssl->handler)) < 0) {
if ((time(NULL) - ssl_time) > SSL_TIMEOUT) {
LogError("%s: SSL service timeout!\n", prog);
return FALSE;
}
if (!handle_error(ssl_error, ssl))
return FALSE;
if (!BIO_should_retry(ssl->socket_bio))
return FALSE;
}
ssl->cipher = (char *)SSL_get_cipher(ssl->handler);
if (!update_ssl_cert_data(ssl) && ssl->clientpemfile) {
LogError("%s: The client did not supply a required client certificate!\n",
prog);
return FALSE;
}
if (SSL_get_verify_result(ssl->handler) > 0) {
LogError("%s: Verification of the certificate has failed!\n", prog);
return FALSE;
}
return TRUE;
}
/**
* Send data package though the ssl connection
* @param ssl ssl connection
* @param buffer array containg the data
* @param len size of the data container
* @param timeout Seconds to wait for data to be written
* @return number of bytes transmitted, -1 in case of an error
*/
int send_ssl_socket(ssl_connection *ssl, void *buffer, size_t len, int timeout) {
int n = 0;
ASSERT(ssl);
do {
n = SSL_write(ssl->handler, buffer, (int)len);
} while (n <= 0 && BIO_should_retry(ssl->socket_bio) && can_write(ssl->socket, timeout));
return (n > 0) ? n : -1;
}
/**
* Receive data package though the ssl connection
* @param ssl ssl connection
* @param buffer array to hold the data
* @param len size of the data container
* @param timeout Seconds to wait for data to be available
* @return number of bytes transmitted, -1 in case of an error
*/
int recv_ssl_socket(ssl_connection *ssl, void *buffer, int len, int timeout) {
int n = 0;
ASSERT(ssl);
do {
n = SSL_read(ssl->handler, buffer, len);
} while (n < 0 && BIO_should_retry(ssl->socket_bio) && can_read(ssl->socket, timeout));
return (n >= 0) ? n : -1;
}
/**
* Stop SSL support library
* @return TRUE, or FALSE if an error has occured.
*/
void stop_ssl() {
if (ssl_initialized) {
int i;
ssl_initialized = FALSE;
ERR_free_strings();
CRYPTO_set_id_callback(NULL);
CRYPTO_set_locking_callback(NULL);
for (i = 0; i < CRYPTO_num_locks(); i++)
assert(pthread_mutex_destroy(&ssl_mutex_table[i]) == 0);
FREE(ssl_mutex_table);
RAND_cleanup();
}
}
/**
* Generate a new ssl connection
* @return ssl connection container
*/
ssl_connection *new_ssl_connection(char *clientpemfile, int sslversion) {
ssl_connection *ssl;
if (!ssl_initialized)
start_ssl();
NEW(ssl);
ssl->socket_bio = NULL;
ssl->handler = NULL;
ssl->cert = NULL;
ssl->cipher = NULL;
ssl->socket = 0;
ssl->next = NULL;
ssl->accepted = FALSE;
ssl->cert_md5 = NULL;
ssl->cert_md5_len = 0;
ssl->clientpemfile = clientpemfile ? Str_dup(clientpemfile) : NULL;
switch (sslversion) {
case SSL_VERSION_AUTO:
#ifdef OPENSSL_FIPS
if (FIPS_mode()) {
ssl->method = TLSv1_client_method();
} else
#endif
ssl->method = SSLv23_client_method();
break;
case SSL_VERSION_SSLV2:
#ifdef OPENSSL_NO_SSL2
LogError("SSLv2 is not allowed - use either SSLv3 or TLSv1");
goto sslerror;
#else
#ifdef OPENSSL_FIPS
if (FIPS_mode()) {
LogError("SSLv2 is not allowed in FIPS mode - use TLSv1");
goto sslerror;
} else
#endif
ssl->method = SSLv2_client_method();
#endif
break;
case SSL_VERSION_SSLV3:
#ifdef OPENSSL_FIPS
if (FIPS_mode()) {
LogError("SSLv3 is not allowed in FIPS mode - use TLSv1");
goto sslerror;
} else
#endif
ssl->method = SSLv3_client_method();
break;
case SSL_VERSION_TLS:
/* fall through */
default:
ssl->method = TLSv1_client_method();
break;
}
if (!ssl->method) {
LogError("%s: Cannot initialize SSL method -- %s\n", prog, SSLERROR);
goto sslerror;
}
if (!(ssl->ctx = SSL_CTX_new(ssl->method))) {
LogError("%s: Cannot initialize SSL server certificate handler -- %s\n", prog, SSLERROR);
goto sslerror;
}
if (ssl->clientpemfile) {
if (SSL_CTX_use_certificate_chain_file(ssl->ctx, ssl->clientpemfile) <= 0) {
LogError("%s: Cannot initialize SSL server certificate -- %s\n", prog, SSLERROR);
goto sslerror;
}
if (SSL_CTX_use_PrivateKey_file(ssl->ctx, ssl->clientpemfile, SSL_FILETYPE_PEM) <= 0) {
LogError("%s: Cannot initialize SSL server private key -- %s\n", prog, SSLERROR);
goto sslerror;
}
if (!SSL_CTX_check_private_key(ssl->ctx)) {
LogError("%s: Private key does not match the certificate public key -- %s\n", prog, SSLERROR);
goto sslerror;
}
}
return ssl;
sslerror:
delete_ssl_socket(ssl);
return NULL;
}
/* ----------------------------------------------------------------- Private */
/**
* Init verification of transmitted client certs
*/
static int verify_init(ssl_server_connection *ssl_server) {
struct stat stat_buf;
if (!ssl_server->clientpemfile) {
SSL_CTX_set_verify(ssl_server->ctx, SSL_VERIFY_NONE, NULL);
return TRUE;
}
if (stat(ssl_server->clientpemfile, &stat_buf) == -1) {
LogError("%s: Cannot stat the SSL pem path '%s' -- %s\n", prog, Run.httpsslclientpem, STRERROR);
return FALSE;
}
if (S_ISDIR(stat_buf.st_mode)) {
if (!SSL_CTX_load_verify_locations(ssl_server->ctx, NULL , ssl_server->clientpemfile)) {
LogError("%s: Error setting verify directory to %s -- %s\n", prog, Run.httpsslclientpem, SSLERROR);
return FALSE;
}
LogInfo("%s: Loaded SSL client pem directory '%s'\n", prog, ssl_server->clientpemfile);
/* Monit's server cert for cli support */
if (!SSL_CTX_load_verify_locations(ssl_server->ctx, ssl_server->pemfile, NULL)) {
LogError("%s: Error loading verify certificates from %s -- %s\n", prog, ssl_server->pemfile, SSLERROR);
return FALSE;
}
LogInfo("%s: Loaded monit's SSL pem server file '%s'\n", prog, ssl_server->pemfile);
} else if (S_ISREG(stat_buf.st_mode)) {
if (!SSL_CTX_load_verify_locations(ssl_server->ctx, ssl_server->clientpemfile, NULL)) {
LogError("%s: Error loading verify certificates from %s -- %s\n", prog, Run.httpsslclientpem, SSLERROR);
return FALSE;
}
LogInfo("%s: Loaded SSL pem client file '%s'\n", prog, ssl_server->clientpemfile);
/* Monits server cert for cli support ! */
if (!SSL_CTX_load_verify_locations(ssl_server->ctx, ssl_server->pemfile, NULL)) {
LogError("%s: Error loading verify certificates from %s -- %s\n", prog, ssl_server->pemfile, SSLERROR);
return FALSE;
}
LogInfo("%s: Loaded monit's SSL pem server file '%s'\n", prog, ssl_server->pemfile);
SSL_CTX_set_client_CA_list(ssl_server->ctx, SSL_load_client_CA_file(ssl_server->clientpemfile));
} else {
LogError("%s: SSL client pem path is no file or directory %s\n", prog, ssl_server->clientpemfile);
return FALSE;
}
SSL_CTX_set_verify(ssl_server->ctx, SSL_VERIFY_PEER, verify_callback);
return TRUE;
}
/**
* Check the transmitted client certs and a compare with client cert database
*/
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
char subject[STRLEN];
X509_OBJECT found_cert;
X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), subject, STRLEN-1);
if (!preverify_ok && !check_preverify(ctx))
return 0;
if (ctx->error_depth == 0 && X509_STORE_get_by_subject(ctx, X509_LU_X509, X509_get_subject_name(ctx->current_cert), &found_cert) != 1) {
LogError("%s: SSL connection rejected. No matching certificate found -- %s\n", prog, SSLERROR);
return 0;
}
return 1;
}
/**
* Analyse errors found before actual verification
* @return TRUE if successful
*/
static int check_preverify(X509_STORE_CTX *ctx) {
if ((ctx->error != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) && (ctx->error != X509_V_ERR_INVALID_PURPOSE)) {
/* Remote site specified a certificate, but it's not correct */
LogError("%s: SSL connection rejected because certificate verification has failed -- error %i\n", prog, ctx->error);
/* Reject connection */
return FALSE;
}
if (Run.allowselfcert && (ctx->error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)) {
/* Let's accept self signed certs for the moment! */
LogInfo("%s: SSL connection accepted with self signed certificate!\n", prog);
ctx->error = 0;
return TRUE;
}
/* Reject connection */
LogError("%s: SSL connection rejected because certificate verification has failed -- error %i!\n", prog, ctx->error);
return FALSE;
}
/**
* Helper function for the SSL threadding support
* @return current thread number
*/
static int unsigned long ssl_thread_id() {
return ((unsigned long) pthread_self());
}
/**
* Helper function for the SSL threadding support
*/
static void ssl_mutex_lock(int mode, int n, const char *file, int line) {
if (mode & CRYPTO_LOCK)
assert(pthread_mutex_lock( & ssl_mutex_table[n]) == 0);
else
assert(pthread_mutex_unlock( & ssl_mutex_table[n]) == 0);
}
/**
* Handle errors during read, write, connect and accept
* @return TRUE if non fatal, FALSE if non fatal and retry
*/
static int handle_error(int code, ssl_connection *ssl) {
int ssl_error = SSL_get_error(ssl->handler, code);
switch (ssl_error) {
case SSL_ERROR_WANT_READ:
if (can_read(ssl->socket, SSL_TIMEOUT))
return TRUE;
LogError("%s: Openssl read timeout error!\n", prog);
break;
case SSL_ERROR_WANT_WRITE:
if (can_read(ssl->socket, SSL_TIMEOUT))
return TRUE;
LogError("%s: Openssl write timeout error!\n", prog);
break;
case SSL_ERROR_SYSCALL:
LogError("%s: Openssl syscall error: %s!\n", prog, STRERROR);
break;
case SSL_ERROR_SSL:
LogError("%s: Openssl engine error: %s\n", prog, SSLERROR);
break;
default:
LogError("%s: Openssl error!\n", prog);
break;
}
return FALSE;
}
/**
* Garbage collection for non reusable parts of the ssl connection
* @param ssl ssl connection
*/
static void cleanup_ssl_socket(ssl_connection *ssl) {
if (!ssl)
return;
if (ssl->cert) {
X509_free(ssl->cert);
ssl->cert = NULL;
}
if (ssl->handler) {
SSL_free(ssl->handler);
ssl->handler = NULL;
}
if (ssl->socket_bio) {
/* no BIO_free(ssl->socket_bio); necessary, because BIO is freed by ssl->handler */
ssl->socket_bio = NULL;
}
FREE(ssl->cert_issuer);
FREE(ssl->cert_subject);
FREE(ssl->cert_md5);
FREE(ssl->clientpemfile);
}
/**
* Garbage collection for a SSL server connection.
* @param ssl_server data for ssl server connection
*/
static void cleanup_ssl_server_socket(ssl_server_connection *ssl_server) {
if (!ssl_server)
return;
FREE(ssl_server->pemfile);
FREE(ssl_server->clientpemfile);
while (ssl_server->ssl_conn_list) {
ssl_connection *ssl = ssl_server->ssl_conn_list;
ssl_server->ssl_conn_list = ssl_server->ssl_conn_list->next;
close_accepted_ssl_socket(ssl_server, ssl);
}
}
/**
* Updates some data in the ssl connection
* @param ssl reference to ssl connection
* @return TRUE, if not successful FALSE
*/
static int update_ssl_cert_data(ssl_connection *ssl) {
unsigned char md5[EVP_MAX_MD_SIZE];
ASSERT(ssl);
if (!(ssl->cert = SSL_get_peer_certificate(ssl->handler)))
return FALSE;
#ifdef OPENSSL_FIPS
if (!FIPS_mode()) {
/* In FIPS-140 mode, MD5 is unavailable. */
#endif
ssl->cert_issuer = X509_NAME_oneline (X509_get_issuer_name(ssl->cert), 0, 0);
ssl->cert_subject = X509_NAME_oneline (X509_get_subject_name(ssl->cert), 0, 0);
X509_digest(ssl->cert, EVP_md5(), md5, &ssl->cert_md5_len);
ssl->cert_md5= (unsigned char *)Str_dup((char *)md5);
#ifdef OPENSSL_FIPS
}
#endif
return TRUE;
}
/**
* Generate a new ssl server connection
* @return ssl server connection container
*/
static ssl_server_connection *new_ssl_server_connection(char * pemfile, char * clientpemfile) {
ssl_server_connection *ssl_server;
ASSERT(pemfile);
NEW(ssl_server);
ssl_server->ctx = NULL;
ssl_server->method = NULL;
ssl_server->server_socket = 0;
ssl_server->ssl_conn_list = NULL;
ssl_server->pemfile = Str_dup(pemfile);
ssl_server->clientpemfile = clientpemfile ? Str_dup(clientpemfile) : NULL;
return ssl_server;
}
#ifdef OPENSSL_FIPS
/**
* Enable FIPS mode, if it isn't enabled yet.
*/
void enable_fips_mode() {
if (!FIPS_mode()) {
ASSERT(FIPS_mode_set(1));
LogInfo("FIPS-140 mode is enabled\n");
}
}
#endif
/**
* Start SSL support library. It has to be run before the SSL support
* can be used.
* @return TRUE, or FALSE if an error has occured.
*/
static int start_ssl() {
if (! ssl_initialized) {
int i;
int locks = CRYPTO_num_locks();
#ifdef OPENSSL_FIPS
if (Run.fipsEnabled)
enable_fips_mode();
#endif
ssl_initialized = TRUE;
ERR_load_crypto_strings();
ssl_mutex_table = CALLOC(locks, sizeof(pthread_mutex_t));
for (i = 0; i < locks; i++)
pthread_mutex_init(&ssl_mutex_table[i], NULL);
CRYPTO_set_id_callback(ssl_thread_id);
CRYPTO_set_locking_callback(ssl_mutex_lock);
SSL_library_init();
if (file_exist(URANDOM_DEVICE)) {
return(RAND_load_file(URANDOM_DEVICE, RANDOM_BYTES)==RANDOM_BYTES);
} else if (file_exist(RANDOM_DEVICE)) {
DEBUG("Gathering entropy from the random device\n");
return(RAND_load_file(RANDOM_DEVICE, RANDOM_BYTES)==RANDOM_BYTES);
}
return FALSE;
}
return TRUE;
}
#endif