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