2008-01-28 21:37:16 +00:00
/* source: xio-openssl.c */
2013-06-21 14:19:02 +00:00
/* Copyright Gerhard Rieger */
2008-01-27 12:00:08 +00:00
/* Published under the GNU General Public License V.2, see file COPYING */
/* this file contains the implementation of the openssl addresses */
# include "xiosysincludes.h"
# if WITH_OPENSSL /* make this address configure dependend */
# include "xioopen.h"
# include "xio-fd.h"
# include "xio-socket.h" /* _xioopen_connect() */
# include "xio-listen.h"
# include "xio-ipapp.h"
# include "xio-openssl.h"
/* the openssl library requires a file descriptor for external communications.
so our best effort is to provide any possible kind of un * x file descriptor
( not only tcp , but also pipes , stdin , files . . . )
for tcp we want to provide support for socks and proxy .
read and write functions must use the openssl crypt versions .
but currently only plain tcp4 is implemented .
*/
/* Linux: "man 3 ssl" */
/* generate a simple openssl server for testing:
1 ) generate a private key
openssl genrsa - out server . key 1024
2 ) generate a self signed cert
openssl req - new - key server . key - x509 - days 3653 - out server . crt
enter fields . . .
3 ) generate the pem file
cat server . key server . crt > server . pem
openssl s_server ( listens on 4433 / tcp )
*/
/* static declaration of ssl's open function */
static int xioopen_openssl_connect ( int argc , const char * argv [ ] , struct opt * opts ,
int xioflags , xiofile_t * fd , unsigned groups ,
int dummy1 , int dummy2 , int dummy3 ) ;
/* static declaration of ssl's open function */
static int xioopen_openssl_listen ( int argc , const char * argv [ ] , struct opt * opts ,
int xioflags , xiofile_t * fd , unsigned groups ,
int dummy1 , int dummy2 , int dummy3 ) ;
static int openssl_SSL_ERROR_SSL ( int level , const char * funcname ) ;
static int openssl_handle_peer_certificate ( struct single * xfd , bool opt_ver ,
int level ) ;
static int xioSSL_set_fd ( struct single * xfd , int level ) ;
static int xioSSL_connect ( struct single * xfd , bool opt_ver , int level ) ;
2008-02-17 13:59:16 +00:00
/* description record for inter-address ssl connect with 0 parameters */
static const struct xioaddr_inter_desc xiointer_openssl_connect0 = {
XIOADDR_INTER , /* this is an embedded address (inter module) */
" openssl-client " , /* keyword for selecting this address type in xioopen calls
2008-01-27 12:00:08 +00:00
( canonical or main name ) */
2008-02-17 13:59:16 +00:00
0 , /* number of required parameters */
XIOBIT_ALL , /* data flow directions this address supports on API layer:
XIOBIT_RDONLY | XIOBIT_WRONLY | XIOBIT_RDWR */
GROUP_CHILD | GROUP_OPENSSL | GROUP_RETRY , /* bitwise OR of address groups this address belongs to.
You might have to specify a new group in xioopts . h */
XIOSHUT_OPENSSL ,
XIOCLOSE_CLOSE ,
2008-01-27 12:00:08 +00:00
xioopen_openssl_connect , /* a function pointer used to "open" these addresses.*/
2008-02-17 13:59:16 +00:00
0 , /* an integer passed to xioopen_openssl; makes it possible to
use the same xioopen_openssl function for slightly different
address types . */
0 , /* like previous argument */
0 , /* like previous arguments, but pointer type.
No trailing comma or semicolon ! */
XIOBIT_RDWR /* SSL is a bidirectional protocol */
HELP ( " " ) /* a text displayed from xio help function.
No trailing comma or semicolon !
only generates this text if WITH_HELP is ! = 0 */
} ;
/* description record for endpoint-address ssl connect with 2 parameters */
static const struct xioaddr_endpoint_desc xioendpoint_openssl_connect2 = {
XIOADDR_ENDPOINT , /* this is not an embedded address but a sys address */
" openssl-client " , /* keyword for selecting this address type in xioopen calls
( canonical or main name ) */
2 , /* number of required parameters */
XIOBIT_ALL , /* data flow directions this address supports on API layer:
XIOBIT_RDONLY | XIOBIT_WRONLY | XIOBIT_RDWR */
2008-01-27 12:00:08 +00:00
GROUP_FD | GROUP_SOCKET | GROUP_SOCK_IP4 | GROUP_SOCK_IP6 | GROUP_IP_TCP | GROUP_CHILD | GROUP_OPENSSL | GROUP_RETRY , /* bitwise OR of address groups this address belongs to.
You might have to specify a new group in xioopts . h */
2008-02-17 13:59:16 +00:00
XIOSHUT_OPENSSL ,
XIOCLOSE_CLOSE ,
xioopen_openssl_connect , /* a function pointer used to "open" these addresses.*/
2008-01-27 12:00:08 +00:00
0 , /* an integer passed to xioopen_openssl; makes it possible to
use the same xioopen_openssl function for slightly different
address types . */
0 , /* like previous argument */
0 /* like previous arguments, but pointer type.
No trailing comma or semicolon ! */
HELP ( " :<host>:<port> " ) /* a text displayed from xio help function.
No trailing comma or semicolon !
only generates this text if WITH_HELP is ! = 0 */
} ;
2008-02-17 13:59:16 +00:00
/* array containing ssl connect description records */
const union xioaddr_desc * xioaddrs_openssl_connect [ ] = {
( union xioaddr_desc * ) & xiointer_openssl_connect0 ,
( union xioaddr_desc * ) & xioendpoint_openssl_connect2 ,
NULL
} ;
2008-01-27 12:00:08 +00:00
# if WITH_LISTEN
2008-02-17 13:59:16 +00:00
/* description record for inter-address ssl listen */
static const struct xioaddr_inter_desc xiointer_openssl_listen0 = {
XIOADDR_INTER , /* this is an embedded address (inter module) */
" openssl-server " , /* keyword for selecting this address type in xioopen calls
2008-01-27 12:00:08 +00:00
( canonical or main name ) */
2008-02-17 13:59:16 +00:00
0 , /* number of required parameters */
XIOBIT_ALL , /* data flow directions this address supports on API layer:
XIOBIT_RDONLY | XIOBIT_WRONLY | XIOBIT_RDWR */
GROUP_CHILD | GROUP_OPENSSL | GROUP_RETRY , /* bitwise OR of address groups this address belongs to.
You might have to specify a new group in xioopts . h */
XIOSHUT_OPENSSL ,
XIOCLOSE_CLOSE ,
2008-01-27 12:00:08 +00:00
xioopen_openssl_listen , /* a function pointer used to "open" these addresses.*/
2008-02-17 13:59:16 +00:00
0 , /* an integer passed to xioopen_openssl_listen; makes it possible to
use the same xioopen_openssl_listen function for slightly different
address types . */
0 , /* like previous argument */
0 , /* like previous arguments, but pointer type.
No trailing comma or semicolon ! */
XIOBIT_RDWR /* SSL is a bidirectional protocol */
HELP ( " " ) /* a text displayed from xio help function.
No trailing comma or semicolon !
only generates this text if WITH_HELP is ! = 0 */
} ;
/* description record for enpoint-address ssl listen */
static const struct xioaddr_endpoint_desc xioendpoint_openssl_listen1 = {
XIOADDR_ENDPOINT , /* this is not an embedded module but a sys module */
" openssl-server " , /* keyword for selecting this address type in xioopen calls
( canonical or main name ) */
1 , /* number of required parameters */
XIOBIT_ALL , /* data flow directions this address supports on API layer:
XIOBIT_RDONLY | XIOBIT_WRONLY | XIOBIT_RDWR */
2008-01-27 12:00:08 +00:00
GROUP_FD | GROUP_SOCKET | GROUP_SOCK_IP4 | GROUP_SOCK_IP6 | GROUP_IP_TCP | GROUP_LISTEN | GROUP_CHILD | GROUP_RANGE | GROUP_OPENSSL | GROUP_RETRY , /* bitwise OR of address groups this address belongs to.
You might have to specify a new group in xioopts . h */
2008-02-17 13:59:16 +00:00
XIOSHUT_OPENSSL ,
XIOCLOSE_CLOSE ,
xioopen_openssl_listen , /* a function pointer used to "open" these addresses.*/
2008-01-27 12:00:08 +00:00
0 , /* an integer passed to xioopen_openssl_listen; makes it possible to
use the same xioopen_openssl_listen function for slightly different
address types . */
0 , /* like previous argument */
0 /* like previous arguments, but pointer type.
No trailing comma or semicolon ! */
HELP ( " :<port> " ) /* a text displayed from xio help function.
No trailing comma or semicolon !
only generates this text if WITH_HELP is ! = 0 */
} ;
2008-02-17 13:59:16 +00:00
/* array containing ssl listen description records */
const union xioaddr_desc * xioaddrs_openssl_listen [ ] = {
( union xioaddr_desc * ) & xiointer_openssl_listen0 ,
( union xioaddr_desc * ) & xioendpoint_openssl_listen1 ,
NULL
} ;
2008-01-27 12:00:08 +00:00
# endif /* WITH_LISTEN */
/* both client and server */
const struct optdesc opt_openssl_cipherlist = { " openssl-cipherlist " , " ciphers " , OPT_OPENSSL_CIPHERLIST , GROUP_OPENSSL , PH_SPEC , TYPE_STRING , OFUNC_SPEC } ;
const struct optdesc opt_openssl_method = { " openssl-method " , " method " , OPT_OPENSSL_METHOD , GROUP_OPENSSL , PH_SPEC , TYPE_STRING , OFUNC_SPEC } ;
const struct optdesc opt_openssl_verify = { " openssl-verify " , " verify " , OPT_OPENSSL_VERIFY , GROUP_OPENSSL , PH_SPEC , TYPE_BOOL , OFUNC_SPEC } ;
const struct optdesc opt_openssl_certificate = { " openssl-certificate " , " cert " , OPT_OPENSSL_CERTIFICATE , GROUP_OPENSSL , PH_SPEC , TYPE_FILENAME , OFUNC_SPEC } ;
const struct optdesc opt_openssl_key = { " openssl-key " , " key " , OPT_OPENSSL_KEY , GROUP_OPENSSL , PH_SPEC , TYPE_FILENAME , OFUNC_SPEC } ;
const struct optdesc opt_openssl_dhparam = { " openssl-dhparam " , " dh " , OPT_OPENSSL_DHPARAM , GROUP_OPENSSL , PH_SPEC , TYPE_FILENAME , OFUNC_SPEC } ;
const struct optdesc opt_openssl_cafile = { " openssl-cafile " , " cafile " , OPT_OPENSSL_CAFILE , GROUP_OPENSSL , PH_SPEC , TYPE_FILENAME , OFUNC_SPEC } ;
const struct optdesc opt_openssl_capath = { " openssl-capath " , " capath " , OPT_OPENSSL_CAPATH , GROUP_OPENSSL , PH_SPEC , TYPE_FILENAME , OFUNC_SPEC } ;
const struct optdesc opt_openssl_egd = { " openssl-egd " , " egd " , OPT_OPENSSL_EGD , GROUP_OPENSSL , PH_SPEC , TYPE_FILENAME , OFUNC_SPEC } ;
const struct optdesc opt_openssl_pseudo = { " openssl-pseudo " , " pseudo " , OPT_OPENSSL_PSEUDO , GROUP_OPENSSL , PH_SPEC , TYPE_BOOL , OFUNC_SPEC } ;
2012-11-16 20:10:44 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x00908000L
const struct optdesc opt_openssl_compress = { " openssl-compress " , " compress " , OPT_OPENSSL_COMPRESS , GROUP_OPENSSL , PH_SPEC , TYPE_STRING , OFUNC_SPEC } ;
# endif
2008-01-27 12:00:08 +00:00
# if WITH_FIPS
const struct optdesc opt_openssl_fips = { " openssl-fips " , " fips " , OPT_OPENSSL_FIPS , GROUP_OPENSSL , PH_SPEC , TYPE_BOOL , OFUNC_SPEC } ;
# endif
/* If FIPS is compiled in, we need to track if the user asked for FIPS mode.
* On forks , the FIPS mode must be reset by a disable , then enable since
* FIPS tracks the process ID that initializes things .
* If FIPS is not compiled in , no tracking variable is needed
* and we make the reset code compile out . This keeps the
* rest of the code below free of FIPS related # ifs
*/
# if WITH_FIPS
static bool xio_openssl_fips = false ;
int xio_reset_fips_mode ( void ) {
if ( xio_openssl_fips ) {
if ( ! sycFIPS_mode_set ( 0 ) | | ! sycFIPS_mode_set ( 1 ) ) {
ERR_load_crypto_strings ( ) ;
ERR_print_errors ( BIO_new_fp ( stderr , BIO_NOCLOSE ) ) ;
Error ( " Failed to reset OpenSSL FIPS mode " ) ;
xio_openssl_fips = false ;
return - 1 ;
}
}
return 0 ;
}
# else
# define xio_reset_fips_mode() 0
# endif
2012-11-16 20:10:44 +00:00
static void openssl_conn_loginfo ( SSL * ssl ) {
Notice1 ( " SSL connection using %s " , SSL_get_cipher ( ssl ) ) ;
# if OPENSSL_VERSION_NUMBER >= 0x00908000L
{
const COMP_METHOD * comp , * expansion ;
comp = sycSSL_get_current_compression ( ssl ) ;
expansion = sycSSL_get_current_expansion ( ssl ) ;
Notice1 ( " SSL connection compression \" %s \" " ,
comp ? sycSSL_COMP_get_name ( comp ) : " none " ) ;
Notice1 ( " SSL connection expansion \" %s \" " ,
expansion ? sycSSL_COMP_get_name ( expansion ) : " none " ) ;
}
# endif
}
2008-01-27 12:00:08 +00:00
/* the open function for OpenSSL client */
static int
xioopen_openssl_connect ( int argc ,
const char * argv [ ] , /* the arguments in the address string */
struct opt * opts ,
int xioflags , /* is the open meant for reading (0),
writing ( 1 ) , or both ( 2 ) ? */
xiofile_t * xxfd , /* a xio file descriptor structure,
already allocated */
unsigned groups , /* the matching address groups... */
int dummy1 , /* first transparent integer value from
addr_openssl */
int dummy2 , /* second transparent integer value from
addr_openssl */
int dummy3 ) /* transparent pointer value from
addr_openssl */
{
struct single * xfd = & xxfd - > stream ;
2009-04-03 09:30:01 +00:00
int rw = ( xioflags & XIO_ACCMODE ) ;
2008-01-27 12:00:08 +00:00
struct opt * opts0 = NULL ;
2008-02-17 13:59:16 +00:00
const char * hostname , * portname = NULL ;
2008-01-27 12:00:08 +00:00
int pf = PF_UNSPEC ;
int ipproto = IPPROTO_TCP ;
int socktype = SOCK_STREAM ;
bool dofork = false ;
union sockaddr_union us_sa , * us = & us_sa ;
union sockaddr_union them_sa , * them = & them_sa ;
socklen_t uslen = sizeof ( us_sa ) ;
socklen_t themlen = sizeof ( them_sa ) ;
bool needbind = false ;
bool lowport = false ;
int level ;
2008-02-17 13:59:16 +00:00
/*0 SSL_CTX* ctx;*/
2008-01-27 12:00:08 +00:00
bool opt_ver = true ; /* verify peer certificate */
char * opt_cert = NULL ; /* file name of client certificate */
int result ;
if ( ! ( xioflags & XIO_MAYCONVERT ) ) {
Error ( " address with data processing not allowed here " ) ;
return STAT_NORETRY ;
}
xfd - > flags | = XIO_DOESCONVERT ;
2008-02-17 13:59:16 +00:00
xfd - > howtoshut = XIOSHUT_OPENSSL ;
xfd - > howtoclose = XIOCLOSE_CLOSE ;
2008-01-27 12:00:08 +00:00
2008-02-17 13:59:16 +00:00
/* we support two forms of openssl-connect */
if ( argc = = 3 ) {
hostname = argv [ 1 ] ;
portname = argv [ 2 ] ;
2008-01-27 12:00:08 +00:00
2008-02-17 13:59:16 +00:00
/* a "terminal" form where we build a tcp connection to given host and
port */
applyopts_single ( xfd , opts , PH_INIT ) ;
applyopts ( - 1 , opts , PH_INIT ) ;
retropt_bool ( opts , OPT_FORK , & dofork ) ;
retropt_string ( opts , OPT_OPENSSL_CERTIFICATE , & opt_cert ) ;
result =
_xioopen_openssl_prepare ( opts , xfd , false , & opt_ver , opt_cert ,
& xfd - > para . openssl . ctx ) ;
if ( result ! = STAT_OK ) return STAT_NORETRY ;
2008-01-27 12:00:08 +00:00
2008-02-17 13:59:16 +00:00
result =
_xioopen_ipapp_prepare ( opts , & opts0 , hostname , portname , & pf , ipproto ,
xfd - > para . socket . ip . res_opts [ 1 ] ,
xfd - > para . socket . ip . res_opts [ 0 ] ,
them , & themlen , us , & uslen ,
2008-10-22 00:33:23 +00:00
& needbind , & lowport , socktype ) ;
2008-02-17 13:59:16 +00:00
if ( result ! = STAT_OK ) return STAT_NORETRY ;
2013-06-21 14:36:18 +00:00
} else if ( argc = = 1 ) {
2008-02-17 13:59:16 +00:00
/* or a "non terminal" address without required parameters */
2009-04-03 09:30:01 +00:00
if ( xfd - > wfd < 0 ) {
2008-02-17 13:59:16 +00:00
Error ( " openssl-connect without hostname and port must be an embedded address " ) ;
return STAT_NORETRY ;
}
if ( applyopts_single ( xfd , opts , PH_INIT ) < 0 ) return - 1 ;
applyopts ( - 1 , opts , PH_INIT ) ;
2008-01-27 12:00:08 +00:00
2008-02-17 13:59:16 +00:00
retropt_bool ( opts , OPT_FORK , & dofork ) ;
retropt_string ( opts , OPT_OPENSSL_CERTIFICATE , & opt_cert ) ;
result =
_xioopen_openssl_prepare ( opts , xfd , false , & opt_ver , opt_cert ,
& xfd - > para . openssl . ctx ) ;
if ( result ! = STAT_OK ) return STAT_NORETRY ;
retropt_bool ( opts , OPT_FORK , & dofork ) ;
} else {
Error1 ( " %s: 0 or 2 parameters required " , argv [ 0 ] ) ;
return STAT_NORETRY ;
}
2008-01-27 12:00:08 +00:00
if ( xioopts . logopt = = ' m ' ) {
Info ( " starting connect loop, switching to syslog " ) ;
diag_set ( ' y ' , xioopts . syslogfac ) ; xioopts . logopt = ' y ' ;
} else {
Info ( " starting connect loop " ) ;
}
do { /* loop over failed connect and SSL handshake attempts */
# if WITH_RETRY
if ( xfd - > forever | | xfd - > retry ) {
level = E_INFO ;
} else
# endif /* WITH_RETRY */
level = E_ERROR ;
2008-02-17 13:59:16 +00:00
/*!!! this belongs only to "old" openssl-connect form */
if ( portname ) {
2008-01-27 12:00:08 +00:00
/* this cannot fork because we retrieved fork option above */
result =
_xioopen_connect ( xfd ,
2014-03-21 12:24:26 +00:00
needbind ? ( struct sockaddr * ) us : NULL , uslen ,
2008-01-27 12:00:08 +00:00
( struct sockaddr * ) them , themlen ,
opts , pf , socktype , ipproto , lowport , level ) ;
switch ( result ) {
case STAT_OK : break ;
# if WITH_RETRY
case STAT_RETRYLATER :
case STAT_RETRYNOW :
if ( xfd - > forever | | xfd - > retry ) {
dropopts ( opts , PH_ALL ) ; opts = copyopts ( opts0 , GROUP_ALL ) ;
if ( result = = STAT_RETRYLATER ) {
Nanosleep ( & xfd - > intervall , NULL ) ;
}
- - xfd - > retry ;
continue ;
}
return STAT_NORETRY ;
# endif /* WITH_RETRY */
default :
return result ;
2008-02-17 13:59:16 +00:00
}
2009-04-03 09:30:01 +00:00
xfd - > wfd = xfd - > rfd ;
}
/*! isn't this too early? */
if ( ( result = _xio_openlate ( xfd , opts ) ) < 0 ) {
return result ;
}
result =
_xioopen_openssl_connect ( xfd , opt_ver , xfd - > para . openssl . ctx , level ) ;
switch ( result ) {
case STAT_OK : break ;
# if WITH_RETRY
case STAT_RETRYLATER :
case STAT_RETRYNOW :
if ( xfd - > forever | | xfd - > retry ) {
Close ( xfd - > rfd ) ;
Close ( xfd - > wfd ) ;
dropopts ( opts , PH_ALL ) ; opts = copyopts ( opts0 , GROUP_ALL ) ;
if ( result = = STAT_RETRYLATER ) {
Nanosleep ( & xfd - > intervall , NULL ) ;
}
- - xfd - > retry ;
continue ;
}
# endif /* WITH_RETRY */
default : return STAT_NORETRY ;
}
if ( dofork ) {
xiosetchilddied ( ) ; /* set SIGCHLD handler */
}
# if WITH_RETRY
if ( dofork ) {
pid_t pid ;
int level = E_ERROR ;
if ( xfd - > forever | | xfd - > retry ) {
level = E_WARN ;
}
while ( ( pid = xio_fork ( false , level ) ) < 0 ) {
if ( xfd - > forever | | - - xfd - > retry ) {
Nanosleep ( & xfd - > intervall , NULL ) ; continue ;
}
return STAT_RETRYLATER ;
}
if ( pid = = 0 ) { /* child process */
xfd - > forever = false ; xfd - > retry = 0 ;
break ;
}
/* parent process */
Notice1 ( " forked off child process " F_pid , pid ) ;
Close ( xfd - > rfd ) ;
Close ( xfd - > wfd ) ;
sycSSL_free ( xfd - > para . openssl . ssl ) ;
xfd - > para . openssl . ssl = NULL ;
/* with and without retry */
Nanosleep ( & xfd - > intervall , NULL ) ;
dropopts ( opts , PH_ALL ) ; opts = copyopts ( opts0 , GROUP_ALL ) ;
continue ; /* with next socket() bind() connect() */
}
# endif /* WITH_RETRY */
break ;
} while ( true ) ; /* drop out on success */
2012-11-16 20:10:44 +00:00
openssl_conn_loginfo ( xfd - > para . openssl . ssl ) ;
2009-04-03 09:30:01 +00:00
/* fill in the fd structure */
return STAT_OK ;
}
/* this function is typically called within the OpenSSL client fork/retry loop.
xfd must be of type DATA_OPENSSL , and its fd must be set with a valid file
descriptor . this function then performs all SSL related step to make a valid
SSL connection from an FD and a CTX . */
int _xioopen_openssl_connect ( struct single * xfd ,
2008-01-27 12:00:08 +00:00
bool opt_ver ,
SSL_CTX * ctx ,
int level ) {
SSL * ssl ;
unsigned long err ;
int result ;
/* create a SSL object */
if ( ( ssl = sycSSL_new ( ctx ) ) = = NULL ) {
if ( ERR_peek_error ( ) = = 0 ) Msg ( level , " SSL_new() failed " ) ;
while ( err = ERR_get_error ( ) ) {
Msg1 ( level , " SSL_new(): %s " , ERR_error_string ( err , NULL ) ) ;
}
/*Error("SSL_new()");*/
return STAT_RETRYLATER ;
}
xfd - > para . openssl . ssl = ssl ;
result = xioSSL_set_fd ( xfd , level ) ;
if ( result ! = STAT_OK ) {
sycSSL_free ( xfd - > para . openssl . ssl ) ;
xfd - > para . openssl . ssl = NULL ;
return result ;
}
result = xioSSL_connect ( xfd , opt_ver , level ) ;
if ( result ! = STAT_OK ) {
sycSSL_free ( xfd - > para . openssl . ssl ) ;
xfd - > para . openssl . ssl = NULL ;
return result ;
}
result = openssl_handle_peer_certificate ( xfd , opt_ver , level ) ;
if ( result ! = STAT_OK ) {
sycSSL_free ( xfd - > para . openssl . ssl ) ;
xfd - > para . openssl . ssl = NULL ;
return result ;
}
return STAT_OK ;
}
# if WITH_LISTEN
static int
xioopen_openssl_listen ( int argc ,
const char * argv [ ] , /* the arguments in the address string */
struct opt * opts ,
int xioflags , /* is the open meant for reading (0),
writing ( 1 ) , or both ( 2 ) ? */
xiofile_t * xxfd , /* a xio file descriptor structure,
already allocated */
unsigned groups , /* the matching address groups... */
int dummy1 , /* first transparent integer value from
addr_openssl */
int dummy2 , /* second transparent integer value from
addr_openssl */
int dummy3 ) /* transparent pointer value from
addr_openssl */
{
struct single * xfd = & xxfd - > stream ;
2008-02-17 13:59:16 +00:00
const char * portname = NULL ;
2008-01-27 12:00:08 +00:00
struct opt * opts0 = NULL ;
union sockaddr_union us_sa , * us = & us_sa ;
socklen_t uslen = sizeof ( us_sa ) ;
int pf ;
int socktype = SOCK_STREAM ;
int ipproto = IPPROTO_TCP ;
/*! lowport? */
int level ;
2008-02-17 13:59:16 +00:00
/*0 SSL_CTX* ctx;*/
2008-01-27 12:00:08 +00:00
bool opt_ver = true ; /* verify peer certificate - changed with 1.6.0 */
char * opt_cert = NULL ; /* file name of server certificate */
int result ;
if ( ! ( xioflags & XIO_MAYCONVERT ) ) {
Error ( " address with data processing not allowed here " ) ;
return STAT_NORETRY ;
}
xfd - > flags | = XIO_DOESCONVERT ;
# if WITH_IP4 && WITH_IP6
pf = xioopts . default_ip = = ' 6 ' ? PF_INET6 : PF_INET ;
# elif WITH_IP6
pf = PF_INET6 ;
# else
pf = PF_INET ;
# endif
2008-02-17 13:59:16 +00:00
if ( argc = = 2 ) {
2008-01-27 12:00:08 +00:00
2008-02-17 13:59:16 +00:00
portname = argv [ 1 ] ;
if ( applyopts_single ( xfd , opts , PH_INIT ) < 0 ) return - 1 ;
applyopts ( - 1 , opts , PH_INIT ) ;
2008-01-27 12:00:08 +00:00
2008-02-17 13:59:16 +00:00
retropt_string ( opts , OPT_OPENSSL_CERTIFICATE , & opt_cert ) ;
if ( opt_cert = = NULL ) {
Warn ( " no certificate given; consider option \" cert \" " ) ;
}
2008-01-27 12:00:08 +00:00
2008-02-17 13:59:16 +00:00
result =
_xioopen_openssl_prepare ( opts , xfd , true , & opt_ver , opt_cert ,
& xfd - > para . openssl . ctx ) ;
if ( result ! = STAT_OK ) return STAT_NORETRY ;
if ( _xioopen_ipapp_listen_prepare ( opts , & opts0 , portname , & pf , ipproto ,
xfd - > para . socket . ip . res_opts [ 1 ] ,
xfd - > para . socket . ip . res_opts [ 0 ] ,
2008-10-22 00:33:23 +00:00
us , & uslen , socktype )
2008-01-27 12:00:08 +00:00
! = STAT_OK ) {
2008-02-17 13:59:16 +00:00
return STAT_NORETRY ;
}
} else if ( argc = = 1 ) {
2009-04-03 09:30:01 +00:00
if ( xfd - > rfd < 0 ) {
2008-02-17 13:59:16 +00:00
Error ( " openssl-listen without port must be an embedded address " ) ;
return STAT_NORETRY ;
}
if ( applyopts_single ( xfd , opts , PH_INIT ) < 0 ) return - 1 ;
applyopts ( - 1 , opts , PH_INIT ) ;
retropt_string ( opts , OPT_OPENSSL_CERTIFICATE , & opt_cert ) ;
if ( opt_cert = = NULL ) {
Warn ( " no certificate given; consider option \" cert \" " ) ;
}
applyopts ( - 1 , opts , PH_EARLY ) ;
result =
_xioopen_openssl_prepare ( opts , xfd , true , & opt_ver , opt_cert ,
& xfd - > para . openssl . ctx ) ;
if ( result ! = STAT_OK ) return STAT_NORETRY ;
} else {
Error1 ( " %s: 1 parameter required " , argv [ 0 ] ) ;
return - 1 ;
2008-01-27 12:00:08 +00:00
}
xfd - > dtype = XIODATA_OPENSSL ;
while ( true ) { /* loop over failed attempts */
# if WITH_RETRY
if ( xfd - > forever | | xfd - > retry ) {
level = E_INFO ;
} else
# endif /* WITH_RETRY */
level = E_ERROR ;
2008-02-17 13:59:16 +00:00
if ( portname ) {
/* tcp listen; this can fork() for us; it only returns on error or on
successful establishment of tcp connection */
result = _xioopen_listen ( xfd , xioflags ,
( struct sockaddr * ) us , uslen ,
opts , pf , socktype , IPPROTO_TCP ,
2008-01-27 12:00:08 +00:00
# if WITH_RETRY
2008-02-17 13:59:16 +00:00
( xfd - > retry | | xfd - > forever ) ? E_INFO : E_ERROR
2008-01-27 12:00:08 +00:00
# else
2008-02-17 13:59:16 +00:00
E_ERROR
2008-01-27 12:00:08 +00:00
# endif /* WITH_RETRY */
2008-02-17 13:59:16 +00:00
) ;
2008-01-27 12:00:08 +00:00
/*! not sure if we should try again on retry/forever */
switch ( result ) {
case STAT_OK : break ;
# if WITH_RETRY
case STAT_RETRYLATER :
case STAT_RETRYNOW :
if ( xfd - > forever | | xfd - > retry ) {
dropopts ( opts , PH_ALL ) ; opts = copyopts ( opts0 , GROUP_ALL ) ;
if ( result = = STAT_RETRYLATER ) {
Nanosleep ( & xfd - > intervall , NULL ) ;
}
dropopts ( opts , PH_ALL ) ; opts = copyopts ( opts0 , GROUP_ALL ) ;
- - xfd - > retry ;
continue ;
}
return STAT_NORETRY ;
# endif /* WITH_RETRY */
default :
return result ;
}
2009-04-03 09:30:01 +00:00
xfd - > wfd = xfd - > rfd ;
}
2008-02-17 13:59:16 +00:00
result =
_xioopen_openssl_listen ( xfd , opt_ver , xfd - > para . openssl . ctx , level ) ;
2008-01-27 12:00:08 +00:00
switch ( result ) {
case STAT_OK : break ;
# if WITH_RETRY
case STAT_RETRYLATER :
case STAT_RETRYNOW :
if ( xfd - > forever | | xfd - > retry ) {
dropopts ( opts , PH_ALL ) ; opts = copyopts ( opts0 , GROUP_ALL ) ;
if ( result = = STAT_RETRYLATER ) {
Nanosleep ( & xfd - > intervall , NULL ) ;
}
dropopts ( opts , PH_ALL ) ; opts = copyopts ( opts0 , GROUP_ALL ) ;
- - xfd - > retry ;
continue ;
}
return STAT_NORETRY ;
# endif /* WITH_RETRY */
default :
return result ;
}
2012-11-16 20:10:44 +00:00
openssl_conn_loginfo ( xfd - > para . openssl . ssl ) ;
2008-01-27 12:00:08 +00:00
break ;
} /* drop out on success */
/* fill in the fd structure */
return STAT_OK ;
}
int _xioopen_openssl_listen ( struct single * xfd ,
bool opt_ver ,
SSL_CTX * ctx ,
int level ) {
char error_string [ 120 ] ;
unsigned long err ;
int errint , ret ;
/* create an SSL object */
if ( ( xfd - > para . openssl . ssl = sycSSL_new ( ctx ) ) = = NULL ) {
if ( ERR_peek_error ( ) = = 0 ) Msg ( level , " SSL_new() failed " ) ;
while ( err = ERR_get_error ( ) ) {
Msg1 ( level , " SSL_new(): %s " , ERR_error_string ( err , NULL ) ) ;
}
/*Error("SSL_new()");*/
return STAT_NORETRY ;
}
/* assign the network connection to the SSL object */
2008-02-17 13:59:16 +00:00
ret = xioSSL_set_fd ( xfd , level ) ;
if ( ret ! = STAT_OK ) {
sycSSL_free ( xfd - > para . openssl . ssl ) ;
xfd - > para . openssl . ssl = NULL ;
return ret ;
2008-01-27 12:00:08 +00:00
}
# if WITH_DEBUG
{
int i = 0 ;
const char * ciphers = NULL ;
Debug ( " available ciphers: " ) ;
do {
ciphers = SSL_get_cipher_list ( xfd - > para . openssl . ssl , i ) ;
if ( ciphers = = NULL ) break ;
Debug2 ( " CIPHERS pri=%d: %s " , i , ciphers ) ;
+ + i ;
} while ( 1 ) ;
}
# endif /* WITH_DEBUG */
/* connect via SSL by performing handshake */
if ( ( ret = sycSSL_accept ( xfd - > para . openssl . ssl ) ) < = 0 ) {
/*if (ERR_peek_error() == 0) Msg(level, "SSL_accept() failed");*/
errint = SSL_get_error ( xfd - > para . openssl . ssl , ret ) ;
switch ( errint ) {
case SSL_ERROR_NONE :
Msg ( level , " ok " ) ; break ;
case SSL_ERROR_ZERO_RETURN :
Msg ( level , " connection closed (wrong version number?) " ) ; break ;
case SSL_ERROR_WANT_READ : case SSL_ERROR_WANT_WRITE :
case SSL_ERROR_WANT_CONNECT :
case SSL_ERROR_WANT_X509_LOOKUP :
Msg ( level , " nonblocking operation did not complete " ) ; break ; /*!*/
case SSL_ERROR_SYSCALL :
if ( ERR_peek_error ( ) = = 0 ) {
if ( ret = = 0 ) {
Msg ( level , " SSL_accept(): socket closed by peer " ) ;
} else if ( ret = = - 1 ) {
Msg1 ( level , " SSL_accept(): %s " , strerror ( errno ) ) ;
}
} else {
Msg ( level , " I/O error " ) ; /*!*/
while ( err = ERR_get_error ( ) ) {
ERR_error_string_n ( err , error_string , sizeof ( error_string ) ) ;
Msg4 ( level , " SSL_accept(): %s / %s / %s / %s " , error_string ,
ERR_lib_error_string ( err ) , ERR_func_error_string ( err ) ,
ERR_reason_error_string ( err ) ) ;
}
/* Msg1(level, "SSL_connect(): %s", ERR_error_string(e, buf));*/
}
break ;
case SSL_ERROR_SSL :
/*ERR_print_errors_fp(stderr);*/
openssl_SSL_ERROR_SSL ( level , " SSL_accept " ) ;
break ;
default :
Msg ( level , " unknown error " ) ;
}
return STAT_RETRYLATER ;
}
if ( openssl_handle_peer_certificate ( xfd , opt_ver , E_ERROR /*!*/ ) < 0 ) {
return STAT_NORETRY ;
}
return STAT_OK ;
}
# endif /* WITH_LISTEN */
2012-11-16 20:10:44 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x00908000L
/* In OpenSSL 0.9.7 compression methods could be added using
* SSL_COMP_add_compression_method ( 3 ) , but the implemntation is not compatible
* with the standard ( RFC3749 ) .
*/
static int openssl_setup_compression ( SSL_CTX * ctx , char * method )
{
STACK_OF ( SSL_COMP ) * comp_methods ;
assert ( method ) ;
/* Getting the stack of compression methods has the intended side-effect of
* initializing the SSL library ' s compression part .
*/
comp_methods = SSL_COMP_get_compression_methods ( ) ;
if ( ! comp_methods ) {
Info ( " OpenSSL built without compression support " ) ;
return STAT_OK ;
}
if ( strcasecmp ( method , " auto " ) = = 0 ) {
Info ( " Using default OpenSSL compression " ) ;
return STAT_OK ;
}
if ( strcasecmp ( method , " none " ) = = 0 ) {
/* Disable compression */
# ifdef SSL_OP_NO_COMPRESSION
Info ( " Disabling OpenSSL compression " ) ;
SSL_CTX_set_options ( ctx , SSL_OP_NO_COMPRESSION ) ;
# else
/* SSL_OP_NO_COMPRESSION was only introduced in OpenSSL 0.9.9 (released
* as 1.0 .0 ) . Removing all compression methods is a work - around for
* earlier versions of OpenSSL , but it affects all SSL connections .
*/
Info ( " Disabling OpenSSL compression globally " ) ;
sk_SSL_COMP_zero ( comp_methods ) ;
# endif
return STAT_OK ;
}
/* zlib compression in OpenSSL before version 0.9.8e-beta1 uses the libc's
* default malloc / free instead of the ones passed to OpenSSL . Should socat
* ever use custom malloc / free functions for OpenSSL , this must be taken
* into consideration . See OpenSSL bug # 1468.
*/
Error1 ( " openssl-compress= \" %s \" : unknown compression method " , method ) ;
return STAT_NORETRY ;
}
# endif
2008-01-27 12:00:08 +00:00
int
_xioopen_openssl_prepare ( struct opt * opts ,
struct single * xfd , /* a xio file descriptor
structure , already allocated
*/
bool server , /* SSL client: false */
bool * opt_ver ,
const char * opt_cert ,
SSL_CTX * * ctx )
{
bool opt_fips = false ;
2013-06-21 14:19:02 +00:00
const SSL_METHOD * method ;
2008-01-27 12:00:08 +00:00
char * me_str = NULL ; /* method string */
char * ci_str = NULL ; /* cipher string */
char * opt_key = NULL ; /* file name of client private key */
char * opt_dhparam = NULL ; /* file name of DH params */
char * opt_cafile = NULL ; /* certificate authority file */
char * opt_capath = NULL ; /* certificate authority directory */
char * opt_egd = NULL ; /* entropy gathering daemon socket path */
2012-11-16 20:10:44 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x00908000L
char * opt_compress = NULL ; /* compression method */
# endif
2008-01-27 12:00:08 +00:00
bool opt_pseudo = false ; /* use pseudo entropy if nothing else */
unsigned long err ;
int result ;
xfd - > dtype = XIODATA_OPENSSL ;
retropt_bool ( opts , OPT_OPENSSL_FIPS , & opt_fips ) ;
retropt_string ( opts , OPT_OPENSSL_METHOD , & me_str ) ;
retropt_string ( opts , OPT_OPENSSL_CIPHERLIST , & ci_str ) ;
retropt_bool ( opts , OPT_OPENSSL_VERIFY , opt_ver ) ;
retropt_string ( opts , OPT_OPENSSL_CAFILE , & opt_cafile ) ;
retropt_string ( opts , OPT_OPENSSL_CAPATH , & opt_capath ) ;
retropt_string ( opts , OPT_OPENSSL_KEY , & opt_key ) ;
retropt_string ( opts , OPT_OPENSSL_DHPARAM , & opt_dhparam ) ;
retropt_string ( opts , OPT_OPENSSL_EGD , & opt_egd ) ;
retropt_bool ( opts , OPT_OPENSSL_PSEUDO , & opt_pseudo ) ;
2012-11-16 20:10:44 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x00908000L
retropt_string ( opts , OPT_OPENSSL_COMPRESS , & opt_compress ) ;
# endif
2008-01-27 12:00:08 +00:00
# if WITH_FIPS
if ( opt_fips ) {
if ( ! sycFIPS_mode_set ( 1 ) ) {
ERR_load_crypto_strings ( ) ;
ERR_print_errors ( BIO_new_fp ( stderr , BIO_NOCLOSE ) ) ;
Error ( " Failed to set FIPS mode " ) ;
} else {
xio_openssl_fips = true ;
}
}
# endif
OpenSSL_add_all_algorithms ( ) ;
OpenSSL_add_all_ciphers ( ) ;
OpenSSL_add_all_digests ( ) ;
sycSSL_load_error_strings ( ) ;
/* OpenSSL preparation */
sycSSL_library_init ( ) ;
/*! actions_to_seed_PRNG();*/
if ( ! server ) {
if ( me_str ! = 0 ) {
if ( ! strcasecmp ( me_str , " SSLv2 " ) | | ! strcasecmp ( me_str , " SSL2 " ) ) {
2013-06-21 14:19:02 +00:00
# if HAVE_SSLv2_client_method
2008-01-27 12:00:08 +00:00
method = sycSSLv2_client_method ( ) ;
2013-06-21 14:19:02 +00:00
# else
Error1 ( " OpenSSL method \" %s \" not provided by library " , me_str ) ;
method = sycSSLv23_server_method ( ) ;
# endif
} else
if ( ! strcasecmp ( me_str , " SSLv3 " ) | | ! strcasecmp ( me_str , " SSL3 " ) ) {
2008-01-27 12:00:08 +00:00
method = sycSSLv3_client_method ( ) ;
} else if ( ! strcasecmp ( me_str , " SSLv23 " ) | | ! strcasecmp ( me_str , " SSL23 " ) | |
! strcasecmp ( me_str , " SSL " ) ) {
method = sycSSLv23_client_method ( ) ;
} else if ( ! strcasecmp ( me_str , " TLSv1 " ) | | ! strcasecmp ( me_str , " TLS1 " ) | |
! strcasecmp ( me_str , " TLS " ) ) {
method = sycTLSv1_client_method ( ) ;
} else {
Error1 ( " openssl-method= \" %s \" : unknown method " , me_str ) ;
2013-06-21 14:19:02 +00:00
method = sycSSLv23_client_method ( ) ;
2008-01-27 12:00:08 +00:00
}
} else {
2013-06-21 14:19:02 +00:00
method = sycSSLv23_client_method ( ) ;
2008-01-27 12:00:08 +00:00
}
} else /* server */ {
if ( me_str ! = 0 ) {
if ( ! strcasecmp ( me_str , " SSLv2 " ) | | ! strcasecmp ( me_str , " SSL2 " ) ) {
2013-06-21 14:19:02 +00:00
# if HAVE_SSLv2_server_method
2008-01-27 12:00:08 +00:00
method = sycSSLv2_server_method ( ) ;
2013-06-21 14:19:02 +00:00
# else
Error1 ( " OpenSSL method \" %s \" not provided by library " , me_str ) ;
method = sycSSLv23_server_method ( ) ;
# endif
} else
if ( ! strcasecmp ( me_str , " SSLv3 " ) | | ! strcasecmp ( me_str , " SSL3 " ) ) {
2008-01-27 12:00:08 +00:00
method = sycSSLv3_server_method ( ) ;
} else if ( ! strcasecmp ( me_str , " SSLv23 " ) | | ! strcasecmp ( me_str , " SSL23 " ) | |
! strcasecmp ( me_str , " SSL " ) ) {
method = sycSSLv23_server_method ( ) ;
} else if ( ! strcasecmp ( me_str , " TLSv1 " ) | | ! strcasecmp ( me_str , " TLS1 " ) | |
! strcasecmp ( me_str , " TLS " ) ) {
method = sycTLSv1_server_method ( ) ;
} else {
Error1 ( " openssl-method= \" %s \" : unknown method " , me_str ) ;
2013-06-21 14:19:02 +00:00
method = sycSSLv23_server_method ( ) ;
2008-01-27 12:00:08 +00:00
}
} else {
2013-06-21 14:19:02 +00:00
method = sycSSLv23_server_method ( ) ;
2008-01-27 12:00:08 +00:00
}
}
if ( opt_egd ) {
sycRAND_egd ( opt_egd ) ;
}
if ( opt_pseudo ) {
long int randdata ;
/* initialize libc random from actual microseconds */
struct timeval tv ;
struct timezone tz ;
tz . tz_minuteswest = 0 ;
tz . tz_dsttime = 0 ;
if ( ( result = Gettimeofday ( & tv , & tz ) ) < 0 ) {
Warn2 ( " gettimeofday(%p, {0,0}): %s " , & tv , strerror ( errno ) ) ;
}
srandom ( tv . tv_sec * 1000000 + tv . tv_usec ) ;
while ( ! RAND_status ( ) ) {
randdata = random ( ) ;
Debug2 ( " RAND_seed(0x{%lx}, " F_Zu " ) " ,
randdata , sizeof ( randdata ) ) ;
RAND_seed ( & randdata , sizeof ( randdata ) ) ;
}
}
if ( ( * ctx = sycSSL_CTX_new ( method ) ) = = NULL ) {
if ( ERR_peek_error ( ) = = 0 ) Error ( " SSL_CTX_new() " ) ;
while ( err = ERR_get_error ( ) ) {
Error1 ( " SSL_CTX_new(): %s " , ERR_error_string ( err , NULL ) ) ;
}
/*ERR_clear_error;*/
return STAT_RETRYLATER ;
}
2012-07-22 15:55:56 +00:00
{
static unsigned char dh512_p [ ] = {
0xDA , 0x58 , 0x3C , 0x16 , 0xD9 , 0x85 , 0x22 , 0x89 , 0xD0 , 0xE4 , 0xAF , 0x75 ,
0x6F , 0x4C , 0xCA , 0x92 , 0xDD , 0x4B , 0xE5 , 0x33 , 0xB8 , 0x04 , 0xFB , 0x0F ,
0xED , 0x94 , 0xEF , 0x9C , 0x8A , 0x44 , 0x03 , 0xED , 0x57 , 0x46 , 0x50 , 0xD3 ,
0x69 , 0x99 , 0xDB , 0x29 , 0xD7 , 0x76 , 0x27 , 0x6B , 0xA2 , 0xD3 , 0xD4 , 0x12 ,
0xE2 , 0x18 , 0xF4 , 0xDD , 0x1E , 0x08 , 0x4C , 0xF6 , 0xD8 , 0x00 , 0x3E , 0x7C ,
0x47 , 0x74 , 0xE8 , 0x33 ,
} ;
static unsigned char dh512_g [ ] = {
0x02 ,
} ;
DH * dh ;
unsigned long err ;
if ( ( dh = DH_new ( ) ) = = NULL ) {
while ( err = ERR_get_error ( ) ) {
Warn1 ( " DH_new(): %s " ,
ERR_error_string ( err , NULL ) ) ;
}
Error ( " DH_new() failed " ) ;
} else {
dh - > p = BN_bin2bn ( dh512_p , sizeof ( dh512_p ) , NULL ) ;
dh - > g = BN_bin2bn ( dh512_g , sizeof ( dh512_g ) , NULL ) ;
if ( ( dh - > p = = NULL ) | | ( dh - > g = = NULL ) ) {
while ( err = ERR_get_error ( ) ) {
Warn1 ( " BN_bin2bn(): %s " ,
ERR_error_string ( err , NULL ) ) ;
}
Error ( " BN_bin2bn() failed " ) ;
} else {
if ( SSL_CTX_set_tmp_dh ( * ctx , dh ) < = 0 ) {
while ( err = ERR_get_error ( ) ) {
2012-11-17 16:15:56 +00:00
Warn3 ( " SSL_CTX_set_tmp_dh(%p, %p): %s " , * ctx , dh ,
2012-07-22 15:55:56 +00:00
ERR_error_string ( err , NULL ) ) ;
}
Error2 ( " SSL_CTX_set_tmp_dh(%p, %p) failed " , * ctx , dh ) ;
}
/*! OPENSSL_free(dh->p,g)? doc does not tell so */
}
DH_free ( dh ) ;
}
}
2008-01-27 12:00:08 +00:00
2012-11-16 20:10:44 +00:00
# if OPENSSL_VERSION_NUMBER >= 0x00908000L
if ( opt_compress ) {
int result ;
result = openssl_setup_compression ( * ctx , opt_compress ) ;
if ( result ! = STAT_OK ) {
return result ;
}
}
# endif
2008-01-27 12:00:08 +00:00
if ( opt_cafile ! = NULL | | opt_capath ! = NULL ) {
if ( sycSSL_CTX_load_verify_locations ( * ctx , opt_cafile , opt_capath ) ! = 1 ) {
int result ;
if ( ( result =
openssl_SSL_ERROR_SSL ( E_ERROR , " SSL_CTX_load_verify_locations " ) )
! = STAT_OK ) {
2008-02-17 13:59:16 +00:00
SSL_CTX_free ( * ctx ) ; * ctx = NULL ;
2008-01-27 12:00:08 +00:00
return STAT_RETRYLATER ;
}
}
}
if ( opt_cert ) {
BIO * bio ;
DH * dh ;
if ( sycSSL_CTX_use_certificate_chain_file ( * ctx , opt_cert ) < = 0 ) {
/*! trace functions */
/*0 ERR_print_errors_fp(stderr);*/
if ( ERR_peek_error ( ) = = 0 )
Error2 ( " SSL_CTX_use_certificate_file(%p, \" %s \" , SSL_FILETYPE_PEM) failed " ,
* ctx , opt_cert ) ;
while ( err = ERR_get_error ( ) ) {
Error1 ( " SSL_CTX_use_certificate_file(): %s " ,
ERR_error_string ( err , NULL ) ) ;
}
return STAT_RETRYLATER ;
}
if ( sycSSL_CTX_use_PrivateKey_file ( * ctx , opt_key ? opt_key : opt_cert , SSL_FILETYPE_PEM ) < = 0 ) {
/*ERR_print_errors_fp(stderr);*/
openssl_SSL_ERROR_SSL ( E_ERROR /*!*/ , " SSL_CTX_use_PrivateKey_file " ) ;
return STAT_RETRYLATER ;
}
if ( opt_dhparam = = NULL ) {
opt_dhparam = ( char * ) opt_cert ;
}
if ( ( bio = sycBIO_new_file ( opt_dhparam , " r " ) ) = = NULL ) {
Warn2 ( " BIO_new_file( \" %s \" , \" r \" ): %s " ,
opt_dhparam , strerror ( errno ) ) ;
} else {
if ( ( dh = sycPEM_read_bio_DHparams ( bio , NULL , NULL , NULL ) ) = = NULL ) {
Info1 ( " PEM_read_bio_DHparams(%p, NULL, NULL, NULL): error " , bio ) ;
} else {
BIO_free ( bio ) ;
if ( sycSSL_CTX_set_tmp_dh ( * ctx , dh ) = = 0 ) {
Error2 ( " SSL_CTX_set_tmp_dh(%p, %p): error " , ctx , dh ) ;
}
}
}
}
/* set pre ssl-connect options */
/* SSL_CIPHERS */
if ( ci_str ! = NULL ) {
if ( sycSSL_CTX_set_cipher_list ( * ctx , ci_str ) < = 0 ) {
if ( ERR_peek_error ( ) = = 0 )
Error1 ( " SSL_set_cipher_list(, \" %s \" ) failed " , ci_str ) ;
while ( err = ERR_get_error ( ) ) {
Error2 ( " SSL_set_cipher_list(, \" %s \" ): %s " ,
ci_str , ERR_error_string ( err , NULL ) ) ;
}
/*Error("SSL_new()");*/
return STAT_RETRYLATER ;
}
}
if ( * opt_ver ) {
sycSSL_CTX_set_verify ( * ctx ,
SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT ,
NULL ) ;
} else {
sycSSL_CTX_set_verify ( * ctx ,
SSL_VERIFY_NONE ,
NULL ) ;
}
return STAT_OK ;
}
/* analyses an OpenSSL error condition, prints the appropriate messages with
severity ' level ' and returns one of STAT_OK , STAT_RETRYLATER , or
STAT_NORETRY */
static int openssl_SSL_ERROR_SSL ( int level , const char * funcname ) {
unsigned long e ;
char buf [ 120 ] ; /* this value demanded by "man ERR_error_string" */
e = ERR_get_error ( ) ;
Debug1 ( " ERR_get_error(): %lx " , e ) ;
if ( e = = ( ( ERR_LIB_RAND < < 24 ) |
( RAND_F_SSLEAY_RAND_BYTES < < 12 ) |
( RAND_R_PRNG_NOT_SEEDED ) ) /*0x24064064*/ ) {
Error ( " too few entropy; use options \" egd \" or \" pseudo \" " ) ;
return STAT_NORETRY ;
} else {
Msg2 ( level , " %s(): %s " , funcname , ERR_error_string ( e , buf ) ) ;
return level = = E_ERROR ? STAT_NORETRY : STAT_RETRYLATER ;
}
return STAT_OK ;
}
static const char * openssl_verify_messages [ ] = {
/* 0 */ " ok " ,
/* 1 */ NULL ,
/* 2 */ " unable to get issuer certificate " ,
/* 3 */ " unable to get certificate CRL " ,
/* 4 */ " unable to decrypt certificate's signature " ,
/* 5 */ " unable to decrypt CRL's signature " ,
/* 6 */ " unable to decode issuer public key " ,
/* 7 */ " certificate signature failure " ,
/* 8 */ " CRL signature failure " ,
/* 9 */ " certificate is not yet valid " ,
/* 10 */ " certificate has expired " ,
/* 11 */ " CRL is not yet valid " ,
/* 12 */ " CRL has expired " ,
/* 13 */ " format error in certificate's notBefore field " ,
/* 14 */ " format error in certificate's notAfter field " ,
/* 15 */ " format error in CRL's lastUpdate field " ,
/* 16 */ " format error in CRL's nextUpdate field " ,
/* 17 */ " out of memory " ,
/* 18 */ " self signed certificate " ,
/* 19 */ " self signed certificate in certificate chain " ,
/* 20 */ " unable to get local issuer certificate " ,
/* 21 */ " unable to verify the first certificate " ,
/* 22 */ " certificate chain too long " ,
/* 23 */ " certificate revoked " ,
/* 24 */ " invalid CA certificate " ,
/* 25 */ " path length constraint exceeded " ,
/* 26 */ " unsupported certificate purpose " ,
/* 27 */ " certificate not trusted " ,
/* 28 */ " certificate rejected " ,
/* 29 */ " subject issuer mismatch " ,
/* 30 */ " authority and subject key identifier mismatch " ,
/* 31 */ " authority and issuer serial number mismatch " ,
/* 32 */ " key usage does not include certificate signing " ,
/* 33 */ NULL ,
/* 34 */ NULL ,
/* 35 */ NULL ,
/* 36 */ NULL ,
/* 37 */ NULL ,
/* 38 */ NULL ,
/* 39 */ NULL ,
/* 40 */ NULL ,
/* 41 */ NULL ,
/* 42 */ NULL ,
/* 43 */ NULL ,
/* 44 */ NULL ,
/* 45 */ NULL ,
/* 46 */ NULL ,
/* 47 */ NULL ,
/* 48 */ NULL ,
/* 49 */ NULL ,
/* 50 */ " application verification failure " ,
} ;
static int openssl_handle_peer_certificate ( struct single * xfd ,
bool opt_ver , int level ) {
X509 * peer_cert ;
char * str ;
char buff [ 2048 ] ; /* hold peer certificate */
int status ;
/* SSL_CTX_add_extra_chain_cert
SSL_get_verify_result
*/
if ( ( peer_cert = SSL_get_peer_certificate ( xfd - > para . openssl . ssl ) ) ! = NULL ) {
Debug ( " peer certificate: " ) ;
if ( ( str = X509_NAME_oneline ( X509_get_subject_name ( peer_cert ) , buff , sizeof ( buff ) ) ) ! = NULL )
Debug1 ( " \t subject: %s " , str ) ; /*free (str); SIGSEGV*/
if ( ( str = X509_NAME_oneline ( X509_get_issuer_name ( peer_cert ) , buff , sizeof ( buff ) ) ) ! = NULL )
Debug1 ( " \t issuer: %s " , str ) ; /*free (str); SIGSEGV*/
}
if ( peer_cert ) {
if ( opt_ver ) {
long verify_result ;
if ( ( verify_result = sycSSL_get_verify_result ( xfd - > para . openssl . ssl ) ) = = X509_V_OK ) {
Info ( " accepted peer certificate " ) ;
status = STAT_OK ;
} else {
const char * message = NULL ;
if ( verify_result > = 0 & &
( size_t ) verify_result <
sizeof ( openssl_verify_messages ) / sizeof ( char * ) )
{
message = openssl_verify_messages [ verify_result ] ;
}
if ( message ) {
Msg1 ( level , " %s " , message ) ;
} else {
Msg1 ( level , " rejected peer certificate with error %ld " , verify_result ) ;
}
status = STAT_RETRYLATER ;
}
} else {
Notice ( " no check of certificate " ) ;
status = STAT_OK ;
}
} else {
if ( opt_ver ) {
Msg ( level , " no peer certificate " ) ;
status = STAT_RETRYLATER ;
} else {
Notice ( " no peer certificate and no check " ) ;
status = STAT_OK ;
}
}
X509_free ( peer_cert ) ;
return status ;
}
static int xioSSL_set_fd ( struct single * xfd , int level ) {
unsigned long err ;
/* assign a network connection to the SSL object */
2009-04-03 09:30:01 +00:00
if ( xfd - > rfd = = xfd - > wfd ) {
if ( sycSSL_set_fd ( xfd - > para . openssl . ssl , xfd - > rfd ) < = 0 ) {
2008-01-27 12:00:08 +00:00
Msg ( level , " SSL_set_fd() failed " ) ;
while ( err = ERR_get_error ( ) ) {
Msg2 ( level , " SSL_set_fd(, %d): %s " ,
2009-04-03 09:30:01 +00:00
xfd - > wfd , ERR_error_string ( err , NULL ) ) ;
2008-01-27 12:00:08 +00:00
}
return STAT_RETRYLATER ;
}
2008-02-17 13:59:16 +00:00
} else {
2009-04-03 09:30:01 +00:00
if ( xfd - > rfd > = 0 ) {
if ( sycSSL_set_rfd ( xfd - > para . openssl . ssl , xfd - > rfd ) < = 0 ) {
Msg ( level , " SSL_set_rfd() failed " ) ;
while ( err = ERR_get_error ( ) ) {
Msg2 ( level , " SSL_set_rfd(, %d): %s " ,
xfd - > rfd , ERR_error_string ( err , NULL ) ) ;
}
return STAT_RETRYLATER ;
2008-02-17 13:59:16 +00:00
}
}
2009-04-03 09:30:01 +00:00
if ( xfd - > wfd > = 0 ) {
if ( sycSSL_set_wfd ( xfd - > para . openssl . ssl , xfd - > wfd ) < = 0 ) {
Msg ( level , " SSL_set_wfd() failed " ) ;
while ( err = ERR_get_error ( ) ) {
Msg2 ( level , " SSL_set_wfd(, %d): %s " ,
xfd - > wfd , ERR_error_string ( err , NULL ) ) ;
}
return STAT_RETRYLATER ;
2008-02-17 13:59:16 +00:00
}
}
2009-04-03 09:30:01 +00:00
}
2008-01-27 12:00:08 +00:00
return STAT_OK ;
}
/* ...
in case of an error condition , this function check forever and retry
options and ev . sleeps an interval . It returns NORETRY when the caller
should not retry for any reason . */
static int xioSSL_connect ( struct single * xfd , bool opt_ver , int level ) {
char error_string [ 120 ] ;
int errint , status , ret ;
unsigned long err ;
/* connect via SSL by performing handshake */
if ( ( ret = sycSSL_connect ( xfd - > para . openssl . ssl ) ) < = 0 ) {
/*if (ERR_peek_error() == 0) Msg(level, "SSL_connect() failed");*/
errint = SSL_get_error ( xfd - > para . openssl . ssl , ret ) ;
switch ( errint ) {
case SSL_ERROR_NONE :
/* this is not an error, but I dare not continue for security reasons*/
Msg ( level , " ok " ) ;
status = STAT_RETRYLATER ;
case SSL_ERROR_ZERO_RETURN :
Msg ( level , " connection closed (wrong version number?) " ) ;
status = STAT_RETRYLATER ;
break ;
case SSL_ERROR_WANT_READ :
case SSL_ERROR_WANT_WRITE :
case SSL_ERROR_WANT_CONNECT :
case SSL_ERROR_WANT_X509_LOOKUP :
Msg ( level , " nonblocking operation did not complete " ) ;
status = STAT_RETRYLATER ;
break ; /*!*/
case SSL_ERROR_SYSCALL :
if ( ERR_peek_error ( ) = = 0 ) {
if ( ret = = 0 ) {
Msg ( level , " SSL_connect(): socket closed by peer " ) ;
} else if ( ret = = - 1 ) {
Msg1 ( level , " SSL_connect(): %s " , strerror ( errno ) ) ;
}
} else {
Msg ( level , " I/O error " ) ; /*!*/
while ( err = ERR_get_error ( ) ) {
ERR_error_string_n ( err , error_string , sizeof ( error_string ) ) ;
Msg4 ( level , " SSL_connect(): %s / %s / %s / %s " , error_string ,
ERR_lib_error_string ( err ) , ERR_func_error_string ( err ) ,
ERR_reason_error_string ( err ) ) ;
}
}
status = STAT_RETRYLATER ;
break ;
case SSL_ERROR_SSL :
status = openssl_SSL_ERROR_SSL ( level , " SSL_connect " ) ;
if ( openssl_handle_peer_certificate ( xfd , opt_ver , level /*!*/ ) < 0 ) {
return STAT_RETRYLATER ;
}
break ;
default :
Msg ( level , " unknown error " ) ;
status = STAT_RETRYLATER ;
break ;
}
return status ;
}
return STAT_OK ;
}
/* on result < 0: errno is set (at least to EIO) */
ssize_t xioread_openssl ( struct single * pipe , void * buff , size_t bufsiz ) {
unsigned long err ;
char error_string [ 120 ] ;
int _errno = EIO ; /* if we have no better idea about nature of error */
int errint , ret ;
ret = sycSSL_read ( pipe - > para . openssl . ssl , buff , bufsiz ) ;
if ( ret < 0 ) {
errint = SSL_get_error ( pipe - > para . openssl . ssl , ret ) ;
switch ( errint ) {
case SSL_ERROR_NONE :
/* this is not an error, but I dare not continue for security reasons*/
Error ( " ok " ) ;
case SSL_ERROR_ZERO_RETURN :
Error ( " connection closed by peer " ) ;
break ;
case SSL_ERROR_WANT_READ :
case SSL_ERROR_WANT_WRITE :
case SSL_ERROR_WANT_CONNECT :
case SSL_ERROR_WANT_X509_LOOKUP :
2012-07-22 06:29:33 +00:00
Info ( " nonblocking operation did not complete " ) ;
errno = EAGAIN ;
return - 1 ;
2008-01-27 12:00:08 +00:00
case SSL_ERROR_SYSCALL :
if ( ERR_peek_error ( ) = = 0 ) {
if ( ret = = 0 ) {
Error ( " SSL_read(): socket closed by peer " ) ;
} else if ( ret = = - 1 ) {
_errno = errno ;
Error1 ( " SSL_read(): %s " , strerror ( errno ) ) ;
}
} else {
Error ( " I/O error " ) ; /*!*/
while ( err = ERR_get_error ( ) ) {
ERR_error_string_n ( err , error_string , sizeof ( error_string ) ) ;
Error4 ( " SSL_read(): %s / %s / %s / %s " , error_string ,
ERR_lib_error_string ( err ) , ERR_func_error_string ( err ) ,
ERR_reason_error_string ( err ) ) ;
}
}
break ;
case SSL_ERROR_SSL :
openssl_SSL_ERROR_SSL ( E_ERROR , " SSL_connect " ) ;
break ;
default :
Error ( " unknown error " ) ;
break ;
}
errno = _errno ;
return - 1 ;
}
return ret ;
}
ssize_t xiopending_openssl ( struct single * pipe ) {
int bytes = sycSSL_pending ( pipe - > para . openssl . ssl ) ;
return bytes ;
}
/* on result < 0: errno is set (at least to EIO) */
ssize_t xiowrite_openssl ( struct single * pipe , const void * buff , size_t bufsiz ) {
unsigned long err ;
char error_string [ 120 ] ;
int _errno = EIO ; /* if we have no better idea about nature of error */
int errint , ret ;
ret = sycSSL_write ( pipe - > para . openssl . ssl , buff , bufsiz ) ;
if ( ret < 0 ) {
errint = SSL_get_error ( pipe - > para . openssl . ssl , ret ) ;
switch ( errint ) {
case SSL_ERROR_NONE :
/* this is not an error, but I dare not continue for security reasons*/
Error ( " ok " ) ;
case SSL_ERROR_ZERO_RETURN :
Error ( " connection closed by peer " ) ;
break ;
case SSL_ERROR_WANT_READ :
case SSL_ERROR_WANT_WRITE :
case SSL_ERROR_WANT_CONNECT :
case SSL_ERROR_WANT_X509_LOOKUP :
Error ( " nonblocking operation did not complete " ) ;
break ; /*!*/
case SSL_ERROR_SYSCALL :
if ( ERR_peek_error ( ) = = 0 ) {
if ( ret = = 0 ) {
Error ( " SSL_write(): socket closed by peer " ) ;
} else if ( ret = = - 1 ) {
_errno = errno ;
Error1 ( " SSL_write(): %s " , strerror ( errno ) ) ;
}
} else {
Error ( " I/O error " ) ; /*!*/
while ( err = ERR_get_error ( ) ) {
ERR_error_string_n ( err , error_string , sizeof ( error_string ) ) ;
Error4 ( " SSL_write(): %s / %s / %s / %s " , error_string ,
ERR_lib_error_string ( err ) , ERR_func_error_string ( err ) ,
ERR_reason_error_string ( err ) ) ;
}
}
break ;
case SSL_ERROR_SSL :
openssl_SSL_ERROR_SSL ( E_ERROR , " SSL_connect " ) ;
break ;
default :
Error ( " unknown error " ) ;
break ;
}
errno = _errno ;
return - 1 ;
}
return ret ;
}
# endif /* WITH_OPENSSL */