dodo  0.0.1
A C++ library to create containerized Linux services
tlssocket.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the dodo library (https://github.com/jmspit/dodo).
3  * Copyright (c) 2019 Jan-Marten Spit.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * 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 /**
19  * @file tlssocket.cpp
20  * Implements the dodo::network::TLSSocket class.
21  */
22 
23 #include <cstring>
24 
25 #include "common/logger.hpp"
26 #include "common/util.hpp"
27 #include "network/tlssocket.hpp"
28 #include "network/x509cert.hpp"
29 
30 namespace dodo::network {
31 
32  TLSSocket::TLSSocket( int socket,
33  TLSContext& tlscontext,
34  const X509Common::SAN& peer_name ) :
35 
36  BaseSocket( socket ), tlscontext_(tlscontext), peer_name_(peer_name) {
37  ssl_ = nullptr;
38  }
39 
40  TLSSocket::TLSSocket( bool blocking,
41  SocketParams params,
42  TLSContext& tlscontext,
43  const X509Common::SAN& peer_name ) :
44  BaseSocket( blocking, params ), tlscontext_(tlscontext), peer_name_(peer_name) {
45  ssl_ = SSL_new( tlscontext .getContext() );
46  if ( !ssl_ ) throw_Exception( common::getSSLErrors( '\n' ) );
47  SSL_set_fd( ssl_, socket_ );
48  if ( tlscontext.isSNIEnabled() ) {
49  SSL_ctrl( ssl_, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (void*)peer_name_.san_name.c_str());
50  }
51  }
52 
54  if (ssl_ ) SSL_free( ssl_ );
55  }
56 
58  common::SystemError error = BaseSocket::connect( address );
59  if ( error == common::SystemError::ecOK ) {
60  auto rc = SSL_set_fd( ssl_, socket_ );
61  if ( rc != 1 ) {
62  auto ssl_error_code = SSL_get_error( ssl_, rc );
63  throw_Exception( ssl_error_code << " " << common::getSSLErrors( '\n' ) );
64  }
65  rc = SSL_connect( ssl_ );
66  if ( rc != 1 ) {
67  auto ssl_error_code = SSL_get_error( ssl_, rc );
68  switch ( ssl_error_code ) {
69  case SSL_ERROR_NONE : return common::SystemError::ecSSL_ERROR_NONE;
70  case SSL_ERROR_ZERO_RETURN : return common::SystemError::ecSSL_ERROR_ZERO_RETURN;
71  case SSL_ERROR_WANT_READ : return common::SystemError::ecSSL_ERROR_WANT_READ;
72  case SSL_ERROR_WANT_WRITE : return common::SystemError::ecSSL_ERROR_WANT_WRITE;
73  case SSL_ERROR_WANT_CONNECT : return common::SystemError::ecSSL_ERROR_WANT_CONNECT;
74  case SSL_ERROR_WANT_ACCEPT : return common::SystemError::ecSSL_ERROR_WANT_ACCEPT;
75  case SSL_ERROR_WANT_X509_LOOKUP : return common::SystemError::ecSSL_ERROR_WANT_X509_LOOKUP;
76  case SSL_ERROR_SSL :
77  default: throw_Exception( ssl_error_code << " " << common::getSSLErrors( '\n' ) );
78  }
79  }
84  } else return common::SystemError::ecOK;
85  } else return error;
86  }
87 
89  auto rc = SSL_accept( ssl_ );
90  if ( rc <= 0 ) {
91  auto ssl_error_code = SSL_get_error( ssl_, rc );
92  log_Error( ssl_error_code << " " << common::getSSLErrors( '\n' ) );
93  return nullptr;
94  } else {
95  TLSSocket* ret = new TLSSocket( rc, tlscontext_, { X509Common::SANType::stDNS, "" } );
96  return ret;
97  }
98  return nullptr;
99  }
100 
102  return SSL_get_peer_certificate(ssl_);
103  }
104 
105  common::SystemError TLSSocket::send( const void* buf, ssize_t len, bool more ) {
106  auto rc = SSL_write( ssl_, buf, (int)len );
107  if ( rc <= 0 ) {
108  switch ( SSL_get_error( ssl_, rc ) ) {
109  case SSL_ERROR_NONE : return common::SystemError::ecSSL_ERROR_NONE;
110  case SSL_ERROR_ZERO_RETURN : return common::SystemError::ecSSL_ERROR_ZERO_RETURN;
111  case SSL_ERROR_WANT_READ : return common::SystemError::ecSSL_ERROR_WANT_READ;
112  case SSL_ERROR_WANT_WRITE : return common::SystemError::ecSSL_ERROR_WANT_WRITE;
113  case SSL_ERROR_WANT_CONNECT : return common::SystemError::ecSSL_ERROR_WANT_CONNECT;
114  case SSL_ERROR_WANT_ACCEPT : return common::SystemError::ecSSL_ERROR_WANT_ACCEPT;
115  case SSL_ERROR_WANT_X509_LOOKUP : return common::SystemError::ecSSL_ERROR_WANT_X509_LOOKUP;
116  case SSL_ERROR_SSL :
117  default: throw_Exception( common::getSSLErrors( '\n' ) );
118  }
119  }
121  }
122 
123  common::SystemError TLSSocket::receive( void* buf, ssize_t request, ssize_t &received ) {
124  received = 0;
125  auto rc = SSL_read( ssl_, buf, (int)request );
126  if ( rc <= 0 ) {
127  auto sge = SSL_get_error( ssl_, (int)rc );
128  switch ( sge ) {
129  case SSL_ERROR_NONE : return common::SystemError::ecSSL_ERROR_NONE;
130  case SSL_ERROR_ZERO_RETURN : return common::SystemError::ecSSL_ERROR_ZERO_RETURN;
131  case SSL_ERROR_WANT_READ : return common::SystemError::ecSSL_ERROR_WANT_READ;
132  case SSL_ERROR_WANT_WRITE : return common::SystemError::ecSSL_ERROR_WANT_WRITE;
133  case SSL_ERROR_WANT_CONNECT : return common::SystemError::ecSSL_ERROR_WANT_CONNECT;
134  case SSL_ERROR_WANT_ACCEPT : return common::SystemError::ecSSL_ERROR_WANT_ACCEPT;
135  case SSL_ERROR_WANT_X509_LOOKUP : return common::SystemError::ecSSL_ERROR_WANT_X509_LOOKUP;
136  case SSL_ERROR_SYSCALL: return common::SystemError::ecSSL_ERROR_SYSCALL;
137  case SSL_ERROR_SSL :
138  default: throw_Exception( "rc=" << rc << " SSL_get_error=" << sge << " " << common::getSSLErrors( '\n' ) );
139  }
140  } else received = rc;
142  }
143 
144 }
dodo::network::TLSSocket::ssl_
SSL * ssl_
The SSL object.
Definition: tlssocket.hpp:154
dodo::common::SystemError::ecSSL_ERROR_WANT_WRITE
@ ecSSL_ERROR_WANT_WRITE
10005 SSL_ERROR_WANT_WRITE
Definition: systemerror.hpp:221
dodo::network::X509Common::SAN
Subject AltName record.
Definition: x509cert.hpp:70
dodo::network::TLSSocket::peer_name_
X509Common::SAN peer_name_
The peer name connected to, for TLS CN and SubjectAltName matching.
Definition: tlssocket.hpp:164
dodo::network::TLSContext
TLS security context.
Definition: tlscontext.hpp:50
dodo::network::BaseSocket::connect
virtual common::SystemError connect(const Address &address)
Connect to the address.
Definition: basesocket.cpp:52
dodo::common::SystemError::ecSSL_ERROR_WANT_READ
@ ecSSL_ERROR_WANT_READ
10004 SSL_ERROR_WANT_READ
Definition: systemerror.hpp:220
dodo::common::SystemError::ecSSL_ERROR_PEERVERIFICATION
@ ecSSL_ERROR_PEERVERIFICATION
10013 When peer verification failed
Definition: systemerror.hpp:229
dodo::network::TLSContext::isSNIEnabled
bool isSNIEnabled() const
Return true when SNI (server Name Information) is to be enabled by TLSSocket objects using this TLSCo...
Definition: tlscontext.hpp:227
dodo::common::SystemError::ecSSL_ERROR_WANT_CONNECT
@ ecSSL_ERROR_WANT_CONNECT
10006 SSL_ERROR_WANT_CONNECT
Definition: systemerror.hpp:222
dodo::network::X509Certificate::verifySAN
static bool verifySAN(const X509 *cert, const SAN &san, bool wildcards=false)
Verify a peer name against this certificate's CN and SubjectAltnames.
Definition: x509cert.cpp:278
dodo::network::Address
Generic network Address, supporting ipv4 and ipv6 transparently.
Definition: address.hpp:90
dodo::network::TLSSocket::send
virtual common::SystemError send(const void *buf, ssize_t len, bool more=false)
Send data.
Definition: tlssocket.cpp:105
dodo::network::X509Common::SANType::stDNS
@ stDNS
A DNS name such as myhost.mydomain.org.
dodo::network::TLSSocket::tlscontext_
TLSContext & tlscontext_
The TLSContext.
Definition: tlssocket.hpp:159
dodo::network::TLSSocket::connect
virtual common::SystemError connect(const Address &address)
Connect to the Address.
Definition: tlssocket.cpp:57
dodo::common::SystemError::ecSSL_ERROR_WANT_ACCEPT
@ ecSSL_ERROR_WANT_ACCEPT
10007 SSL_ERROR_WANT_ACCEPT
Definition: systemerror.hpp:223
tlssocket.hpp
dodo::common::SystemError::ecSSL_ERROR_ZERO_RETURN
@ ecSSL_ERROR_ZERO_RETURN
10003 SSL_ERROR_ZERO_RETURN
Definition: systemerror.hpp:219
dodo::network::TLSSocket::receive
virtual common::SystemError receive(void *buf, ssize_t request, ssize_t &received)
Receive data.
Definition: tlssocket.cpp:123
dodo::network::TLSSocket
Socket for TLS encrypted traffic between trusted endpoints.
Definition: tlssocket.hpp:40
log_Error
#define log_Error(what)
Macro to log Error.
Definition: logger.hpp:277
dodo::network::SocketParams
Socket parameters - the family (domain), socket type and protocol triplet.
Definition: socketparams.hpp:35
dodo::network::BaseSocket::socket_
int socket_
The socket file decsriptor.
Definition: basesocket.hpp:503
dodo::network::TLSContext::getPeerVerification
PeerVerification getPeerVerification() const
Return the getPeerVerification mode.
Definition: tlscontext.hpp:221
dodo::common::SystemError::ecSSL_ERROR_SYSCALL
@ ecSSL_ERROR_SYSCALL
10012 SSL_ERROR_SYSCALL
Definition: systemerror.hpp:228
dodo::network::TLSContext::PeerVerification::pvVerifyFQDN
@ pvVerifyFQDN
As pvVerifyPeer, but the remote DNS name must match either the peer cert commonname or match one of t...
throw_Exception
#define throw_Exception(what)
Throws an Exception, passes FILE and LINE to constructor.
Definition: exception.hpp:174
dodo::common::SystemError::ecOK
@ ecOK
0 Not an error, success
Definition: systemerror.hpp:60
dodo::network
Interface for network communication.
Definition: address.hpp:37
dodo::network::TLSSocket::TLSSocket
TLSSocket(int socket, TLSContext &tlscontext, const X509Common::SAN &peer_name)
Construct from existing socket file descriptor.
Definition: tlssocket.cpp:32
dodo::common::getSSLErrors
std::string getSSLErrors(char terminator)
Get all OpenSSL errors as a single string, and clear their error state.
Definition: util.cpp:154
dodo::network::TLSSocket::~TLSSocket
virtual ~TLSSocket()
Destructor.
Definition: tlssocket.cpp:53
dodo::network::TLSSocket::accept
virtual TLSSocket * accept()
Accept a connection.
Definition: tlssocket.cpp:88
dodo::network::X509Common::SAN::san_name
std::string san_name
The name.
Definition: x509cert.hpp:79
dodo::common::SystemError
Linux system error primitive to provide a consistent interface to Linux error codes.
Definition: systemerror.hpp:53
logger.hpp
dodo::common::SystemError::ecSSL_ERROR_NONE
@ ecSSL_ERROR_NONE
10003 SSL_ERROR_NONE
Definition: systemerror.hpp:218
dodo::common::SystemError::ecSSL_ERROR_WANT_X509_LOOKUP
@ ecSSL_ERROR_WANT_X509_LOOKUP
10008 SSL_ERROR_WANT_X509_LOOKUP
Definition: systemerror.hpp:224
x509cert.hpp
dodo::network::TLSSocket::getPeerCertificate
X509 * getPeerCertificate() const
Get the peer's certificate.
Definition: tlssocket.cpp:101
dodo::network::TLSContext::isAllowSANWildcards
bool isAllowSANWildcards() const
If true, TLS will allow SAN wildcard matching.
Definition: tlscontext.hpp:233
util.hpp
dodo::network::BaseSocket
Interface to and common implementation of concrete sockets (Socket, TLSSocket).
Definition: basesocket.hpp:36