E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gssl_mbedtls.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_mbedtls.cpp
19 //
20
21 #include "gdef.h"
22 #include "gssl.h"
23 #include "gssl_mbedtls.h"
24 #include "ghashstate.h"
25 #include "gpath.h"
26 #include "groot.h"
27 #include "gtest.h"
28 #include "gstr.h"
29 #include "gfile.h"
30 #include "glog.h"
31 #include "gassert.h"
32 #include <mbedtls/ssl.h>
33 #include <mbedtls/ssl_ciphersuites.h>
34 #include <mbedtls/entropy.h>
35 #include <mbedtls/ctr_drbg.h>
36 #include <mbedtls/certs.h>
37 #include <mbedtls/error.h>
38 #include <mbedtls/version.h>
39 #include <mbedtls/pem.h>
40 #include <mbedtls/base64.h>
41 #include <mbedtls/debug.h>
42 #include <sstream>
43 #include <fstream>
44 #include <exception>
45 #include <iomanip>
46
47 namespace
48 {
49 template <typename T>
50 struct Cleanup
51 {
52 explicit Cleanup( void (*fn)(T*) ) : m_p(nullptr) , m_fn(fn) {}
53 ~Cleanup() { reset() ; }
54 void reset( T * p = nullptr ) { if(m_p) m_fn(m_p) ; m_p = p ; }
55 void release() { m_p = nullptr ; }
56 T * m_p ;
57 void (*m_fn)(T*) ;
58 } ;
59 }
60
61 GSsl::MbedTls::LibraryImp::LibraryImp( G::StringArray & library_config , Library::LogFn log_fn , bool verbose ) :
62 m_log_fn(log_fn) ,
63 m_config(library_config)
64 {
65 mbedtls_debug_set_threshold( verbose ? 3 : 1 ) ; // "Messages that have a level over the threshold value are ignored."
66 }
67
68 GSsl::MbedTls::LibraryImp::~LibraryImp()
69 {
70 }
71
72 const GSsl::MbedTls::Rng & GSsl::MbedTls::LibraryImp::rng() const
73 {
74 return m_rng ;
75 }
76
77 void GSsl::MbedTls::LibraryImp::addProfile( const std::string & profile_name , bool is_server_profile ,
78 const std::string & key_file , const std::string & cert_file , const std::string & ca_file ,
79 const std::string & default_peer_certificate_name , const std::string & default_peer_host_name ,
80 const std::string & profile_config )
81 {
82 shared_ptr<ProfileImp> profile_ptr(
83 new ProfileImp(*this,is_server_profile,key_file,cert_file,ca_file,
84 default_peer_certificate_name,default_peer_host_name,profile_config) ) ;
85 m_profile_map.insert( Map::value_type(profile_name,profile_ptr) ) ;
86 }
87
88 bool GSsl::MbedTls::LibraryImp::hasProfile( const std::string & profile_name ) const
89 {
90 Map::const_iterator p = m_profile_map.find( profile_name ) ;
91 return p != m_profile_map.end() ;
92 }
93
94 const GSsl::Profile & GSsl::MbedTls::LibraryImp::profile( const std::string & profile_name ) const
95 {
96 Map::const_iterator p = m_profile_map.find( profile_name ) ;
97 if( p == m_profile_map.end() ) throw Error( "no such profile: [" + profile_name + "]" ) ;
98 return *(*p).second.get() ;
99 }
100
101 GSsl::Library::LogFn GSsl::MbedTls::LibraryImp::log() const
102 {
103 return m_log_fn ;
104 }
105
106 std::string GSsl::MbedTls::LibraryImp::sid()
107 {
108 std::vector<char> buffer( 100U ) ; // "at least 18"
109 G_ASSERT( buffer.size() >= 18U ) ;
110 mbedtls_version_get_string_full( &buffer[0] ) ;
111 buffer[buffer.size()-1U] = '\0' ;
112 return G::Str::printable( std::string(&buffer[0]) ) ;
113 }
114
115 std::string GSsl::MbedTls::LibraryImp::id() const
116 {
117 return sid() ;
118 }
119
120 GSsl::MbedTls::Config GSsl::MbedTls::LibraryImp::config() const
121 {
122 return m_config ;
123 }
124
125 std::string GSsl::MbedTls::LibraryImp::credit( const std::string & prefix , const std::string & eol , const std::string & eot )
126 {
127 std::ostringstream ss ;
128 ss
129 << prefix << "mbed TLS: Copy" << "right (C) 2006-2016, ARM Limited" << eol
130 << eot ;
131 return ss.str() ;
132 }
133
134 G::StringArray GSsl::MbedTls::LibraryImp::digesters( bool ) const
135 {
136 G::StringArray result ;
137 result.push_back( "MD5" ) ;
138 result.push_back( "SHA1" ) ;
139 result.push_back( "SHA256" ) ;
140 return result ;
141 }
142
143 GSsl::Digester GSsl::MbedTls::LibraryImp::digester( const std::string & hash_type , const std::string & state ) const
144 {
145 return Digester( new MbedTls::DigesterImp(hash_type,state) ) ;
146 }
147
148 // ==
149
150 GSsl::MbedTls::Config::Config( G::StringArray & config ) :
151 m_noverify(consume(config,"noverify")),
152 m_min(-1),
153 m_max(-1)
154 {
155 static const int SSL_v3 = MBEDTLS_SSL_MINOR_VERSION_0 ;
156 static const int TLS_v1_0 = MBEDTLS_SSL_MINOR_VERSION_1 ;
157 static const int TLS_v1_1 = MBEDTLS_SSL_MINOR_VERSION_2 ;
158 static const int TLS_v1_2 = MBEDTLS_SSL_MINOR_VERSION_3 ;
159
160 G_ASSERT( SSL_v3 >= 0 ) ;
161 G_ASSERT( TLS_v1_0 >= 0 ) ;
162 G_ASSERT( TLS_v1_1 >= 0 ) ;
163 G_ASSERT( TLS_v1_2 >= 0 ) ;
164
165 if( consume(config,"sslv3") ) m_min = SSL_v3 ;
166 if( consume(config,"tlsv1.0") ) m_min = TLS_v1_0 ;
167 if( consume(config,"tlsv1.1") ) m_min = TLS_v1_1 ;
168 if( consume(config,"tlsv1.2") ) m_min = TLS_v1_2 ;
169
170 if( consume(config,"-sslv3") ) m_max = SSL_v3 ;
171 if( consume(config,"-tlsv1.0") ) m_max = TLS_v1_0 ;
172 if( consume(config,"-tlsv1.1") ) m_max = TLS_v1_1 ;
173 if( consume(config,"-tlsv1.2") ) m_max = TLS_v1_2 ;
174 }
175
176 int GSsl::MbedTls::Config::min_() const
177 {
178 return m_min ;
179 }
180
181 int GSsl::MbedTls::Config::max_() const
182 {
183 return m_max ;
184 }
185
186 bool GSsl::MbedTls::Config::noverify() const
187 {
188 return m_noverify ;
189 }
190
191 bool GSsl::MbedTls::Config::consume( G::StringArray & list , const std::string & item )
192 {
193 return LibraryImp::consume( list , item ) ;
194 }
195
196 // ==
197
198 GSsl::MbedTls::DigesterImp::DigesterImp( const std::string & hash_type , const std::string & state ) :
199 m_hash_type(G::Str::upper(hash_type))
200 {
201 if( m_hash_type == "MD5" )
202 {
203 mbedtls_md5_init( &m_md5 ) ;
204 if( state.empty() )
205 mbedtls_md5_starts( &m_md5 ) ;
206 else
207 G::HashState<16,uint32_t,uint32_t>::decode( state , m_md5.state , m_md5.total[0] ) ;
208 }
209 else if( m_hash_type == "SHA1" )
210 {
211 mbedtls_sha1_init( &m_sha1 ) ;
212 if( state.empty() )
213 mbedtls_sha1_starts( &m_sha1 ) ;
214 else
215 G::HashState<20,uint32_t,uint32_t>::decode( state , m_sha1.state , m_sha1.total[0] ) ;
216 }
217 else if( m_hash_type == "SHA256" )
218 {
219 mbedtls_sha256_init( &m_sha256 ) ;
220 if( state.empty() )
221 mbedtls_sha256_starts( &m_sha256 , 0 ) ;
222 else
223 G::HashState<32,uint32_t,uint32_t>::decode( state , m_sha256.state , m_sha256.total[0] ) ;
224 }
225 }
226
227 GSsl::MbedTls::DigesterImp::~DigesterImp()
228 {
229 if( m_hash_type == "MD5" )
230 mbedtls_md5_free( &m_md5 ) ;
231 else if( m_hash_type == "SHA1" )
232 mbedtls_sha1_free( &m_sha1 ) ;
233 else if( m_hash_type == "SHA256" )
234 mbedtls_sha256_free( &m_sha256 ) ;
235 }
236
237 void GSsl::MbedTls::DigesterImp::add( const std::string & s )
238 {
239 if( m_hash_type == "MD5" )
240 mbedtls_md5_update( &m_md5 , reinterpret_cast<const unsigned char*>(s.data()) , s.size() ) ;
241 else if( m_hash_type == "SHA1" )
242 mbedtls_sha1_update( &m_sha1 , reinterpret_cast<const unsigned char*>(s.data()) , s.size() ) ;
243 else if( m_hash_type == "SHA256" )
244 mbedtls_sha256_update( &m_sha256 , reinterpret_cast<const unsigned char*>(s.data()) , s.size() ) ;
245 }
246
247 std::string GSsl::MbedTls::DigesterImp::value()
248 {
249 if( m_hash_type == "MD5" )
250 {
251 unsigned char buffer[16] ;
252 mbedtls_md5_finish( &m_md5 , buffer ) ;
253 return std::string( reinterpret_cast<const char*>(buffer) , sizeof(buffer) ) ;
254 }
255 else if( m_hash_type == "SHA1" )
256 {
257 unsigned char buffer[20] ;
258 mbedtls_sha1_finish( &m_sha1 , buffer ) ;
259 return std::string( reinterpret_cast<const char*>(buffer) , sizeof(buffer) ) ;
260 }
261 else if( m_hash_type == "SHA256" )
262 {
263 unsigned char buffer[32] ;
264 mbedtls_sha256_finish( &m_sha256 , buffer ) ;
265 return std::string( reinterpret_cast<const char*>(buffer) , sizeof(buffer) ) ;
266 }
267 else
268 {
269 return std::string() ;
270 }
271 }
272
273 std::string GSsl::MbedTls::DigesterImp::state()
274 {
275 if( m_hash_type == "MD5" )
276 return G::HashState<16,uint32_t,uint32_t>::encode( m_md5.state , m_md5.total[0] ) ;
277 else if( m_hash_type == "SHA1" )
278 return G::HashState<20,uint32_t,uint32_t>::encode( m_sha1.state , m_sha1.total[0] ) ;
279 else if( m_hash_type == "SHA256" )
280 return G::HashState<32,uint32_t,uint32_t>::encode( m_sha256.state , m_sha256.total[0] ) ;
281 else
282 return std::string() ;
283 }
284
285 size_t GSsl::MbedTls::DigesterImp::blocksize() const
286 {
287 return 64U ;
288 }
289
290 size_t GSsl::MbedTls::DigesterImp::valuesize() const
291 {
292 if( m_hash_type == "MD5" )
293 return 16U ;
294 else if( m_hash_type == "SHA1" )
295 return 20U ;
296 else if( m_hash_type == "SHA256" )
297 return 32U ;
298 else
299 return 0U ;
300 }
301
302 size_t GSsl::MbedTls::DigesterImp::statesize() const
303 {
304 return valuesize() + 4U ;
305 }
306
307 // ==
308
309 GSsl::MbedTls::ProfileImp::ProfileImp( const LibraryImp & library_imp , bool is_server_profile ,
310 const std::string & key_file , const std::string & cert_file , const std::string & ca_path ,
311 const std::string & default_peer_certificate_name , const std::string & default_peer_host_name ,
312 const std::string & profile_config ) :
313 m_library_imp(library_imp) ,
314 m_default_peer_certificate_name(default_peer_certificate_name) ,
315 m_default_peer_host_name(default_peer_host_name)
316 {
317 Cleanup<mbedtls_ssl_config> cleanup( mbedtls_ssl_config_free ) ;
318
319 // use library config, or override with profile config
320 Config extra_config = library_imp.config() ;
321 if( !profile_config.empty() )
322 {
323 G::StringArray profile_config_list = G::Str::splitIntoTokens( profile_config , "," ) ;
324 extra_config = Config( profile_config_list ) ;
325 if( !profile_config_list.empty() )
326 G_WARNING( "GSsl::MbedTls::ProfileImp::ctor: tls-config: tls " << (is_server_profile?"server":"client")
327 << " profile configuration ignored: [" << G::Str::join(",",profile_config_list) << "]" ) ;
328 }
329
330 // initialise the mbedtls_ssl_config structure
331 {
332 static mbedtls_ssl_config config_zero ;
333 m_config = config_zero ;
334 mbedtls_ssl_config_init( &m_config ) ;
335 cleanup.reset( &m_config ) ;
336 int rc = mbedtls_ssl_config_defaults( &m_config ,
337 is_server_profile ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT , // see also mbedtls_ssl_conf_endpoint()
338 MBEDTLS_SSL_TRANSPORT_STREAM ,
339 MBEDTLS_SSL_PRESET_DEFAULT ) ;
340 if( rc ) throw Error( "mbedtls_ssl_config_defaults" , rc ) ;
341 }
342
343 // load up the certificate and private key ready for mbedtls_ssl_conf_own_cert()
344 if( !key_file.empty() )
345 m_pk.load( key_file ) ;
346 if( !cert_file.empty() )
347 m_certificate.load( cert_file ) ;
348
349 // identify our certificate/private-key combination
350 if( m_certificate.loaded() )
351 {
352 int rc = mbedtls_ssl_conf_own_cert( &m_config , m_certificate.ptr() , m_pk.ptr() ) ;
353 if( rc != 0 )
354 throw Error( "mbedtls_ssl_conf_own_cert" , rc ) ;
355 }
356
357 // configure verification
358 {
359 std::string ca_path_default = "/etc/ssl/certs/ca-certificates.crt" ; // see "man update-ca-certificates"
360 if( ca_path.empty() )
361 {
362 // verify if possible, but continue on failure - see mbedtls_ssl_get_verify_result()
363 mbedtls_ssl_conf_authmode( &m_config , MBEDTLS_SSL_VERIFY_OPTIONAL ) ;
364 }
365 else if( ca_path == "<none>" )
366 {
367 // dont verify
368 mbedtls_ssl_conf_authmode( &m_config , MBEDTLS_SSL_VERIFY_NONE ) ;
369 }
370 else if( ca_path == "<default>" )
371 {
372 // verify against the default ca database
373 m_ca_list.load( ca_path_default ) ;
374 bool no_verify = extra_config.noverify() ;
375 mbedtls_ssl_conf_ca_chain( &m_config , m_ca_list.ptr() , crl() ) ;
376 mbedtls_ssl_conf_authmode( &m_config , no_verify ? MBEDTLS_SSL_VERIFY_OPTIONAL : MBEDTLS_SSL_VERIFY_REQUIRED ) ;
377 }
378 else
379 {
380 // verify against the given ca database
381 m_ca_list.load( ca_path ) ;
382 bool no_verify = extra_config.noverify() ;
383 mbedtls_ssl_conf_ca_chain( &m_config , m_ca_list.ptr() , crl() ) ;
384 mbedtls_ssl_conf_authmode( &m_config , no_verify ? MBEDTLS_SSL_VERIFY_OPTIONAL : MBEDTLS_SSL_VERIFY_REQUIRED ) ;
385 }
386 }
387
388 // configure protocol version
389 {
390 if( extra_config.min_() >= 0 )
391 mbedtls_ssl_conf_min_version( &m_config , MBEDTLS_SSL_MAJOR_VERSION_3 , extra_config.min_() ) ;
392 if( extra_config.max_() >= 0 )
393 mbedtls_ssl_conf_max_version( &m_config , MBEDTLS_SSL_MAJOR_VERSION_3 , extra_config.max_() ) ;
394 }
395
396 // hooks
397 {
398 mbedtls_ssl_conf_rng( &m_config , mbedtls_ctr_drbg_random , m_library_imp.rng().ptr() ) ;
399 mbedtls_ssl_conf_dbg( &m_config , onDebug , this ) ;
400 }
401
402 // other configuration
403 {
404 mbedtls_ssl_conf_renegotiation( &m_config , MBEDTLS_SSL_RENEGOTIATION_DISABLED ) ;
405 }
406 cleanup.release() ;
407 }
408
409 GSsl::MbedTls::ProfileImp::~ProfileImp()
410 {
411 mbedtls_ssl_config_free( &m_config ) ;
412 }
413
414 GSsl::ProtocolImpBase * GSsl::MbedTls::ProfileImp::newProtocol( const std::string & peer_certificate_name ,
415 const std::string & peer_host_name ) const
416 {
417 return
418 new MbedTls::ProtocolImp( *this ,
419 peer_certificate_name.empty()?defaultPeerCertificateName():peer_certificate_name ,
420 peer_host_name.empty()?defaultPeerHostName():peer_host_name ) ;
421 }
422
423 mbedtls_x509_crl * GSsl::MbedTls::ProfileImp::crl() const
424 {
425 // TODO certificate revocation list
426 return nullptr ;
427 }
428
429 const mbedtls_ssl_config * GSsl::MbedTls::ProfileImp::config() const
430 {
431 return &m_config ;
432 }
433
434 void GSsl::MbedTls::ProfileImp::onDebug( void * This , int level_in , const char * file , int line , const char * message )
435 {
436 try
437 {
438 reinterpret_cast<ProfileImp*>(This)->doDebug( level_in , file , line , message ) ;
439 }
440 catch(...) // callback from c code
441 {
442 }
443 }
444
445 void GSsl::MbedTls::ProfileImp::doDebug( int level_in , const char * file , int line , const char * message )
446 {
447 // in practice even level 0 messages are too noisy, so discard them all :(
448 level_in = 4 ;
449
450 // map from 'level_in' mbedtls levels to GSsl::Library::LogFn levels...
451 // 4 -> <discarded>
452 // 3 -> logAt(1) (verbose-debug)
453 // 2 -> logAt(1) (verbose-debug)
454 // 1 -> logAt(3) (errors-and-warnings)
455 // 0 -> logAt(3) (errors-and-warnings)
456 // ... with this code doing its own logAt(2) (useful-information)
457 // see also mbedtls_debug_set_threshold() in LibraryImp::ctor()
458 //
459 int level_out = level_in >= 4 ? 0 : ( level_in >= 2 ? 1 : 3 ) ;
460 if( m_library_imp.log() && level_out )
461 {
462 std::ostringstream ss ;
463 G::Path path( file ? std::string(file) : std::string() ) ;
464 ss << path.basename() << "(" << line << "): " << (message?message:"") ;
465 logAt( level_out , ss.str() ) ;
466 }
467 }
468
469 void GSsl::MbedTls::ProfileImp::logAt( int level_out , std::string s ) const
470 {
471 Library::LogFn log_fn = m_library_imp.log() ;
472 if( log_fn )
473 {
474 s = G::Str::printable( G::Str::trimmed(s,G::Str::ws()) ) ;
475 if( !s.empty() )
476 (*log_fn)( level_out , s ) ;
477 }
478 }
479
480 const std::string & GSsl::MbedTls::ProfileImp::defaultPeerCertificateName() const
481 {
482 return m_default_peer_certificate_name ;
483 }
484
485 const std::string & GSsl::MbedTls::ProfileImp::defaultPeerHostName() const
486 {
487 return m_default_peer_host_name ;
488 }
489
490 // ==
491
492 GSsl::MbedTls::ProtocolImp::ProtocolImp( const ProfileImp & profile , const std::string & required_peer_certificate_name ,
493 const std::string & target_peer_host_name ) :
494 m_profile(profile) ,
495 m_io(nullptr) ,
496 m_ssl(profile.config()) ,
497 m_verified(false)
498 {
499 mbedtls_ssl_set_bio( m_ssl.ptr() , this , doSend , doRecv , nullptr/*doRecvTimeout*/ ) ;
500
501 // the mbedtls api uses the same function for the peer-certificate-name validation
502 // and peer-host-name indication -- it also interprets wildcards in the certificate
503 // cname ("www.example.com" matches "CN=*.example.com") so the peer-host-name is
504 // preferred here over the peer-certificate-name
505 //
506 std::string name = target_peer_host_name.empty() ? required_peer_certificate_name : target_peer_host_name ;
507 if( !name.empty() )
508 mbedtls_ssl_set_hostname( m_ssl.ptr() , name.c_str() ) ;
509 }
510
511 GSsl::MbedTls::ProtocolImp::~ProtocolImp()
512 {
513 }
514
515 GSsl::Protocol::Result GSsl::MbedTls::ProtocolImp::read( char * buffer , size_t buffer_size_in , ssize_t & data_size_out )
516 {
517 int rc = mbedtls_ssl_read( m_ssl.ptr() , reinterpret_cast<unsigned char*>(buffer) , buffer_size_in ) ;
518 data_size_out = rc < 0 ? 0 : rc ;
519 if( rc == 0 ) return Protocol::Result_error ; // disconnected
520 size_t available = rc > 0 ? mbedtls_ssl_get_bytes_avail( m_ssl.ptr() ) : 0U ;
521 return convert( "mbedtls_ssl_read" , rc , available > 0U ) ;
522 }
523
524 GSsl::Protocol::Result GSsl::MbedTls::ProtocolImp::write( const char * buffer_in , size_t data_size_in ,
525 ssize_t & data_size_out )
526 {
527 const unsigned char * p = reinterpret_cast<const unsigned char *>(buffer_in) ;
528 ssize_t n = static_cast<ssize_t>(data_size_in) ; G_ASSERT( n >= 0 ) ;
529 for(;;)
530 {
531 int rc = mbedtls_ssl_write( m_ssl.ptr() , p , n ) ;
532 if( rc >= n )
533 {
534 data_size_out = static_cast<ssize_t>(data_size_in) ;
535 return Protocol::Result_ok ;
536 }
537 else if( rc >= 0 )
538 {
539 n -= rc ;
540 p += rc ;
541 }
542 else
543 {
544 return convert( "mbedtls_ssl_write" , rc ) ;
545 }
546 }
547 }
548
549 GSsl::Protocol::Result GSsl::MbedTls::ProtocolImp::stop()
550 {
551 return Protocol::Result_ok ;
552 }
553
554 int GSsl::MbedTls::ProtocolImp::doRecvTimeout( void * This , unsigned char * p , size_t n , uint32_t timeout_ms )
555 {
556 // with event-driven i/o the timeout is probably not useful since
557 // higher layers will time out eventually
558 return doRecv( This , p , n ) ;
559 }
560
561 int GSsl::MbedTls::ProtocolImp::doRecv( void * This , unsigned char * p , size_t n )
562 {
563 G::ReadWrite * io = reinterpret_cast<ProtocolImp*>(This)->m_io ;
564 G_ASSERT( io != nullptr ) ;
565 ssize_t rc = io->read( reinterpret_cast<char*>(p) , n ) ;
566 if( rc < 0 && io->eWouldBlock() ) return MBEDTLS_ERR_SSL_WANT_READ ;
567 if( rc < 0 ) return MBEDTLS_ERR_NET_RECV_FAILED ; // or CONN_RESET for EPIPE or ECONNRESET
568 return rc ;
569 }
570
571 int GSsl::MbedTls::ProtocolImp::doSend( void * This , const unsigned char * p , size_t n )
572 {
573 G::ReadWrite * io = reinterpret_cast<ProtocolImp*>(This)->m_io ;
574 G_ASSERT( io != nullptr ) ;
575 ssize_t rc = io->write( reinterpret_cast<const char*>(p) , n ) ;
576 if( rc < 0 && io->eWouldBlock() ) return MBEDTLS_ERR_SSL_WANT_WRITE ;
577 if( rc < 0 ) return MBEDTLS_ERR_NET_SEND_FAILED ; // or CONN_RESET for EPIPE or ECONNRESET
578 return rc ;
579 }
580
581 GSsl::Protocol::Result GSsl::MbedTls::ProtocolImp::convert( const char * fnname , int rc , bool more )
582 {
583 // Result_ok , Result_read , Result_write , Result_error , Result_more
584 if( rc == MBEDTLS_ERR_SSL_WANT_READ ) return Protocol::Result_read ;
585 if( rc == MBEDTLS_ERR_SSL_WANT_WRITE ) return Protocol::Result_write ;
586 if( rc == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY ) return Protocol::Result_error ;
587 if( rc < 0 ) // throw on error -- moot
588 {
589 throw Error( fnname , rc , verifyResultString(rc) ) ;
590 }
591 return more ? Protocol::Result_more : Protocol::Result_ok ;
592 }
593
594 std::string GSsl::MbedTls::ProtocolImp::verifyResultString( int rc )
595 {
596 if( rc == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED )
597 {
598 int verify_result = mbedtls_ssl_get_verify_result( m_ssl.ptr() ) ; // MBEDTLS_X509_BADCERT_...
599 std::vector<char> buffer( 1024U ) ;
600 mbedtls_x509_crt_verify_info( &buffer[0] , buffer.size() , "" , verify_result ) ;
601 buffer[buffer.size()-1U] = '\0' ;
602 return G::Str::printable( &buffer[0] ) ;
603 }
604 else
605 {
606 return std::string() ;
607 }
608 }
609
610 GSsl::Protocol::Result GSsl::MbedTls::ProtocolImp::connect( G::ReadWrite & io )
611 {
612 m_io = &io ;
613 return handshake() ;
614 }
615
616 GSsl::Protocol::Result GSsl::MbedTls::ProtocolImp::accept( G::ReadWrite & io )
617 {
618 m_io = &io ;
619 return handshake() ;
620 }
621
622 GSsl::Protocol::Result GSsl::MbedTls::ProtocolImp::handshake()
623 {
624 int rc = mbedtls_ssl_handshake( m_ssl.ptr() ) ;
625 Result result = convert( "mbedtls_ssl_handshake" , rc ) ;
626 if( result == Protocol::Result_ok )
627 {
628 const char * vstr = "" ;
629 if( m_ssl.ptr()->conf->authmode == MBEDTLS_SSL_VERIFY_NONE )
630 {
631 m_verified = false ;
632 vstr = "peer certificate not verified" ;
633 }
634 else if( m_ssl.ptr()->conf->authmode == MBEDTLS_SSL_VERIFY_OPTIONAL )
635 {
636 int v = mbedtls_ssl_get_verify_result( m_ssl.ptr() ) ;
637 m_verified = v == 0 ;
638 vstr = v == 0 ? "peer certificate verified" : "peer certificate failed to verify" ;
639 if( v & MBEDTLS_X509_BADCERT_EXPIRED ) vstr = "peer certificate has expired" ;
640 if( v & MBEDTLS_X509_BADCERT_REVOKED ) vstr = "peer certificate has been revoked" ;
641 if( v & MBEDTLS_X509_BADCERT_NOT_TRUSTED ) vstr = "peer certificate not signed by a trusted ca" ;
642 if( v & MBEDTLS_X509_BADCERT_MISSING ) vstr = "peer certificate missing" ;
643 if( v & MBEDTLS_X509_BADCERT_SKIP_VERIFY ) vstr = "peer certificate verification was skipped" ;
644 }
645 else // MBEDTLS_SSL_VERIFY_REQUIRED
646 {
647 m_verified = true ;
648 vstr = "peer certificate verified" ;
649 }
650
651 m_peer_certificate = getPeerCertificate() ;
652 m_peer_certificate_chain = m_peer_certificate ; // not implemted
653
654 m_profile.logAt( 2 , std::string() + "protocol=" + protocol() + ", cipher=" + cipher() + ", verification=[" + vstr + "]" ) ;
655 }
656 return result ;
657 }
658
659 std::string GSsl::MbedTls::ProtocolImp::protocol() const
660 {
661 const char * p = mbedtls_ssl_get_version( m_ssl.ptr() ) ;
662 return G::Str::printable(p?std::string(p):std::string()) ;
663 }
664
665 std::string GSsl::MbedTls::ProtocolImp::cipher() const
666 {
667 const char * p = mbedtls_ssl_get_ciphersuite( m_ssl.ptr() ) ;
668 return G::Str::printable(p?std::string(p):std::string()) ;
669 }
670
671 const GSsl::Profile & GSsl::MbedTls::ProtocolImp::profile() const
672 {
673 return m_profile ;
674 }
675
676 std::string GSsl::MbedTls::ProtocolImp::getPeerCertificate()
677 {
678 std::string result ;
679 const mbedtls_x509_crt * certificate = mbedtls_ssl_get_peer_cert( m_ssl.ptr() ) ;
680 if( certificate != nullptr )
681 {
682 const char * head = "-----BEGIN CERTIFICATE-----\n" ;
683 const char * tail = "-----END CERTIFICATE-----\n" ;
684
685 size_t n = 0U ;
686 unsigned char c = '\0' ;
687 int rc = mbedtls_pem_write_buffer( head , tail , certificate->raw.p , certificate->raw.len , &c , 0 , &n ) ;
688 if( n == 0U || rc != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL )
689 throw Error( "certificate error" ) ;
690 n = n + n ; // old polarssl bug required this
691
692 std::vector<unsigned char> buffer( n ) ;
693 rc = mbedtls_pem_write_buffer( head , tail , certificate->raw.p , certificate->raw.len ,
694 &buffer[0] , buffer.size() , &n ) ;
695 if( n == 0 || rc != 0 )
696 throw Error( "certificate error" ) ;
697
698 result = std::string( reinterpret_cast<const char *>(&buffer[0]) , n-1U ) ;
699 if( std::string(result.c_str()) != result || result.find(tail) == std::string::npos )
700 throw Error( "certificate error" ) ;
701 }
702 return result ;
703 }
704
705 std::string GSsl::MbedTls::ProtocolImp::peerCertificate() const
706 {
707 return m_peer_certificate ;
708 }
709
710 std::string GSsl::MbedTls::ProtocolImp::peerCertificateChain() const
711 {
712 return m_peer_certificate_chain ;
713 }
714
715 bool GSsl::MbedTls::ProtocolImp::verified() const
716 {
717 return m_verified ;
718 }
719
720 // ==
721
722 GSsl::MbedTls::Context::Context( const mbedtls_ssl_config * config_p )
723 {
724 static mbedtls_ssl_context x_zero ;
725 x = x_zero ;
726 mbedtls_ssl_init( &x ) ;
727
728 int rc = mbedtls_ssl_setup( &x , config_p ) ;
729 if( rc ) throw Error( "mbedtls_ssl_setup" , rc ) ;
730 }
731
732 GSsl::MbedTls::Context::~Context()
733 {
734 mbedtls_ssl_free( &x ) ;
735 }
736
737 mbedtls_ssl_context * GSsl::MbedTls::Context::ptr()
738 {
739 return &x ;
740 }
741
742 mbedtls_ssl_context * GSsl::MbedTls::Context::ptr() const
743 {
744 return const_cast<mbedtls_ssl_context*>(&x) ;
745 }
746
747 // ==
748
749 GSsl::MbedTls::Rng::Rng()
750 {
751 static mbedtls_entropy_context entropy_zero ;
752 entropy = entropy_zero ;
753 mbedtls_entropy_init( &entropy ) ;
754
755 static mbedtls_ctr_drbg_context x_zero ;
756 x = x_zero ;
757 mbedtls_ctr_drbg_init( &x ) ;
758
759 unsigned char extra[] = "sdflkjsdlkjsdfkljxmvnxcvmxmncvx" ;
760 int rc = mbedtls_ctr_drbg_seed( &x , mbedtls_entropy_func , &entropy , extra , sizeof(extra) ) ;
761 if( rc != 0 )
762 {
763 mbedtls_entropy_free( &entropy ) ;
764 throw Error( "mbedtls_ctr_drbg_init" , rc ) ;
765 }
766 }
767
768 GSsl::MbedTls::Rng::~Rng()
769 {
770 mbedtls_ctr_drbg_free( &x ) ;
771 mbedtls_entropy_free( &entropy ) ;
772 }
773
774 mbedtls_ctr_drbg_context * GSsl::MbedTls::Rng::ptr()
775 {
776 return &x ;
777 }
778
779 mbedtls_ctr_drbg_context * GSsl::MbedTls::Rng::ptr() const
780 {
781 return const_cast<mbedtls_ctr_drbg_context*>(&x) ;
782 }
783
784 // ==
785
786 static void scrub( unsigned char *p_in , size_t n )
787 {
788 // see also SecureZeroMemory(), memset_s(3) and mbedtls_zeroize()
789 volatile unsigned char *p = p_in ;
790 while( n-- )
791 *p++ = 0U ;
792 }
793
794 GSsl::MbedTls::SecureFile::SecureFile( const std::string & path , bool with_nul )
795 {
796 std::filebuf f ;
797 std::filebuf * fp = nullptr ;
798 try
799 {
800 const char * path_p = path.c_str() ;
801 {
802 G::Root claim_root ;
803 fp = f.open( path_p , std::ios_base::in | std::ios_base::binary ) ;
804 }
805 std::streamoff n = 0 ;
806 bool ok = fp != nullptr ;
807 if( ok ) n = fp->pubseekoff( 0 , std::ios_base::end , std::ios_base::in ) ;
808 if( ok ) m_buffer.resize( static_cast<size_t>(n) ) ;
809 if( ok ) fp->pubseekpos( 0 , std::ios_base::in ) ;
810 if( ok ) ok = fp->sgetn( &m_buffer[0] , m_buffer.size() ) == static_cast<std::streamsize>(m_buffer.size()) ;
811 if( !ok ) scrub( pu() , size() ) ;
812 if( fp ) { fp->close() ; fp = nullptr ; }
813 if( ok && with_nul ) m_buffer.push_back( '\0' ) ;
814 }
815 catch(...)
816 {
817 scrub( pu() , size() ) ;
818 if( fp ) fp->close() ;
819 throw ;
820 }
821 }
822
823 GSsl::MbedTls::SecureFile::~SecureFile()
824 {
825 scrub( pu() , size() ) ;
826 }
827
828 const char * GSsl::MbedTls::SecureFile::p() const
829 {
830 static char c = '\0' ;
831 return m_buffer.size() ? &m_buffer[0] : &c ;
832 }
833
834 const unsigned char * GSsl::MbedTls::SecureFile::pu() const
835 {
836 return reinterpret_cast<const unsigned char*>(p()) ;
837 }
838
839 unsigned char * GSsl::MbedTls::SecureFile::pu()
840 {
841 return const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(p())) ;
842 }
843
844 size_t GSsl::MbedTls::SecureFile::size() const
845 {
846 return m_buffer.size() ;
847 }
848
849 bool GSsl::MbedTls::SecureFile::empty() const
850 {
851 return size() == 0U ;
852 }
853
854 // ==
855
856 GSsl::MbedTls::Key::Key()
857 {
858 mbedtls_pk_init( &x ) ;
859 }
860
861 GSsl::MbedTls::Key::~Key()
862 {
863 mbedtls_pk_free( &x ) ;
864 }
865
866 void GSsl::MbedTls::Key::load( const std::string & pem_file )
867 {
868 SecureFile file( pem_file , true ) ;
869 if( file.empty() )
870 throw Error( "cannot load private key from " + pem_file ) ;
871
872 int rc = mbedtls_pk_parse_key( &x , file.pu() , file.size() , nullptr , 0 ) ;
873 if( rc < 0 ) // negative error code
874 throw Error( "mbedtls_pk_parse_key" , rc ) ;
875 else if( rc > 0 ) // positive number of failed parts
876 {;} // no-op because the file can contain non-private-key parts
877 }
878
879 mbedtls_pk_context * GSsl::MbedTls::Key::ptr()
880 {
881 return &x ;
882 }
883
884 mbedtls_pk_context * GSsl::MbedTls::Key::ptr() const
885 {
886 return const_cast<mbedtls_pk_context*>(&x) ;
887 }
888
889 // ==
890
891 GSsl::MbedTls::Certificate::Certificate() :
892 m_loaded(false)
893 {
894 mbedtls_x509_crt_init( &x ) ;
895 }
896
897 GSsl::MbedTls::Certificate::~Certificate()
898 {
899 mbedtls_x509_crt_free( &x ) ;
900 }
901
902 void GSsl::MbedTls::Certificate::load( const std::string & path )
903 {
904 SecureFile file( path , true ) ;
905 if( file.empty() )
906 throw Error( "cannot load certificates from " + path ) ;
907
908 int rc = mbedtls_x509_crt_parse( &x , file.pu() , file.size() ) ;
909 if( rc < 0 ) // negative error code
910 throw Error( "mbedtls_x509_crt_parse" , rc ) ;
911 else if( rc > 0 ) // positive number of failed parts
912 throw Error( "mbedtls_x509_crt_parse" ) ;
913
914 m_loaded = true ;
915 }
916
917 bool GSsl::MbedTls::Certificate::loaded() const
918 {
919 return m_loaded ;
920 }
921
922 mbedtls_x509_crt * GSsl::MbedTls::Certificate::ptr()
923 {
924 return loaded() ? &x : nullptr ;
925 }
926
927 mbedtls_x509_crt * GSsl::MbedTls::Certificate::ptr() const
928 {
929 if( loaded() )
930 return const_cast<mbedtls_x509_crt*>(&x) ;
931 else
932 return nullptr ;
933 }
934
935 // ==
936
937 GSsl::MbedTls::Error::Error( const std::string & s ) :
938 m_what("tls error: "+s)
939 {
940 }
941
942 GSsl::MbedTls::Error::Error( const std::string & fnname , int rc , const std::string & more ) :
943 m_what("tls error: "+fnname+"()")
944 {
945 char buffer[200] = { '\0' } ;
946 mbedtls_strerror( rc , buffer , sizeof(buffer) ) ;
947 buffer[sizeof(buffer)-1] = '\0' ;
948
949 std::ostringstream ss ;
950 ss << ": mbedtls [" << G::Str::printable(buffer) << "]" ;
951 if( !more.empty() )
952 ss << " [" << more << "]" ;
953
954 m_what.append( ss.str() ) ;
955 }
956
957 GSsl::MbedTls::Error::~Error() throw ()
958 {
959 }
960
961 const char * GSsl::MbedTls::Error::what() const throw ()
962 {
963 return m_what.c_str() ;
964 }
965
966 /// \file gssl_mbedtls.cpp
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
G::ReadWrite::write
virtual ssize_type write(const char *buf, size_type len)=0
Sends data.
GSsl::MbedTls::LibraryImp::id
virtual std::string id() const override
Implements Library::id().
Definition: gssl_mbedtls.cpp:115
ghashstate.h
GSsl::MbedTls::Config
Holds protocol version information, etc.
Definition: gssl_mbedtls.h:188
glog.h
GSsl::MbedTls::LibraryImp
An implementation of the GSsl::LibraryImpBase interface for mbedtls.
Definition: gssl_mbedtls.h:208
GSsl::MbedTls::Error
An exception class for GSsl::MbedTls classes.
Definition: gssl_mbedtls.h:150
GSsl::MbedTls::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_mbedtls.cpp:414
G::HashState::decode
static void decode(const std::string &, uint_type *, size_type &)
Converts an encode()d string back into a hash state of N integers and a data size returned by referen...
Definition: ghashstate.h:91
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
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
GSsl::MbedTls::LibraryImp::profile
virtual const Profile & profile(const std::string &profile_name) const override
Implements Library::profile().
Definition: gssl_mbedtls.cpp:94
GSsl::MbedTls::DigesterImp::blocksize
virtual size_t blocksize() const override
Implements Digester::blocksize().
Definition: gssl_mbedtls.cpp:285
GSsl::MbedTls::ProtocolImp::connect
virtual Result connect(G::ReadWrite &) override
Implements Protocol::connect().
Definition: gssl_mbedtls.cpp:610
groot.h
gtest.h
GSsl::MbedTls::Rng
Holds a mbedtls_ctr_drbg_context structure.
Definition: gssl_mbedtls.h:91
GSsl::MbedTls::DigesterImp::add
virtual void add(const std::string &) override
Implements Digester::add().
Definition: gssl_mbedtls.cpp:237
GSsl::MbedTls::DigesterImp
An implementation of the GSsl::DigesterImpBase interface for MbedTls.
Definition: gssl_mbedtls.h:332
GSsl::MbedTls::ProtocolImp
An implementation of the GSsl::ProtocolImpBase interface for mbedtls.
Definition: gssl_mbedtls.h:286
G::ReadWrite::read
virtual ssize_type read(char *buffer, size_type buffer_length)=0
Reads data.
GSsl::MbedTls::ProtocolImp::read
virtual Result read(char *buffer, size_t buffer_size_in, ssize_t &data_size_out) override
Implements Protocol::read().
Definition: gssl_mbedtls.cpp:515
GSsl::MbedTls::ProtocolImp::verified
virtual bool verified() const override
Implements Protocol::verified().
Definition: gssl_mbedtls.cpp:715
GSsl::MbedTls::LibraryImp::hasProfile
virtual bool hasProfile(const std::string &profile_name) const override
Implements Library::hasProfile().
Definition: gssl_mbedtls.cpp:88
gstr.h
gassert.h
G
Low-level classes.
Definition: garg.h:29
GSsl::Digester
Returns an object that can perform a cryptographic hash.
Definition: gssl.h:194
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
GSsl::MbedTls::ProtocolImp::peerCertificate
virtual std::string peerCertificate() const override
Implements Protocol::peerCertificate().
Definition: gssl_mbedtls.cpp:705
GSsl::MbedTls::ProfileImp
An implementation of the GSsl::Profile interface for mbedtls.
Definition: gssl_mbedtls.h:248
gfile.h
GSsl::MbedTls::ProtocolImp::accept
virtual Result accept(G::ReadWrite &) override
Implements Protocol::accept().
Definition: gssl_mbedtls.cpp:616
GSsl::MbedTls::DigesterImp::statesize
virtual size_t statesize() const override
Implements Digester::statesize().
Definition: gssl_mbedtls.cpp:302
G::Str::trimmed
static std::string trimmed(const std::string &s, const std::string &ws)
Returns a trim()med version of s.
Definition: gstr.cpp:251
GSsl::MbedTls::ProtocolImp::write
virtual Result write(const char *buffer, size_t data_size_in, ssize_t &data_size_out) override
Implements Protocol::write().
Definition: gssl_mbedtls.cpp:524
GSsl::MbedTls::DigesterImp::state
virtual std::string state() override
Implements Digester::state().
Definition: gssl_mbedtls.cpp:273
GSsl::MbedTls::ProtocolImp::peerCertificateChain
virtual std::string peerCertificateChain() const override
Implements Protocol::peerCertificateChain().
Definition: gssl_mbedtls.cpp:710
gpath.h
GSsl::MbedTls::DigesterImp::value
virtual std::string value() override
Implements Digester::value().
Definition: gssl_mbedtls.cpp:247
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::MbedTls::ProtocolImp::stop
virtual Result stop() override
Implements Protocol::stop().
Definition: gssl_mbedtls.cpp:549
GSsl::MbedTls::DigesterImp::valuesize
virtual size_t valuesize() const override
Implements Digester::valuesize().
Definition: gssl_mbedtls.cpp:290
G::HashState::encode
static std::string encode(const uint_type *)
Returns the hash state as a string, typically containing non-printing characters. ...
Definition: ghashstate.h:68
GSsl::MbedTls::LibraryImp::digesters
virtual G::StringArray digesters(bool) const override
Implements Library::digesters().
Definition: gssl_mbedtls.cpp:134
gssl_mbedtls.h
GSsl::MbedTls::LibraryImp::digester
virtual Digester digester(const std::string &, const std::string &) const override
Implements Library::digester().
Definition: gssl_mbedtls.cpp:143
GSsl::MbedTls::LibraryImp::addProfile
virtual void addProfile(const std::string &profile_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_mbedtls.cpp:77
G::Path
A Path object represents a file system path.
Definition: gpath.h:72
G::Str::ws
static std::string ws()
Returns a string of standard whitespace characters.
Definition: gstr.cpp:1078