SSLClient.cc

Go to the documentation of this file.
00001 // SSLClient.cc
00002 
00003 // This file is part of bes, A C++ back-end server implementation framework
00004 // for the OPeNDAP Data Access Protocol.
00005 
00006 // Copyright (c) 2004,2005 University Corporation for Atmospheric Research
00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 // 
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 // 
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact University Corporation for Atmospheric Research at
00024 // 3080 Center Green Drive, Boulder, CO 80301
00025  
00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
00028 //
00029 // Authors:
00030 //      pwest       Patrick West <pwest@ucar.edu>
00031 //      jgarcia     Jose Garcia <jgarcia@ucar.edu>
00032 
00033 #include "config.h"
00034 
00035 #include <openssl/ssl.h>
00036 #include <openssl/err.h>
00037 #include <sys/socket.h> // for socket connect
00038 #include <netinet/in.h> // for inet_addr
00039 #include <arpa/inet.h>  // for htons
00040 #include <netdb.h>      // for gethostbyname
00041 #include <ctype.h>      // for isdigit
00042 
00043 #include <cstring>
00044 #include <iostream>
00045 #ifdef HAVE_UNISTD_H
00046 #include <unistd.h>
00047 #endif
00048 
00049 using std::endl ;
00050 
00051 #include "SSLClient.h"
00052 #include "BESInternalError.h"
00053 #include "BESDebug.h"
00054 
00055 SSLClient::SSLClient( const string &hostStr, int portVal,
00056                       const string &cert_file, const string &key_file )
00057     : SSLConnection(),
00058       _host( hostStr ),
00059       _port( portVal ),
00060       _cfile( cert_file ),
00061       _kfile( key_file )
00062 {
00063 }
00064     
00065 SSLClient::~SSLClient()
00066 {
00067 }
00068 
00069 void
00070 SSLClient::initConnection()
00071 {
00072     BESDEBUG( "ppt", "Loading SSL error strings ... " )
00073     SSL_load_error_strings() ;
00074     BESDEBUG( "ppt", "OK" << endl )
00075 
00076     BESDEBUG( "ppt", "Initializing SSL library ... " )
00077     SSL_library_init() ;
00078     BESDEBUG( "ppt", "OK" << endl )
00079 
00080     SSL_METHOD *method = NULL ;
00081     SSL_CTX *context = NULL ;
00082 
00083     BESDEBUG( "ppt", "Creating method and context ... " )
00084     method = SSLv3_client_method() ;
00085     if( method )
00086     {
00087         context = SSL_CTX_new( method ) ;
00088     }
00089     if( !context )
00090     {
00091         string msg = "Failed to create SSL context\n" ;
00092         msg += ERR_error_string( ERR_get_error(), NULL ) ;
00093         throw BESInternalError( msg, __FILE__, __LINE__ ) ;
00094     }
00095     else
00096     {
00097         BESDEBUG( "ppt", "OK" << endl )
00098     }
00099 
00100     bool ok_2_continue = false ;
00101     string err_msg ;
00102     BESDEBUG( "ppt", "Setting certificate and key ... " )
00103     if( SSL_CTX_use_certificate_file( context, _cfile.c_str(), SSL_FILETYPE_PEM ) <= 0 )
00104     {
00105         err_msg = "FAILED to use certificate file " + _cfile + "\n" ;
00106         err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00107     }
00108     else if( SSL_CTX_use_PrivateKey_file( context, _kfile.c_str(), SSL_FILETYPE_PEM ) <= 0 )
00109     {
00110         err_msg = "FAILED to use private key file " + _kfile + "\n" ;
00111         err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00112     }
00113     else if( !SSL_CTX_check_private_key( context ) )
00114     {
00115         err_msg = "FAILED to authenticate private key\n" ;
00116         err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00117     }
00118     else
00119     {
00120         ok_2_continue = true ;
00121     }
00122 
00123     if( ok_2_continue )
00124     {
00125         BESDEBUG( "ppt", "OK" << endl )
00126         BESDEBUG( "ppt", "Certificate setup ... " )
00127         SSL_CTX_set_verify( context, SSL_VERIFY_PEER, SSLClient::verify_server ) ;
00128         SSL_CTX_set_client_CA_list( context, SSL_load_client_CA_file( _cfile.c_str() ));
00129         if( ( !SSL_CTX_load_verify_locations( context, _cfile.c_str(), NULL )) ||
00130             ( !SSL_CTX_set_default_verify_paths( context ) ) )
00131         {
00132             err_msg = "Certificate setup failed\n" ;
00133             err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00134             ok_2_continue = false ;
00135         }
00136     }
00137 
00138     int sock_fd = -1 ;
00139     if( ok_2_continue )
00140     {
00141         BESDEBUG( "ppt", "OK" << endl )
00142 
00143         BESDEBUG( "ppt", "Establishing TCP connection to " << _host << ":" << _port << " ... " )
00144         sock_fd = connect_to_server() ;
00145         if( sock_fd < 0 )
00146         {
00147             err_msg = "Failed to establish TCP connection" ;
00148             ok_2_continue = false ;
00149         }
00150     }
00151 
00152     if( ok_2_continue )
00153     {
00154         BESDEBUG( "ppt", "OK" << endl )
00155 
00156         BESDEBUG( "ppt", "Establishing secure connection ... " )
00157         int ssl_ret = 0 ;
00158         _connection = SSL_new( context ) ;
00159         if( !_connection )
00160         {
00161             err_msg =  "FAILED to create new connection\n" ;
00162             err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00163             ok_2_continue = false ;
00164         }
00165         else if( ( ssl_ret = SSL_set_fd( _connection, sock_fd ) )  < 0 )
00166         {
00167             err_msg = "FAILED to set the socket descriptor\n" ;
00168             err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00169             ok_2_continue = false ;
00170         }
00171         else if( ( ssl_ret = SSL_connect( _connection ) ) < 0 )
00172         {
00173             err_msg = "FAILED to create SSL connection\n" ;
00174             err_msg += ERR_error_string( SSL_get_error( _connection, ssl_ret ), NULL ) ;
00175             ok_2_continue = false ;
00176         }
00177         else if( verify_connection() < 0 )
00178         {
00179             err_msg = "FAILED to verify SSL connection\n" ;
00180             err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00181             ok_2_continue = false ;
00182         }
00183     }
00184 
00185     if( ok_2_continue )
00186     {
00187         BESDEBUG( "ppt", "OK" << endl )
00188     }
00189     else
00190     {
00191         BESDEBUG( "ppt", "FAILED" << endl )
00192         if( _context ) SSL_CTX_free( _context ) ; _context = NULL ;
00193         throw BESInternalError( err_msg, __FILE__, __LINE__ ) ;
00194     }
00195 
00196     _connected = true ;
00197 }
00198 
00199 int
00200 SSLClient::connect_to_server( )
00201 {
00202     int fd = -1 ;
00203     struct sockaddr_in addr ;
00204 
00205     fd = socket( PF_INET, SOCK_STREAM, 0 ) ;
00206     if( fd < 0 ) return -1 ;
00207 
00208     memset( &addr, 0, sizeof( addr ) ) ;
00209     addr.sin_family = AF_INET ;
00210     addr.sin_port = htons( _port ) ;
00211     if( isdigit( (int)*_host.c_str() ) )
00212     {
00213         addr.sin_addr.s_addr = inet_addr( _host.c_str() ) ;
00214     }
00215     else
00216     {
00217         struct hostent *hostEntry ;
00218         if( ( hostEntry = gethostbyname( _host.c_str() ) ) != 0 )
00219         {
00220             memcpy( &addr.sin_addr, hostEntry->h_addr, hostEntry->h_length ) ;
00221         }
00222         else
00223         {
00224             close( fd ) ;
00225             return -1 ;
00226         }
00227     }
00228     if( connect( fd, (struct sockaddr *)&addr, sizeof( addr ) ) < 0 )
00229     {
00230         close( fd ) ;
00231         return -1 ;
00232     }
00233 
00234     return fd ;
00235 }
00236 
00237 int
00238 SSLClient::verify_connection( )
00239 {
00240     X509 *server_cert = NULL ;
00241     char *str = NULL ;
00242 
00243     /*
00244     server_cert = SSL_get_peer_certificate( _connection ) ;
00245     if( server_cert == NULL )
00246     {
00247         cout << "server doesn't have a certificate" << endl ;
00248     }
00249     */
00250 
00251     return 1 ;
00252 }
00253 
00254 int
00255 SSLClient::verify_server( int ok, X509_STORE_CTX *ctx )
00256 {
00257     if( ok )
00258     {
00259         BESDEBUG( "ppt", "VERIFIED " )
00260     }
00261     else
00262     {
00263         char mybuf[256] ;
00264         X509 *err_cert ;
00265         int err ;
00266 
00267         err_cert = X509_STORE_CTX_get_current_cert( ctx ) ;
00268         err = X509_STORE_CTX_get_error( ctx ) ;
00269         X509_NAME_oneline( X509_get_subject_name( err_cert ), mybuf, 256 ) ;
00270         BESDEBUG( "ppt", "FAILED for " << mybuf << endl )
00271         BESDEBUG( "ppt", "  " << X509_verify_cert_error_string( err ) << endl )
00272         switch( ctx->error )
00273         {
00274             case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
00275             {
00276                 X509_NAME_oneline( X509_get_issuer_name( err_cert ), mybuf, 256 ) ;
00277                 BESDEBUG( "ppt", "  issuer = " << mybuf << endl )
00278                 break ;
00279             }
00280 
00281             case X509_V_ERR_CERT_NOT_YET_VALID:
00282             case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
00283             {
00284                 BESDEBUG( "ppt", "  not yet valid!" << endl )
00285                 break ;
00286             }
00287 
00288             case X509_V_ERR_CERT_HAS_EXPIRED:
00289             case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
00290             {
00291                 BESDEBUG( "ppt", "  expired!" << endl )
00292                 break ;
00293             }
00294         }
00295     }
00296 
00297     return 1 ;
00298 }
00299 
00306 void
00307 SSLClient::dump( ostream &strm ) const
00308 {
00309     strm << BESIndent::LMarg << "SSLClient::dump - ("
00310                              << (void *)this << ")" << endl ;
00311     BESIndent::Indent() ;
00312     strm << BESIndent::LMarg << "host: " << _host << endl ;
00313     strm << BESIndent::LMarg << "port: " << _port << endl ;
00314     strm << BESIndent::LMarg << "certificate file: " << _cfile << endl ;
00315     strm << BESIndent::LMarg << "key file: " << _kfile << endl ;
00316     SSLConnection::dump( strm ) ;
00317     BESIndent::UnIndent() ;
00318 }
00319 

Generated on Tue Mar 4 23:13:37 2008 for OPeNDAP Back End Server (BES) by  doxygen 1.5.1