/*
* Copyright 2007 Stephen Liu
* For license terms, see the file COPYING along with this library.
*/
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include "spopenssl.hpp"
#include "spsession.hpp"
#include "spbuffer.hpp"
#include "speventcb.hpp"
#include "sputils.hpp"
#include "spmsgblock.hpp"
#include "spioutils.hpp"
SP_OpensslChannel :: SP_OpensslChannel( SSL_CTX * ctx )
{
mCtx = ctx;
mSsl = NULL;
}
SP_OpensslChannel :: ~SP_OpensslChannel()
{
if( NULL != mSsl ) SSL_free( mSsl );
mSsl = NULL;
}
int SP_OpensslChannel :: init( int fd )
{
char errmsg[ 256 ] = { 0 };
mSsl = SSL_new( mCtx );
SSL_set_fd( mSsl, fd );
/* we run in an independence thread, and we can block when SSL_accept */
SP_IOUtils::setBlock( fd );
int ret = SSL_accept( mSsl );
if( ret <= 0 ) {
ERR_error_string_n( SSL_get_error( mSsl, ret ), errmsg, sizeof( errmsg ) );
syslog( LOG_EMERG, "SSL_accept fail, %s", errmsg );
return -1;
}
SP_IOUtils::setNonblock( fd );
/* Get the cipher - opt */
syslog( LOG_NOTICE, "SSL connection using %s", SSL_get_cipher( mSsl ) );
/* Get client's certificate (note: beware of dynamic allocation) - opt */
X509 * client_cert = SSL_get_peer_certificate( mSsl );
if( client_cert != NULL ) {
syslog( LOG_NOTICE, "Client certificate:" );
char * str = X509_NAME_oneline( X509_get_subject_name( client_cert ), 0, 0 );
if( NULL != str ) {
syslog( LOG_NOTICE, "subject: %s", str );
OPENSSL_free( str );
}
str = X509_NAME_oneline( X509_get_issuer_name( client_cert ), 0, 0 );
if( NULL != str ) {
syslog( LOG_NOTICE, "issuer: %s", str );
OPENSSL_free( str );
}
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
X509_free( client_cert );
} else {
syslog( LOG_WARNING, "Client does not have certificate" );
}
return 0;
}
int SP_OpensslChannel :: receive( SP_Session * session )
{
char buffer[ 4096 ] = { 0 };
int ret = SSL_read( mSsl, buffer, sizeof( buffer ) );
if( ret > 0 ) {
session->getInBuffer()->append( buffer, ret );
} else if( ret < 0 ) {
ERR_error_string_n( ERR_get_error(), buffer, sizeof( buffer ) );
syslog( LOG_EMERG, "SSL_read fail, %s", buffer );
}
return ret;
}
int SP_OpensslChannel :: write_vec( struct iovec * iovArray, int iovSize )
{
int len = 0;
for( int i = 0; i < iovSize; i++ ) {
int ret = SSL_write( mSsl, iovArray[i].iov_base, iovArray[i].iov_len );
if( ret > 0 ) len += ret;
if( ret != (int)iovArray[i].iov_len ) break;
}
return len;
}
//---------------------------------------------------------
SP_OpensslChannelFactory :: SP_OpensslChannelFactory()
{
mCtx = NULL;
}
SP_OpensslChannelFactory :: ~SP_OpensslChannelFactory()
{
if( NULL != mCtx ) SSL_CTX_free( mCtx );
mCtx = NULL;
}
SP_IOChannel * SP_OpensslChannelFactory :: create() const
{
return new SP_OpensslChannel( mCtx );
}
int SP_OpensslChannelFactory :: init( const char * certFile, const char * keyFile )
{
unsigned char strRand[ 32 ] ;
memset( strRand, 0, sizeof( strRand ) );
snprintf( (char*)strRand, sizeof( strRand ), "%d%ld", getpid(), time(NULL) );
RAND_seed( strRand, sizeof( strRand ) );
RAND_load_file( "/dev/urandom", 256 );
int ret = 0;
char errmsg[ 256 ] = { 0 };
ERR_load_crypto_strings ();
SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
mCtx = SSL_CTX_new( SSLv23_server_method() );
if( ! mCtx ) {
ERR_error_string_n( ERR_get_error(), errmsg, sizeof( errmsg ) );
syslog( LOG_WARNING, "SSL_CTX_new fail, %s", errmsg );
ret = -1;
}
if( 0 == ret ) {
if( SSL_CTX_use_certificate_file( mCtx, certFile, SSL_FILETYPE_PEM ) <= 0 ) {
ERR_error_string_n( ERR_get_error(), errmsg, sizeof( errmsg ) );
syslog( LOG_WARNING, "SSL_CTX_use_certificate_file fail, %s", errmsg );
ret = -1;
}
}
if( 0 == ret ) {
if( SSL_CTX_use_PrivateKey_file( mCtx, keyFile, SSL_FILETYPE_PEM ) <= 0 ) {
ERR_error_string_n( ERR_get_error(), errmsg, sizeof( errmsg ) );
syslog( LOG_WARNING, "SSL_CTX_use_PrivateKey_file fail, %s", errmsg );
ret = -1;
}
}
if( 0 == ret ) {
if( !SSL_CTX_check_private_key( mCtx ) ) {
ERR_error_string_n( ERR_get_error(), errmsg, sizeof( errmsg ) );
syslog( LOG_WARNING, "Private key does not match the certificate public key, %s", errmsg );
ret = -1;
}
}
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1