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
75 {
76 }
77
78 void load_identity(const std::string &subject_match)
79 {
80 identity = load_identity_(subject_match);
81 if (!identity())
82 OPENVPN_THROW(ssl_context_error, "AppleSSLContext: identity '" << subject_match << "' undefined");
83 }
84
86 {
87 return SSLFactoryAPI::Ptr(new AppleSSLContext(this));
88 }
89
90 void set_mode(const Mode &mode_arg) override
91 {
92 mode = mode_arg;
93 }
94
95 const Mode &get_mode() const override
96 {
97 return mode;
98 }
99
100 void set_frame(const Frame::Ptr &frame_arg) override
101 {
102 frame = frame_arg;
103 }
104
105 void load(const OptionList &opt, const unsigned int lflags) override
106 {
107 // client/server
108 if (lflags & LF_PARSE_MODE)
109 mode = opt.exists("client") ? Mode(Mode::CLIENT) : Mode(Mode::SERVER);
110
111 // identity
112 {
113 const std::string &subject_match = opt.get("identity", 1, 256);
114 load_identity(subject_match);
115 }
116 }
117
118 void set_external_pki_callback(ExternalPKIBase *external_pki_arg, const std::string &alias) override
119 {
120 not_implemented("set_external_pki_callback");
121 }
122
123 void set_private_key_password(const std::string &pwd) override
124 {
125 return not_implemented("set_private_key_password");
126 }
127
128 void load_ca(const std::string &ca_txt, bool strict) override
129 {
130 return not_implemented("load_ca");
131 }
132
133 void load_crl(const std::string &crl_txt) override
134 {
135 return not_implemented("load_crl");
136 }
137
138 void load_cert(const std::string &cert_txt) override
139 {
140 return not_implemented("load_cert");
141 }
142
143 void load_cert(const std::string &cert_txt, const std::string &extra_certs_txt) override
144 {
145 return not_implemented("load_cert");
146 }
147
148 void load_private_key(const std::string &key_txt) override
149 {
150 return not_implemented("load_private_key");
151 }
152
153 void load_dh(const std::string &dh_txt) override
154 {
155 return not_implemented("load_dh");
156 }
157
158 void set_debug_level(const int debug_level) override
159 {
160 return not_implemented("set_debug_level");
161 }
162
163 void set_flags(const unsigned int flags_arg) override
164 {
165 return not_implemented("set_flags");
166 }
167
168 void set_ns_cert_type(const NSCert::Type ns_cert_type_arg) override
169 {
170 return not_implemented("set_ns_cert_type");
171 }
172
174 {
175 return not_implemented("set_remote_cert_tls");
176 }
177
178 void set_tls_remote(const std::string &tls_remote_arg) override
179 {
180 return not_implemented("set_tls_remote");
181 }
182
183 void set_tls_version_min(const TLSVersion::Type tvm) override
184 {
185 return not_implemented("set_tls_version_min");
186 }
187
188 void set_local_cert_enabled(const bool v) override
189 {
190 return not_implemented("set_local_cert_enabled");
191 }
192
193 void set_enable_renegotiation(const bool v) override
194 {
195 return not_implemented("set_enable_renegotiation");
196 }
197
198 void set_rng(const StrongRandomAPI::Ptr &rng_arg) override
199 {
200 return not_implemented("set_rng");
201 }
202
203 private:
204 void not_implemented(const char *funcname)
205 {
206 OPENVPN_LOG("AppleSSL: " << funcname << " not implemented");
207 }
208
210 CF::Array identity; // as returned by load_identity
212 };
213
214 // Represents an actual SSL session.
215 // Normally instantiated by AppleSSLContext::ssl().
216 class SSL : public SSLAPI
217 {
218 friend class AppleSSLContext;
219
220 public:
222
223 void start_handshake() override
224 {
225 SSLHandshake(ssl);
226 }
227
228 ssize_t write_cleartext_unbuffered(const void *data, const size_t size) override
229 {
230 size_t actual = 0;
231 const OSStatus status = SSLWrite(ssl, data, size, &actual);
232 if (status < 0)
233 {
234 if (status == errSSLWouldBlock)
236 else
237 throw CFException("AppleSSLContext::SSL::write_cleartext failed", status);
238 }
239 else
240 return actual;
241 }
242
243 ssize_t read_cleartext(void *data, const size_t capacity) override
244 {
245 if (!overflow)
246 {
247 size_t actual = 0;
248 const OSStatus status = SSLRead(ssl, data, capacity, &actual);
249 if (status < 0)
250 {
251 if (status == errSSLWouldBlock)
253 else
254 throw CFException("AppleSSLContext::SSL::read_cleartext failed", status);
255 }
256 else
257 return actual;
258 }
259 else
260 throw ssl_ciphertext_in_overflow();
261 }
262
263 bool read_cleartext_ready() const override
264 {
265 // fixme: need to detect data buffered at SSL layer
266 return !ct_in.empty();
267 }
268
269 void write_ciphertext(const BufferPtr &buf) override
270 {
272 ct_in.write_buf(buf);
273 else
274 overflow = true;
275 }
276
277 bool read_ciphertext_ready() const override
278 {
279 return !ct_out.empty();
280 }
281
283 {
284 return ct_out.read_buf();
285 }
286
287 std::string ssl_handshake_details() const override // fixme -- code me
288 {
289 return "[AppleSSL not implemented]";
290 }
291
292 const AuthCert::Ptr &auth_cert() const override
293 {
294 OPENVPN_THROW(ssl_context_error, "AppleSSL::SSL: auth_cert() not implemented");
295 }
296
298 {
299 ssl_erase();
300 }
301
302 private:
304 {
305 ssl_clear();
306 try
307 {
308 OSStatus s;
309
310#ifdef OPENVPN_PLATFORM_IPHONE
311 // init SSL object, select client or server mode
312 if (ctx.mode().is_server())
313 ssl = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType);
314 else if (ctx.mode().is_client())
315 ssl = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
316 else
317 OPENVPN_THROW(ssl_context_error, "AppleSSLContext::SSL: unknown client/server mode");
318 if (ssl == nullptr)
319 throw CFException("SSLCreateContext failed");
320
321 // use TLS v1
322 s = SSLSetProtocolVersionMin(ssl, kTLSProtocol1);
323 if (s)
324 throw CFException("SSLSetProtocolVersionMin failed", s);
325#else
326 // init SSL object, select client or server mode
327 if (ctx.mode().is_server())
328 s = SSLNewContext(true, &ssl);
329 else if (ctx.mode().is_client())
330 s = SSLNewContext(false, &ssl);
331 else
332 OPENVPN_THROW(ssl_context_error, "AppleSSLContext::SSL: unknown client/server mode");
333 if (s)
334 throw CFException("SSLNewContext failed", s);
335
336 // use TLS v1
337 s = SSLSetProtocolVersionEnabled(ssl, kSSLProtocol2, false);
338 if (s)
339 throw CFException("SSLSetProtocolVersionEnabled !S2 failed", s);
340 s = SSLSetProtocolVersionEnabled(ssl, kSSLProtocol3, false);
341 if (s)
342 throw CFException("SSLSetProtocolVersionEnabled !S3 failed", s);
343 s = SSLSetProtocolVersionEnabled(ssl, kTLSProtocol1, true);
344 if (s)
345 throw CFException("SSLSetProtocolVersionEnabled T1 failed", s);
346#endif
347 // configure cert, private key, and supporting CAs via identity wrapper
348 s = SSLSetCertificate(ssl, ctx.identity()());
349 if (s)
350 throw CFException("SSLSetCertificate failed", s);
351
352 // configure ciphertext buffers
353 ct_in.set_frame(ctx.frame());
354 ct_out.set_frame(ctx.frame());
355
356 // configure the "connection" object to be self
357 s = SSLSetConnection(ssl, this);
358 if (s)
359 throw CFException("SSLSetConnection", s);
360
361 // configure ciphertext read/write callbacks
362 s = SSLSetIOFuncs(ssl, ct_read_func, ct_write_func);
363 if (s)
364 throw CFException("SSLSetIOFuncs failed", s);
365 }
366 catch (...)
367 {
368 ssl_erase();
369 throw;
370 }
371 }
372
373 static OSStatus ct_read_func(SSLConnectionRef cref, void *data, size_t *length)
374 {
375 try
376 {
377 SSL *self = (SSL *)cref;
378 const size_t actual = self->ct_in.read((unsigned char *)data, *length);
379 const OSStatus ret = (*length == actual) ? 0 : errSSLWouldBlock;
380 *length = actual;
381 return ret;
382 }
383 catch (...)
384 {
385 return errSSLInternal;
386 }
387 }
388
389 static OSStatus ct_write_func(SSLConnectionRef cref, const void *data, size_t *length)
390 {
391 try
392 {
393 SSL *self = (SSL *)cref;
394 self->ct_out.write((const unsigned char *)data, *length);
395 return 0;
396 }
397 catch (...)
398 {
399 return errSSLInternal;
400 }
401 }
402
404 {
405 ssl = nullptr;
406 overflow = false;
407 }
408
410 {
411 if (ssl)
412 {
413#ifdef OPENVPN_PLATFORM_IPHONE
414 CFRelease(ssl);
415#else
416 SSLDisposeContext(ssl);
417#endif
418 }
419 ssl_clear();
420 }
421
422 SSLContextRef ssl; // underlying SSL connection object
423 MemQStream ct_in; // write ciphertext to here
424 MemQStream ct_out; // read ciphertext from here
426 };
427
429
430 // create a new SSL instance
431 SSLAPI::Ptr ssl() override
432 {
433 return SSL::Ptr(new SSL(*this));
434 }
435
436 // like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
437 SSLAPI::Ptr ssl(const std::string &hostname) override
438 {
439 OPENVPN_THROW(ssl_context_error, "AppleSSLContext: ssl session with CommonName and/or SubjectAltName verification not implemented");
440 }
441
442 const Mode &mode() const override
443 {
444 return config_->mode;
445 }
446
447 private:
449 : config_(config)
450 {
451 if (!config_->identity())
452 OPENVPN_THROW(ssl_context_error, "AppleSSLContext: identity undefined");
453 }
454
455 const Frame::Ptr &frame() const
456 {
457 return config_->frame;
458 }
459 const CF::Array &identity() const
460 {
461 return config_->identity;
462 }
463
464 // load an identity from keychain, return as an array that can
465 // be passed to SSLSetCertificate
466 static CF::Array load_identity_(const std::string &subj_match)
467 {
468 const CF::String label = CF::string(subj_match);
469 const void *keys[] = {kSecClass, kSecMatchSubjectContains, kSecMatchTrustedOnly, kSecReturnRef};
470 const void *values[] = {kSecClassIdentity, label(), kCFBooleanTrue, kCFBooleanTrue};
471 const CF::Dict query = CF::dict(keys, values, sizeof(keys) / sizeof(keys[0]));
472 CF::Generic result;
473 const OSStatus s = SecItemCopyMatching(query(), result.mod_ref());
474 if (!s && result.defined())
475 {
476 const void *asrc[] = {result()};
477 return CF::array(asrc, 1);
478 }
479 else
480 return CF::Array(); // not found
481 }
482
484};
485
487
488} // namespace openvpn
489
490#endif // OPENVPN_APPLECRYPTO_SSL_SSLCTX_H
void load(const OptionList &opt, const unsigned int lflags) override
Definition sslctx.hpp:105
void load_private_key(const std::string &key_txt) override
Definition sslctx.hpp:148
void load_crl(const std::string &crl_txt) override
Definition sslctx.hpp:133
void set_rng(const StrongRandomAPI::Ptr &rng_arg) override
Definition sslctx.hpp:198
SSLFactoryAPI::Ptr new_factory() override
Definition sslctx.hpp:85
void set_frame(const Frame::Ptr &frame_arg) override
Definition sslctx.hpp:100
void load_dh(const std::string &dh_txt) override
Definition sslctx.hpp:153
void set_remote_cert_tls(const KUParse::TLSWebType wt) override
Definition sslctx.hpp:173
void set_local_cert_enabled(const bool v) override
Definition sslctx.hpp:188
void set_ns_cert_type(const NSCert::Type ns_cert_type_arg) override
Definition sslctx.hpp:168
void set_private_key_password(const std::string &pwd) override
Definition sslctx.hpp:123
void set_tls_version_min(const TLSVersion::Type tvm) override
Definition sslctx.hpp:183
void load_cert(const std::string &cert_txt) override
Definition sslctx.hpp:138
void load_ca(const std::string &ca_txt, bool strict) override
Definition sslctx.hpp:128
void set_flags(const unsigned int flags_arg) override
Definition sslctx.hpp:163
void set_tls_remote(const std::string &tls_remote_arg) override
Definition sslctx.hpp:178
void set_mode(const Mode &mode_arg) override
Definition sslctx.hpp:90
void not_implemented(const char *funcname)
Definition sslctx.hpp:204
void set_external_pki_callback(ExternalPKIBase *external_pki_arg, const std::string &alias) override
Definition sslctx.hpp:118
void set_debug_level(const int debug_level) override
Definition sslctx.hpp:158
void set_enable_renegotiation(const bool v) override
Definition sslctx.hpp:193
const Mode & get_mode() const override
Definition sslctx.hpp:95
void load_cert(const std::string &cert_txt, const std::string &extra_certs_txt) override
Definition sslctx.hpp:143
void load_identity(const std::string &subject_match)
Definition sslctx.hpp:78
const AuthCert::Ptr & auth_cert() const override
Definition sslctx.hpp:292
ssize_t write_cleartext_unbuffered(const void *data, const size_t size) override
Definition sslctx.hpp:228
ssize_t read_cleartext(void *data, const size_t capacity) override
Definition sslctx.hpp:243
void write_ciphertext(const BufferPtr &buf) override
Definition sslctx.hpp:269
SSL(const AppleSSLContext &ctx)
Definition sslctx.hpp:303
bool read_ciphertext_ready() const override
Definition sslctx.hpp:277
std::string ssl_handshake_details() const override
Definition sslctx.hpp:287
void start_handshake() override
Definition sslctx.hpp:223
static OSStatus ct_read_func(SSLConnectionRef cref, void *data, size_t *length)
Definition sslctx.hpp:373
BufferPtr read_ciphertext() override
Definition sslctx.hpp:282
bool read_cleartext_ready() const override
Definition sslctx.hpp:263
static OSStatus ct_write_func(SSLConnectionRef cref, const void *data, size_t *length)
Definition sslctx.hpp:389
SSLAPI::Ptr ssl() override
Definition sslctx.hpp:431
static CF::Array load_identity_(const std::string &subj_match)
Definition sslctx.hpp:466
const CF::Array & identity() const
Definition sslctx.hpp:459
const Mode & mode() const override
Definition sslctx.hpp:442
AppleSSLContext(Config *config)
Definition sslctx.hpp:448
SSLAPI::Ptr ssl(const std::string &hostname) override
Definition sslctx.hpp:437
const Frame::Ptr & frame() const
Definition sslctx.hpp:455
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:1254
bool exists(const std::string &name) const
Definition options.hpp:1325
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
Support deferred server-side state creation when client connects.
Definition ovpncli.cpp:95
AppleSSLContext::Ptr AppleSSLContextPtr
Definition sslctx.hpp:486
const std::string cert_txt
std::string ret
static const char config[]