gloox
1.0
|
00001 /* 00002 Copyright (c) 2009 by Jakob Schroeter <js@camaya.net> 00003 This file is part of the gloox library. http://camaya.net/gloox 00004 00005 This software is distributed under a license. The full license 00006 agreement can be found in the file LICENSE in this distribution. 00007 This software may not be copied, modified, sold or distributed 00008 other than expressed in the named license agreement. 00009 00010 This software is distributed without any warranty. 00011 */ 00012 00013 00014 00015 #include "tlsopensslbase.h" 00016 00017 #ifdef HAVE_OPENSSL 00018 00019 #include <algorithm> 00020 #include <cctype> 00021 #include <ctime> 00022 #include <cstdlib> 00023 00024 #include <openssl/err.h> 00025 00026 namespace gloox 00027 { 00028 00029 OpenSSLBase::OpenSSLBase( TLSHandler* th, const std::string& server ) 00030 : TLSBase( th, server ), m_ssl( 0 ), m_ctx( 0 ), m_buf( 0 ), m_bufsize( 17000 ) 00031 { 00032 m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) ); 00033 } 00034 00035 OpenSSLBase::~OpenSSLBase() 00036 { 00037 m_handler = 0; 00038 free( m_buf ); 00039 SSL_CTX_free( m_ctx ); 00040 SSL_shutdown( m_ssl ); 00041 SSL_free( m_ssl ); 00042 BIO_free( m_nbio ); 00043 cleanup(); 00044 } 00045 00046 bool OpenSSLBase::init( const std::string& clientKey, 00047 const std::string& clientCerts, 00048 const StringList& cacerts ) 00049 { 00050 if( m_initLib ) 00051 SSL_library_init(); 00052 00053 SSL_COMP_add_compression_method( 193, COMP_zlib() ); 00054 00055 OpenSSL_add_all_algorithms(); 00056 00057 if( !setType() ) //inits m_ctx 00058 return false; 00059 00060 setClientCert( clientKey, clientCerts ); 00061 setCACerts( cacerts ); 00062 00063 if( !SSL_CTX_set_cipher_list( m_ctx, "HIGH:MEDIUM:AES:@STRENGTH" ) ) 00064 return false; 00065 00066 m_ssl = SSL_new( m_ctx ); 00067 if( !m_ssl ) 00068 return false; 00069 00070 if( !BIO_new_bio_pair( &m_ibio, 0, &m_nbio, 0 ) ) 00071 return false; 00072 00073 SSL_set_bio( m_ssl, m_ibio, m_ibio ); 00074 SSL_set_mode( m_ssl, SSL_MODE_AUTO_RETRY | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE ); 00075 00076 ERR_load_crypto_strings(); 00077 SSL_load_error_strings(); 00078 00079 if( !privateInit() ) 00080 return false; 00081 00082 m_valid = true; 00083 return true; 00084 } 00085 00086 bool OpenSSLBase::encrypt( const std::string& data ) 00087 { 00088 m_sendBuffer += data; 00089 00090 if( !m_secure ) 00091 { 00092 handshake(); 00093 return 0; 00094 } 00095 00096 doTLSOperation( TLSWrite ); 00097 return true; 00098 } 00099 00100 int OpenSSLBase::decrypt( const std::string& data ) 00101 { 00102 m_recvBuffer += data; 00103 00104 if( !m_secure ) 00105 { 00106 handshake(); 00107 return 0; 00108 } 00109 00110 doTLSOperation( TLSRead ); 00111 return true; 00112 } 00113 00114 void OpenSSLBase::setCACerts( const StringList& cacerts ) 00115 { 00116 m_cacerts = cacerts; 00117 00118 StringList::const_iterator it = m_cacerts.begin(); 00119 for( ; it != m_cacerts.end(); ++it ) 00120 SSL_CTX_load_verify_locations( m_ctx, (*it).c_str(), 0 ); 00121 } 00122 00123 void OpenSSLBase::setClientCert( const std::string& clientKey, const std::string& clientCerts ) 00124 { 00125 m_clientKey = clientKey; 00126 m_clientCerts = clientCerts; 00127 00128 if( !m_clientKey.empty() && !m_clientCerts.empty() ) 00129 { 00130 if( SSL_CTX_use_certificate_chain_file( m_ctx, m_clientCerts.c_str() ) != 1 ) 00131 { 00132 // FIXME 00133 } 00134 if( SSL_CTX_use_RSAPrivateKey_file( m_ctx, m_clientKey.c_str(), SSL_FILETYPE_PEM ) != 1 ) 00135 { 00136 // FIXME 00137 } 00138 } 00139 00140 if ( SSL_CTX_check_private_key( m_ctx ) != 1 ) 00141 { 00142 // FIXME 00143 } 00144 } 00145 00146 void OpenSSLBase::cleanup() 00147 { 00148 if( !m_mutex.trylock() ) 00149 return; 00150 00151 m_secure = false; 00152 m_valid = false; 00153 00154 m_mutex.unlock(); 00155 } 00156 00157 void OpenSSLBase::doTLSOperation( TLSOperation op ) 00158 { 00159 if( !m_handler ) 00160 return; 00161 00162 int ret = 0; 00163 bool onceAgain = false; 00164 00165 do 00166 { 00167 switch( op ) 00168 { 00169 case TLSHandshake: 00170 ret = handshakeFunction(); 00171 break; 00172 case TLSWrite: 00173 ret = SSL_write( m_ssl, m_sendBuffer.c_str(), m_sendBuffer.length() ); 00174 break; 00175 case TLSRead: 00176 ret = SSL_read( m_ssl, m_buf, m_bufsize ); 00177 break; 00178 } 00179 00180 switch( SSL_get_error( m_ssl, ret ) ) 00181 { 00182 case SSL_ERROR_WANT_READ: 00183 case SSL_ERROR_WANT_WRITE: 00184 pushFunc(); 00185 break; 00186 case SSL_ERROR_NONE: 00187 if( op == TLSHandshake ) 00188 m_secure = true; 00189 else if( op == TLSWrite ) 00190 m_sendBuffer.erase( 0, ret ); 00191 else if( op == TLSRead ) 00192 m_handler->handleDecryptedData( this, std::string( m_buf, ret ) ); 00193 pushFunc(); 00194 break; 00195 default: 00196 if( !m_secure ) 00197 m_handler->handleHandshakeResult( this, false, m_certInfo ); 00198 return; 00199 break; 00200 } 00201 if( !onceAgain && !m_recvBuffer.length() ) 00202 onceAgain = true; 00203 else if( onceAgain ) 00204 onceAgain = false; 00205 } 00206 while( ( ( onceAgain || m_recvBuffer.length() ) && ( !m_secure || op == TLSRead ) ) 00207 || ( ( op == TLSWrite ) && ( ret > 0 ) )); 00208 } 00209 00210 int OpenSSLBase::openSSLTime2UnixTime( const char* time_string ) 00211 { 00212 char tstring[19]; 00213 00214 // making seperate c string out of time string 00215 int m = 0; 00216 for( int n = 0; n < 12; n += 2 ) 00217 { 00218 tstring[m] = time_string[n]; 00219 tstring[m + 1] = time_string[n + 1]; 00220 tstring[m + 2] = 0; 00221 m += 3; 00222 } 00223 00224 // converting to struct tm 00225 tm time_st; 00226 time_st.tm_year = ( atoi( &tstring[3 * 0] ) >= 70 ) ? atoi( &tstring[3 * 0] ) 00227 : atoi( &tstring[3 * 0] ) + 100; 00228 time_st.tm_mon = atoi( &tstring[3 * 1] ) - 1; 00229 time_st.tm_mday = atoi( &tstring[3 * 2] ); 00230 time_st.tm_hour = atoi( &tstring[3 * 3] ); 00231 time_st.tm_min = atoi( &tstring[3 * 4] ); 00232 time_st.tm_sec = atoi( &tstring[3 * 5] ); 00233 00234 time_t unixt = mktime( &time_st ); 00235 return unixt; 00236 } 00237 00238 bool OpenSSLBase::handshake() 00239 { 00240 00241 doTLSOperation( TLSHandshake ); 00242 00243 if( !m_secure ) 00244 return true; 00245 00246 int res = SSL_get_verify_result( m_ssl ); 00247 if( res != X509_V_OK ) 00248 m_certInfo.status = CertInvalid; 00249 else 00250 m_certInfo.status = CertOk; 00251 00252 X509* peer = SSL_get_peer_certificate( m_ssl ); 00253 if( peer ) 00254 { 00255 char peer_CN[256]; 00256 X509_NAME_get_text_by_NID( X509_get_issuer_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) ); 00257 m_certInfo.issuer = peer_CN; 00258 X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) ); 00259 m_certInfo.server = peer_CN; 00260 m_certInfo.date_from = openSSLTime2UnixTime( (char*) (peer->cert_info->validity->notBefore->data) ); 00261 m_certInfo.date_to = openSSLTime2UnixTime( (char*) (peer->cert_info->validity->notAfter->data) ); 00262 std::string p( peer_CN ); 00263 std::transform( p.begin(), p.end(), p.begin(), tolower ); 00264 if( p != m_server ) 00265 m_certInfo.status |= CertWrongPeer; 00266 00267 if( ASN1_UTCTIME_cmp_time_t( X509_get_notBefore( peer ), time( 0 ) ) != -1 ) 00268 m_certInfo.status |= CertNotActive; 00269 00270 if( ASN1_UTCTIME_cmp_time_t( X509_get_notAfter( peer ), time( 0 ) ) != 1 ) 00271 m_certInfo.status |= CertExpired; 00272 } 00273 else 00274 { 00275 m_certInfo.status = CertInvalid; 00276 } 00277 00278 const char* tmp; 00279 tmp = SSL_get_cipher_name( m_ssl ); 00280 if( tmp ) 00281 m_certInfo.cipher = tmp; 00282 00283 tmp = SSL_get_cipher_version( m_ssl ); 00284 if( tmp ) 00285 m_certInfo.protocol = tmp; 00286 00287 tmp = SSL_COMP_get_name( SSL_get_current_compression( m_ssl ) ); 00288 if( tmp ) 00289 m_certInfo.compression = tmp; 00290 00291 m_valid = true; 00292 00293 m_handler->handleHandshakeResult( this, true, m_certInfo ); 00294 return true; 00295 } 00296 00297 void OpenSSLBase::pushFunc() 00298 { 00299 int wantwrite; 00300 size_t wantread; 00301 int frombio; 00302 int tobio; 00303 00304 while( ( wantwrite = BIO_ctrl_pending( m_nbio ) ) > 0 ) 00305 { 00306 if( wantwrite > m_bufsize ) 00307 wantwrite = m_bufsize; 00308 00309 if( !wantwrite ) 00310 break; 00311 00312 frombio = BIO_read( m_nbio, m_buf, wantwrite ); 00313 00314 if( m_handler ) 00315 m_handler->handleEncryptedData( this, std::string( m_buf, frombio ) ); 00316 } 00317 00318 while( ( wantread = BIO_ctrl_get_read_request( m_nbio ) ) > 0 ) 00319 { 00320 if( wantread > m_recvBuffer.length() ) 00321 wantread = m_recvBuffer.length(); 00322 00323 if( !wantread ) 00324 break; 00325 00326 tobio = BIO_write( m_nbio, m_recvBuffer.c_str(), wantread ); 00327 m_recvBuffer.erase( 0, tobio ); 00328 } 00329 } 00330 00331 } 00332 00333 #endif // HAVE_OPENSSL