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-2009 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 &cert_auth_file,
00057                       const string &key_file )
00058     : SSLConnection(),
00059       _host( hostStr ),
00060       _port( portVal ),
00061       _cfile( cert_file ),
00062       _cafile( cert_auth_file),
00063       _kfile( key_file )
00064 {
00065 }
00066 
00067 SSLClient::~SSLClient()
00068 {
00069 }
00070 
00071 void
00072 SSLClient::initConnection()
00073 {
00074     BESDEBUG( "ppt", "Loading SSL error strings ... " << endl ) ;
00075     SSL_load_error_strings() ;
00076     BESDEBUG( "ppt", "OK" << endl ) ;
00077 
00078     BESDEBUG( "ppt", "Initializing SSL library ... " << endl ) ;
00079     SSL_library_init() ;
00080     BESDEBUG( "ppt", "OK" << endl ) ;
00081 
00082 #if OPENSSL_VERSION_NUMBER < 0x10000000L
00083     SSL_METHOD *method = NULL ;
00084 #else
00085     const SSL_METHOD *method = NULL ;
00086 #endif
00087     SSL_CTX *context = NULL ;
00088 
00089     BESDEBUG( "ppt", "Creating method and context ... " << endl ) ;
00090     method = SSLv3_client_method() ;
00091     if( method )
00092     {
00093         context = SSL_CTX_new( method ) ;
00094     }
00095     if( !context )
00096     {
00097         string msg = "Failed to create SSL context\n" ;
00098         msg += ERR_error_string( ERR_get_error(), NULL ) ;
00099         throw BESInternalError( msg, __FILE__, __LINE__ ) ;
00100     }
00101     else
00102     {
00103         BESDEBUG( "ppt", "OK" << endl ) ;
00104     }
00105 
00106     bool ok_2_continue = false ;
00107     string err_msg ;
00108     BESDEBUG( "ppt", "Setting certificate and key ... " << endl ) ;
00109     if( SSL_CTX_use_certificate_file( context, _cfile.c_str(), SSL_FILETYPE_PEM ) <= 0 )
00110     {
00111         err_msg = "FAILED to use certificate file " + _cfile + "\n" ;
00112         err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00113     }
00114     else if( SSL_CTX_use_PrivateKey_file( context, _kfile.c_str(), SSL_FILETYPE_PEM ) <= 0 )
00115     {
00116         err_msg = "FAILED to use private key file " + _kfile + "\n" ;
00117         err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00118     }
00119     else if( !SSL_CTX_check_private_key( context ) )
00120     {
00121         err_msg = "FAILED to authenticate private key\n" ;
00122         err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00123     }
00124     else
00125     {
00126         ok_2_continue = true ;
00127     }
00128 
00129     if( ok_2_continue )
00130     {
00131         BESDEBUG( "ppt", "OK" << endl ) ;
00132         BESDEBUG( "ppt", "Certificate setup ... " << endl ) ;
00133         SSL_CTX_set_verify( context, SSL_VERIFY_PEER, SSLClient::verify_server ) ;
00134         SSL_CTX_set_client_CA_list( context, SSL_load_client_CA_file( _cafile.c_str() ));
00135         if( ( !SSL_CTX_load_verify_locations( context, _cafile.c_str(), NULL )) ||
00136             ( !SSL_CTX_set_default_verify_paths( context ) ) )
00137         {
00138             err_msg = "Certificate setup failed\n" ;
00139             err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00140             ok_2_continue = false ;
00141         }
00142     }
00143 
00144     int sock_fd = -1 ;
00145     if( ok_2_continue )
00146     {
00147         BESDEBUG( "ppt", "OK" << endl ) ;
00148 
00149         BESDEBUG( "ppt", "Establishing TCP connection to "
00150                          << _host << ":" << _port << " ... " << endl ) ;
00151         sock_fd = connect_to_server() ;
00152         if( sock_fd < 0 )
00153         {
00154             err_msg = "Failed to establish TCP connection" ;
00155             ok_2_continue = false ;
00156         }
00157     }
00158 
00159     if( ok_2_continue )
00160     {
00161         BESDEBUG( "ppt", "OK" << endl ) ;
00162 
00163         BESDEBUG( "ppt", "Establishing secure connection ... " << endl ) ;
00164         int ssl_ret = 0 ;
00165         _connection = SSL_new( context ) ;
00166         if( !_connection )
00167         {
00168             err_msg =  "FAILED to create new connection\n" ;
00169             err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00170             ok_2_continue = false ;
00171         }
00172         else if( ( ssl_ret = SSL_set_fd( _connection, sock_fd ) )  < 0 )
00173         {
00174             err_msg = "FAILED to set the socket descriptor\n" ;
00175             err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00176             ok_2_continue = false ;
00177         }
00178         else if( ( ssl_ret = SSL_connect( _connection ) ) < 0 )
00179         {
00180             err_msg = "FAILED to create SSL connection\n" ;
00181             err_msg += ERR_error_string( SSL_get_error( _connection, ssl_ret ), NULL ) ;
00182             ok_2_continue = false ;
00183         }
00184         else if( verify_connection() < 0 )
00185         {
00186             err_msg = "FAILED to verify SSL connection\n" ;
00187             err_msg += ERR_error_string( ERR_get_error(), NULL ) ;
00188             ok_2_continue = false ;
00189         }
00190     }
00191 
00192     if( ok_2_continue )
00193     {
00194         BESDEBUG( "ppt", "OK" << endl ) ;
00195     }
00196     else
00197     {
00198         BESDEBUG( "ppt", "FAILED" << endl ) ;
00199         if( _context ) SSL_CTX_free( _context ) ; _context = NULL ;
00200         throw BESInternalError( err_msg, __FILE__, __LINE__ ) ;
00201     }
00202 
00203     _connected = true ;
00204 }
00205 
00206 int
00207 SSLClient::connect_to_server( )
00208 {
00209     int fd = -1 ;
00210     struct sockaddr_in addr ;
00211 
00212     fd = socket( PF_INET, SOCK_STREAM, 0 ) ;
00213     if( fd < 0 ) return -1 ;
00214 
00215     memset( &addr, 0, sizeof( addr ) ) ;
00216     addr.sin_family = AF_INET ;
00217     addr.sin_port = htons( _port ) ;
00218     if( isdigit( (int)*_host.c_str() ) )
00219     {
00220         addr.sin_addr.s_addr = inet_addr( _host.c_str() ) ;
00221     }
00222     else
00223     {
00224         struct hostent *hostEntry ;
00225         if( ( hostEntry = gethostbyname( _host.c_str() ) ) != 0 )
00226         {
00227             if ( hostEntry->h_length > sizeof(addr.sin_addr) )
00228                 throw BESInternalError("Memory exception.", __FILE__, __LINE__);
00229             memcpy( &addr.sin_addr, hostEntry->h_addr, hostEntry->h_length ) ;
00230         }
00231         else
00232         {
00233             close( fd ) ;
00234             return -1 ;
00235         }
00236     }
00237     if( connect( fd, (struct sockaddr *)&addr, sizeof( addr ) ) < 0 )
00238     {
00239         close( fd ) ;
00240         return -1 ;
00241     }
00242 
00243     return fd ;
00244 }
00245 
00246 int
00247 SSLClient::verify_connection( )
00248 {
00249     X509 *server_cert = NULL ;
00250     char *str = NULL ;
00251 
00252     /*
00253     server_cert = SSL_get_peer_certificate( _connection ) ;
00254     if( server_cert == NULL )
00255     {
00256         cout << "server doesn't have a certificate" << endl ;
00257     }
00258     */
00259 
00260     return 1 ;
00261 }
00262 
00263 int
00264 SSLClient::verify_server( int ok, X509_STORE_CTX *ctx )
00265 {
00266     if( ok )
00267     {
00268         BESDEBUG( "ppt", "VERIFIED " << endl ) ;
00269     }
00270     else
00271     {
00272         char mybuf[256] ;
00273         X509 *err_cert ;
00274         int err ;
00275 
00276         err_cert = X509_STORE_CTX_get_current_cert( ctx ) ;
00277         err = X509_STORE_CTX_get_error( ctx ) ;
00278         X509_NAME_oneline( X509_get_subject_name( err_cert ), mybuf, 256 ) ;
00279         BESDEBUG( "ppt", "FAILED for " << mybuf << endl ) ;
00280         BESDEBUG( "ppt", "  " << X509_verify_cert_error_string( err )
00281                          << endl ) ;
00282         switch( ctx->error )
00283         {
00284             case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
00285             {
00286                 X509_NAME_oneline( X509_get_issuer_name( err_cert ), mybuf, 256 ) ;
00287                 BESDEBUG( "ppt", "  issuer = " << mybuf << endl ) ;
00288                 break ;
00289             }
00290 
00291             case X509_V_ERR_CERT_NOT_YET_VALID:
00292             case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
00293             {
00294                 BESDEBUG( "ppt", "  not yet valid!" << endl ) ;
00295                 break ;
00296             }
00297 
00298             case X509_V_ERR_CERT_HAS_EXPIRED:
00299             case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
00300             {
00301                 BESDEBUG( "ppt", "  expired!" << endl ) ;
00302                 break ;
00303             }
00304         }
00305     }
00306 
00307     return 1 ;
00308 }
00309 
00316 void
00317 SSLClient::dump( ostream &strm ) const
00318 {
00319     strm << BESIndent::LMarg << "SSLClient::dump - ("
00320                              << (void *)this << ")" << endl ;
00321     BESIndent::Indent() ;
00322     strm << BESIndent::LMarg << "host: " << _host << endl ;
00323     strm << BESIndent::LMarg << "port: " << _port << endl ;
00324     strm << BESIndent::LMarg << "cert file: " << _cfile << endl ;
00325     strm << BESIndent::LMarg << "cert authority file: " << _cafile << endl ;
00326     strm << BESIndent::LMarg << "key file: " << _kfile << endl ;
00327     SSLConnection::dump( strm ) ;
00328     BESIndent::UnIndent() ;
00329 }
00330 
Generated by  doxygen 1.6.2-20100208