OpenVPN 3 Core Library
Loading...
Searching...
No Matches
sslctx.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// Wrap the Apple SSL API as defined in <Security/SecureTransport.h>
13// so that it can be used as the SSL layer by the OpenVPN core.
14// NOTE: not used in production code.
15
16// Note that the Apple SSL API is missing some functionality (as of
17// Mac OS X 10.8) that makes it difficult to use as a drop in replacement
18// for OpenSSL or MbedTLS. The biggest issue is that the API doesn't
19// allow an SSL context to be built out of PEM-based certificates and
20// keys. It requires an "Identity" in the Keychain that was imported
21// by the user as a PKCS#12 file.
22
23#ifndef OPENVPN_APPLECRYPTO_SSL_SSLCTX_H
24#define OPENVPN_APPLECRYPTO_SSL_SSLCTX_H
25
26#include <string>
27
28#include <Security/SecImportExport.h>
29#include <Security/SecItem.h>
30#include <Security/SecureTransport.h>
31#include <Security/SecKey.h>
32
45
46// An SSL Context is essentially a configuration that can be used
47// to generate an arbitrary number of actual SSL connections objects.
48
49// AppleSSLContext is an SSL Context implementation that uses the
50// Mac/iOS SSL library as a backend.
51
52namespace openvpn {
53
54// Represents an SSL configuration that can be used
55// to instantiate actual SSL sessions.
57{
58 public:
60
61 enum
62 {
64 };
65
66 // The data needed to construct an AppleSSLContext.
67 class Config : public SSLConfigAPI
68 {
69 friend class AppleSSLContext;
70
71 public:
73
74 Config() = default;
75
76 void load_identity(const std::string &subject_match)
77 {
78 identity = load_identity_(subject_match);
79 if (!identity())
80 OPENVPN_THROW(ssl_context_error, "AppleSSLContext: identity '" << subject_match << "' undefined");
81 }
82
84 {
85 return SSLFactoryAPI::Ptr(new AppleSSLContext(this));
86 }
87
88 void set_mode(const Mode &mode_arg) override
89 {
90 mode = mode_arg;
91 }
92
93 const Mode &get_mode() const override
94 {
95 return mode;
96 }
97
98 void set_frame(const Frame::Ptr &frame_arg) override
99 {
100 frame = frame_arg;
101 }
102
103 void load(const OptionList &opt, const unsigned int lflags) override
104 {
105 // client/server
106 if (lflags & LF_PARSE_MODE)
107 mode = opt.exists("client") ? Mode(Mode::CLIENT) : Mode(Mode::SERVER);
108
109 // identity
110 {
111 const std::string &subject_match = opt.get("identity", 1, 256);
112 load_identity(subject_match);
113 }
114 }
115
116 void set_external_pki_callback(ExternalPKIBase *external_pki_arg, const std::string &alias) override
117 {
118 not_implemented("set_external_pki_callback");
119 }
120
121 void set_private_key_password(const std::string &pwd) override
122 {
123 return not_implemented("set_private_key_password");
124 }
125
126 void load_ca(const std::string &ca_txt, bool strict) override
127 {
128 return not_implemented("load_ca");
129 }
130
131 void load_crl(const std::string &crl_txt) override
132 {
133 return not_implemented("load_crl");
134 }
135
136 void load_cert(const std::string &cert_txt) override
137 {
138 return not_implemented("load_cert");
139 }
140
141 void load_cert(const std::string &cert_txt, const std::string &extra_certs_txt) override
142 {
143 return not_implemented("load_cert");
144 }
145
146 void load_private_key(const std::string &key_txt) override
147 {
148 return not_implemented("load_private_key");
149 }
150
151 void load_dh(const std::string &dh_txt) override
152 {
153 return not_implemented("load_dh");
154 }
155
156 void set_debug_level(const int debug_level) override
157 {
158 return not_implemented("set_debug_level");
159 }
160
161 void set_flags(const unsigned int flags_arg) override
162 {
163 return not_implemented("set_flags");
164 }
165
166 void set_ns_cert_type(const NSCert::Type ns_cert_type_arg) override
167 {
168 return not_implemented("set_ns_cert_type");
169 }
170
172 {
173 return not_implemented("set_remote_cert_tls");
174 }
175
176 void set_tls_remote(const std::string &tls_remote_arg) override
177 {
178 return not_implemented("set_tls_remote");
179 }
180
181 void set_tls_version_min(const TLSVersion::Type tvm) override
182 {
183 return not_implemented("set_tls_version_min");
184 }
185
186 void set_local_cert_enabled(const bool v) override
187 {
188 return not_implemented("set_local_cert_enabled");
189 }
190
191 void set_enable_renegotiation(const bool v) override
192 {
193 return not_implemented("set_enable_renegotiation");
194 }
195
196 void set_rng(const StrongRandomAPI::Ptr &rng_arg) override
197 {
198 return not_implemented("set_rng");
199 }
200
201 private:
202 void not_implemented(const char *funcname)
203 {
204 OPENVPN_LOG("AppleSSL: " << funcname << " not implemented");
205 }
206
208 CF::Array identity; // as returned by load_identity
210 };
211
212 // Represents an actual SSL session.
213 // Normally instantiated by AppleSSLContext::ssl().
214 class SSL : public SSLAPI
215 {
216 friend class AppleSSLContext;
217
218 public:
220
221 void start_handshake() override
222 {
223 SSLHandshake(ssl);
224 }
225
226 ssize_t write_cleartext_unbuffered(const void *data, const size_t size) override
227 {
228 size_t actual = 0;
229 const OSStatus status = SSLWrite(ssl, data, size, &actual);
230 if (status < 0)
231 {
232 if (status == errSSLWouldBlock)
234 else
235 throw CFException("AppleSSLContext::SSL::write_cleartext failed", status);
236 }
237 else
238 return actual;
239 }
240
241 ssize_t read_cleartext(void *data, const size_t capacity) override
242 {
243 if (!overflow)
244 {
245 size_t actual = 0;
246 const OSStatus status = SSLRead(ssl, data, capacity, &actual);
247 if (status < 0)
248 {
249 if (status == errSSLWouldBlock)
251 else
252 throw CFException("AppleSSLContext::SSL::read_cleartext failed", status);
253 }
254 else
255 return actual;
256 }
257 else
258 throw ssl_ciphertext_in_overflow();
259 }
260
261 bool read_cleartext_ready() const override
262 {
263 // fixme: need to detect data buffered at SSL layer
264 return !ct_in.empty();
265 }
266
267 void write_ciphertext(const BufferPtr &buf) override
268 {
270 ct_in.write_buf(buf);
271 else
272 overflow = true;
273 }
274
275 bool read_ciphertext_ready() const override
276 {
277 return !ct_out.empty();
278 }
279
281 {
282 return ct_out.read_buf();
283 }
284
285 std::string ssl_handshake_details() const override // fixme -- code me
286 {
287 return "[AppleSSL not implemented]";
288 }
289
290 const AuthCert::Ptr &auth_cert() const override
291 {
292 OPENVPN_THROW(ssl_context_error, "AppleSSL::SSL: auth_cert() not implemented");
293 }
294
296 {
297 ssl_erase();
298 }
299
300 private:
302 {
303 ssl_clear();
304 try
305 {
306 OSStatus s;
307
308#ifdef OPENVPN_PLATFORM_IPHONE
309 // init SSL object, select client or server mode
310 if (ctx.mode().is_server())
311 ssl = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType);
312 else if (ctx.mode().is_client())
313 ssl = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
314 else
315 OPENVPN_THROW(ssl_context_error, "AppleSSLContext::SSL: unknown client/server mode");
316 if (ssl == nullptr)
317 throw CFException("SSLCreateContext failed");
318
319 // use TLS v1
320 s = SSLSetProtocolVersionMin(ssl, kTLSProtocol1);
321 if (s)
322 throw CFException("SSLSetProtocolVersionMin failed", s);
323#else
324 // init SSL object, select client or server mode
325 if (ctx.mode().is_server())
326 s = SSLNewContext(true, &ssl);
327 else if (ctx.mode().is_client())
328 s = SSLNewContext(false, &ssl);
329 else
330 OPENVPN_THROW(ssl_context_error, "AppleSSLContext::SSL: unknown client/server mode");
331 if (s)
332 throw CFException("SSLNewContext failed", s);
333
334 // use TLS v1
335 s = SSLSetProtocolVersionEnabled(ssl, kSSLProtocol2, false);
336 if (s)
337 throw CFException("SSLSetProtocolVersionEnabled !S2 failed", s);
338 s = SSLSetProtocolVersionEnabled(ssl, kSSLProtocol3, false);
339 if (s)
340 throw CFException("SSLSetProtocolVersionEnabled !S3 failed", s);
341 s = SSLSetProtocolVersionEnabled(ssl, kTLSProtocol1, true);
342 if (s)
343 throw CFException("SSLSetProtocolVersionEnabled T1 failed", s);
344#endif
345 // configure cert, private key, and supporting CAs via identity wrapper
346 s = SSLSetCertificate(ssl, ctx.identity()());
347 if (s)
348 throw CFException("SSLSetCertificate failed", s);
349
350 // configure ciphertext buffers
351 ct_in.set_frame(ctx.frame());
352 ct_out.set_frame(ctx.frame());
353
354 // configure the "connection" object to be self
355 s = SSLSetConnection(ssl, this);
356 if (s)
357 throw CFException("SSLSetConnection", s);
358
359 // configure ciphertext read/write callbacks
360 s = SSLSetIOFuncs(ssl, ct_read_func, ct_write_func);
361 if (s)
362 throw CFException("SSLSetIOFuncs failed", s);
363 }
364 catch (...)
365 {
366 ssl_erase();
367 throw;
368 }
369 }
370
371 static OSStatus ct_read_func(SSLConnectionRef cref, void *data, size_t *length)
372 {
373 try
374 {
375 SSL *self = (SSL *)cref;
376 const size_t actual = self->ct_in.read((unsigned char *)data, *length);
377 const OSStatus ret = (*length == actual) ? 0 : errSSLWouldBlock;
378 *length = actual;
379 return ret;
380 }
381 catch (...)
382 {
383 return errSSLInternal;
384 }
385 }
386
387 static OSStatus ct_write_func(SSLConnectionRef cref, const void *data, size_t *length)
388 {
389 try
390 {
391 SSL *self = (SSL *)cref;
392 self->ct_out.write((const unsigned char *)data, *length);
393 return 0;
394 }
395 catch (...)
396 {
397 return errSSLInternal;
398 }
399 }
400
402 {
403 ssl = nullptr;
404 overflow = false;
405 }
406
408 {
409 if (ssl)
410 {
411#ifdef OPENVPN_PLATFORM_IPHONE
412 CFRelease(ssl);
413#else
414 SSLDisposeContext(ssl);
415#endif
416 }
417 ssl_clear();
418 }
419
420 SSLContextRef ssl; // underlying SSL connection object
421 MemQStream ct_in; // write ciphertext to here
422 MemQStream ct_out; // read ciphertext from here
424 };
425
427
428 // create a new SSL instance
429 SSLAPI::Ptr ssl() override
430 {
431 return SSL::Ptr(new SSL(*this));
432 }
433
434 // like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
435 SSLAPI::Ptr ssl(const std::string &hostname) override
436 {
437 OPENVPN_THROW(ssl_context_error, "AppleSSLContext: ssl session with CommonName and/or SubjectAltName verification not implemented");
438 }
439
440 const Mode &mode() const override
441 {
442 return config_->mode;
443 }
444
445 private:
447 : config_(config)
448 {
449 if (!config_->identity())
450 OPENVPN_THROW(ssl_context_error, "AppleSSLContext: identity undefined");
451 }
452
453 const Frame::Ptr &frame() const
454 {
455 return config_->frame;
456 }
457 const CF::Array &identity() const
458 {
459 return config_->identity;
460 }
461
462 // load an identity from keychain, return as an array that can
463 // be passed to SSLSetCertificate
464 static CF::Array load_identity_(const std::string &subj_match)
465 {
466 const CF::String label = CF::string(subj_match);
467 const void *keys[] = {kSecClass, kSecMatchSubjectContains, kSecMatchTrustedOnly, kSecReturnRef};
468 const void *values[] = {kSecClassIdentity, label(), kCFBooleanTrue, kCFBooleanTrue};
469 const CF::Dict query = CF::dict(keys, values, sizeof(keys) / sizeof(keys[0]));
470 CF::Generic result;
471 const OSStatus s = SecItemCopyMatching(query(), result.mod_ref());
472 if (!s && result.defined())
473 {
474 const void *asrc[] = {result()};
475 return CF::array(asrc, 1);
476 }
477 else
478 return CF::Array(); // not found
479 }
480
482};
483
485
486} // namespace openvpn
487
488#endif // OPENVPN_APPLECRYPTO_SSL_SSLCTX_H
void load(const OptionList &opt, const unsigned int lflags) override
Definition sslctx.hpp:103
void load_private_key(const std::string &key_txt) override
Definition sslctx.hpp:146
void load_crl(const std::string &crl_txt) override
Definition sslctx.hpp:131
void set_rng(const StrongRandomAPI::Ptr &rng_arg) override
Definition sslctx.hpp:196
SSLFactoryAPI::Ptr new_factory() override
Definition sslctx.hpp:83
void set_frame(const Frame::Ptr &frame_arg) override
Definition sslctx.hpp:98
void load_dh(const std::string &dh_txt) override
Definition sslctx.hpp:151
void set_remote_cert_tls(const KUParse::TLSWebType wt) override
Definition sslctx.hpp:171
void set_local_cert_enabled(const bool v) override
Definition sslctx.hpp:186
void set_ns_cert_type(const NSCert::Type ns_cert_type_arg) override
Definition sslctx.hpp:166
void set_private_key_password(const std::string &pwd) override
Definition sslctx.hpp:121
void set_tls_version_min(const TLSVersion::Type tvm) override
Definition sslctx.hpp:181
void load_cert(const std::string &cert_txt) override
Definition sslctx.hpp:136
void load_ca(const std::string &ca_txt, bool strict) override
Definition sslctx.hpp:126
void set_flags(const unsigned int flags_arg) override
Definition sslctx.hpp:161
void set_tls_remote(const std::string &tls_remote_arg) override
Definition sslctx.hpp:176
void set_mode(const Mode &mode_arg) override
Definition sslctx.hpp:88
void not_implemented(const char *funcname)
Definition sslctx.hpp:202
void set_external_pki_callback(ExternalPKIBase *external_pki_arg, const std::string &alias) override
Definition sslctx.hpp:116
void set_debug_level(const int debug_level) override
Definition sslctx.hpp:156
void set_enable_renegotiation(const bool v) override
Definition sslctx.hpp:191
const Mode & get_mode() const override
Definition sslctx.hpp:93
void load_cert(const std::string &cert_txt, const std::string &extra_certs_txt) override
Definition sslctx.hpp:141
void load_identity(const std::string &subject_match)
Definition sslctx.hpp:76
const AuthCert::Ptr & auth_cert() const override
Definition sslctx.hpp:290
ssize_t write_cleartext_unbuffered(const void *data, const size_t size) override
Definition sslctx.hpp:226
ssize_t read_cleartext(void *data, const size_t capacity) override
Definition sslctx.hpp:241
void write_ciphertext(const BufferPtr &buf) override
Definition sslctx.hpp:267
SSL(const AppleSSLContext &ctx)
Definition sslctx.hpp:301
bool read_ciphertext_ready() const override
Definition sslctx.hpp:275
std::string ssl_handshake_details() const override
Definition sslctx.hpp:285
void start_handshake() override
Definition sslctx.hpp:221
static OSStatus ct_read_func(SSLConnectionRef cref, void *data, size_t *length)
Definition sslctx.hpp:371
BufferPtr read_ciphertext() override
Definition sslctx.hpp:280
bool read_cleartext_ready() const override
Definition sslctx.hpp:261
static OSStatus ct_write_func(SSLConnectionRef cref, const void *data, size_t *length)
Definition sslctx.hpp:387
SSLAPI::Ptr ssl() override
Definition sslctx.hpp:429
static CF::Array load_identity_(const std::string &subj_match)
Definition sslctx.hpp:464
const CF::Array & identity() const
Definition sslctx.hpp:457
const Mode & mode() const override
Definition sslctx.hpp:440
AppleSSLContext(Config *config)
Definition sslctx.hpp:446
SSLAPI::Ptr ssl(const std::string &hostname) override
Definition sslctx.hpp:435
const Frame::Ptr & frame() const
Definition sslctx.hpp:453
RCPtr< AppleSSLContext > Ptr
Definition sslctx.hpp:59
T * mod_ref()
Definition cf.hpp:168
bool defined() const
Definition cf.hpp:122
bool empty() const
Definition memq.hpp:37
size_t size() const
Definition memq.hpp:32
BufferPtr read_buf()
Definition memq.hpp:60
void write_buf(const BufferPtr &bp)
Definition memq.hpp:54
void set_frame(const Frame::Ptr &frame)
void write(const unsigned char *data, size_t size)
size_t read(unsigned char *data, size_t len)
bool is_server() const
Definition mode.hpp:36
bool is_client() const
Definition mode.hpp:40
const Option & get(const std::string &name) const
Definition options.hpp:1250
bool exists(const std::string &name) const
Definition options.hpp:1321
The smart pointer class.
Definition rc.hpp:119
RCPtr< SSLFactoryAPI > Ptr
Definition sslapi.hpp:91
#define OPENVPN_THROW(exc, stuff)
#define OPENVPN_LOG(args)
Array array(const void **values, CFIndex numValues)
Definition cf.hpp:271
String string(const char *str)
Definition cf.hpp:221
Dict dict(const void **keys, const void **values, CFIndex numValues)
Definition cf.hpp:276
AppleSSLContext::Ptr AppleSSLContextPtr
Definition sslctx.hpp:484
const std::string cert_txt
std::string ret
static const char config[]