dodo  0.0.1
A C++ library to create containerized Linux services
tlscontext.hpp
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 tlscontext.hpp
20  * Defines the dodo::network::TLSContext class.
21  */
22 
23 #ifndef network_tlscontext_hpp
24 #define network_tlscontext_hpp
25 
26 #include <iostream>
27 #include <openssl/bio.h>
28 #include <openssl/err.h>
29 #include <openssl/ossl_typ.h>
30 #include <openssl/ssl.h>
31 #include <string>
32 
33 #include "common/exception.hpp"
34 #include "common/systemerror.hpp"
35 
36 
37 
38 namespace dodo::network {
39 
40  /**
41  * TLS security context. A single TLSContext can be shared among multiple TLSSocket classes.
42  *
43  *
44  * See @ref developer_networking for more information on the role of this class.
45  *
46  * @todo Implement the OCSP protocol for revoked cert check
47  * @see http://www.zedwood.com/article/cpp-x509-certificate-revocation-check-by-ocsp
48  * @see https://stackoverflow.com/questions/56253312/how-to-create-ocsp-request-using-openssl-in-c
49  */
51  public:
52 
53  /**
54  * The TLS version. Use tlsBest.
55  */
56  enum class TLSVersion {
57  tls1_1, /**< TLS 1.1 disables SSLv2, SSLv3 and TLS 1.0*/
58  tls1_2, /**< TLS 1.2 disables SSLv2, SSLv3, TLS 1.0 and TLS 1.1 */
59  tls1_3, /**< TLS 1.3 disables SSLv2, SSLv3, TLS 1.0, TLS 1.1 and TLS 1.2 */
60  tlsBest = tls1_3 /**< Use as default TLS version*/
61  };
62 
63  /**
64  * The TLS peer verification method.
65  *
66  * | PeerVerification | SSL_CTX_set_verify |
67  * | -----------------| ----------------------------------------------------|
68  * | pvVerifyNone | SSL_VERIFY_NONE |
69  * | pvVerifyPeer | SSL_VERIFY_PEER \| SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
70  * | pvVerifyFQDN | SSL_VERIFY_PEER \| SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
71  *
72  * | PeerVerification | Server / accept | Client / connect |
73  * |------------------|-----------------|------------------|
74  * | pvVerifyNone | encryption of traffic | encryption of traffic |
75  * | pvVerifyPeer | pvVerifyNone + client must present trusted cert | pvVerifyNone + server must present trusted cert |
76  * | pvVerifyFQDN | pvVerifyPeer | pvVerifyPeer + X509Certificate::verifyName() |
77  *
78  * @see https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_verify.html
79  */
80  enum class PeerVerification {
81  pvVerifyNone, /**< No peer verification - transmission is encrypted, peer is accepted even if
82  peer certificate is invalid - so any peer can read all data sent.*/
83  pvVerifyPeer, /**< The peer must have a trusted certificate (unless a anonymous cipher is used). */
84  pvVerifyFQDN, /**< As pvVerifyPeer, but the remote DNS name must match either the peer cert commonname
85  or match one of the peer cert subjectAltNames */
86  };
87 
88  /**
89  * Construct a TLS context. Use depends on the context being either server or client side, see TLSContext::PeerVerification.
90  *
91  * Example for a server-side setup enforcing at least TLS1.3, requiring the peer to present a trusted certificate (pvVerifyPeer).
92  * ```C
93  * TLSContext tlscontext( PeerVerification::pvVerifyPeer,
94  * TLSContext::TLSVersion::tls1_3,
95  false, // server-side does not need SNI
96  false ); // server-side does not do SAN matching
97  tlscontext.loadPEMIdentity( "server.crt", "server.key", "passphrase" );
98  * ```
99  * @param peerverficiation The TLSContext::PeerVerification method to use.
100  * @param tlsversion The TLS version to use. Use of default is less future code hassle.
101  * @param enableSNI Enable the Server Name Indication extension. Note that this exposes the target hostname
102  * @param allowSANWildcards Allow SAN wildcard matching under pvVerifyFQDN
103  * of TLSSocket connections as the hostname is sent unencrypted, facilitated all kinds of evil such as
104  * censorship. Use only when you must connect to a server that requires it.
105  *
106  * @see https://en.wikipedia.org/wiki/Server_Name_Indication
107  */
109  const TLSVersion& tlsversion = TLSVersion::tlsBest,
110  bool enableSNI = true,
111  bool allowSANWildcards = true );
112 
113  /**
114  * Construct a TLSContext from a YAML node.
115  * ```YAML
116  * tlscontext:
117  * peer-verification: pvVerifyFQDN # mandatory
118  * tls-version: 1.3 # mandatory
119  * enable-sni: true # mandatory
120  * allow-san-wildcards: true # mandatory
121  * trust: # optional
122  * file: <path to PEM file> # at least one of file or path
123  * path: <path to directory with PEM files> # at least one of file or path
124  * ciphers: # mandatory
125  * - TLS_AES_256_GCM_SHA384 # at least one entry
126  * - TLS_AES_128_GCM_SHA256
127  * ```
128  * If the PEM format is used to provide keys and passphrase:
129  * ```YAML
130  * tlscontext:
131  * pem:
132  * private: <path to private key PEM> # mandatory
133  * public: <path to public key PEM> # mandatory
134  * passphrase: <ENC[...]> # mandatory
135  * ```
136  * If the PKCS12 format is used to provide keys and passphrase:
137  * ```YAML
138  * tlscontext:
139  * pkcs12:
140  * file: <path to PKCS12 file> # mandatory
141  * passphrase: <ENC[...]> # mandatory
142  * ```
143  * @param yaml The YAML node to read from, which would be 'tlscontext' in the above examples.
144  */
145  TLSContext( const YAML::Node &yaml );
146 
147 
148  virtual ~TLSContext();
149 
150  /**
151  * Load a certificate and the corresponding private key for an identity.
152  * @param certfile The certificate PEM file.
153  * @param keyfile The private key PEM file.
154  * @param passphrase The passphrase for the private key PEM file. If the private key is not protected by a
155  * passphrase its value is stored in this object nonetheless but unused.
156  */
157  void loadPEMIdentity( const std::string& certfile,
158  const std::string& keyfile,
159  const std::string& passphrase );
160 
161  /**
162  * Loads a private key, matching certificate and optional CA certificates (eg a truststore) from a
163  * PKCS12 file.
164  * @param p12file The PKCS12 file to read from.
165  * @param p12passphrase The passphrase for the PKCS12 file.
166  */
167  void loadPKCS12( const std::string &p12file,
168  const std::string &p12passphrase );
169 
170  /**
171  * Set a list of ciphers, separated by a colon, the TLSContext will accept. There are differences between TLSVersion tough,
172  *
173  * A few examples (note the hyphens and underscores)
174  *
175  * - TLS 1.3 TLS_AES_256_GCM_SHA384
176  * - TLS 1.2 DHE-RSA-AES256-GCM-SHA384
177  * - TLS 1.1 DHE-RSA-AES256-GCM-SHA384
178  *
179  * @see https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_cipher_list.html
180  *
181  * A list of available ciphers is given by
182  * ```
183  * $ openssl ciphers -tls1_2 -s
184  * $ openssl ciphers -tls1_3 -s
185  * ```
186  *
187  * Note that this call will not return a SystemError, but throws a dodo::common::Exception when the cipher list
188  * is invalid.
189  *
190  * @param cipherlist The cipherlist.
191  * @throw dodo::common::Exception
192  */
193  void setCipherList( const std::string& cipherlist );
194 
195  /**
196  * Set SSL options
197  * @param option The option or OR-ed options to apply.
198  * @return The options applied.
199  * @see https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_options.html
200  */
201  long setOptions( long option );
202 
203  /**
204  * Trust all certificates (PEM format) in the specified file and/or directory.
205  * @param cafile A PEM file containing one or more certificates. If an empty string, unused.
206  * @param capath A directory containing certificate files. If an empty string, unused.
207  */
208  void setTrustPaths( const std::string& cafile,
209  const std::string& capath );
210 
211  /**
212  * Return a pointer to the SSL_CTX
213  * @return a pointer to the SSL_CTX
214  */
215  SSL_CTX* getContext() const { return tlsctx_; };
216 
217  /**
218  * Return the getPeerVerification mode.
219  * @return the getPeerVerification.
220  */
222 
223  /**
224  * Return true when SNI (server Name Information) is to be enabled by TLSSocket objects using this TLSContext.
225  * @return true when SNI is enabled.
226  */
227  bool isSNIEnabled() const { return enable_sni_; }
228 
229  /**
230  * If true, TLS will allow SAN wildcard matching.
231  * @return True if wildcard matching on the SAN is allowed.
232  */
233  bool isAllowSANWildcards() const { return allow_san_wildcards_; }
234 
235 
236  /**
237  * Get a PeerVerfication enum from a string. The comparison is case sensitive and must match the enum name
238  * ( "pvVerifyNone", "pvVerifyPeer", "pvVerifyFQDN" ).
239  * If the name does not translate, a common::Exception is thrown.
240  * @param src The source string.
241  * @return the PeerVerification.
242  */
243  static PeerVerification peerVerficiationFromString( const std::string &src );
244 
245  /**
246  * Convert the src string to a TLSVersion or throw a common::Exception if that mapping fails. TLSversion strings
247  * could be "1.1", "1.2" and "1.3".
248  * @param src The source string.
249  * @return The TLSVersion specified by the string.
250  */
251  static TLSVersion tlsVersionFromString( const std::string &src );
252 
253  private:
254  /**
255  * Initialize the SSL library
256  */
257  static void InitializeSSL();
258 
259  /**
260  * Shutdown the SSL library
261  */
262  static void ShutdownSSL();
263 
264  /**
265  * Construct the TLSContext.
266  * @param peerverficiation The TLSContext::PeerVerification method to use.
267  * @param tlsversion The TLS version to use. Use of default is less future code hassle.
268  * @param enableSNI Enable the Server Name Indication extension. Note that this exposes the target hostname
269  * @param allowSANWildcards Allow SAN wildcard matching under pvVerifyFQDN
270  */
271  void construct( const PeerVerification& peerverficiation,
272  const TLSVersion& tlsversion,
273  bool enableSNI,
274  bool allowSANWildcards );
275 
276  /**
277  * Password callback, returns the passphrase set in the TLS context by the passphrase argument of
278  * loadCertificate or pkeypassphrase argument of the loadPKCS12 method.
279  * @param buf The passphrase should be copied to here.
280  * @param size No more than size bytes should be copied into buf.
281  * @param rwflag 0 = decryption 1 = encryption
282  * @param userdata Pass a pointer to the TLSContext object.
283  * @return the character length of the passphrase string.
284  */
285  static int pem_passwd_cb( char *buf, int size, int rwflag, void *userdata );
286 
287  /**
288  * The openssl SSL_CTX
289  */
290  SSL_CTX* tlsctx_;
291 
292  /**
293  * The TLS version
294  */
296 
297  /**
298  * The peer verification method used.
299  */
301 
302  /**
303  * The passphrase to decrypt encrypted private keys (may be empty when the key is not encrypted).
304  */
305  std::string passphrase_;
306 
307  /**
308  * Enable / disable SNI on TLSSocket objects using this TLSContext.
309  */
311 
312  /**
313  * Allow SAN names to match agains wildcards (eg foo.domain.org matches *.domain.org).
314  */
316 
317  /**
318  * Enable / disable CRL (Certificate Revocation List) checking.
319  */
321 
322 
323  };
324 
325 }
326 
327 #endif
dodo::network::TLSContext::peerverficiation_
PeerVerification peerverficiation_
The peer verification method used.
Definition: tlscontext.hpp:300
dodo::network::TLSContext::ShutdownSSL
static void ShutdownSSL()
Shutdown the SSL library.
Definition: tlscontext.cpp:44
dodo::network::TLSContext::TLSVersion::tls1_3
@ tls1_3
TLS 1.3 disables SSLv2, SSLv3, TLS 1.0, TLS 1.1 and TLS 1.2.
dodo::network::TLSContext
TLS security context.
Definition: tlscontext.hpp:50
dodo::network::TLSContext::TLSContext
TLSContext(const PeerVerification &peerverficiation=PeerVerification::pvVerifyFQDN, const TLSVersion &tlsversion=TLSVersion::tlsBest, bool enableSNI=true, bool allowSANWildcards=true)
Construct a TLS context.
Definition: tlscontext.cpp:49
dodo::network::TLSContext::pem_passwd_cb
static int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata)
Password callback, returns the passphrase set in the TLS context by the passphrase argument of loadCe...
Definition: tlscontext.cpp:202
dodo::network::TLSContext::enable_clr_
bool enable_clr_
Enable / disable CRL (Certificate Revocation List) checking.
Definition: tlscontext.hpp:320
dodo::network::TLSContext::setTrustPaths
void setTrustPaths(const std::string &cafile, const std::string &capath)
Trust all certificates (PEM format) in the specified file and/or directory.
Definition: tlscontext.cpp:226
dodo::common::DebugObject
Interface to objects that support dumping their state to a string.
Definition: exception.hpp:39
dodo::network::TLSContext::construct
void construct(const PeerVerification &peerverficiation, const TLSVersion &tlsversion, bool enableSNI, bool allowSANWildcards)
Construct the TLSContext.
Definition: tlscontext.cpp:75
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::network::TLSContext::PeerVerification::pvVerifyNone
@ pvVerifyNone
No peer verification - transmission is encrypted, peer is accepted even if peer certificate is invali...
dodo::network::TLSContext::peerVerficiationFromString
static PeerVerification peerVerficiationFromString(const std::string &src)
Get a PeerVerfication enum from a string.
Definition: tlscontext.cpp:195
dodo::network::TLSContext::allow_san_wildcards_
bool allow_san_wildcards_
Allow SAN names to match agains wildcards (eg foo.domain.org matches *.domain.org).
Definition: tlscontext.hpp:315
dodo::network::TLSContext::TLSVersion::tls1_2
@ tls1_2
TLS 1.2 disables SSLv2, SSLv3, TLS 1.0 and TLS 1.1.
dodo::network::TLSContext::TLSVersion::tls1_1
@ tls1_1
TLS 1.1 disables SSLv2, SSLv3 and TLS 1.0.
dodo::network::TLSContext::PeerVerification
PeerVerification
The TLS peer verification method.
Definition: tlscontext.hpp:80
dodo::network::TLSContext::PeerVerification::pvVerifyPeer
@ pvVerifyPeer
The peer must have a trusted certificate (unless a anonymous cipher is used).
dodo::network::TLSContext::passphrase_
std::string passphrase_
The passphrase to decrypt encrypted private keys (may be empty when the key is not encrypted).
Definition: tlscontext.hpp:305
dodo::network::TLSContext::getPeerVerification
PeerVerification getPeerVerification() const
Return the getPeerVerification mode.
Definition: tlscontext.hpp:221
dodo::network::TLSContext::loadPEMIdentity
void loadPEMIdentity(const std::string &certfile, const std::string &keyfile, const std::string &passphrase)
Load a certificate and the corresponding private key for an identity.
Definition: tlscontext.cpp:126
dodo::network::TLSContext::InitializeSSL
static void InitializeSSL()
Initialize the SSL library.
Definition: tlscontext.cpp:38
dodo::network::TLSContext::tlsversion_
TLSVersion tlsversion_
The TLS version.
Definition: tlscontext.hpp:295
dodo::network::TLSContext::enable_sni_
bool enable_sni_
Enable / disable SNI on TLSSocket objects using this TLSContext.
Definition: tlscontext.hpp:310
dodo::network::TLSContext::TLSVersion
TLSVersion
The TLS version.
Definition: tlscontext.hpp:56
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...
dodo::network
Interface for network communication.
Definition: address.hpp:37
dodo::network::TLSContext::setCipherList
void setCipherList(const std::string &cipherlist)
Set a list of ciphers, separated by a colon, the TLSContext will accept.
Definition: tlscontext.cpp:211
dodo::network::TLSContext::tlsVersionFromString
static TLSVersion tlsVersionFromString(const std::string &src)
Convert the src string to a TLSVersion or throw a common::Exception if that mapping fails.
Definition: tlscontext.cpp:237
dodo::network::TLSContext::tlsctx_
SSL_CTX * tlsctx_
The openssl SSL_CTX.
Definition: tlscontext.hpp:290
exception.hpp
dodo::network::TLSContext::setOptions
long setOptions(long option)
Set SSL options.
Definition: tlscontext.cpp:222
dodo::network::TLSContext::loadPKCS12
void loadPKCS12(const std::string &p12file, const std::string &p12passphrase)
Loads a private key, matching certificate and optional CA certificates (eg a truststore) from a PKCS1...
Definition: tlscontext.cpp:142
systemerror.hpp
dodo::network::TLSContext::getContext
SSL_CTX * getContext() const
Return a pointer to the SSL_CTX.
Definition: tlscontext.hpp:215
dodo::network::TLSContext::TLSVersion::tlsBest
@ tlsBest
Use as default TLS version.
dodo::network::TLSContext::isAllowSANWildcards
bool isAllowSANWildcards() const
If true, TLS will allow SAN wildcard matching.
Definition: tlscontext.hpp:233