2009-09-08 17:10:09 +00:00
/*
* mod_openssl - ssl support
*
* Description :
* mod_openssl listens on separate sockets for ssl connections ( https : //...)
*
* Setups :
2011-11-30 00:08:37 +00:00
* openssl - setup a ssl socket ; takes a hash of following parameters :
* listen - ( mandatory ) the socket address ( same as standard listen )
* pemfile - ( mandatory ) contains key and direct certificate for the key ( PEM format )
* ca - file - contains certificate chain
* ciphers - contains colon separated list of allowed ciphers
2011-12-01 17:20:50 +00:00
* default : " ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM "
* options - ( list of strings ) set OpenSSL - specific options ( default : NO_SSLv2 , CIPHER_SERVER_PREFERENCE , NO_COMPRESSION )
* to overwrite defaults you need to explicitly specify the reverse flag ( toggle " NO_ " prefix )
* example : use sslv2 and compression : [ options : ( " SSLv2 " , " COMPRESSION " ) ]
2011-11-30 00:08:37 +00:00
* verify - ( boolean ) enable client certificate verification ( default : false )
* verify - any - ( boolean ) allow all CAs and self - signed certificates ( for manual checking , default : false )
* verify - depth - ( number ) sets client verification depth ( default : 1 )
* verify - require - ( boolean ) abort clients failing verification ( default : false )
* client - ca - file - ( string ) path to file containing client CA certificates
2009-09-08 17:10:09 +00:00
*
2011-11-30 01:06:26 +00:00
* Actions :
* openssl . setenv [ options ] - set SSL environment strings
* options : ( list ) , may contain strings :
* " client " - set SSL_CLIENT_S_DN_ short - named entries
* " client-cert " - set SSL_CLIENT_CERT to client certificate PEM
* " server " - set SSL_SERVER_S_DN_ short - named entries
* " server-cert " - set SSL_SERVER_CERT to server certificate PEM
*
2009-09-08 17:10:09 +00:00
* Example config :
* setup openssl [ " listen " : " 0.0.0.0:8443 " , " pemfile " : " server.pem " ] ;
* setup openssl [ " listen " : " [::]:8443 " , " pemfile " : " server.pem " ] ;
2011-11-30 01:06:26 +00:00
* openssl . setenv " client " ;
2009-09-08 17:10:09 +00:00
*
* Author :
2013-05-20 08:17:47 +00:00
* Copyright ( c ) 2009 - 2013 Stefan B ü hler , Joe Presbrey
2009-09-08 17:10:09 +00:00
*/
# include <lighttpd/base.h>
2013-05-21 15:16:44 +00:00
# include <lighttpd/throttle.h>
2009-09-08 17:10:09 +00:00
# include <lighttpd/plugin_core.h>
2013-05-20 08:17:47 +00:00
# include "openssl_filter.h"
2009-09-08 17:10:09 +00:00
# include <openssl/ssl.h>
# include <openssl/err.h>
# include <openssl/rand.h>
2013-05-20 08:17:47 +00:00
2009-09-08 17:10:09 +00:00
LI_API gboolean mod_openssl_init ( liModules * mods , liModule * mod ) ;
LI_API gboolean mod_openssl_free ( liModules * mods , liModule * mod ) ;
typedef struct openssl_connection_ctx openssl_connection_ctx ;
typedef struct openssl_context openssl_context ;
struct openssl_connection_ctx {
2013-05-20 08:17:47 +00:00
liConnection * con ;
liOpenSSLFilter * ssl_filter ;
2011-12-02 12:38:22 +00:00
2013-05-20 08:17:47 +00:00
liIOStream * sock_stream ;
gpointer simple_socket_data ;
2009-09-08 17:10:09 +00:00
} ;
struct openssl_context {
SSL_CTX * ssl_ctx ;
} ;
2011-11-30 01:06:26 +00:00
enum {
SE_CLIENT = 0x1 ,
SE_CLIENT_CERT = 0x2 ,
SE_SERVER = 0x4 ,
SE_SERVER_CERT = 0x8
} ;
2013-05-22 15:26:33 +00:00
2013-05-20 08:17:47 +00:00
static void tcp_io_cb ( liIOStream * stream , liIOStreamEvent event ) {
openssl_connection_ctx * conctx = stream - > data ;
assert ( NULL = = conctx - > sock_stream | | conctx - > sock_stream = = stream ) ;
2013-05-22 15:26:33 +00:00
2013-05-20 08:17:47 +00:00
if ( LI_IOSTREAM_DESTROY = = event ) {
li_stream_simple_socket_close ( stream , TRUE ) ; /* kill it, ssl sent an close alert message */
2009-09-08 17:10:09 +00:00
}
2013-05-20 08:17:47 +00:00
li_connection_simple_tcp ( & conctx - > con , stream , & conctx - > simple_socket_data , event ) ;
2009-09-08 17:10:09 +00:00
2013-05-20 08:17:47 +00:00
if ( NULL ! = conctx - > con & & conctx - > con - > out_has_all_data
& & ( NULL = = stream - > stream_out . out | | 0 = = stream - > stream_out . out - > length )
& & li_streams_empty ( conctx - > con - > con_sock . raw_out , NULL ) ) {
li_connection_request_done ( conctx - > con ) ;
2009-09-08 17:10:09 +00:00
}
2009-10-02 08:44:08 +00:00
2013-05-20 08:17:47 +00:00
switch ( event ) {
case LI_IOSTREAM_DESTROY :
assert ( NULL = = conctx - > sock_stream ) ;
assert ( NULL = = conctx - > ssl_filter ) ;
assert ( NULL = = conctx - > con ) ;
stream - > data = NULL ;
g_slice_free ( openssl_connection_ctx , conctx ) ;
return ;
2011-12-02 12:38:22 +00:00
default :
2013-05-20 08:17:47 +00:00
break ;
2011-12-02 12:38:22 +00:00
}
}
2013-05-20 08:17:47 +00:00
static void handshake_cb ( liOpenSSLFilter * f , gpointer data , liStream * plain_source , liStream * plain_drain ) {
openssl_connection_ctx * conctx = data ;
liConnection * con = conctx - > con ;
UNUSED ( f ) ;
2009-09-08 17:10:09 +00:00
2013-05-20 08:17:47 +00:00
if ( NULL ! = con ) {
li_stream_connect ( plain_source , con - > con_sock . raw_in ) ;
li_stream_connect ( con - > con_sock . raw_out , plain_drain ) ;
} else {
li_stream_reset ( plain_source ) ;
li_stream_reset ( plain_drain ) ;
2011-12-02 12:38:22 +00:00
}
2009-09-08 17:10:09 +00:00
}
2013-05-20 08:17:47 +00:00
static void close_cb ( liOpenSSLFilter * f , gpointer data ) {
openssl_connection_ctx * conctx = data ;
liConnection * con = conctx - > con ;
assert ( conctx - > ssl_filter = = f ) ;
conctx - > ssl_filter = NULL ;
li_openssl_filter_free ( f ) ;
if ( NULL ! = conctx - > con ) {
liStream * raw_out = con - > con_sock . raw_out , * raw_in = con - > con_sock . raw_in ;
assert ( con - > con_sock . data = = conctx ) ;
conctx - > con = NULL ;
con - > con_sock . data = NULL ;
li_stream_acquire ( raw_in ) ;
li_stream_reset ( raw_out ) ;
li_stream_reset ( raw_in ) ;
li_stream_release ( raw_in ) ;
2013-05-22 15:26:33 +00:00
}
2013-05-20 08:17:47 +00:00
if ( NULL ! = conctx - > sock_stream ) {
liIOStream * stream = conctx - > sock_stream ;
conctx - > sock_stream = NULL ;
li_iostream_release ( stream ) ;
2013-05-22 15:26:33 +00:00
}
}
2013-05-20 08:17:47 +00:00
static const liOpenSSLFilterCallbacks filter_callbacks = {
handshake_cb ,
close_cb
} ;
2013-05-22 15:26:33 +00:00
2013-05-20 08:17:47 +00:00
static void openssl_tcp_finished ( liConnection * con , gboolean aborted ) {
openssl_connection_ctx * conctx = con - > con_sock . data ;
UNUSED ( aborted ) ;
2013-05-22 15:26:33 +00:00
2013-05-20 08:17:47 +00:00
con - > info . is_ssl = FALSE ;
con - > con_sock . callbacks = NULL ;
2013-05-22 15:26:33 +00:00
2013-05-20 08:17:47 +00:00
if ( NULL ! = conctx ) {
assert ( con = = conctx - > con ) ;
close_cb ( conctx - > ssl_filter , conctx ) ;
2013-05-22 15:26:33 +00:00
}
2013-05-20 08:17:47 +00:00
{
liStream * raw_out = con - > con_sock . raw_out , * raw_in = con - > con_sock . raw_in ;
con - > con_sock . raw_out = con - > con_sock . raw_in = NULL ;
if ( NULL ! = raw_out ) { li_stream_reset ( raw_out ) ; li_stream_release ( raw_out ) ; }
if ( NULL ! = raw_in ) { li_stream_reset ( raw_in ) ; li_stream_release ( raw_in ) ; }
2013-05-22 15:26:33 +00:00
}
}
2013-05-21 15:16:44 +00:00
static liThrottleState * openssl_tcp_throttle_out ( liConnection * con ) {
openssl_connection_ctx * conctx = con - > con_sock . data ;
if ( NULL = = conctx ) return NULL ;
if ( NULL = = conctx - > sock_stream - > throttle_out ) conctx - > sock_stream - > throttle_out = li_throttle_new ( con - > wrk ) ;
return conctx - > sock_stream - > throttle_out ;
}
static liThrottleState * openssl_tcp_throttle_in ( liConnection * con ) {
openssl_connection_ctx * conctx = con - > con_sock . data ;
if ( NULL = = conctx ) return NULL ;
if ( NULL = = conctx - > sock_stream - > throttle_in ) conctx - > sock_stream - > throttle_in = li_throttle_new ( con - > wrk ) ;
return conctx - > sock_stream - > throttle_in ;
}
2013-05-22 15:26:33 +00:00
static const liConnectionSocketCallbacks openssl_tcp_cbs = {
2013-05-21 15:16:44 +00:00
openssl_tcp_finished ,
openssl_tcp_throttle_out ,
openssl_tcp_throttle_in
2013-05-22 15:26:33 +00:00
} ;
static gboolean openssl_con_new ( liConnection * con , int fd ) {
2013-05-20 08:17:47 +00:00
liEventLoop * loop = & con - > wrk - > loop ;
2013-05-22 15:26:33 +00:00
liServer * srv = con - > srv ;
openssl_context * ctx = con - > srv_sock - > data ;
openssl_connection_ctx * conctx = g_slice_new0 ( openssl_connection_ctx ) ;
2013-05-20 08:17:47 +00:00
conctx - > sock_stream = li_iostream_new ( con - > wrk , fd , tcp_io_cb , conctx ) ;
2013-05-22 15:26:33 +00:00
2013-05-20 08:17:47 +00:00
conctx - > ssl_filter = li_openssl_filter_new ( srv , con - > wrk , & filter_callbacks , conctx , ctx - > ssl_ctx ,
& conctx - > sock_stream - > stream_in , & conctx - > sock_stream - > stream_out ) ;
2013-05-22 15:26:33 +00:00
2013-05-20 08:17:47 +00:00
if ( NULL = = conctx - > ssl_filter ) {
2013-05-22 15:26:33 +00:00
ERROR ( srv , " SSL_new: %s " , ERR_error_string ( ERR_get_error ( ) , NULL ) ) ;
2013-05-20 08:17:47 +00:00
li_iostream_reset ( conctx - > sock_stream ) ;
g_slice_free ( openssl_connection_ctx , conctx ) ;
return FALSE ;
2013-05-22 15:26:33 +00:00
}
2013-05-20 08:17:47 +00:00
conctx - > con = con ;
2013-05-22 15:26:33 +00:00
con - > con_sock . data = conctx ;
2013-05-20 08:17:47 +00:00
con - > con_sock . callbacks = & openssl_tcp_cbs ;
con - > con_sock . raw_out = li_stream_plug_new ( loop ) ;
con - > con_sock . raw_in = li_stream_plug_new ( loop ) ;
2013-05-22 15:26:33 +00:00
con - > info . is_ssl = TRUE ;
return TRUE ;
}
2009-09-08 17:10:09 +00:00
static void openssl_sock_release ( liServerSocket * srv_sock ) {
openssl_context * ctx = srv_sock - > data ;
if ( ! ctx ) return ;
SSL_CTX_free ( ctx - > ssl_ctx ) ;
g_slice_free ( openssl_context , ctx ) ;
}
2011-11-30 01:06:26 +00:00
static void openssl_setenv_X509_add_entries ( liVRequest * vr , X509 * x509 , const gchar * prefix , guint prefix_len ) {
guint i , j ;
GString * k = vr - > wrk - > tmp_str ;
X509_NAME * xn = X509_get_subject_name ( x509 ) ;
X509_NAME_ENTRY * xe ;
const char * xobjsn ;
g_string_truncate ( k , 0 ) ;
g_string_append_len ( k , prefix , prefix_len ) ;
for ( i = 0 , j = X509_NAME_entry_count ( xn ) ; i < j ; + + i ) {
if ( ! ( xe = X509_NAME_get_entry ( xn , i ) )
| | ! ( xobjsn = OBJ_nid2sn ( OBJ_obj2nid ( ( ASN1_OBJECT * ) X509_NAME_ENTRY_get_object ( xe ) ) ) ) )
continue ;
g_string_truncate ( k , prefix_len ) ;
g_string_append ( k , xobjsn ) ;
li_environment_set ( & vr - > env , GSTR_LEN ( k ) , ( const gchar * ) xe - > value - > data , xe - > value - > length ) ;
}
}
static void openssl_setenv_X509_add_PEM ( liVRequest * vr , X509 * x509 , const gchar * key , guint key_len ) {
gint n ;
GString * v = vr - > wrk - > tmp_str ;
BIO * bio ;
if ( NULL ! = ( bio = BIO_new ( BIO_s_mem ( ) ) ) ) {
PEM_write_bio_X509 ( bio , x509 ) ;
n = BIO_pending ( bio ) ;
g_string_set_size ( v , n ) ;
BIO_read ( bio , v - > str , n ) ;
BIO_free ( bio ) ;
li_environment_set ( & vr - > env , key , key_len , GSTR_LEN ( v ) ) ;
}
}
static liHandlerResult openssl_setenv ( liVRequest * vr , gpointer param , gpointer * context ) {
liConnection * con ;
openssl_connection_ctx * conctx ;
SSL * ssl ;
X509 * x0 = NULL , * x1 = NULL ;
guint params = GPOINTER_TO_UINT ( param ) ;
UNUSED ( context ) ;
if ( ! ( con = li_connection_from_vrequest ( vr ) )
| | ! ( con - > srv_sock & & con - > srv_sock - > new_cb = = openssl_con_new )
2013-05-22 15:26:33 +00:00
| | ! ( conctx = con - > con_sock . data )
2013-05-20 08:17:47 +00:00
| | ! ( ssl = li_openssl_filter_ssl ( conctx - > ssl_filter ) ) )
2011-11-30 01:06:26 +00:00
return LI_HANDLER_GO_ON ;
if ( ( params & SE_CLIENT ) & & ( x1 | | ( x1 = SSL_get_peer_certificate ( ssl ) ) ) )
openssl_setenv_X509_add_entries ( vr , x1 , CONST_STR_LEN ( " SSL_CLIENT_S_DN_ " ) ) ;
if ( ( params & SE_CLIENT_CERT ) & & ( x1 | | ( x1 = SSL_get_peer_certificate ( ssl ) ) ) )
openssl_setenv_X509_add_PEM ( vr , x1 , CONST_STR_LEN ( " SSL_CLIENT_CERT " ) ) ;
if ( ( params & SE_SERVER ) & & ( x0 | | ( x0 = SSL_get_certificate ( ssl ) ) ) )
openssl_setenv_X509_add_entries ( vr , x0 , CONST_STR_LEN ( " SSL_SERVER_S_DN_ " ) ) ;
if ( ( params & SE_SERVER_CERT ) & & ( x0 | | ( x0 = SSL_get_certificate ( ssl ) ) ) )
openssl_setenv_X509_add_PEM ( vr , x0 , CONST_STR_LEN ( " SSL_SERVER_CERT " ) ) ;
/* only peer increases ref count */
if ( x1 ) X509_free ( x1 ) ;
return LI_HANDLER_GO_ON ;
}
2013-05-20 08:17:47 +00:00
static const char openssl_setenv_config_error [ ] = " openssl.setenv expects a string or a list of strings consisting of: client, client-cert, server, server-cert " ;
2011-11-30 01:06:26 +00:00
static liAction * openssl_setenv_create ( liServer * srv , liWorker * wrk , liPlugin * p , liValue * val , gpointer userdata ) {
guint i ;
liValue * v ;
guint params = 0 ;
UNUSED ( srv ) ; UNUSED ( wrk ) ; UNUSED ( p ) ; UNUSED ( userdata ) ;
if ( val & & val - > type = = LI_VALUE_STRING )
li_value_wrap_in_list ( val ) ;
if ( ! val | | val - > type ! = LI_VALUE_LIST ) {
ERROR ( srv , " %s " , openssl_setenv_config_error ) ;
return NULL ;
}
for ( i = 0 ; i < val - > data . list - > len ; i + + ) {
v = g_array_index ( val - > data . list , liValue * , i ) ;
if ( v - > type ! = LI_VALUE_STRING ) {
ERROR ( srv , " %s " , openssl_setenv_config_error ) ;
return NULL ;
}
if ( li_strncase_equal ( v - > data . string , CONST_STR_LEN ( " client " ) ) ) {
params | = SE_CLIENT ;
} else if ( li_strncase_equal ( v - > data . string , CONST_STR_LEN ( " client-cert " ) ) ) {
params | = SE_CLIENT_CERT ;
} else if ( li_strncase_equal ( v - > data . string , CONST_STR_LEN ( " server " ) ) ) {
params | = SE_SERVER ;
} else if ( li_strncase_equal ( v - > data . string , CONST_STR_LEN ( " server-cert " ) ) ) {
params | = SE_SERVER_CERT ;
} else {
ERROR ( srv , " %s " , openssl_setenv_config_error ) ;
return NULL ;
}
}
return li_action_new_function ( openssl_setenv , NULL , NULL , GUINT_TO_POINTER ( params ) ) ;
}
2009-09-08 17:10:09 +00:00
static void openssl_setup_listen_cb ( liServer * srv , int fd , gpointer data ) {
openssl_context * ctx = data ;
liServerSocket * srv_sock ;
UNUSED ( data ) ;
if ( - 1 = = fd ) {
SSL_CTX_free ( ctx - > ssl_ctx ) ;
g_slice_free ( openssl_context , ctx ) ;
return ;
}
srv_sock = li_server_listen ( srv , fd ) ;
srv_sock - > data = ctx ;
srv_sock - > new_cb = openssl_con_new ;
srv_sock - > release_cb = openssl_sock_release ;
}
2011-12-01 17:20:50 +00:00
static gboolean openssl_options_set_string ( long * options , GString * s ) {
# define S(s) CONST_STR_LEN(s)
static const struct {
char * name ; /* without "NO_" prefix */
guint name_len ;
long value ;
char positive ; /* 0 means option is usually prefixed with "NO_"; otherwise use 1 */
} option_table [ ] = {
{ S ( " MICROSOFT_SESS_ID_BUG " ) , SSL_OP_MICROSOFT_SESS_ID_BUG , 1 } ,
{ S ( " NETSCAPE_CHALLENGE_BUG " ) , SSL_OP_NETSCAPE_CHALLENGE_BUG , 1 } ,
# ifdef SSL_OP_LEGACY_SERVER_CONNECT
{ S ( " LEGACY_SERVER_CONNECT " ) , SSL_OP_LEGACY_SERVER_CONNECT , 1 } ,
# endif
{ S ( " NETSCAPE_REUSE_CIPHER_CHANGE_BUG " ) , SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG , 1 } ,
{ S ( " SSLREF2_REUSE_CERT_TYPE_BUG " ) , SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG , 1 } ,
{ S ( " MICROSOFT_BIG_SSLV3_BUFFER " ) , SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER , 1 } ,
{ S ( " MSIE_SSLV2_RSA_PADDING " ) , SSL_OP_MSIE_SSLV2_RSA_PADDING , 1 } ,
{ S ( " SSLEAY_080_CLIENT_DH_BUG " ) , SSL_OP_SSLEAY_080_CLIENT_DH_BUG , 1 } ,
{ S ( " TLS_D5_BUG " ) , SSL_OP_TLS_D5_BUG , 1 } ,
{ S ( " TLS_BLOCK_PADDING_BUG " ) , SSL_OP_TLS_BLOCK_PADDING_BUG , 1 } ,
# ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
{ S ( " DONT_INSERT_EMPTY_FRAGMENTS " ) , SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS , 1 } ,
# endif
{ S ( " ALL " ) , SSL_OP_ALL , 1 } ,
# ifdef SSL_OP_NO_QUERY_MTU
{ S ( " QUERY_MTU " ) , SSL_OP_NO_QUERY_MTU , 0 } ,
# endif
# ifdef SSL_OP_COOKIE_EXCHANGE
{ S ( " COOKIE_EXCHANGE " ) , SSL_OP_COOKIE_EXCHANGE , 1 } ,
# endif
# ifdef SSL_OP_NO_TICKET
{ S ( " TICKET " ) , SSL_OP_NO_TICKET , 0 } ,
# endif
# ifdef SSL_OP_CISCO_ANYCONNECT
{ S ( " CISCO_ANYCONNECT " ) , SSL_OP_CISCO_ANYCONNECT , 1 } ,
# endif
# ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
{ S ( " SESSION_RESUMPTION_ON_RENEGOTIATION " ) , SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION , 0 } ,
# endif
# ifdef SSL_OP_NO_COMPRESSION
{ S ( " COMPRESSION " ) , SSL_OP_NO_COMPRESSION , 0 } ,
# endif
# ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
{ S ( " ALLOW_UNSAFE_LEGACY_RENEGOTIATION " ) , SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION , 1 } ,
# endif
# ifdef SSL_OP_SINGLE_ECDH_USE
{ S ( " SINGLE_ECDH_USE " ) , SSL_OP_SINGLE_ECDH_USE , 1 } ,
# endif
{ S ( " SINGLE_DH_USE " ) , SSL_OP_SINGLE_DH_USE , 1 } ,
{ S ( " EPHEMERAL_RSA " ) , SSL_OP_EPHEMERAL_RSA , 1 } ,
# ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
{ S ( " CIPHER_SERVER_PREFERENCE " ) , SSL_OP_CIPHER_SERVER_PREFERENCE , 1 } ,
# endif
{ S ( " TLS_ROLLBACK_BUG " ) , SSL_OP_TLS_ROLLBACK_BUG , 1 } ,
{ S ( " SSLv2 " ) , SSL_OP_NO_SSLv2 , 0 } ,
{ S ( " SSLv3 " ) , SSL_OP_NO_SSLv3 , 0 } ,
{ S ( " TLSv1 " ) , SSL_OP_NO_TLSv1 , 0 } ,
{ S ( " PKCS1_CHECK_1 " ) , SSL_OP_PKCS1_CHECK_1 , 1 } ,
{ S ( " PKCS1_CHECK_2 " ) , SSL_OP_PKCS1_CHECK_2 , 1 } ,
{ S ( " NETSCAPE_CA_DN_BUG " ) , SSL_OP_NETSCAPE_CA_DN_BUG , 1 } ,
{ S ( " NETSCAPE_DEMO_CIPHER_CHANGE_BUG " ) , SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG , 1 } ,
# ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG
{ S ( " CRYPTOPRO_TLSEXT_BUG " ) , SSL_OP_CRYPTOPRO_TLSEXT_BUG , 1 }
# endif
} ;
# undef S
GString key = * s ;
guint i ;
char positive = 1 ;
if ( 0 = = g_ascii_strncasecmp ( key . str , CONST_STR_LEN ( " NO_ " ) ) ) {
key . str + = 3 ;
key . len - = 3 ;
positive = 0 ;
}
for ( i = 0 ; i < G_N_ELEMENTS ( option_table ) ; i + + ) {
if ( option_table [ i ] . name_len = = key . len & & 0 = = g_ascii_strcasecmp ( key . str , option_table [ i ] . name ) ) {
if ( option_table [ i ] . positive = = positive ) {
* options | = option_table [ i ] . value ;
} else {
* options & = ~ option_table [ i ] . value ;
}
return TRUE ;
}
}
return FALSE ;
}
2011-11-30 00:08:37 +00:00
static int openssl_verify_any_cb ( int ok , X509_STORE_CTX * ctx ) { UNUSED ( ok ) ; UNUSED ( ctx ) ; return 1 ; }
2009-12-21 11:29:14 +00:00
static gboolean openssl_setup ( liServer * srv , liPlugin * p , liValue * val , gpointer userdata ) {
2009-09-08 17:10:09 +00:00
openssl_context * ctx ;
GHashTableIter hti ;
gpointer hkey , hvalue ;
GString * htkey ;
liValue * htval ;
2011-11-30 00:08:37 +00:00
STACK_OF ( X509_NAME ) * client_ca_list ;
2011-12-01 17:20:50 +00:00
liValue * v ;
2009-09-08 17:10:09 +00:00
2011-12-01 17:20:50 +00:00
/* setup defaults */
2009-09-08 17:10:09 +00:00
GString * ipstr = NULL ;
2011-12-01 17:20:50 +00:00
const char
* ciphers = " ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM " ,
* pemfile = NULL , * ca_file = NULL , * client_ca_file = NULL ;
long
2011-12-02 12:43:54 +00:00
options = SSL_OP_NO_SSLv2 | SSL_OP_CIPHER_SERVER_PREFERENCE
# ifdef SSL_OP_NO_COMPRESSION
| SSL_OP_NO_COMPRESSION
# endif
;
2011-12-01 17:20:50 +00:00
guint
verify_mode = 0 , verify_depth = 1 ;
gboolean
verify_any = FALSE ;
2009-09-08 17:10:09 +00:00
2009-12-21 11:29:14 +00:00
UNUSED ( p ) ; UNUSED ( userdata ) ;
2009-09-08 17:10:09 +00:00
if ( val - > type ! = LI_VALUE_HASH ) {
ERROR ( srv , " %s " , " openssl expects a hash as parameter " ) ;
return FALSE ;
}
g_hash_table_iter_init ( & hti , val - > data . hash ) ;
while ( g_hash_table_iter_next ( & hti , & hkey , & hvalue ) ) {
htkey = hkey ; htval = hvalue ;
if ( g_str_equal ( htkey - > str , " listen " ) ) {
if ( htval - > type ! = LI_VALUE_STRING ) {
2011-12-16 19:57:26 +00:00
ERROR ( srv , " %s " , " openssl listen expects a string as parameter " ) ;
2009-09-08 17:10:09 +00:00
return FALSE ;
}
ipstr = htval - > data . string ;
} else if ( g_str_equal ( htkey - > str , " pemfile " ) ) {
if ( htval - > type ! = LI_VALUE_STRING ) {
ERROR ( srv , " %s " , " openssl pemfile expects a string as parameter " ) ;
return FALSE ;
}
pemfile = htval - > data . string - > str ;
} else if ( g_str_equal ( htkey - > str , " ca-file " ) ) {
if ( htval - > type ! = LI_VALUE_STRING ) {
ERROR ( srv , " %s " , " openssl ca-file expects a string as parameter " ) ;
return FALSE ;
}
ca_file = htval - > data . string - > str ;
} else if ( g_str_equal ( htkey - > str , " ciphers " ) ) {
if ( htval - > type ! = LI_VALUE_STRING ) {
ERROR ( srv , " %s " , " openssl ciphers expects a string as parameter " ) ;
return FALSE ;
}
ciphers = htval - > data . string - > str ;
2011-12-01 17:20:50 +00:00
} else if ( g_str_equal ( htkey - > str , " options " ) ) {
guint i ;
if ( htval - > type ! = LI_VALUE_LIST ) {
ERROR ( srv , " %s " , " openssl options expects a list of strings as parameter " ) ;
2009-09-08 17:10:09 +00:00
return FALSE ;
}
2011-12-01 17:20:50 +00:00
for ( i = 0 ; i < htval - > data . list - > len ; i + + ) {
v = g_array_index ( htval - > data . list , liValue * , i ) ;
if ( v - > type ! = LI_VALUE_STRING ) {
ERROR ( srv , " %s " , " openssl options expects a list of strings as parameter " ) ;
return FALSE ;
}
if ( ! openssl_options_set_string ( & options , v - > data . string ) ) {
ERROR ( srv , " openssl option unknown: %s " , v - > data . string - > str ) ;
return FALSE ;
}
}
2011-11-30 00:08:37 +00:00
} else if ( g_str_equal ( htkey - > str , " verify " ) ) {
if ( htval - > type ! = LI_VALUE_BOOLEAN ) {
ERROR ( srv , " %s " , " openssl verify expects a boolean as parameter " ) ;
return FALSE ;
}
if ( htval - > data . boolean )
verify_mode | = SSL_VERIFY_PEER ;
} else if ( g_str_equal ( htkey - > str , " verify-any " ) ) {
if ( htval - > type ! = LI_VALUE_BOOLEAN ) {
ERROR ( srv , " %s " , " openssl verify-any expects a boolean as parameter " ) ;
return FALSE ;
}
verify_any = htval - > data . boolean ;
} else if ( g_str_equal ( htkey - > str , " verify-depth " ) ) {
if ( htval - > type ! = LI_VALUE_NUMBER ) {
ERROR ( srv , " %s " , " openssl verify-depth expects a number as parameter " ) ;
return FALSE ;
}
verify_depth = htval - > data . number ;
} else if ( g_str_equal ( htkey - > str , " verify-require " ) ) {
if ( htval - > type ! = LI_VALUE_BOOLEAN ) {
ERROR ( srv , " %s " , " openssl verify-require expects a boolean as parameter " ) ;
return FALSE ;
}
if ( htval - > data . boolean )
verify_mode | = SSL_VERIFY_FAIL_IF_NO_PEER_CERT ;
} else if ( g_str_equal ( htkey - > str , " client-ca-file " ) ) {
if ( htval - > type ! = LI_VALUE_STRING ) {
ERROR ( srv , " %s " , " openssl client-ca-file expects a string as parameter " ) ;
return FALSE ;
}
client_ca_file = htval - > data . string - > str ;
2009-09-08 17:10:09 +00:00
}
}
if ( ! ipstr ) {
ERROR ( srv , " %s " , " openssl needs a listen parameter " ) ;
return FALSE ;
}
if ( ! pemfile ) {
ERROR ( srv , " %s " , " openssl needs a pemfile " ) ;
return FALSE ;
}
ctx = g_slice_new0 ( openssl_context ) ;
if ( NULL = = ( ctx - > ssl_ctx = SSL_CTX_new ( SSLv23_server_method ( ) ) ) ) {
ERROR ( srv , " SSL_CTX_new: %s " , ERR_error_string ( ERR_get_error ( ) , NULL ) ) ;
goto error_free_socket ;
}
2011-12-01 17:20:50 +00:00
if ( ! SSL_CTX_set_options ( ctx - > ssl_ctx , options ) ) {
ERROR ( srv , " SSL_CTX_set_options(%lx): %s " , options , ERR_error_string ( ERR_get_error ( ) , NULL ) ) ;
goto error_free_socket ;
2009-09-08 17:10:09 +00:00
}
if ( ciphers ) {
/* Disable support for low encryption ciphers */
if ( SSL_CTX_set_cipher_list ( ctx - > ssl_ctx , ciphers ) ! = 1 ) {
ERROR ( srv , " SSL_CTX_set_cipher_list('%s'): %s " , ciphers , ERR_error_string ( ERR_get_error ( ) , NULL ) ) ;
goto error_free_socket ;
}
}
if ( ca_file ) {
if ( 1 ! = SSL_CTX_load_verify_locations ( ctx - > ssl_ctx , ca_file , NULL ) ) {
ERROR ( srv , " SSL_CTX_load_verify_locations('%s'): %s " , ca_file , ERR_error_string ( ERR_get_error ( ) , NULL ) ) ;
goto error_free_socket ;
}
}
if ( SSL_CTX_use_certificate_file ( ctx - > ssl_ctx , pemfile , SSL_FILETYPE_PEM ) < 0 ) {
ERROR ( srv , " SSL_CTX_use_certificate_file('%s'): %s " , pemfile ,
ERR_error_string ( ERR_get_error ( ) , NULL ) ) ;
goto error_free_socket ;
}
if ( SSL_CTX_use_PrivateKey_file ( ctx - > ssl_ctx , pemfile , SSL_FILETYPE_PEM ) < 0 ) {
ERROR ( srv , " SSL_CTX_use_PrivateKey_file('%s'): %s " , pemfile ,
ERR_error_string ( ERR_get_error ( ) , NULL ) ) ;
goto error_free_socket ;
}
if ( SSL_CTX_check_private_key ( ctx - > ssl_ctx ) ! = 1 ) {
ERROR ( srv , " SSL: Private key '%s' does not match the certificate public key, reason: %s " , pemfile ,
ERR_error_string ( ERR_get_error ( ) , NULL ) ) ;
goto error_free_socket ;
}
2011-11-30 00:08:37 +00:00
if ( verify_mode ) {
if ( SSL_CTX_set_session_id_context ( ctx - > ssl_ctx , ( void * ) & srv , sizeof ( srv ) ) ! = 1 ) {
ERROR ( srv , " SSL_CTX_set_session_id_context(): %s " , ERR_error_string ( ERR_get_error ( ) , NULL ) ) ;
goto error_free_socket ;
}
SSL_CTX_set_verify ( ctx - > ssl_ctx , verify_mode , verify_any ? openssl_verify_any_cb : NULL ) ;
SSL_CTX_set_verify_depth ( ctx - > ssl_ctx , verify_depth ) ;
}
if ( client_ca_file ) {
if ( SSL_CTX_load_verify_locations ( ctx - > ssl_ctx , client_ca_file , NULL ) ! = 1 ) {
ERROR ( srv , " SSL_CTX_load_verify_locations('%s'): %s " , client_ca_file , ERR_error_string ( ERR_get_error ( ) , NULL ) ) ;
goto error_free_socket ;
}
if ( ( client_ca_list = SSL_load_client_CA_file ( client_ca_file ) ) = = NULL ) {
ERROR ( srv , " SSL_load_client_CA_file('%s'): %s " , client_ca_file , ERR_error_string ( ERR_get_error ( ) , NULL ) ) ;
goto error_free_socket ;
}
SSL_CTX_set_client_CA_list ( ctx - > ssl_ctx , client_ca_list ) ;
}
2009-09-08 17:10:09 +00:00
SSL_CTX_set_default_read_ahead ( ctx - > ssl_ctx , 1 ) ;
SSL_CTX_set_mode ( ctx - > ssl_ctx , SSL_CTX_get_mode ( ctx - > ssl_ctx ) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER ) ;
li_angel_listen ( srv , ipstr , openssl_setup_listen_cb , ctx ) ;
return TRUE ;
error_free_socket :
if ( ctx ) {
if ( ctx - > ssl_ctx ) SSL_CTX_free ( ctx - > ssl_ctx ) ;
g_slice_free ( openssl_context , ctx ) ;
}
return FALSE ;
}
static const liPluginOption options [ ] = {
2010-01-24 20:30:41 +00:00
{ NULL , 0 , 0 , NULL }
2009-09-08 17:10:09 +00:00
} ;
static const liPluginAction actions [ ] = {
2011-11-30 01:06:26 +00:00
{ " openssl.setenv " , openssl_setenv_create , NULL } ,
2009-12-21 11:29:14 +00:00
{ NULL , NULL , NULL }
2009-09-08 17:10:09 +00:00
} ;
static const liPluginSetup setups [ ] = {
2009-12-21 11:29:14 +00:00
{ " openssl " , openssl_setup , NULL } ,
2009-09-08 17:10:09 +00:00
2009-12-21 11:29:14 +00:00
{ NULL , NULL , NULL }
2009-09-08 17:10:09 +00:00
} ;
2009-12-21 11:29:14 +00:00
static void plugin_init ( liServer * srv , liPlugin * p , gpointer userdata ) {
UNUSED ( srv ) ; UNUSED ( userdata ) ;
2009-09-08 17:10:09 +00:00
p - > options = options ;
p - > actions = actions ;
p - > setups = setups ;
}
2009-10-02 12:04:28 +00:00
static GMutex * * ssl_locks ;
static void ssl_lock_cb ( int mode , int n , const char * file , int line ) {
UNUSED ( file ) ;
UNUSED ( line ) ;
if ( mode & CRYPTO_LOCK ) {
g_mutex_lock ( ssl_locks [ n ] ) ;
} else if ( mode & CRYPTO_UNLOCK ) {
g_mutex_unlock ( ssl_locks [ n ] ) ;
}
}
static unsigned long ssl_id_cb ( void ) {
return ( intptr_t ) g_thread_self ( ) ;
}
static void sslthread_init ( ) {
int n = CRYPTO_num_locks ( ) , i ;
ssl_locks = g_slice_alloc0 ( sizeof ( GMutex * ) * n ) ;
for ( i = 0 ; i < n ; i + + ) {
ssl_locks [ i ] = g_mutex_new ( ) ;
}
CRYPTO_set_locking_callback ( ssl_lock_cb ) ;
CRYPTO_set_id_callback ( ssl_id_cb ) ;
}
static void sslthread_free ( ) {
int n = CRYPTO_num_locks ( ) , i ;
for ( i = 0 ; i < n ; i + + ) {
g_mutex_free ( ssl_locks [ i ] ) ;
}
g_slice_free1 ( sizeof ( GMutex * ) * n , ssl_locks ) ;
}
2009-09-08 17:10:09 +00:00
gboolean mod_openssl_init ( liModules * mods , liModule * mod ) {
MODULE_VERSION_CHECK ( mods ) ;
2009-10-02 12:04:28 +00:00
sslthread_init ( ) ;
2009-09-08 17:10:09 +00:00
SSL_load_error_strings ( ) ;
SSL_library_init ( ) ;
if ( 0 = = RAND_status ( ) ) {
ERROR ( mods - > main , " SSL: %s " , " not enough entropy in the pool " ) ;
return FALSE ;
}
2009-12-21 11:29:14 +00:00
mod - > config = li_plugin_register ( mods - > main , " mod_openssl " , plugin_init , NULL ) ;
2009-09-08 17:10:09 +00:00
return mod - > config ! = NULL ;
}
gboolean mod_openssl_free ( liModules * mods , liModule * mod ) {
if ( mod - > config )
li_plugin_free ( mods - > main , mod - > config ) ;
2009-10-02 10:09:37 +00:00
ERR_free_strings ( ) ;
2009-10-02 12:04:28 +00:00
sslthread_free ( ) ;
2009-09-08 17:10:09 +00:00
return TRUE ;
}