gloox 1.0.24
tlsopensslbase.cpp
1/*
2 Copyright (c) 2009-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 "tlsopensslbase.h"
16
17#ifdef HAVE_OPENSSL
18
19#include <algorithm>
20#include <cctype>
21#include <ctime>
22#include <cstdlib>
23
24#include <openssl/err.h>
25#include <openssl/x509v3.h>
26
27#ifndef OPENSSL_NO_COMP
28 #include <openssl/comp.h>
29#endif
30
31#include <string.h>
32
33namespace gloox
34{
35
36 OpenSSLBase::OpenSSLBase( TLSHandler* th, const std::string& server )
37 : TLSBase( th, server ), m_ssl( 0 ), m_ctx( 0 ), m_buf( 0 ), m_bufsize( 17000 )
38 {
39 m_buf = static_cast<char*>( calloc( m_bufsize + 1, sizeof( char ) ) );
40 }
41
43 {
44 m_handler = 0;
45 free( m_buf );
46 SSL_CTX_free( m_ctx );
47 SSL_shutdown( m_ssl );
48 SSL_free( m_ssl );
49 BIO_free( m_nbio );
50 cleanup();
51 }
52
53 bool OpenSSLBase::init( const std::string& clientKey,
54 const std::string& clientCerts,
55 const StringList& cacerts )
56 {
57#if defined OPENSSL_VERSION_NUMBER && ( OPENSSL_VERSION_NUMBER < 0x10100000 )
58 if( m_initLib )
59 SSL_library_init();
60#endif // OPENSSL_VERSION_NUMBER < 0x10100000
61
62#ifndef OPENSSL_NO_COMP
63 SSL_COMP_add_compression_method( 193, COMP_zlib() );
64#endif // OPENSSL_NO_COMP
65
66 OpenSSL_add_all_algorithms();
67
68 if( !setType() ) //inits m_ctx
69 return false;
70
71 setClientCert( clientKey, clientCerts );
72 setCACerts( cacerts );
73
74 if( !SSL_CTX_set_cipher_list( m_ctx, "HIGH:MEDIUM:AES:@STRENGTH" ) )
75 return false;
76
77 m_ssl = SSL_new( m_ctx );
78 if( !m_ssl )
79 return false;
80
81 if( !BIO_new_bio_pair( &m_ibio, 0, &m_nbio, 0 ) )
82 return false;
83
84 SSL_set_bio( m_ssl, m_ibio, m_ibio );
85 SSL_set_mode( m_ssl, SSL_MODE_AUTO_RETRY | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE );
86
87 ERR_load_crypto_strings();
88 SSL_load_error_strings();
89
90 if( !privateInit() )
91 return false;
92
93 m_valid = true;
94 return true;
95 }
96
97 bool OpenSSLBase::encrypt( const std::string& data )
98 {
99 m_sendBuffer += data;
100
101 if( !m_secure )
102 {
103 handshake();
104 return 0;
105 }
106
107 doTLSOperation( TLSWrite );
108 return true;
109 }
110
111 int OpenSSLBase::decrypt( const std::string& data )
112 {
113 m_recvBuffer += data;
114
115 if( !m_secure )
116 {
117 handshake();
118 return 0;
119 }
120
121 doTLSOperation( TLSRead );
122 return true;
123 }
124
125 void OpenSSLBase::setCACerts( const StringList& cacerts )
126 {
127 m_cacerts = cacerts;
128
129 StringList::const_iterator it = m_cacerts.begin();
130 for( ; it != m_cacerts.end(); ++it )
131 SSL_CTX_load_verify_locations( m_ctx, (*it).c_str(), 0 );
132 }
133
134 void OpenSSLBase::setClientCert( const std::string& clientKey, const std::string& clientCerts )
135 {
136 m_clientKey = clientKey;
137 m_clientCerts = clientCerts;
138
139 if( !m_clientKey.empty() && !m_clientCerts.empty() )
140 {
141 if( SSL_CTX_use_certificate_chain_file( m_ctx, m_clientCerts.c_str() ) != 1 )
142 {
143 // FIXME
144 }
145 if( SSL_CTX_use_RSAPrivateKey_file( m_ctx, m_clientKey.c_str(), SSL_FILETYPE_PEM ) != 1 )
146 {
147 // FIXME
148 }
149 }
150
151 if ( SSL_CTX_check_private_key( m_ctx ) != 1 )
152 {
153 // FIXME
154 }
155 }
156
158 {
159 if( !m_mutex.trylock() )
160 return;
161
162 m_secure = false;
163 m_valid = false;
164
165 m_mutex.unlock();
166 }
167
168 void OpenSSLBase::doTLSOperation( TLSOperation op )
169 {
170 if( !m_handler )
171 return;
172
173 int ret = 0;
174 bool onceAgain = false;
175
176 do
177 {
178 switch( op )
179 {
180 case TLSHandshake:
181 ret = handshakeFunction();
182 break;
183 case TLSWrite:
184 ret = SSL_write( m_ssl, m_sendBuffer.c_str(), static_cast<int>( m_sendBuffer.length() ) );
185 break;
186 case TLSRead:
187 ret = SSL_read( m_ssl, m_buf, m_bufsize );
188 break;
189 }
190
191 switch( SSL_get_error( m_ssl, ret ) )
192 {
193 case SSL_ERROR_WANT_READ:
194 case SSL_ERROR_WANT_WRITE:
195 pushFunc();
196 break;
197 case SSL_ERROR_NONE:
198 if( op == TLSHandshake )
199 m_secure = true;
200 else if( op == TLSWrite )
201 m_sendBuffer.erase( 0, ret );
202 else if( op == TLSRead )
203 m_handler->handleDecryptedData( this, std::string( m_buf, ret ) );
204 pushFunc();
205 break;
206 default:
207 if( !m_secure )
208 m_handler->handleHandshakeResult( this, false, m_certInfo );
209 return;
210 break;
211 }
212 if( !onceAgain && !m_recvBuffer.length() )
213 onceAgain = true;
214 else if( onceAgain )
215 onceAgain = false;
216 }
217 while( ( ( onceAgain || m_recvBuffer.length() ) && ( !m_secure || op == TLSRead ) )
218 || ( ( op == TLSWrite ) && ( ret > 0 ) ));
219 }
220
221 int OpenSSLBase::ASN1Time2UnixTime( ASN1_TIME* time )
222 {
223 struct tm t;
224 const char* str = reinterpret_cast<const char*>( time->data );
225 size_t i = 0;
226
227 memset( &t, 0, sizeof(t) );
228
229 if( time->type == V_ASN1_UTCTIME ) /* two digit year */
230 {
231 t.tm_year = ( str[i++] - '0' ) * 10;
232 t.tm_year += ( str[i++] - '0' );
233
234 if( t.tm_year < 70 )
235 t.tm_year += 100;
236 }
237 else if( time->type == V_ASN1_GENERALIZEDTIME ) /* four digit year */
238 {
239 t.tm_year = ( str[i++] - '0' ) * 1000;
240 t.tm_year += ( str[i++] - '0' ) * 100;
241 t.tm_year += ( str[i++] - '0' ) * 10;
242 t.tm_year += ( str[i++] - '0' );
243 t.tm_year -= 1900;
244 }
245
246 t.tm_mon = ( str[i++] - '0' ) * 10;
247 t.tm_mon += ( str[i++] - '0' ) - 1; // -1 since January is 0 not 1.
248 t.tm_mday = ( str[i++] - '0' ) * 10;
249 t.tm_mday += ( str[i++] - '0' );
250 t.tm_hour = ( str[i++] - '0' ) * 10;
251 t.tm_hour += ( str[i++] - '0' );
252 t.tm_min = ( str[i++] - '0' ) * 10;
253 t.tm_min += ( str[i++] - '0' );
254 t.tm_sec = ( str[i++] - '0' ) * 10;
255 t.tm_sec += ( str[i++] - '0' );
256
257 return static_cast<int>( mktime( &t ) );
258 }
259
260#if defined OPENSSL_VERSION_NUMBER && ( OPENSSL_VERSION_NUMBER < 0x10100000 )
261 int SSL_SESSION_get_protocol_version( const SSL_SESSION* s )
262 {
263 return s->ssl_version;
264 }
265#endif // OPENSSL_VERSION_NUMBER < 0x10100000
266
268 {
269
270 doTLSOperation( TLSHandshake );
271
272 if( !m_secure )
273 return true;
274
275 long res = SSL_get_verify_result( m_ssl );
276 if( res != X509_V_OK )
277 m_certInfo.status = CertInvalid;
278 else
279 m_certInfo.status = CertOk;
280
281 X509* peer = SSL_get_peer_certificate( m_ssl );
282 if( peer )
283 {
284 char peer_CN[256];
285 X509_NAME_get_text_by_NID( X509_get_issuer_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
286 m_certInfo.issuer = peer_CN;
287 X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
288 m_certInfo.server = peer_CN;
289 m_certInfo.date_from = ASN1Time2UnixTime( X509_get_notBefore( peer ) );
290 m_certInfo.date_to = ASN1Time2UnixTime( X509_get_notAfter( peer ) );
291 std::string p( peer_CN );
292 std::transform( p.begin(), p.end(), p.begin(), tolower );
293
294#if defined OPENSSL_VERSION_NUMBER && ( OPENSSL_VERSION_NUMBER >= 0x10002000 )
295 res = X509_check_host( peer, p.c_str(), p.length(), X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS, 0 );
296 if( res <= 0 ) // 0: verification failed; -1: internal error; -2 input is malformed
297 m_certInfo.status |= CertWrongPeer;
298#else
299 if( p != m_server )
300 m_certInfo.status |= CertWrongPeer;
301#endif // OPENSSL_VERSION_NUMBER >= 0x10002000
302
303 if( ASN1_UTCTIME_cmp_time_t( X509_get_notBefore( peer ), time( 0 ) ) != -1 )
304 m_certInfo.status |= CertNotActive;
305
306 if( ASN1_UTCTIME_cmp_time_t( X509_get_notAfter( peer ), time( 0 ) ) != 1 )
307 m_certInfo.status |= CertExpired;
308
309 X509_free( peer );
310 }
311 else
312 {
313 m_certInfo.status = CertInvalid;
314 }
315
316 const char* tmp;
317 tmp = SSL_get_cipher_name( m_ssl );
318 if( tmp )
319 m_certInfo.cipher = tmp;
320
321 SSL_SESSION* sess = SSL_get_session( m_ssl );
322 if( sess )
323 {
324 switch( SSL_SESSION_get_protocol_version( sess ) )
325 {
326 case TLS1_VERSION:
327 m_certInfo.protocol = "TLSv1";
328 break;
329 case TLS1_1_VERSION:
330 m_certInfo.protocol = "TLSv1.1";
331 break;
332 case TLS1_2_VERSION:
333 m_certInfo.protocol = "TLSv1.2";
334 break;
335#ifdef TLS1_3_VERSION
336 case TLS1_3_VERSION:
337 m_certInfo.protocol = "TLSv1.3";
338 break;
339#endif // TLS1_3_VERSION
340 default:
341 m_certInfo.protocol = "Unknown TLS version";
342 break;
343 }
344 }
345
346 tmp = SSL_COMP_get_name( SSL_get_current_compression( m_ssl ) );
347 if( tmp )
348 m_certInfo.compression = tmp;
349
350 m_valid = true;
351
352 m_handler->handleHandshakeResult( this, true, m_certInfo );
353 return true;
354 }
355
356 void OpenSSLBase::pushFunc()
357 {
358 int wantwrite;
359 size_t wantread;
360 long frombio;
361 long tobio;
362
363 while( ( wantwrite = BIO_pending( m_nbio ) ) > 0 )
364 {
365 if( wantwrite > m_bufsize )
366 wantwrite = m_bufsize;
367
368 if( !wantwrite )
369 break;
370
371 frombio = BIO_read( m_nbio, m_buf, wantwrite );
372
373 if( m_handler )
374 m_handler->handleEncryptedData( this, std::string( m_buf, frombio ) );
375 }
376
377 while( ( wantread = BIO_ctrl_get_read_request( m_nbio ) ) > 0 )
378 {
379 if( wantread > m_recvBuffer.length() )
380 wantread = m_recvBuffer.length();
381
382 if( !wantread )
383 break;
384
385 tobio = BIO_write( m_nbio, m_recvBuffer.c_str(), static_cast<int>( wantread ) );
386 m_recvBuffer.erase( 0, tobio );
387 }
388 }
389
390}
391
392#endif // HAVE_OPENSSL
virtual bool encrypt(const std::string &data)
virtual void setCACerts(const StringList &cacerts)
virtual bool handshake()
virtual void cleanup()
virtual void setClientCert(const std::string &clientKey, const std::string &clientCerts)
virtual bool init(const std::string &clientKey=EmptyString, const std::string &clientCerts=EmptyString, const StringList &cacerts=StringList())
OpenSSLBase(TLSHandler *th, const std::string &server=EmptyString)
virtual int decrypt(const std::string &data)
An abstract base class for TLS implementations.
Definition: tlsbase.h:32
An interface that allows for interacting with TLS implementations derived from TLSBase.
Definition: tlshandler.h:35
virtual void handleDecryptedData(const TLSBase *base, const std::string &data)=0
virtual void handleEncryptedData(const TLSBase *base, const std::string &data)=0
virtual void handleHandshakeResult(const TLSBase *base, bool success, CertInfo &certinfo)=0
The namespace for the gloox library.
Definition: adhoc.cpp:28
std::list< std::string > StringList
Definition: gloox.h:1251
@ CertExpired
Definition: gloox.h:979
@ CertOk
Definition: gloox.h:975
@ CertWrongPeer
Definition: gloox.h:981
@ CertInvalid
Definition: gloox.h:976
@ CertNotActive
Definition: gloox.h:980
std::string cipher
Definition: gloox.h:1002
std::string server
Definition: gloox.h:994
int date_from
Definition: gloox.h:995
std::string protocol
Definition: gloox.h:1001
std::string issuer
Definition: gloox.h:993
std::string compression
Definition: gloox.h:1004