1 /* 2 Copyright (c) 2005-2019 by Jakob Schröter <js@camaya.net> 3 This file is part of the gloox library. http://camaya.net/gloox 4 5 This software is distributed under a license. The full license 6 agreement can be found in the file LICENSE in this distribution. 7 This software may not be copied, modified, sold or distributed 8 other than expressed in the named license agreement. 9 10 This software is distributed without any warranty. 11 */ 12 13 14 15 #include "tlsgnutlsclient.h" 16 17 #ifdef HAVE_GNUTLS 18 19 #include <errno.h> 20 21 namespace gloox 22 { 23 GnuTLSClient(TLSHandler * th,const std::string & server)24 GnuTLSClient::GnuTLSClient( TLSHandler* th, const std::string& server ) 25 : GnuTLSBase( th, server ) 26 { 27 } 28 ~GnuTLSClient()29 GnuTLSClient::~GnuTLSClient() 30 { 31 } 32 cleanup()33 void GnuTLSClient::cleanup() 34 { 35 GnuTLSBase::cleanup(); 36 if( m_credentials ) 37 gnutls_certificate_free_credentials( m_credentials ); 38 init(); 39 } 40 init(const std::string &,const std::string &,const StringList &)41 bool GnuTLSClient::init( const std::string& /*clientKey*/, 42 const std::string& /*clientCerts*/, 43 const StringList& /*cacerts*/ ) 44 { 45 if( m_initLib && gnutls_global_init() != 0 ) 46 return false; 47 48 if( gnutls_certificate_allocate_credentials( &m_credentials ) < 0 ) 49 return false; 50 51 if( gnutls_init( m_session, GNUTLS_CLIENT ) != 0 ) 52 { 53 gnutls_certificate_free_credentials( m_credentials ); 54 return false; 55 } 56 57 #if GNUTLS_VERSION_NUMBER >= 0x020600 58 int ret = gnutls_priority_set_direct( *m_session, "SECURE128:+PFS:+COMP-ALL:+VERS-TLS-ALL:-VERS-SSL3.0:-VERS-TLS1.3:+SIGN-ALL:+CURVE-ALL", 0 ); 59 if( ret != GNUTLS_E_SUCCESS ) 60 return false; 61 #else 62 const int protocolPriority[] = { 63 #ifdef GNUTLS_TLS1_2 64 GNUTLS_TLS1_2, 65 #endif 66 GNUTLS_TLS1_1, GNUTLS_TLS1, 0 }; 67 const int kxPriority[] = { GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_DHE_DSS, 0 }; 68 const int cipherPriority[] = { GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_128_CBC, 69 GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_ARCFOUR, 0 }; 70 const int compPriority[] = { GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 }; 71 const int macPriority[] = { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 }; 72 gnutls_protocol_set_priority( *m_session, protocolPriority ); 73 gnutls_cipher_set_priority( *m_session, cipherPriority ); 74 gnutls_compression_set_priority( *m_session, compPriority ); 75 gnutls_kx_set_priority( *m_session, kxPriority ); 76 gnutls_mac_set_priority( *m_session, macPriority ); 77 #endif 78 79 gnutls_certificate_set_x509_system_trust( m_credentials ); 80 gnutls_credentials_set( *m_session, GNUTLS_CRD_CERTIFICATE, m_credentials ); 81 82 gnutls_transport_set_ptr( *m_session, static_cast<gnutls_transport_ptr_t>( this ) ); 83 gnutls_transport_set_push_function( *m_session, pushFunc ); 84 gnutls_transport_set_pull_function( *m_session, pullFunc ); 85 86 m_valid = true; 87 return true; 88 } 89 setCACerts(const StringList & cacerts)90 void GnuTLSClient::setCACerts( const StringList& cacerts ) 91 { 92 m_cacerts = cacerts; 93 94 StringList::const_iterator it = m_cacerts.begin(); 95 for( ; it != m_cacerts.end(); ++it ) 96 gnutls_certificate_set_x509_trust_file( m_credentials, (*it).c_str(), GNUTLS_X509_FMT_PEM ); 97 } 98 setClientCert(const std::string & clientKey,const std::string & clientCerts)99 void GnuTLSClient::setClientCert( const std::string& clientKey, const std::string& clientCerts ) 100 { 101 m_clientKey = clientKey; 102 m_clientCerts = clientCerts; 103 104 if( !m_clientKey.empty() && !m_clientCerts.empty() ) 105 { 106 gnutls_certificate_set_x509_key_file( m_credentials, m_clientCerts.c_str(), 107 m_clientKey.c_str(), GNUTLS_X509_FMT_PEM ); 108 } 109 } 110 getCertInfo()111 void GnuTLSClient::getCertInfo() 112 { 113 unsigned int status; 114 bool error = false; 115 116 gnutls_certificate_free_ca_names( m_credentials ); 117 118 if( gnutls_certificate_verify_peers2( *m_session, &status ) < 0 ) 119 error = true; 120 121 m_certInfo.status = 0; 122 if( status & GNUTLS_CERT_INVALID ) 123 m_certInfo.status |= CertInvalid; 124 if( status & GNUTLS_CERT_SIGNER_NOT_FOUND ) 125 m_certInfo.status |= CertSignerUnknown; 126 if( status & GNUTLS_CERT_REVOKED ) 127 m_certInfo.status |= CertRevoked; 128 if( status & GNUTLS_CERT_SIGNER_NOT_CA ) 129 m_certInfo.status |= CertSignerNotCa; 130 131 const gnutls_datum_t* certList = 0; 132 unsigned int certListSize = 0; 133 if( !error && ( ( certList = gnutls_certificate_get_peers( *m_session, &certListSize ) ) == 0 ) ) 134 error = true; 135 136 unsigned int certListSizeFull = certListSize; 137 138 gnutls_x509_crt_t* cert = new gnutls_x509_crt_t[certListSize]; 139 for( unsigned int i=0; !error && ( i<certListSize ); ++i ) 140 { 141 if( gnutls_x509_crt_init( &cert[i] ) < 0 142 || gnutls_x509_crt_import( cert[i], &certList[i], GNUTLS_X509_FMT_DER ) < 0 ) 143 error = true; 144 } 145 146 if( certListSize > 1 && ( gnutls_x509_crt_check_issuer( cert[certListSize-1], cert[certListSize-1] ) > 0 ) ) 147 certListSize--; 148 149 for( unsigned int i=1; !error && ( i<certListSize ); ++i ) 150 { 151 error = !verifyAgainst( cert[i-1], cert[i] ); 152 if( error ) 153 m_certInfo.status |= CertInvalid; 154 } 155 156 m_certInfo.chain = verifyAgainstCAs( cert[certListSize-1], 0 /*CAList*/, 0 /*CAListSize*/ ); 157 158 time_t t = gnutls_x509_crt_get_activation_time( cert[0] ); 159 if( t == -1 ) 160 error = true; 161 else if( t > time( 0 ) ) 162 m_certInfo.status |= CertNotActive; 163 m_certInfo.date_from = static_cast<int>( t ); 164 165 t = gnutls_x509_crt_get_expiration_time( cert[0] ); 166 if( t == -1 ) 167 error = true; 168 else if( t < time( 0 ) ) 169 m_certInfo.status |= CertExpired; 170 m_certInfo.date_to = static_cast<int>( t ); 171 172 char name[64]; 173 size_t nameSize = sizeof( name ); 174 gnutls_x509_crt_get_issuer_dn( cert[0], name, &nameSize ); 175 m_certInfo.issuer = name; 176 177 nameSize = sizeof( name ); 178 gnutls_x509_crt_get_dn( cert[0], name, &nameSize ); 179 m_certInfo.server = name; 180 181 const char* info; 182 info = gnutls_compression_get_name( gnutls_compression_get( *m_session ) ); 183 if( info ) 184 m_certInfo.compression = info; 185 186 info = gnutls_mac_get_name( gnutls_mac_get( *m_session ) ); 187 if( info ) 188 m_certInfo.mac = info; 189 190 info = gnutls_cipher_get_name( gnutls_cipher_get( *m_session ) ); 191 if( info ) 192 m_certInfo.cipher = info; 193 194 info = gnutls_protocol_get_name( gnutls_protocol_get_version( *m_session ) ); 195 if( info ) 196 m_certInfo.protocol = info; 197 198 if( !gnutls_x509_crt_check_hostname( cert[0], m_server.c_str() ) ) 199 m_certInfo.status |= CertWrongPeer; 200 201 for( unsigned int i = 0; i < certListSizeFull; ++i ) 202 gnutls_x509_crt_deinit( cert[i] ); 203 204 delete[] cert; 205 206 m_valid = true; 207 } 208 verifyCert(gnutls_x509_crt_t cert,unsigned result)209 static bool verifyCert( gnutls_x509_crt_t cert, unsigned result ) 210 { 211 return ! ( ( result & GNUTLS_CERT_INVALID ) 212 || gnutls_x509_crt_get_expiration_time( cert ) < time( 0 ) 213 || gnutls_x509_crt_get_activation_time( cert ) > time( 0 ) ); 214 } 215 verifyAgainst(gnutls_x509_crt_t cert,gnutls_x509_crt_t issuer)216 bool GnuTLSClient::verifyAgainst( gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer ) 217 { 218 unsigned int result; 219 gnutls_x509_crt_verify( cert, &issuer, 1, 0, &result ); 220 return verifyCert( cert, result ); 221 } 222 verifyAgainstCAs(gnutls_x509_crt_t cert,gnutls_x509_crt_t * CAList,int CAListSize)223 bool GnuTLSClient::verifyAgainstCAs( gnutls_x509_crt_t cert, gnutls_x509_crt_t* CAList, int CAListSize ) 224 { 225 unsigned int result; 226 gnutls_x509_crt_verify( cert, CAList, CAListSize, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, &result ); 227 return verifyCert( cert, result ); 228 } 229 230 } 231 232 #endif // HAVE_GNUTLS 233