E-MailRelay

gssl_openssl.h
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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/// \file gssl_openssl.h
19///
20
21#ifndef G_SSL_OPENSSL_H
22#define G_SSL_OPENSSL_H
23
24#include "gdef.h"
25#include "gssl.h"
26#include "gstringview.h"
27#include "gassert.h"
28#include <openssl/ssl.h>
29#include <openssl/err.h>
30#include <openssl/rand.h>
31#include <openssl/conf.h>
32#include <openssl/evp.h>
33#include <openssl/md5.h>
34#include <openssl/sha.h>
35#include <openssl/hmac.h>
36#include <memory>
37#include <stdexcept>
38#include <functional>
39#include <map>
40
41#if OPENSSL_VERSION_NUMBER < 0x10100000L
42#error "openssl is too old"
43#endif
44#ifndef GCONFIG_HAVE_OPENSSL_HASH_FUNCTIONS
45#if OPENSSL_VERSION_NUMBER >= 0x30000000L
46#define GCONFIG_HAVE_OPENSSL_HASH_FUNCTIONS 0
47#else
48#define GCONFIG_HAVE_OPENSSL_HASH_FUNCTIONS 1
49#endif
50#endif
51
52// debugging...
53// * network logging
54// $ sudo tcpdump -s 0 -n -i eth0 -X tcp port 587
55// * emailrelay smtp proxy to gmail
56// $ emailrelay --client-tls --forward-to smtp.gmail.com:587 ...
57// * openssl smtp client to gmail
58// $ openssl s_client -tls1 -msg -debug -starttls smtp -crlf -connect smtp.gmail.com:587
59// * certificate
60// $ openssl req -x509 -nodes -subj /CN=example.com -newkey rsa:1024 -keyout example.pem -out example.pem
61// $ cp example.pem /etc/ssl/certs/
62// $ cd /etc/ssl/certs && ln -s example.pem `openssl x509 -noout -hash -in example.pem`.0
63// * openssl server (without smtp)
64// $ openssl s_server -accept 10025 -cert /etc/ssl/certs/example.pem -debug -msg -tls1
65//
66
67 namespace GSsl
68{
69 namespace OpenSSL /// A namespace for implementing the GSsl interface using the OpenSSL library.
70 {
71 class Error ;
72 class Certificate ;
73 class CertificateChain ;
74 class LibraryImp ;
75 class ProfileImp ;
76 class ProtocolImp ;
77 class DigesterImp ;
78 class Config ;
79 }
80}
81
82//| \class GSsl::OpenSSL::Certificate
83/// Holds a certificate taken from an OpenSSL X509 structure.
84///
85 class GSsl::OpenSSL::Certificate
86{
87 public:
88 Certificate( X509* , bool do_free ) ;
89 std::string str() const ;
90
91 private:
92 std::string m_str ;
93} ;
94
95//| \class GSsl::OpenSSL::Config
96/// Holds protocol version information, etc.
97///
98 class GSsl::OpenSSL::Config
99{
100 public:
101 using Fn = const SSL_METHOD *(*)() ;
102 explicit Config( G::StringArray & config ) ;
103 Fn fn( bool server ) ;
104 long set() const ;
105 long reset() const ;
106 int min_() const ;
107 int max_() const ;
108 bool noverify() const ;
109
110 private:
111 static bool consume( G::StringArray & , std::string_view ) ;
112 static int map( int , int ) ;
113
114 private:
115 Fn m_server_fn ;
116 Fn m_client_fn ;
117 int m_min {0} ;
118 int m_max {0} ;
119 long m_options_set {0L} ;
120 long m_options_reset {0L} ;
121 bool m_noverify ;
122} ;
123
124//| \class GSsl::OpenSSL::CertificateChain
125/// Holds a certificate chain taken from a stack of OpenSSL X509 structures.
126///
127 class GSsl::OpenSSL::CertificateChain
128{
129 public:
130 explicit CertificateChain( STACK_OF(X509) * chain ) ;
131 std::string str() const ;
132
133 private:
134 std::string m_str ;
135} ;
136
137//| \class GSsl::OpenSSL::Error
138/// An exception class for GSsl::OpenSSL classes.
139///
140 class GSsl::OpenSSL::Error : public std::runtime_error
141{
142 public:
143 explicit Error( const std::string & ) ;
144 Error( const std::string & , unsigned long ) ;
145 Error( const std::string & , unsigned long , const std::string & path ) ;
146 static void clearErrors() ;
147
148 private:
149 static std::string text( unsigned long ) ;
150} ;
151
152//| \class GSsl::OpenSSL::ProfileImp
153/// An implementation of the GSsl::Profile interface for OpenSSL.
154///
155 class GSsl::OpenSSL::ProfileImp : public Profile
156{
157 public:
158 using Error = OpenSSL::Error ;
159
160 ProfileImp( const LibraryImp & , bool is_server_profile , const std::string & key_file ,
161 const std::string & cert_file , const std::string & ca_file ,
162 const std::string & default_peer_certificate_name , const std::string & default_peer_host_name ,
163 const std::string & profile_config ) ;
164 ~ProfileImp() override ;
165 SSL_CTX * p() const ;
166 const LibraryImp & lib() const ;
167 const std::string & defaultPeerCertificateName() const ;
168 const std::string & defaultPeerHostName() const ;
169 void apply( const Config & ) ;
170
171 private: // overrides
172 std::unique_ptr<ProtocolImpBase> newProtocol( const std::string & , const std::string & ) const override ;
173
174 public:
175 ProfileImp( const ProfileImp & ) = delete ;
176 ProfileImp( ProfileImp && ) = delete ;
177 ProfileImp & operator=( const ProfileImp & ) = delete ;
178 ProfileImp & operator=( ProfileImp && ) = delete ;
179
180 private:
181 static void check( int , const std::string & , const std::string & = {} ) ;
182 static int verifyPass( int , X509_STORE_CTX * ) ;
183 static int verifyPeerName( int , X509_STORE_CTX * ) ;
184 static std::string name( X509_NAME * ) ;
185 static void deleter( SSL_CTX * ) ;
186
187 private:
188 const LibraryImp & m_library_imp ;
189 const std::string m_default_peer_certificate_name ;
190 const std::string m_default_peer_host_name ;
191 std::unique_ptr<SSL_CTX,std::function<void(SSL_CTX*)>> m_ssl_ctx ;
192} ;
193
194//| \class GSsl::OpenSSL::LibraryImp
195/// An implementation of the GSsl::LibraryImpBase interface for OpenSSL.
196///
197 class GSsl::OpenSSL::LibraryImp : public LibraryImpBase
198{
199 public:
200 using Error = GSsl::OpenSSL::Error ;
201
202 LibraryImp( G::StringArray & library_config , Library::LogFn , bool verbose ) ;
203 ~LibraryImp() override ;
204 Config config() const ;
205 bool noverify() const ;
206
207 Library::LogFn log() const ;
208 bool verbose() const ;
209 int index() const ;
210 static std::string credit( const std::string & prefix , const std::string & eol , const std::string & eot ) ;
211 static std::string sid() ;
212
213 private: // overrides
214 void addProfile( const std::string & name , bool is_server_profile ,
215 const std::string & key_file , const std::string & cert_file , const std::string & ca_file ,
216 const std::string & default_peer_certificate_name , const std::string & default_peer_host_name ,
217 const std::string & profile_config ) override ;
218 bool hasProfile( const std::string & ) const override ;
219 const GSsl::Profile & profile( const std::string & ) const override ;
220 std::string id() const override ;
221 G::StringArray digesters( bool ) const override ;
222 Digester digester( const std::string & , const std::string & , bool ) const override ;
223
224 public:
225 LibraryImp( const LibraryImp & ) = delete ;
226 LibraryImp( LibraryImp && ) = delete ;
227 LibraryImp & operator=( const LibraryImp & ) = delete ;
228 LibraryImp & operator=( LibraryImp && ) = delete ;
229
230 private:
231 static void cleanup() ;
232
233 private:
234 using Map = std::map<std::string,std::shared_ptr<ProfileImp>> ;
235 std::string m_library_config ;
236 Library::LogFn m_log_fn ;
237 bool m_verbose ;
238 Map m_profile_map ;
239 int m_index {-1} ; // SSL_get_ex_new_index()
240 Config m_config ;
241} ;
242
243//| \class GSsl::OpenSSL::ProtocolImp
244/// An implementation of the GSsl::ProtocolImpBase interface for OpenSSL.
245///
246 class GSsl::OpenSSL::ProtocolImp : public ProtocolImpBase
247{
248 public:
249 using Result = Protocol::Result ;
250 using Error = OpenSSL::Error ;
251 using Certificate = OpenSSL::Certificate ;
252 using CertificateChain = OpenSSL::CertificateChain ;
253
254 ProtocolImp( const ProfileImp & , const std::string & , const std::string & ) ;
255 ~ProtocolImp() override ;
256 std::string requiredPeerCertificateName() const ;
257
258 private: // overrides
259 Result connect( G::ReadWrite & ) override ;
260 Result accept( G::ReadWrite & ) override ;
261 Result shutdown() override ;
262 Result read( char * buffer , std::size_t buffer_size , ssize_t & read_size ) override ;
263 Result write( const char * buffer , std::size_t size_in , ssize_t & size_out ) override ;
264 std::string peerCertificate() const override ;
265 std::string peerCertificateChain() const override ;
266 std::string protocol() const override ;
267 std::string cipher() const override ;
268 bool verified() const override ;
269
270 public:
271 ProtocolImp( const ProtocolImp & ) = delete ;
272 ProtocolImp( ProtocolImp && ) = delete ;
273 ProtocolImp & operator=( const ProtocolImp & ) = delete ;
274 ProtocolImp & operator=( ProtocolImp && ) = delete ;
275
276 private:
277 int error( const char * , int ) const ;
278 void set( int ) ;
279 Result connect() ;
280 Result accept() ;
281 static Result convert( int ) ;
282 static void clearErrors() ;
283 void logErrors( const std::string & op , int rc , int e , const std::string & ) const ;
284 void saveResult() ;
285 static void deleter( SSL * ) ;
286
287 private:
288 std::unique_ptr<SSL,std::function<void(SSL*)>> m_ssl ;
289 Library::LogFn m_log_fn ;
290 bool m_verbose ;
291 bool m_fd_set {false} ;
292 std::string m_required_peer_certificate_name ;
293 std::string m_peer_certificate ;
294 std::string m_peer_certificate_chain ;
295 bool m_verified {false} ;
296} ;
297
298//| \class GSsl::OpenSSL::DigesterImp
299/// An implementation of the GSsl::DigesterImpBase interface for OpenSSL.
300///
301 class GSsl::OpenSSL::DigesterImp : public GSsl::DigesterImpBase
302{
303 public:
304 DigesterImp( const std::string & , const std::string & , bool ) ;
305 ~DigesterImp() override ;
306
307 private: // overrides
308 void add( std::string_view ) override ;
309 std::string value() override ;
310 std::string state() override ;
311 std::size_t blocksize() const noexcept override ;
312 std::size_t valuesize() const noexcept override ;
313 std::size_t statesize() const noexcept override ;
314
315public:
316 DigesterImp( const DigesterImp & ) = delete ;
317 DigesterImp( DigesterImp && ) = delete ;
318 DigesterImp & operator=( const DigesterImp & ) = delete ;
319 DigesterImp & operator=( DigesterImp && ) = delete ;
320
321private:
322 enum class Type { Md5 , Sha1 , Sha256 , Other } ;
323 Type m_hash_type {Type::Other} ;
324 #if GCONFIG_HAVE_OPENSSL_HASH_FUNCTIONS
325 MD5_CTX m_md5 {} ;
326 SHA_CTX m_sha1 {} ;
327 SHA256_CTX m_sha256 {} ;
328 #endif
329 EVP_MD_CTX * m_evp_ctx {nullptr} ;
330 std::size_t m_block_size {0U} ;
331 std::size_t m_value_size {0U} ;
332 std::size_t m_state_size {0U} ;
333} ;
334
335#endif
GSsl::DigesterImpBase
A base interface for GSsl::Digester pimple classes.
Definition: gssl.h:472
GSsl::DigesterImpBase::state
virtual std::string state()=0
Implements Digester::state().
GSsl::DigesterImpBase::blocksize
virtual std::size_t blocksize() const noexcept=0
Implements Digester::blocksize().
GSsl::DigesterImpBase::value
virtual std::string value()=0
Implements Digester::value().
GSsl::DigesterImpBase::add
virtual void add(std::string_view)=0
Implements Digester::add().
GSsl::OpenSSL::CertificateChain
Holds a certificate chain taken from a stack of OpenSSL X509 structures.
Definition: gssl_openssl.h:128
GSsl::OpenSSL::Certificate
Holds a certificate taken from an OpenSSL X509 structure.
Definition: gssl_openssl.h:86
GSsl::OpenSSL::Config
Holds protocol version information, etc.
Definition: gssl_openssl.h:99
GSsl::OpenSSL::Error
An exception class for GSsl::OpenSSL classes.
Definition: gssl_openssl.h:141
GSsl::Profile
A base interface for profile classes that work with concrete classes derived from GSsl::LibraryImpBas...
Definition: gssl.h:419
GSsl::Profile::newProtocol
virtual std::unique_ptr< ProtocolImpBase > newProtocol(const std::string &, const std::string &) const =0
Factory method for a new Protocol object.
GSsl::ProtocolImpBase::verified
virtual bool verified() const =0
Implements Protocol::verified().
GSsl::ProtocolImpBase::peerCertificate
virtual std::string peerCertificate() const =0
Implements Protocol::peerCertificate().
GSsl::ProtocolImpBase::connect
virtual Protocol::Result connect(G::ReadWrite &)=0
Implements Protocol::connect().
GSsl::ProtocolImpBase::cipher
virtual std::string cipher() const =0
Implements Protocol::cipher().
GSsl::ProtocolImpBase::write
virtual Protocol::Result write(const char *, std::size_t, ssize_t &)=0
Implements Protocol::write().
GSsl::ProtocolImpBase::protocol
virtual std::string protocol() const =0
Implements Protocol::protocol().
GSsl::ProtocolImpBase::read
virtual Protocol::Result read(char *, std::size_t, ssize_t &)=0
Implements Protocol::read().
GSsl::ProtocolImpBase::peerCertificateChain
virtual std::string peerCertificateChain() const =0
Implements Protocol::peerCertificateChain().
GSsl::ProtocolImpBase::accept
virtual Protocol::Result accept(G::ReadWrite &)=0
Implements Protocol::accept().
GSsl::ProtocolImpBase::shutdown
virtual Protocol::Result shutdown()=0
Implements Protocol::shutdown().
G::ReadWrite
An abstract interface for reading and writing from a non-blocking i/o channel.
Definition: greadwrite.h:50
gassert.h
gssl.h
An interface to an underlying TLS library.
gstringview.h
GSsl
TLS/SSL transport layer security classes.
Definition: gssl.h:36
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstringarray.h:30
std
STL namespace.