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