gloox 1.0.27
tlsopensslbase.cpp
1/*
2 Copyright (c) 2009-2023 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 if( m_cacerts.empty() )
130 SSL_CTX_set_default_verify_paths( m_ctx );
131
132 StringList::const_iterator it = m_cacerts.begin();
133 for( ; it != m_cacerts.end(); ++it )
134 SSL_CTX_load_verify_locations( m_ctx, (*it).c_str(), 0 );
135 }
136
137 void OpenSSLBase::setClientCert( const std::string& clientKey, const std::string& clientCerts )
138 {
139 m_clientKey = clientKey;
140 m_clientCerts = clientCerts;
141
142 if( !m_clientKey.empty() && !m_clientCerts.empty() )
143 {
144 if( SSL_CTX_use_certificate_chain_file( m_ctx, m_clientCerts.c_str() ) != 1 )
145 {
146 // FIXME
147 }
148 if( SSL_CTX_use_RSAPrivateKey_file( m_ctx, m_clientKey.c_str(), SSL_FILETYPE_PEM ) != 1 )
149 {
150 // FIXME
151 }
152 }
153
154 if ( SSL_CTX_check_private_key( m_ctx ) != 1 )
155 {
156 // FIXME
157 }
158 }
159
161 {
162 if( !m_mutex.trylock() )
163 return;
164
165 m_secure = false;
166 m_valid = false;
167
168 m_mutex.unlock();
169 }
170
171 void OpenSSLBase::doTLSOperation( TLSOperation op )
172 {
173 if( !m_handler )
174 return;
175
176 int ret = 0;
177 bool onceAgain = false;
178
179 do
180 {
181 switch( op )
182 {
183 case TLSHandshake:
184 ret = handshakeFunction();
185 break;
186 case TLSWrite:
187 ret = SSL_write( m_ssl, m_sendBuffer.c_str(), static_cast<int>( m_sendBuffer.length() ) );
188 break;
189 case TLSRead:
190 ret = SSL_read( m_ssl, m_buf, m_bufsize );
191 break;
192 }
193
194 switch( SSL_get_error( m_ssl, ret ) )
195 {
196 case SSL_ERROR_WANT_READ:
197 case SSL_ERROR_WANT_WRITE:
198 pushFunc();
199 break;
200 case SSL_ERROR_NONE:
201 if( op == TLSHandshake )
202 m_secure = true;
203 else if( op == TLSWrite )
204 m_sendBuffer.erase( 0, ret );
205 else if( op == TLSRead )
206 m_handler->handleDecryptedData( this, std::string( m_buf, ret ) );
207 pushFunc();
208 break;
209 default:
210 if( !m_secure )
211 m_handler->handleHandshakeResult( this, false, m_certInfo );
212 return;
213 break;
214 }
215 if( !onceAgain && !m_recvBuffer.length() )
216 onceAgain = true;
217 else if( onceAgain )
218 onceAgain = false;
219 }
220 while( ( ( onceAgain || m_recvBuffer.length() ) && ( !m_secure || op == TLSRead ) )
221 || ( ( op == TLSWrite ) && ( ret > 0 ) ));
222 }
223
224 int OpenSSLBase::ASN1Time2UnixTime( ASN1_TIME* time )
225 {
226 struct tm t;
227 const char* str = reinterpret_cast<const char*>( time->data );
228 size_t i = 0;
229
230 memset( &t, 0, sizeof(t) );
231
232 if( time->type == V_ASN1_UTCTIME ) /* two digit year */
233 {
234 t.tm_year = ( str[i++] - '0' ) * 10;
235 t.tm_year += ( str[i++] - '0' );
236
237 if( t.tm_year < 70 )
238 t.tm_year += 100;
239 }
240 else if( time->type == V_ASN1_GENERALIZEDTIME ) /* four digit year */
241 {
242 t.tm_year = ( str[i++] - '0' ) * 1000;
243 t.tm_year += ( str[i++] - '0' ) * 100;
244 t.tm_year += ( str[i++] - '0' ) * 10;
245 t.tm_year += ( str[i++] - '0' );
246 t.tm_year -= 1900;
247 }
248
249 t.tm_mon = ( str[i++] - '0' ) * 10;
250 t.tm_mon += ( str[i++] - '0' ) - 1; // -1 since January is 0 not 1.
251 t.tm_mday = ( str[i++] - '0' ) * 10;
252 t.tm_mday += ( str[i++] - '0' );
253 t.tm_hour = ( str[i++] - '0' ) * 10;
254 t.tm_hour += ( str[i++] - '0' );
255 t.tm_min = ( str[i++] - '0' ) * 10;
256 t.tm_min += ( str[i++] - '0' );
257 t.tm_sec = ( str[i++] - '0' ) * 10;
258 t.tm_sec += ( str[i++] - '0' );
259
260 return static_cast<int>( mktime( &t ) );
261 }
262
263#if defined OPENSSL_VERSION_NUMBER && ( OPENSSL_VERSION_NUMBER < 0x10100000 )
264 int SSL_SESSION_get_protocol_version( const SSL_SESSION* s )
265 {
266 return s->ssl_version;
267 }
268#endif // OPENSSL_VERSION_NUMBER < 0x10100000
269
271 {
272
273 doTLSOperation( TLSHandshake );
274
275 if( !m_secure )
276 return true;
277
278 long res = SSL_get_verify_result( m_ssl );
279 if( res != X509_V_OK )
280 m_certInfo.status = CertInvalid;
281 else
282 m_certInfo.status = CertOk;
283
284 X509* peer = SSL_get_peer_certificate( m_ssl );
285 if( peer )
286 {
287 char peer_CN[256];
288 X509_NAME_get_text_by_NID( X509_get_issuer_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
289 m_certInfo.issuer = peer_CN;
290 X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) );
291 m_certInfo.server = peer_CN;
292 m_certInfo.date_from = ASN1Time2UnixTime( X509_get_notBefore( peer ) );
293 m_certInfo.date_to = ASN1Time2UnixTime( X509_get_notAfter( peer ) );
294 std::string p( peer_CN );
295 std::transform( p.begin(), p.end(), p.begin(), tolower );
296
297#if defined OPENSSL_VERSION_NUMBER && ( OPENSSL_VERSION_NUMBER >= 0x10002000 )
298 res = X509_check_host( peer, p.c_str(), p.length(), X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS, 0 );
299 if( res <= 0 ) // 0: verification failed; -1: internal error; -2 input is malformed
300 m_certInfo.status |= CertWrongPeer;
301#else
302 if( p != m_server )
303 m_certInfo.status |= CertWrongPeer;
304#endif // OPENSSL_VERSION_NUMBER >= 0x10002000
305
306 if( ASN1_UTCTIME_cmp_time_t( X509_get_notBefore( peer ), time( 0 ) ) != -1 )
307 m_certInfo.status |= CertNotActive;
308
309 if( ASN1_UTCTIME_cmp_time_t( X509_get_notAfter( peer ), time( 0 ) ) != 1 )
310 m_certInfo.status |= CertExpired;
311
312 X509_free( peer );
313 }
314 else
315 {
316 m_certInfo.status = CertInvalid;
317 }
318
319 const char* tmp;
320 tmp = SSL_get_cipher_name( m_ssl );
321 if( tmp )
322 m_certInfo.cipher = tmp;
323
324 SSL_SESSION* sess = SSL_get_session( m_ssl );
325 if( sess )
326 {
327 switch( SSL_SESSION_get_protocol_version( sess ) )
328 {
329 case TLS1_VERSION:
330 m_certInfo.protocol = "TLSv1.0";
331 break;
332 case TLS1_1_VERSION:
333 m_certInfo.protocol = "TLSv1.1";
334 break;
335 case TLS1_2_VERSION:
336 m_certInfo.protocol = "TLSv1.2";
337 break;
338 case TLS1_3_VERSION:
339 m_certInfo.protocol = "TLSv1.3";
340 break;
341 default:
342 m_certInfo.protocol = "Unknown TLS version";
343 break;
344 }
345 }
346
347 tmp = SSL_COMP_get_name( SSL_get_current_compression( m_ssl ) );
348 if( tmp )
349 m_certInfo.compression = tmp;
350
351 m_valid = true;
352
353 m_handler->handleHandshakeResult( this, true, m_certInfo );
354 return true;
355 }
356
357 void OpenSSLBase::pushFunc()
358 {
359 int wantwrite;
360 size_t wantread;
361 long frombio;
362 long tobio;
363
364 while( ( wantwrite = BIO_pending( m_nbio ) ) > 0 )
365 {
366 if( wantwrite > m_bufsize )
367 wantwrite = m_bufsize;
368
369 if( !wantwrite )
370 break;
371
372 frombio = BIO_read( m_nbio, m_buf, wantwrite );
373
374 if( m_handler )
375 m_handler->handleEncryptedData( this, std::string( m_buf, frombio ) );
376 }
377
378 while( ( wantread = BIO_ctrl_get_read_request( m_nbio ) ) > 0 )
379 {
380 if( wantread > m_recvBuffer.length() )
381 wantread = m_recvBuffer.length();
382
383 if( !wantread )
384 break;
385
386 tobio = BIO_write( m_nbio, m_recvBuffer.c_str(), static_cast<int>( wantread ) );
387 m_recvBuffer.erase( 0, tobio );
388 }
389 }
390
391}
392
393#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
std::string protocol
Definition gloox.h:1001
std::string issuer
Definition gloox.h:993
std::string compression
Definition gloox.h:1004