E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gssl_openssl.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2018 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gssl_openssl.cpp
19 //
20
21 #include "gdef.h"
22 #include "gssl.h"
23 #include "gssl_openssl.h"
24 #include "gtest.h"
25 #include "gstr.h"
26 #include "gpath.h"
27 #include "gprocess.h"
28 #include "gfile.h"
29 #include "groot.h"
30 #include "gexception.h"
31 #include "gdebug.h"
32 #include <openssl/ssl.h>
33 #include <openssl/err.h>
34 #include <openssl/rand.h>
35 #include <openssl/conf.h>
36 #include <openssl/evp.h>
37 #include <openssl/hmac.h>
38 #include <exception>
39 #include <functional>
40 #include <vector>
41 #include <map>
42 #include <iomanip>
43 #include <fstream>
44 #include <sstream>
45 #include <utility>
46 #include <algorithm>
47
48 GSsl::OpenSSL::LibraryImp::LibraryImp( G::StringArray & library_config , Library::LogFn log_fn , bool verbose ) :
49 m_evp_ctx(nullptr) ,
50 m_log_fn(log_fn) ,
51 m_verbose(verbose) ,
52 m_config(library_config)
53 {
54 SSL_load_error_strings() ;
55 SSL_library_init() ;
56 int rc = RAND_status() ; G_IGNORE_VARIABLE(rc) ;
57 OpenSSL_add_all_digests() ;
58
59 // allocate a slot for a pointer from SSL to ProtocolImp
60 m_index = SSL_get_ex_new_index( 0 , nullptr , nullptr , nullptr , nullptr ) ;
61 if( m_index < 0 )
62 {
63 cleanup() ;
64 throw Error( "SSL_get_ex_new_index" ) ;
65 }
66 }
67
68 GSsl::OpenSSL::LibraryImp::~LibraryImp()
69 {
70 cleanup() ;
71 }
72
73 void GSsl::OpenSSL::LibraryImp::cleanup()
74 {
75 // ffs...
76 ERR_free_strings() ;
77 RAND_cleanup() ;
78 CRYPTO_cleanup_all_ex_data();
79 }
80
81 std::string GSsl::OpenSSL::LibraryImp::sid()
82 {
83 return G::Str::printable( SSLeay_version(SSLEAY_VERSION) ) ;
84 }
85
86 std::string GSsl::OpenSSL::LibraryImp::id() const
87 {
88 return sid() ;
89 }
90
91 GSsl::OpenSSL::Config GSsl::OpenSSL::LibraryImp::config() const
92 {
93 return m_config ;
94 }
95
96 std::string GSsl::OpenSSL::LibraryImp::credit( const std::string & prefix , const std::string & eol , const std::string & eot )
97 {
98 std::ostringstream ss ;
99 ss
100 << prefix << "This product includes software developed by the OpenSSL Project" << eol
101 << prefix << "for use in the OpenSSL Toolkit (http://www.openssl.org/)" << eol
102 << eot ;
103 return ss.str() ;
104 }
105
106 void GSsl::OpenSSL::LibraryImp::addProfile( const std::string & profile_name , bool is_server_profile ,
107 const std::string & key_file , const std::string & cert_file , const std::string & ca_file ,
108 const std::string & default_peer_certificate_name , const std::string & default_peer_host_name ,
109 const std::string & profile_config )
110 {
111 shared_ptr<ProfileImp> profile_ptr(
112 new ProfileImp(*this,is_server_profile,key_file,cert_file,ca_file,
113 default_peer_certificate_name,default_peer_host_name,profile_config) ) ;
114 m_profile_map.insert( Map::value_type(profile_name,profile_ptr) ) ;
115 }
116
117 bool GSsl::OpenSSL::LibraryImp::hasProfile( const std::string & profile_name ) const
118 {
119 Map::const_iterator p = m_profile_map.find( profile_name ) ;
120 return p != m_profile_map.end() ;
121 }
122
123 const GSsl::Profile & GSsl::OpenSSL::LibraryImp::profile( const std::string & profile_name ) const
124 {
125 Map::const_iterator p = m_profile_map.find( profile_name ) ;
126 if( p == m_profile_map.end() ) throw Error( "no such profile: [" + profile_name + "]" ) ;
127 return *(*p).second.get() ;
128 }
129
130 GSsl::Library::LogFn GSsl::OpenSSL::LibraryImp::log() const
131 {
132 return m_log_fn ;
133 }
134
135 bool GSsl::OpenSSL::LibraryImp::verbose() const
136 {
137 return m_verbose ;
138 }
139
140 int GSsl::OpenSSL::LibraryImp::index() const
141 {
142 return m_index ;
143 }
144
145 G::StringArray GSsl::OpenSSL::LibraryImp::digesters( bool require_state ) const
146 {
147 G::StringArray result ;
148 if( require_state )
149 {
150 ; // state() not implemented for any
151 }
152 else
153 {
154 result.push_back( "SHA512" ) ;
155 result.push_back( "SHA256" ) ;
156 result.push_back( "SHA1" ) ;
157 result.push_back( "MD5" ) ;
158 }
159 return result ;
160 }
161
162 GSsl::Digester GSsl::OpenSSL::LibraryImp::digester( const std::string & hash_type , const std::string & state ) const
163 {
164 return Digester( new OpenSSL::DigesterImp(hash_type,state) ) ;
165 }
166
167 GSsl::OpenSSL::DigesterImp::DigesterImp( const std::string & hash_type , const std::string & state ) :
168 m_evp_ctx(EVP_MD_CTX_create())
169 {
170 const EVP_MD * md = EVP_get_digestbyname( hash_type.c_str() ) ;
171 if( md == nullptr )
172 throw Error( "unsupported hash function name: [" + hash_type + "]" ) ;
173
174 m_block_size = static_cast<size_t>( EVP_MD_block_size(md) ) ;
175 m_value_size = static_cast<size_t>( EVP_MD_size(md) ) ;
176 m_state_size = 0U ; // not implemented
177
178 if( m_state_size == 0U && !state.empty() )
179 throw Error( "hash state resoration not implemented for " + hash_type ) ;
180
181 EVP_DigestInit_ex( m_evp_ctx , md , 0/*engine*/ ) ;
182 }
183
184 GSsl::OpenSSL::DigesterImp::~DigesterImp()
185 {
186 EVP_MD_CTX_destroy( m_evp_ctx ) ;
187 }
188
189 size_t GSsl::OpenSSL::DigesterImp::blocksize() const
190 {
191 return m_block_size ;
192 }
193
194 size_t GSsl::OpenSSL::DigesterImp::valuesize() const
195 {
196 return m_value_size ;
197 }
198
199 size_t GSsl::OpenSSL::DigesterImp::statesize() const
200 {
201 return m_state_size ; // zero when not implemented
202 }
203
204 std::string GSsl::OpenSSL::DigesterImp::state()
205 {
206 return std::string() ; // not implemented
207 }
208
209 void GSsl::OpenSSL::DigesterImp::add( const std::string & data )
210 {
211 EVP_DigestUpdate( m_evp_ctx , data.data() , data.size() ) ;
212 }
213
214 std::string GSsl::OpenSSL::DigesterImp::value()
215 {
216 std::vector<unsigned char> output( EVP_MAX_MD_SIZE ) ;
217 unsigned int output_size = 0 ;
218 EVP_DigestFinal_ex( m_evp_ctx , &output[0] , &output_size ) ;
219 char * p = reinterpret_cast<char*>(&output[0]) ;
220 size_t n = static_cast<size_t>(output_size) ;
221 return std::string( p , n ) ;
222 }
223
224 // ==
225
226 GSsl::OpenSSL::ProfileImp::ProfileImp( const LibraryImp & library_imp , bool is_server_profile ,
227 const std::string & key_file , const std::string & cert_file , const std::string & ca_path ,
228 const std::string & default_peer_certificate_name , const std::string & default_peer_host_name ,
229 const std::string & profile_config ) :
230 m_library_imp(library_imp) ,
231 m_default_peer_certificate_name(default_peer_certificate_name) ,
232 m_default_peer_host_name(default_peer_host_name) ,
233 m_ssl_ctx(nullptr,std::ptr_fun(deleter))
234 {
235 Config extra_config = m_library_imp.config() ;
236 if( !profile_config.empty() )
237 {
238 G::StringArray profile_config_list = G::Str::splitIntoTokens( profile_config , "," ) ;
239 extra_config = Config( profile_config_list ) ;
240 if( !profile_config_list.empty() )
241 G_WARNING( "GSsl::OpenSSL::ProfileImp::ctor: tls-config: tls " << (is_server_profile?"server":"client")
242 << " profile configuration ignored: [" << G::Str::join(",",profile_config_list) << "]" ) ;
243 }
244
245 if( m_ssl_ctx.get() == nullptr )
246 {
247 Config::Fn version_fn = extra_config.fn( is_server_profile ) ;
248 m_ssl_ctx.reset( SSL_CTX_new( version_fn() ) ) ;
249 if( m_ssl_ctx.get() != nullptr )
250 apply( extra_config ) ;
251 }
252
253 if( m_ssl_ctx.get() == nullptr )
254 throw Error( "SSL_CTX_new" , ERR_get_error() ) ;
255
256 if( !key_file.empty() )
257 {
258 G::Root claim_root ;
259 if( !G::File::exists(key_file) )
260 G_WARNING( "GSsl::Profile: cannot open ssl key file: " + key_file ) ;
261
262 check( SSL_CTX_use_PrivateKey_file(m_ssl_ctx.get(),key_file.c_str(),SSL_FILETYPE_PEM) ,
263 "use_PrivateKey_file" , key_file ) ;
264 }
265
266 if( !cert_file.empty() )
267 {
268 G::Root claim_root ;
269 if( !G::File::exists(cert_file) )
270 G_WARNING( "GSsl::Profile: cannot open ssl certificate file: " + cert_file ) ;
271
272 check( SSL_CTX_use_certificate_chain_file(m_ssl_ctx.get(),cert_file.c_str()) ,
273 "use_certificate_chain_file" , cert_file ) ;
274 }
275
276 if( ca_path.empty() )
277 {
278 // ask for peer certificates but just log them without verifying - we don't
279 // use set_client_CA_list() so we allow the client to not send a certificate
280 SSL_CTX_set_verify( m_ssl_ctx.get() , SSL_VERIFY_PEER , verifyPass ) ;
281 }
282 else if( ca_path == "<none>" )
283 {
284 // dont ask for client certificates (server-side)
285 SSL_CTX_set_verify( m_ssl_ctx.get() , SSL_VERIFY_NONE , nullptr ) ;
286 }
287 else if( ca_path == "<default>" )
288 {
289 // ask for certificates, make sure they verify against the default ca database, and check the name in the certificate (if given)
290 bool no_verify = extra_config.noverify() ;
291 SSL_CTX_set_verify( m_ssl_ctx.get() , no_verify ? SSL_VERIFY_NONE : (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT) , verifyPeerName ) ;
292 check( SSL_CTX_set_default_verify_paths( m_ssl_ctx.get() ) , "set_default_verify_paths" ) ;
293 }
294 else
295 {
296 // ask for certificates, make sure they verify against the given ca database, and check the name in the certificate (if given)
297 bool ca_path_is_dir = G::File::isDirectory( ca_path ) ;
298 const char * ca_file_p = ca_path_is_dir ? nullptr : ca_path.c_str() ;
299 const char * ca_dir_p = ca_path_is_dir ? ca_path.c_str() : nullptr ;
300 bool no_verify = extra_config.noverify() ;
301 SSL_CTX_set_verify( m_ssl_ctx.get() , no_verify ? SSL_VERIFY_NONE : (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT) , verifyPeerName ) ;
302 check( SSL_CTX_load_verify_locations( m_ssl_ctx.get() , ca_file_p , ca_dir_p ) , "load_verify_locations" , ca_path ) ;
303 }
304
305 SSL_CTX_set_quiet_shutdown( m_ssl_ctx.get() , 1 ) ;
306 SSL_CTX_set_cipher_list( m_ssl_ctx.get() , "DEFAULT" ) ;
307 SSL_CTX_set_session_cache_mode( m_ssl_ctx.get() , SSL_SESS_CACHE_OFF ) ;
308 if( is_server_profile )
309 {
310 static std::string x = "GSsl.OpenSSL." + G::Path(G::Process::exe()).basename() ;
311 SSL_CTX_set_session_id_context( m_ssl_ctx.get() , reinterpret_cast<const unsigned char *>(x.data()) , x.size() ) ;
312 }
313 }
314
315 GSsl::OpenSSL::ProfileImp::~ProfileImp()
316 {
317 }
318
319 void GSsl::OpenSSL::ProfileImp::deleter( SSL_CTX * p )
320 {
321 if( p != nullptr )
322 SSL_CTX_free( p ) ;
323 }
324
325 GSsl::ProtocolImpBase * GSsl::OpenSSL::ProfileImp::newProtocol( const std::string & peer_certificate_name ,
326 const std::string & peer_host_name ) const
327 {
328 return
329 new OpenSSL::ProtocolImp( *this ,
330 peer_certificate_name.empty()?defaultPeerCertificateName():peer_certificate_name ,
331 peer_host_name.empty()?defaultPeerHostName():peer_host_name ) ;
332 }
333
334 SSL_CTX * GSsl::OpenSSL::ProfileImp::p() const
335 {
336 return const_cast<SSL_CTX*>( m_ssl_ctx.get() ) ;
337 }
338
339 const GSsl::OpenSSL::LibraryImp & GSsl::OpenSSL::ProfileImp::lib() const
340 {
341 return m_library_imp ;
342 }
343
344 const std::string & GSsl::OpenSSL::ProfileImp::defaultPeerCertificateName() const
345 {
346 return m_default_peer_certificate_name ;
347 }
348
349 const std::string & GSsl::OpenSSL::ProfileImp::defaultPeerHostName() const
350 {
351 return m_default_peer_host_name ;
352 }
353
354 void GSsl::OpenSSL::ProfileImp::check( int rc , const std::string & fnname_tail , const std::string & file )
355 {
356 if( rc != 1 )
357 {
358 std::string fnname = "SSL_CTX_" + fnname_tail ;
359 throw Error( fnname , ERR_get_error() , file ) ;
360 }
361 }
362
363 void GSsl::OpenSSL::ProfileImp::apply( const Config & config )
364 {
365 #if GCONFIG_HAVE_OPENSSL_MIN_MAX
366 if( config.min_() )
367 SSL_CTX_set_min_proto_version( m_ssl_ctx.get() , config.min_() ) ;
368
369 if( config.max_() )
370 SSL_CTX_set_max_proto_version( m_ssl_ctx.get() , config.max_() ) ;
371 #else
372 if( config.reset() != 0L )
373 SSL_CTX_clear_options( m_ssl_ctx.get() , config.reset() ) ;
374
375 if( config.set() != 0L )
376 SSL_CTX_set_options( m_ssl_ctx.get() , config.set() ) ;
377 #endif
378 }
379
380 int GSsl::OpenSSL::ProfileImp::verifyPass( int ok , X509_STORE_CTX * )
381 {
382 return 1 ;
383 }
384
385 int GSsl::OpenSSL::ProfileImp::verifyPeerName( int ok , X509_STORE_CTX * ctx )
386 {
387 try
388 {
389 if( ok && X509_STORE_CTX_get_error_depth(ctx) == 0 )
390 {
391 using namespace GSsl ;
392 SSL * ssl = reinterpret_cast<SSL*>( X509_STORE_CTX_get_ex_data( ctx , SSL_get_ex_data_X509_STORE_CTX_idx() ) ) ; if(ssl==nullptr) throw 1 ;
393 OpenSSL::LibraryImp & library = dynamic_cast<OpenSSL::LibraryImp&>( Library::impstance() ) ;
394 OpenSSL::ProtocolImp * protocol = reinterpret_cast<OpenSSL::ProtocolImp*>( SSL_get_ex_data(ssl,library.index()) ) ; if(protocol==nullptr) throw 1 ;
395 std::string required_peer_certificate_name = protocol->requiredPeerCertificateName() ;
396 if( !required_peer_certificate_name.empty() )
397 {
398 X509 * cert = X509_STORE_CTX_get_current_cert( ctx ) ;
399 std::string subject = name(X509_get_subject_name(cert)) ;
400 G::StringArray subject_parts = G::Str::splitIntoTokens( subject , "/" ) ;
401 bool found = std::find( subject_parts.begin() , subject_parts.end() , "CN="+required_peer_certificate_name ) != subject_parts.end() ;
402 library.log()( 2 , "certificate-subject=[" + subject + "] required-peer-name=[" + required_peer_certificate_name + "] ok=" + (found?"1":"0") ) ;
403 if( !found )
404 {
405 ok = 0 ;
406 }
407 }
408 }
409 return ok ;
410 }
411 catch(...) // callback from c code
412 {
413 return 0 ;
414 }
415 }
416
417 std::string GSsl::OpenSSL::ProfileImp::name( X509_NAME * x509_name )
418 {
419 if( x509_name == nullptr ) return std::string() ;
420 std::vector<char> buffer( 2048U ) ; // 200 in openssl code
421 X509_NAME_oneline( x509_name , &buffer[0] , buffer.size() ) ;
422 buffer.back() = '\0' ;
423 return G::Str::printable( std::string(&buffer[0]) ) ;
424 }
425
426 // ==
427
428 GSsl::OpenSSL::ProtocolImp::ProtocolImp( const ProfileImp & profile , const std::string & required_peer_certificate_name ,
429 const std::string & target_peer_host_name ) :
430 m_profile(profile) ,
431 m_ssl(nullptr,std::ptr_fun(deleter)) ,
432 m_log_fn(profile.lib().log()) ,
433 m_verbose(profile.lib().verbose()) ,
434 m_fd_set(false) ,
435 m_required_peer_certificate_name(required_peer_certificate_name) ,
436 m_verified(false)
437 {
438 m_ssl.reset( SSL_new(profile.p()) ) ;
439 if( m_ssl.get() == nullptr )
440 throw Error( "SSL_new" , ERR_get_error() ) ;
441
442 // TODO feature test for SSL_set_tlsext_host_name() ?
443 if( !target_peer_host_name.empty() )
444 SSL_set_tlsext_host_name( m_ssl.get() , target_peer_host_name.c_str() ) ;
445
446 // store a pointer from SSL to ProtocolImp
447 SSL_set_ex_data( m_ssl.get() , profile.lib().index() , this ) ;
448 }
449
450 GSsl::OpenSSL::ProtocolImp::~ProtocolImp()
451 {
452 }
453
454 void GSsl::OpenSSL::ProtocolImp::deleter( SSL * p )
455 {
456 if( p != nullptr )
457 SSL_free( p ) ;
458 }
459
460 void GSsl::OpenSSL::ProtocolImp::clearErrors()
461 {
462 // "The current thread's error queue must be empty before [SSL_connect,SSL_accept,SSL_read,SSL_write] "
463 // "is attempted, or SSL_get_error() will not work reliably."
464 Error::clearErrors() ;
465 }
466
467 int GSsl::OpenSSL::ProtocolImp::error( const char * op , int rc ) const
468 {
469 int e = SSL_get_error( m_ssl.get() , rc ) ;
470 logErrors( op , rc , e , Protocol::str(convert(e)) ) ;
471 return e ;
472 }
473
474 GSsl::Protocol::Result GSsl::OpenSSL::ProtocolImp::convert( int e )
475 {
476 if( e == SSL_ERROR_WANT_READ ) return Protocol::Result_read ;
477 if( e == SSL_ERROR_WANT_WRITE ) return Protocol::Result_write ;
478 return Protocol::Result_error ;
479 }
480
481 GSsl::Protocol::Result GSsl::OpenSSL::ProtocolImp::connect( G::ReadWrite & io )
482 {
483 set( io.fd() ) ;
484 return connect() ;
485 }
486
487 GSsl::Protocol::Result GSsl::OpenSSL::ProtocolImp::accept( G::ReadWrite & io )
488 {
489 set( io.fd() ) ;
490 return accept() ;
491 }
492
493 void GSsl::OpenSSL::ProtocolImp::set( int fd )
494 {
495 if( !m_fd_set )
496 {
497 int rc = SSL_set_fd( m_ssl.get() , fd ) ;
498 if( rc == 0 )
499 throw Error( "SSL_set_fd" , ERR_get_error() ) ;
500
501 if( G::Test::enabled("log-openssl-bio") ) // log bio activity directly to stderr
502 {
503 BIO_set_callback( SSL_get_rbio(m_ssl.get()) , BIO_debug_callback ) ;
504 BIO_set_callback( SSL_get_wbio(m_ssl.get()) , BIO_debug_callback ) ;
505 }
506
507 m_fd_set = true ;
508 }
509 }
510
511 GSsl::Protocol::Result GSsl::OpenSSL::ProtocolImp::connect()
512 {
513 clearErrors() ;
514 int rc = SSL_connect( m_ssl.get() ) ;
515 if( rc >= 1 )
516 {
517 saveResult() ;
518 return Protocol::Result_ok ;
519 }
520 else if( rc == 0 )
521 {
522 return convert(error("SSL_connect",rc)) ;
523 }
524 else // rc < 0
525 {
526 return convert(error("SSL_connect",rc)) ;
527 }
528 }
529
530 GSsl::Protocol::Result GSsl::OpenSSL::ProtocolImp::accept()
531 {
532 clearErrors() ;
533 int rc = SSL_accept( m_ssl.get() ) ;
534 if( rc >= 1 )
535 {
536 saveResult() ;
537 return Protocol::Result_ok ;
538 }
539 else if( rc == 0 )
540 {
541 return convert(error("SSL_accept",rc)) ;
542 }
543 else // rc < 0
544 {
545 return convert(error("SSL_accept",rc)) ;
546 }
547 }
548
549 void GSsl::OpenSSL::ProtocolImp::saveResult()
550 {
551 m_peer_certificate = Certificate(SSL_get_peer_certificate(m_ssl.get()),true).str() ;
552 m_peer_certificate_chain = CertificateChain(SSL_get_peer_cert_chain(m_ssl.get())).str() ;
553 m_verified = !m_peer_certificate.empty() && SSL_get_verify_result(m_ssl.get()) == X509_V_OK ;
554 }
555
556 GSsl::Protocol::Result GSsl::OpenSSL::ProtocolImp::stop()
557 {
558 int rc = SSL_shutdown( m_ssl.get() ) ;
559 return rc == 1 ? Protocol::Result_ok : Protocol::Result_error ; // since quiet shutdown
560 }
561
562 GSsl::Protocol::Result GSsl::OpenSSL::ProtocolImp::read( char * buffer , size_t buffer_size_in , ssize_t & read_size )
563 {
564 read_size = 0 ;
565
566 clearErrors() ;
567 int buffer_size = static_cast<int>(buffer_size_in) ;
568 int rc = SSL_read( m_ssl.get() , buffer , buffer_size ) ;
569 if( rc > 0 )
570 {
571 read_size = static_cast<ssize_t>(rc) ;
572 return SSL_pending(m_ssl.get()) ? Protocol::Result_more : Protocol::Result_ok ;
573 }
574 else if( rc == 0 )
575 {
576 return convert(error("SSL_read",rc)) ;
577 }
578 else // rc < 0
579 {
580 return convert(error("SSL_read",rc)) ;
581 }
582 }
583
584 GSsl::Protocol::Result GSsl::OpenSSL::ProtocolImp::write( const char * buffer , size_t size_in , ssize_t & size_out )
585 {
586 size_out = 0 ;
587 clearErrors() ;
588 int size = static_cast<int>(size_in) ;
589 int rc = SSL_write( m_ssl.get() , buffer , size ) ;
590 if( rc > 0 )
591 {
592 size_out = static_cast<ssize_t>(rc) ;
593 return Protocol::Result_ok ;
594 }
595 else if( rc == 0 )
596 {
597 return convert(error("SSL_write",rc)) ;
598 }
599 else // rc < 0
600 {
601 return convert(error("SSL_write",rc)) ;
602 }
603 }
604
605 std::string GSsl::OpenSSL::ProtocolImp::peerCertificate() const
606 {
607 return m_peer_certificate ;
608 }
609
610 std::string GSsl::OpenSSL::ProtocolImp::peerCertificateChain() const
611 {
612 return m_peer_certificate_chain ;
613 }
614
615 bool GSsl::OpenSSL::ProtocolImp::verified() const
616 {
617 return m_verified ;
618 }
619
620 void GSsl::OpenSSL::ProtocolImp::logErrors( const std::string & op , int rc , int e , const std::string & strerr ) const
621 {
622 if( m_log_fn != nullptr )
623 {
624 if( m_verbose )
625 {
626 std::ostringstream ss ;
627 ss << op << ": rc=" << rc << ": error " << e << " => " << strerr ;
628 (*m_log_fn)( 1 , ss.str() ) ; // 1 => verbose-debug
629 }
630
631 unsigned long ee = 0 ;
632 for( int i = 2 ; i < 10000 ; i++ )
633 {
634 ee = ERR_get_error() ;
635 if( ee == 0 ) break ;
636 Error eee( op , ee ) ;
637 (*m_log_fn)( 3 , std::string() + eee.what() ) ; // 3 => errors-and-warnings
638 }
639 }
640 }
641
642 std::string GSsl::OpenSSL::ProtocolImp::requiredPeerCertificateName() const
643 {
644 return m_required_peer_certificate_name ;
645 }
646
647 // ==
648
649 GSsl::OpenSSL::Error::Error( const std::string & s ) :
650 m_what( "tls error: " + s )
651 {
652 }
653
654 GSsl::OpenSSL::Error::Error( const std::string & fnname , unsigned long e ) :
655 m_what( "tls error: " + fnname + "(): [" + text(e) + "]" )
656 {
657 clearErrors() ;
658 }
659
660 GSsl::OpenSSL::Error::Error( const std::string & fnname , unsigned long e , const std::string & file ) :
661 m_what( "tls error: " + fnname + "(): [" + text(e) + "]: file=[" + file + "]" )
662 {
663 clearErrors() ;
664 }
665
666 void GSsl::OpenSSL::Error::clearErrors()
667 {
668 for( int i = 0 ; ERR_get_error() && i < 10000 ; i++ )
669 {;}
670 }
671
672 GSsl::OpenSSL::Error::~Error() throw ()
673 {
674 }
675
676 const char * GSsl::OpenSSL::Error::what() const throw ()
677 {
678 return m_what.c_str() ;
679 }
680
681 std::string GSsl::OpenSSL::Error::text( unsigned long e )
682 {
683 std::vector<char> v( 300 ) ;
684 ERR_error_string_n( e , &v[0] , v.size() ) ;
685 std::string s( &v[0] , v.size() ) ;
686 return std::string( s.c_str() ) ; // no nuls
687 }
688
689 // ==
690
691 GSsl::OpenSSL::CertificateChain::CertificateChain( STACK_OF(X509) * chain )
692 {
693 for( int i = 0 ; chain != nullptr && i < sk_X509_num(chain) ; i++ )
694 {
695 void * p = sk_X509_value(chain,i) ; if( p == nullptr ) break ;
696 X509 * x509 = reinterpret_cast<X509*>(p) ;
697 m_str.append( Certificate(x509,false).str() ) ;
698 }
699 }
700
701 std::string GSsl::OpenSSL::CertificateChain::str() const
702 {
703 return m_str ;
704 }
705
706 // ==
707
708 GSsl::OpenSSL::Certificate::Certificate( X509 * x509 , bool do_free )
709 {
710 if( x509 == nullptr ) return ;
711 BIO * bio = BIO_new( BIO_s_mem() ) ;
712 int rc = PEM_write_bio_X509( bio , x509 ) ;
713 if( !rc ) return ;
714 BUF_MEM * mem = nullptr ;
715 BIO_get_mem_ptr( bio , &mem ) ;
716 size_t n = mem ? static_cast<size_t>(mem->length) : 0U ;
717 const char * p = mem ? mem->data : nullptr ;
718 std::string data = p&&n ? std::string(p,n) : std::string() ;
719 BIO_free( bio ) ;
720 if( do_free ) X509_free( x509 ) ;
721
722 // sanitise to be strictly printable with embedded newlines
723 std::string result = G::Str::printable( data , '\0' ) ;
724 G::Str::replaceAll( result , std::string(1U,'\0')+"n" , "\n" ) ;
725 G::Str::replaceAll( result , std::string(1U,'\0') , "\\" ) ;
726 m_str = result ;
727 }
728
729 std::string GSsl::OpenSSL::Certificate::str() const
730 {
731 return m_str ;
732 }
733
734 // ==
735
736 GSsl::OpenSSL::Config::Config( G::StringArray & cfg ) :
737 m_min(0) ,
738 m_max(0) ,
739 m_options_set(0L) ,
740 m_options_reset(0L) ,
741 m_noverify(consume(cfg,"noverify"))
742 {
743 #if GCONFIG_HAVE_OPENSSL_SSLv23_METHOD
744 m_server_fn = SSLv23_server_method ;
745 m_client_fn = SSLv23_client_method ;
746 #else
747 m_server_fn = TLS_server_method ;
748 m_client_fn = TLS_client_method ;
749 #endif
750
751 #if GCONFIG_HAVE_OPENSSL_MIN_MAX
752
753 #ifdef SSL3_VERSION
754 if( consume(cfg,"sslv3") ) m_min = SSL3_VERSION ;
755 if( consume(cfg,"-sslv3") ) m_max = SSL3_VERSION ;
756 #endif
757 #ifdef TLS1_VERSION
758 if( consume(cfg,"tlsv1.0") ) m_min = TLS1_VERSION ;
759 if( consume(cfg,"-tlsv1.0") ) m_max = TLS1_VERSION ;
760 #endif
761 #ifdef TLS1_1_VERSION
762 if( consume(cfg,"tlsv1.1") ) m_min = TLS1_1_VERSION ;
763 if( consume(cfg,"-tlsv1.1") ) m_max = TLS1_1_VERSION ;
764 #endif
765 #ifdef TLS1_2_VERSION
766 if( consume(cfg,"tlsv1.2") ) m_min = TLS1_2_VERSION ;
767 if( consume(cfg,"-tlsv1.2") ) m_max = TLS1_2_VERSION ;
768 #endif
769
770 #else
771
772 #ifndef SSL_OP_NO_SSLv2
773 const long SSL_OP_NO_SSLv2 = 0L ;
774 #endif
775 #ifndef SSL_OP_NO_SSLv3
776 const long SSL_OP_NO_SSLv3 = 0L ;
777 #endif
778
779 if( false ) {;}
780
781 #if GCONFIG_HAVE_OPENSSL_SSLv23_METHOD
782 else if( consume(cfg,"sslv2") )
783 {
784 // allow anything - disable all current and future deprecations
785 m_options_reset =
786 SSL_OP_NO_SSLv2 |
787 SSL_OP_NO_SSLv3 |
788 SSL_OP_NO_TLSv1 |
789 SSL_OP_NO_TLSv1_1 |
790 SSL_OP_NO_TLSv1_2 |
791 0L ;
792 m_options_set = 0L ; // disable nothing
793 }
794 #endif
795
796 else if( consume(cfg,"sslv3") )
797 {
798 #if GCONFIG_HAVE_OPENSSL_SSLv3_METHOD
799 if( consume(cfg,"-sslv3") )
800 {
801 m_server_fn = SSLv3_server_method ;
802 m_client_fn = SSLv3_client_method ;
803 }
804 else
805 #endif
806 {
807 m_options_reset =
808 SSL_OP_NO_SSLv3 |
809 SSL_OP_NO_TLSv1 |
810 SSL_OP_NO_TLSv1_1 |
811 SSL_OP_NO_TLSv1_2 |
812 0L ;
813 m_options_set =
814 SSL_OP_NO_SSLv2 |
815 0L ;
816 }
817 }
818
819 else if( consume(cfg,"tlsv1.0") )
820 {
821 #if GCONFIG_HAVE_OPENSSL_TLSv1_METHOD
822 if( consume(cfg,"-tlsv1.0") )
823 {
824 m_server_fn = TLSv1_server_method ;
825 m_client_fn = TLSv1_client_method ;
826 }
827 else
828 #endif
829 {
830 m_options_reset =
831 SSL_OP_NO_TLSv1 |
832 SSL_OP_NO_TLSv1_1 |
833 SSL_OP_NO_TLSv1_2 |
834 0L ;
835 m_options_set =
836 SSL_OP_NO_SSLv2 |
837 SSL_OP_NO_SSLv3 |
838 0L ;
839 }
840 }
841
842 else if( consume(cfg,"tlsv1.1") )
843 {
844 #if GCONFIG_HAVE_OPENSSL_TLSv1_1_METHOD
845 if( consume(cfg,"-tlsv1.1") )
846 {
847 m_server_fn = TLSv1_1_server_method ;
848 m_client_fn = TLSv1_1_client_method ;
849 }
850 else
851 #endif
852 {
853 m_options_reset =
854 SSL_OP_NO_TLSv1_1 |
855 SSL_OP_NO_TLSv1_2 |
856 0L ;
857 m_options_set =
858 SSL_OP_NO_SSLv2 |
859 SSL_OP_NO_SSLv3 |
860 SSL_OP_NO_TLSv1 |
861 0L ;
862 }
863 }
864
865 else if( consume(cfg,"tlsv1.2") )
866 {
867 #if GCONFIG_HAVE_OPENSSL_TLSv1_2_METHOD
868 if( consume(cfg,"-tlsv1.2") )
869 {
870 m_server_fn = TLSv1_2_server_method ;
871 m_client_fn = TLSv1_2_client_method ;
872 }
873 else
874 #endif
875 {
876 m_options_reset =
877 SSL_OP_NO_TLSv1_2 |
878 0L ;
879 m_options_set =
880 SSL_OP_NO_SSLv2 |
881 SSL_OP_NO_SSLv3 |
882 SSL_OP_NO_TLSv1 |
883 SSL_OP_NO_TLSv1_1 |
884 0L ;
885 }
886 }
887
888 // exact protocol versions are handled above because this is not future-proof...
889 if( consume(cfg,"-sslv3") )
890 {
891 m_options_set |=
892 SSL_OP_NO_TLSv1 |
893 SSL_OP_NO_TLSv1_1 |
894 SSL_OP_NO_TLSv1_2 |
895 0L ;
896 }
897 else if( consume(cfg,"-tlsv1.0") )
898 {
899 m_options_set |=
900 SSL_OP_NO_TLSv1_1 |
901 SSL_OP_NO_TLSv1_2 |
902 0L ;
903 }
904 else if( consume(cfg,"-tlsv1.1") )
905 {
906 m_options_set |=
907 SSL_OP_NO_TLSv1_2 |
908 0L ;
909 }
910 #endif
911 }
912
913 bool GSsl::OpenSSL::Config::consume( G::StringArray & list , const std::string & item )
914 {
915 return LibraryImp::consume( list , item ) ;
916 }
917
918 GSsl::OpenSSL::Config::Fn GSsl::OpenSSL::Config::fn( bool server )
919 {
920 return server ? m_server_fn : m_client_fn ;
921 }
922
923 long GSsl::OpenSSL::Config::set() const
924 {
925 return m_options_set ;
926 }
927
928 long GSsl::OpenSSL::Config::reset() const
929 {
930 return m_options_reset ;
931 }
932
933 int GSsl::OpenSSL::Config::min_() const
934 {
935 return m_min ;
936 }
937
938 int GSsl::OpenSSL::Config::max_() const
939 {
940 return m_max ;
941 }
942
943 bool GSsl::OpenSSL::Config::noverify() const
944 {
945 return m_noverify ;
946 }
947 /// \file gssl_openssl.cpp
GSsl::OpenSSL::ProtocolImp::peerCertificateChain
std::string peerCertificateChain() const
Implements Protocol::peerCertificateChain().
Definition: gssl_openssl.cpp:610
GSsl::Profile
A base interface for profile classes that work with concrete classes derived from GSsl::LibraryImpBas...
Definition: gssl.h:392
gssl.h
An interface to an underlying TLS library.
G::Str::printable
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:701
G::ReadWrite
An abstract interface for reading and writing from a non-blocking i/o channel.
Definition: greadwrite.h:49
GSsl::ProtocolImpBase
A base interface for GSsl::Protocol pimple classes.
Definition: gssl.h:405
GSsl::OpenSSL::ProtocolImp::write
Result write(const char *buffer, size_t size_in, ssize_t &size_out)
Implements Protocol::write().
Definition: gssl_openssl.cpp:584
GSsl::OpenSSL::ProtocolImp::accept
Result accept(G::ReadWrite &)
Implements Protocol::accept().
Definition: gssl_openssl.cpp:487
GSsl::OpenSSL::LibraryImp::digester
virtual Digester digester(const std::string &, const std::string &) const override
Implements Library::digester().
Definition: gssl_openssl.cpp:162
GSsl::OpenSSL::LibraryImp::digesters
virtual G::StringArray digesters(bool) const override
Implements Library::digesters().
Definition: gssl_openssl.cpp:145
GSsl::OpenSSL::ProfileImp::newProtocol
virtual ProtocolImpBase * newProtocol(const std::string &, const std::string &) const override
Factory method for a new Protocol object on the heap.
Definition: gssl_openssl.cpp:325
gssl_openssl.h
GSsl::OpenSSL::ProtocolImp::stop
Result stop()
Implements Protocol::stop().
Definition: gssl_openssl.cpp:556
G::Path::basename
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Definition: gpath.cpp:310
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
G::File::isDirectory
static bool isDirectory(const Path &path)
Returns true if the path exists() and is a directory.
Definition: gfile_unix.cpp:73
G::Root
A class which acquires the process's special privileges on construction and releases them on destruct...
Definition: groot.h:49
G::Str::splitIntoTokens
static void splitIntoTokens(const std::string &in, StringArray &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:908
std
STL namespace.
GSsl::Protocol::str
static std::string str(Result result)
Converts a result enumeration into a printable string.
Definition: gssl.cpp:182
groot.h
gtest.h
GSsl::OpenSSL::LibraryImp
An implementation of the GSsl::LibraryImpBase interface for OpenSSL.
Definition: gssl_openssl.h:176
GSsl::OpenSSL::DigesterImp::add
virtual void add(const std::string &) override
Implements Digester::add().
Definition: gssl_openssl.cpp:209
GSsl::Library::impstance
static LibraryImpBase & impstance()
Returns a reference to the pimple object when enabled().
Definition: gssl.cpp:114
GSsl::OpenSSL::DigesterImp::statesize
virtual size_t statesize() const override
Implements Digester::statesize().
Definition: gssl_openssl.cpp:199
GSsl::OpenSSL::Config
Holds protocol version information, etc.
Definition: gssl_openssl.h:78
gstr.h
G::Str::replaceAll
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurances of sub-string 'from' with 'to'...
Definition: gstr.cpp:195
G::ReadWrite::fd
virtual SOCKET fd() const =0
Returns the file descriptor.
GSsl::OpenSSL::Error
An exception class for GSsl::OpenSSL classes.
Definition: gssl_openssl.h:120
GSsl::Digester
Returns an object that can perform a cryptographic hash.
Definition: gssl.h:194
G::Test::enabled
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:69
GSsl::LibraryImpBase::consume
static bool consume(G::StringArray &list, const std::string &item)
A convenience function that removes the item from the list and returns true iff is was removed...
Definition: gssl.cpp:259
gfile.h
gprocess.h
GSsl::OpenSSL::ProtocolImp::verified
bool verified() const
Implements Protocol::verified().
Definition: gssl_openssl.cpp:615
G::File::exists
static bool exists(const Path &file)
Returns true if the file (directory, device etc.) exists.
Definition: gfile.cpp:141
GSsl::OpenSSL::LibraryImp::addProfile
virtual void addProfile(const std::string &name, bool is_server_profile, const std::string &key_file, const std::string &cert_file, const std::string &ca_file, const std::string &default_peer_certificate_name, const std::string &default_peer_host_name, const std::string &profile_config) override
Implements Library::addProfile().
Definition: gssl_openssl.cpp:106
GSsl::OpenSSL::DigesterImp
An implementation of the GSsl::DigesterImpBase interface for OpenSSL.
Definition: gssl_openssl.h:268
gexception.h
GSsl::OpenSSL::ProtocolImp::read
Result read(char *buffer, size_t buffer_size, ssize_t &read_size)
Implements Protocol::read().
Definition: gssl_openssl.cpp:562
GSsl::OpenSSL::DigesterImp::valuesize
virtual size_t valuesize() const override
Implements Digester::valuesize().
Definition: gssl_openssl.cpp:194
gpath.h
GSsl::OpenSSL::ProtocolImp::connect
Result connect(G::ReadWrite &)
Implements Protocol::connect().
Definition: gssl_openssl.cpp:481
GSsl::OpenSSL::ProtocolImp::peerCertificate
std::string peerCertificate() const
Implements Protocol::peerCertificate().
Definition: gssl_openssl.cpp:605
G::Str::join
static std::string join(const std::string &sep, const StringArray &strings)
Concatenates an array of strings with separators.
Definition: gstr.cpp:1023
GSsl::OpenSSL::DigesterImp::blocksize
virtual size_t blocksize() const override
Implements Digester::blocksize().
Definition: gssl_openssl.cpp:189
GSsl::OpenSSL::LibraryImp::hasProfile
virtual bool hasProfile(const std::string &) const override
Implements Library::hasProfile().
Definition: gssl_openssl.cpp:117
GSsl::OpenSSL::DigesterImp::state
virtual std::string state() override
Implements Digester::state().
Definition: gssl_openssl.cpp:204
GSsl
TLS/SSL transport layer security classes.
Definition: gssl.h:32
GSsl::OpenSSL::LibraryImp::id
virtual std::string id() const override
Implements Library::id().
Definition: gssl_openssl.cpp:86
GSsl::OpenSSL::LibraryImp::profile
virtual const GSsl::Profile & profile(const std::string &) const override
Implements Library::profile().
Definition: gssl_openssl.cpp:123
GSsl::OpenSSL::DigesterImp::value
virtual std::string value() override
Implements Digester::value().
Definition: gssl_openssl.cpp:214
G::Path
A Path object represents a file system path.
Definition: gpath.h:72
GSsl::OpenSSL::ProtocolImp
An implementation of the GSsl::ProtocolImpBase interface for OpenSSL.
Definition: gssl_openssl.h:220
GSsl::OpenSSL::ProfileImp
An implementation of the GSsl::Profile interface for OpenSSL.
Definition: gssl_openssl.h:140
gdebug.h