• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 1669

13 Sep 2023 06:44PM UTC coverage: 91.193% (-0.07%) from 91.258%
1669

push

Evergreen

GitHub
Update History Command tool to work with realms with file format version 23 (#6970)

95936 of 175880 branches covered (0.0%)

233596 of 256155 relevant lines covered (91.19%)

6735051.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.95
/src/realm/sync/network/network_ssl.hpp
1
#pragma once
2

3
#include <cstddef>
4
#include <limits>
5
#include <memory>
6
#include <string>
7
#include <exception>
8
#include <system_error>
9

10
#include <realm/sync/network/network.hpp>
11

12
#include <realm/util/features.h>
13
#include <realm/util/assert.hpp>
14
#include <realm/util/misc_errors.hpp>
15
#include <realm/util/optional.hpp>
16
#include <realm/util/logger.hpp>
17

18
#if REALM_HAVE_OPENSSL
19
#include <openssl/ssl.h>
20
#include <openssl/err.h>
21
#elif REALM_HAVE_SECURE_TRANSPORT
22
#include <realm/util/cf_ptr.hpp>
23
#include <Security/Security.h>
24
#include <Security/SecureTransport.h>
25

26
#define REALM_HAVE_KEYCHAIN_APIS (TARGET_OS_MAC && !TARGET_OS_IPHONE)
27

28
#endif
29

30
// FIXME: Add necessary support for customizing the SSL server and client
31
// configurations.
32

33
// FIXME: Currently, the synchronous SSL operations (handshake, read, write,
34
// shutdown) do not automatically retry if the underlying SSL function returns
35
// with SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. This normally never
36
// happens, but it can happen according to the man pages, but in the case of
37
// SSL_write(), only when a renegotiation has to take place. It is likely that
38
// the solution is to to wrap the SSL calls inside a loop, such that they keep
39
// retrying until they succeed, however, such a simple scheme will fail if the
40
// synchronous operations were to be used with an underlying TCP socket in
41
// nonblocking mode. Currently, the underlying TCP socket is always in blocking
42
// mode when performing synchronous operations, but that may continue to be the
43
// case in the future.
44

45

46
namespace realm::sync::network::ssl {
47

48
enum class Errors {
49
    tls_handshake_failed = 1,
50
};
51

52
class ErrorCategory : public std::error_category {
53
public:
54
    const char* name() const noexcept override final;
55
    std::string message(int) const override final;
56
    bool equivalent(const std::error_code&, int) const noexcept override final;
57
};
58

59
/// The error category associated with \ref Errors. The name of this category is
60
/// `realm.sync.network.ssl`.
61
extern ErrorCategory error_category;
62

63
inline std::error_code make_error_code(Errors err)
64
{
×
65
    return std::error_code(int(err), error_category);
×
66
}
×
67

68
inline std::error_condition make_error_condition(Errors err)
69
{
10✔
70
    return std::error_condition(int(err), error_category);
10✔
71
}
10✔
72

73
} // namespace realm::sync::network::ssl
74

75
namespace std {
76

77
template <>
78
class is_error_condition_enum<realm::sync::network::ssl::Errors> {
79
public:
80
    static const bool value = true;
81
};
82

83
} // namespace std
84

85
namespace realm::sync::network {
86

87
class OpensslErrorCategory : public std::error_category {
88
public:
89
    const char* name() const noexcept override final;
90
    std::string message(int) const override final;
91
};
92

93
/// The error category associated with error codes produced by the third-party
94
/// library, OpenSSL. The name of this category is `openssl`.
95
extern OpensslErrorCategory openssl_error_category;
96

97
class SecureTransportErrorCategory : public std::error_category {
98
public:
99
    const char* name() const noexcept override final;
100
    std::string message(int) const override final;
101
};
102

103
/// The error category associated with error codes produced by Apple's
104
/// SecureTransport library. The name of this category is `securetransport`.
105
extern SecureTransportErrorCategory secure_transport_error_category;
106

107
namespace ssl {
108

109
class ProtocolNotSupported;
110

111

112
/// `VerifyMode::none` corresponds to OpenSSL's `SSL_VERIFY_NONE`, and
113
/// `VerifyMode::peer` to `SSL_VERIFY_PEER`.
114
enum class VerifyMode { none, peer };
115

116

117
class Context {
118
public:
119
    Context();
120
    ~Context() noexcept;
121

122
    /// File must be in PEM format. Corresponds to OpenSSL's
123
    /// `SSL_CTX_use_certificate_chain_file()`.
124
    void use_certificate_chain_file(const std::string& path);
125

126
    /// File must be in PEM format. Corresponds to OpenSSL's
127
    /// `SSL_CTX_use_PrivateKey_file()`.
128
    void use_private_key_file(const std::string& path);
129

130
    /// Calling use_default_verify() will make a client use the device
131
    /// default certificates for server verification. For OpenSSL,
132
    /// use_default_verify() corresponds to
133
    /// SSL_CTX_set_default_verify_paths(SSL_CTX*);
134
    void use_default_verify();
135

136
    /// The verify file is a PEM file containing trust certificates that the
137
    /// client will use to verify the server certificate. If use_verify_file()
138
    /// is not called, the default device trust store will be used.
139
    /// use_verify_file() corresponds roughly to OpenSSL's
140
    /// SSL_CTX_load_verify_locations().
141
    void use_verify_file(const std::string& path);
142

143
private:
144
    void ssl_init();
145
    void ssl_destroy() noexcept;
146
    void ssl_use_certificate_chain_file(const std::string& path, std::error_code&);
147
    void ssl_use_private_key_file(const std::string& path, std::error_code&);
148
    void ssl_use_default_verify(std::error_code&);
149
    void ssl_use_verify_file(const std::string& path, std::error_code&);
150

151
#if REALM_HAVE_OPENSSL
152
    SSL_CTX* m_ssl_ctx = nullptr;
153

154
#elif REALM_HAVE_SECURE_TRANSPORT
155

156
#if REALM_HAVE_KEYCHAIN_APIS
157
    std::error_code open_temporary_keychain_if_needed();
158
    std::error_code update_identity_if_needed();
159

160
    util::CFPtr<SecKeychainRef> m_keychain;
161
    std::string m_keychain_path;
162

163
    util::CFPtr<SecCertificateRef> m_certificate;
164
    util::CFPtr<SecKeyRef> m_private_key;
165
    util::CFPtr<SecIdentityRef> m_identity;
166

167
    util::CFPtr<CFArrayRef> m_certificate_chain;
168

169
#else
170
    using SecKeychainRef = std::nullptr_t;
171

172
#endif // REALM_HAVE_KEYCHAIN_APIS
173
    static util::CFPtr<CFArrayRef> load_pem_file(const std::string& path, SecKeychainRef, std::error_code&);
174

175
    util::CFPtr<CFArrayRef> m_trust_anchors;
176
    util::CFPtr<CFDataRef> m_pinned_certificate;
177

178
#endif
179

180
    friend class Stream;
181
};
182

183

184
/// Switching between synchronous and asynchronous operations is allowed, but
185
/// only in a nonoverlapping fashion. That is, a synchronous operation is not
186
/// allowed to run concurrently with an asynchronous one on the same
187
/// stream. Note that an asynchronous operation is considered to be running
188
/// until its completion handler starts executing.
189
class Stream {
190
public:
191
#if REALM_HAVE_SECURE_TRANSPORT
192
    struct MockSSLError;
193
#endif
194

195
    using port_type = network::Endpoint::port_type;
196
    using SSLVerifyCallback = bool(const std::string& server_address, port_type server_port, const char* pem_data,
197
                                   size_t pem_size, int preverify_ok, int depth);
198

199
    enum HandshakeType { client, server };
200

201
    util::Logger* logger = nullptr;
202

203
    Stream(Socket&, Context&, HandshakeType);
204
    ~Stream() noexcept;
205

206
    /// \brief set_logger() set a logger for the stream class. If
207
    /// set_logger() is not called, no logging will take place by
208
    /// the Stream class.
209
    void set_logger(util::Logger*);
210

211
    /// \brief Set the certificate verification mode for this SSL stream.
212
    ///
213
    /// Corresponds to OpenSSL's `SSL_set_verify()` with null passed as
214
    /// `verify_callback`.
215
    ///
216
    /// Clients should always set it to `VerifyMode::peer`, such that the client
217
    /// verifies the servers certificate. Servers should only set it to
218
    /// `VerifyMode::peer` if they want to request a certificate from the
219
    /// client. When testing with self-signed certificates, it is necessary to
220
    /// set it to `VerifyMode::none` for clients too.
221
    ///
222
    /// It is an error if this function is called after the handshake operation
223
    /// is initiated.
224
    ///
225
    /// The default verify mode is `VerifyMode::none`.
226
    void set_verify_mode(VerifyMode);
227

228
    /// \brief Check the certificate against a host_name.
229
    ///
230
    /// On the client side, this enables the Server Name Indication (TLS/SNI)
231
    /// extension if supported by the underlying platform. For OpenSSL, it is
232
    /// enabled starting from version 1.1.1.
233
    ///
234
    /// Additionally, this turns on a host name check as part of certificate
235
    /// verification, if certificate verification is enabled
236
    /// (set_verify_mode()).
237
    ///
238
    /// NOTE: With Secure Transport on macos, host name check will be turned on
239
    /// regardless of whether certificate verification is enabled (see
240
    /// https://github.com/curl/curl/pull/1240#issuecomment-285281512).
241
    void set_host_name(std::string host_name);
242

243
    /// get_server_port() and set_server_port() are getter and setter for
244
    /// the server port. They are only used by the verify callback function
245
    /// below.
246
    void set_server_port(port_type server_port);
247

248
    /// If use_verify_callback() is called, the SSL certificate chain of
249
    /// the server is presented to callback, one certificate at a time.
250
    /// The SSL connection is accepted if and only if callback returns true
251
    /// for all certificates.
252
    /// The signature of \param callback is
253
    ///
254
    /// bool(const std::string& server_address,
255
    ///      port_type server_port,
256
    ///      const char* pem_data,
257
    ///      size_t pem_size,
258
    ///      int preverify_ok,
259
    ///      int depth);
260
    //
261
    /// server address and server_port is the address and port of the server
262
    /// that a SSL connection is being established to.
263
    /// pem_data is the certificate of length pem_size in
264
    /// the PEM format. preverify_ok is OpenSSL's preverification of the
265
    /// certificate. preverify_ok is either 0, or 1. If preverify_ok is 1,
266
    /// OpenSSL has accepted the certificate and it will generally be safe
267
    /// to trust that certificate. depth represents the position of the
268
    /// certificate in the certificate chain sent by the server. depth = 0
269
    /// represents the actual server certificate that should contain the
270
    /// host name(server address) of the server. The highest depth is the
271
    /// root certificate.
272
    /// The callback function will receive the certificates starting from
273
    /// the root certificate and moving down the chain until it reaches the
274
    /// server's own certificate with a host name. The depth of the last
275
    /// certificate is 0. The depth of the first certificate is chain
276
    /// length - 1.
277
    ///
278
    /// The return value of the callback function decides whether the
279
    /// client accepts the certificate. If the return value is false, the
280
    /// processing of the certificate chain is interrupted and the SSL
281
    /// connection is rejected. If the return value is true, the verification
282
    /// process continues. If the callback function returns true for all
283
    /// presented certificates including the depth == 0 certificate, the
284
    /// SSL connection is accepted.
285
    ///
286
    /// A recommended way of using the callback function is to return true
287
    /// if preverify_ok = 1 and depth > 0,
288
    /// always check the host name if depth = 0,
289
    /// and use an independent verification step if preverify_ok = 0.
290
    ///
291
    /// Another possible way of using the callback is to collect all the
292
    /// certificates until depth = 0, and present the entire chain for
293
    /// independent verification.
294
    void use_verify_callback(const std::function<SSLVerifyCallback>& callback);
295

296
#if REALM_INCLUDE_CERTS
297
    /// use_included_certificates() loads a set of certificates that are
298
    /// included in the header file src/realm/noinst/root_certs.hpp. By using
299
    /// the included certificates, the client can verify a server in the case
300
    /// where the relevant certificate cannot be found, or is absent, in the
301
    /// system trust store. This function is only implemented for OpenSSL.
302
    void use_included_certificates();
303
#endif
304

305
    /// @{
306
    ///
307
    /// Read and write operations behave the same way as they do on \ref
308
    /// network::Socket, except that after cancellation of asynchronous
309
    /// operations (`lowest_layer().cancel()`), the stream may be left in a bad
310
    /// state (see below).
311
    ///
312
    /// The handshake operation must complete successfully before any read,
313
    /// write, or shutdown operations are performed.
314
    ///
315
    /// The shutdown operation sends the shutdown alert to the peer, and
316
    /// returns/completes as soon as the alert message has been written to the
317
    /// underlying socket. It is an error if the shutdown operation is initiated
318
    /// while there are read or write operations in progress. No read or write
319
    /// operations are allowed to be initiated after the shutdown operation has
320
    /// been initiated. When the shutdown operation has completed, it is safe to
321
    /// close the underlying socket (`lowest_layer().close()`).
322
    ///
323
    /// If a write operation is executing while, or is initiated after a close
324
    /// notify alert is received from the remote peer, the write operation will
325
    /// fail with error::broken_pipe.
326
    ///
327
    /// Callback functions for async read and write operations must take two
328
    /// arguments, an std::error_code(), and an integer of a type std::size_t
329
    /// indicating the number of transferred bytes (other types are allowed as
330
    /// long as implicit conversion can take place).
331
    ///
332
    /// Callback functions for async handshake and shutdown operations must take
333
    /// a single argument of type std::error_code() (other types are allowed as
334
    /// long as implicit conversion can take place).
335
    ///
336
    /// Resumption of stream operation after cancellation of asynchronous
337
    /// operations is not supported (does not work). Since the shutdown
338
    /// operation involves network communication, that operation is also not
339
    /// allowed after cancellation. The only thing that is allowed, is to
340
    /// destroy the stream object. Other stream objects are not affected.
341

342
    void handshake();
343
    std::error_code handshake(std::error_code&);
344

345
    std::size_t read(char* buffer, std::size_t size);
346
    std::size_t read(char* buffer, std::size_t size, std::error_code& ec);
347
    std::size_t read(char* buffer, std::size_t size, ReadAheadBuffer&);
348
    std::size_t read(char* buffer, std::size_t size, ReadAheadBuffer&, std::error_code& ec);
349
    std::size_t read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer&);
350
    std::size_t read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer&, std::error_code& ec);
351

352
    std::size_t write(const char* data, std::size_t size);
353
    std::size_t write(const char* data, std::size_t size, std::error_code& ec);
354

355
    std::size_t read_some(char* buffer, std::size_t size);
356
    std::size_t read_some(char* buffer, std::size_t size, std::error_code&);
357

358
    std::size_t write_some(const char* data, std::size_t size);
359
    std::size_t write_some(const char* data, std::size_t size, std::error_code&);
360

361
    void shutdown();
362
    std::error_code shutdown(std::error_code&);
363

364
    template <class H>
365
    void async_handshake(H handler);
366

367
    template <class H>
368
    void async_read(char* buffer, std::size_t size, H handler);
369
    template <class H>
370
    void async_read(char* buffer, std::size_t size, ReadAheadBuffer&, H handler);
371
    template <class H>
372
    void async_read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer&, H handler);
373

374
    template <class H>
375
    void async_write(const char* data, std::size_t size, H handler);
376

377
    template <class H>
378
    void async_read_some(char* buffer, std::size_t size, H handler);
379

380
    template <class H>
381
    void async_write_some(const char* data, std::size_t size, H handler);
382

383
    template <class H>
384
    void async_shutdown(H handler);
385

386
    /// @}
387

388
    /// Returns a reference to the underlying socket.
389
    Socket& lowest_layer() noexcept;
390

391
#if REALM_HAVE_SECURE_TRANSPORT
392
    /// Mock the error value returned by ssl_perform() - currently only used by Apple Secure Transport
393
    void set_mock_ssl_perform_error(std::unique_ptr<MockSSLError>&& error = nullptr);
394
#endif
395

396
private:
397
    using Want = Service::Want;
398
    using StreamOps = Service::BasicStreamOps<Stream>;
399

400
    class HandshakeOperBase;
401
    template <class H>
402
    class HandshakeOper;
403
    class ShutdownOperBase;
404
    template <class H>
405
    class ShutdownOper;
406

407
    using LendersHandshakeOperPtr = std::unique_ptr<HandshakeOperBase, Service::LendersOperDeleter>;
408
    using LendersShutdownOperPtr = std::unique_ptr<ShutdownOperBase, Service::LendersOperDeleter>;
409

410
    Socket& m_tcp_socket;
411
    Context& m_ssl_context;
412
    const HandshakeType m_handshake_type;
413

414
    // The host name that the certificate should be checked against.
415
    // The host name is called server address in the certificate verify
416
    // callback function.
417
    std::string m_host_name;
418

419
    // The port of the server which is used in the certificate verify
420
    // callback function.
421
    port_type m_server_port;
422

423
    // The callback for certificate verification and an
424
    // opaque argument that will be supplied to the callback.
425
    const std::function<SSLVerifyCallback>* m_ssl_verify_callback = nullptr;
426

427
    bool m_valid_certificate_in_chain = false;
428

429

430
    // See Service::BasicStreamOps for details on these these 6 functions.
431
    void do_init_read_async(std::error_code&, Want&) noexcept;
432
    void do_init_write_async(std::error_code&, Want&) noexcept;
433
    std::size_t do_read_some_sync(char* buffer, std::size_t size, std::error_code&) noexcept;
434
    std::size_t do_write_some_sync(const char* data, std::size_t size, std::error_code&) noexcept;
435
    std::size_t do_read_some_async(char* buffer, std::size_t size, std::error_code&, Want&) noexcept;
436
    std::size_t do_write_some_async(const char* data, std::size_t size, std::error_code&, Want&) noexcept;
437

438
    // The meaning of the arguments and return values of ssl_read() and
439
    // ssl_write() are identical to do_read_some_async() and
440
    // do_write_some_async() respectively, except that when the return value is
441
    // nonzero, `want` is always `Want::nothing`, meaning that after bytes have
442
    // been transferred, ssl_read() and ssl_write() must be called again to
443
    // figure out whether it is necessary to wait for read or write readiness.
444
    //
445
    // The first invocation of ssl_shutdown() must send the shutdown alert to
446
    // the peer. In blocking mode it must wait until the alert has been sent. In
447
    // nonblocking mode, it must keep setting `want` to something other than
448
    // `Want::nothing` until the alert has been sent. When the shutdown alert
449
    // has been sent, it is safe to shut down the sending side of the underlying
450
    // socket. On failure, ssl_shutdown() must set `ec` to something different
451
    // than `std::error_code()` and return false. On success, it must set `ec`
452
    // to `std::error_code()`, and return true if a shutdown alert from the peer
453
    // has already been received, otherwise it must return false. When it sets
454
    // `want` to something other than `Want::nothing`, it must set `ec` to
455
    // `std::error_code()` and return false.
456
    //
457
    // The second invocation of ssl_shutdown() (after the first invocation
458
    // completed) must wait for reception on the peers shutdown alert.
459
    //
460
    // Note: The semantics around the second invocation of shutdown is currently
461
    // unused by the higher level API, because of a requirement of compatibility
462
    // with Apple's Secure Transport API.
463
    void ssl_init();
464
    void ssl_destroy() noexcept;
465
    void ssl_set_verify_mode(VerifyMode, std::error_code&);
466
    void ssl_set_host_name(const std::string&, std::error_code&);
467
    void ssl_use_verify_callback(const std::function<SSLVerifyCallback>&, std::error_code&);
468
    void ssl_use_included_certificates(std::error_code&);
469

470
    void ssl_handshake(std::error_code&, Want& want) noexcept;
471
    bool ssl_shutdown(std::error_code& ec, Want& want) noexcept;
472
    std::size_t ssl_read(char* buffer, std::size_t size, std::error_code&, Want& want) noexcept;
473
    std::size_t ssl_write(const char* data, std::size_t size, std::error_code&, Want& want) noexcept;
474

475
#if REALM_HAVE_OPENSSL
476
    class BioMethod;
477
    static BioMethod s_bio_method;
478
    SSL* m_ssl = nullptr;
479
    std::error_code m_bio_error_code;
480

481
    int m_ssl_index = -1;
482

483
    template <class Oper>
484
    std::size_t ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept;
485

486
    int do_ssl_accept() noexcept;
487
    int do_ssl_connect() noexcept;
488
    int do_ssl_shutdown() noexcept;
489
    int do_ssl_read(char* buffer, std::size_t size) noexcept;
490
    int do_ssl_write(const char* data, std::size_t size) noexcept;
491

492
    static int bio_write(BIO*, const char*, int) noexcept;
493
    static int bio_read(BIO*, char*, int) noexcept;
494
    static int bio_puts(BIO*, const char*) noexcept;
495
    static long bio_ctrl(BIO*, int, long, void*) noexcept;
496
    static int bio_create(BIO*) noexcept;
497
    static int bio_destroy(BIO*) noexcept;
498

499
    // verify_callback_using_hostname is used as an argument to OpenSSL's SSL_set_verify function.
500
    // verify_callback_using_hostname verifies that the certificate is valid and contains
501
    // m_host_name as a Common Name or Subject Alternative Name.
502
    static int verify_callback_using_hostname(int preverify_ok, X509_STORE_CTX* ctx) noexcept;
503

504
    // verify_callback_using_delegate() is also used as an argument to OpenSSL's set_verify_function.
505
    // verify_callback_using_delegate() calls out to the user supplied verify callback.
506
    static int verify_callback_using_delegate(int preverify_ok, X509_STORE_CTX* ctx) noexcept;
507

508
    // verify_callback_using_root_certs is used by OpenSSL to handle certificate verification
509
    // using the included root certifictes.
510
    static int verify_callback_using_root_certs(int preverify_ok, X509_STORE_CTX* ctx);
511
#elif REALM_HAVE_SECURE_TRANSPORT
512
    util::CFPtr<SSLContextRef> m_ssl;
513
    VerifyMode m_verify_mode = VerifyMode::none;
514
    std::unique_ptr<MockSSLError> m_mock_ssl_perform_error;
515

516
    enum class BlockingOperation {
517
        read,
518
        write,
519
    };
520
    util::Optional<BlockingOperation> m_last_operation;
521

522
    // Details of the underlying I/O error that lead to errSecIO being returned
523
    // from a SecureTransport function.
524
    std::error_code m_last_error;
525

526
    // The number of bytes accepted by SSWrite() but not yet confirmed to be
527
    // written to the underlying socket.
528
    std::size_t m_num_partially_written_bytes = 0;
529

530
    template <class Oper>
531
    std::size_t ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept;
532

533
    std::pair<OSStatus, std::size_t> do_ssl_handshake() noexcept;
534
    std::pair<OSStatus, std::size_t> do_ssl_shutdown() noexcept;
535
    std::pair<OSStatus, std::size_t> do_ssl_read(char* buffer, std::size_t size) noexcept;
536
    std::pair<OSStatus, std::size_t> do_ssl_write(const char* data, std::size_t size) noexcept;
537

538
    static OSStatus tcp_read(SSLConnectionRef, void*, std::size_t* length) noexcept;
539
    static OSStatus tcp_write(SSLConnectionRef, const void*, std::size_t* length) noexcept;
540

541
    OSStatus tcp_read(void*, std::size_t* length) noexcept;
542
    OSStatus tcp_write(const void*, std::size_t* length) noexcept;
543

544
    OSStatus verify_peer() noexcept;
545
#endif
546

547
    friend class Service::BasicStreamOps<Stream>;
548
    friend class network::ReadAheadBuffer;
549
#if REALM_HAVE_SECURE_TRANSPORT
550
    friend struct MockSSLError; // for access to Service::Want
551
#endif
552
};
553

554

555
// Implementation
556

557
class ProtocolNotSupported : public std::exception {
558
public:
559
    const char* what() const noexcept override final;
560
};
561

562
inline Context::Context()
563
{
212✔
564
    ssl_init(); // Throws
212✔
565
}
212✔
566

567
inline Context::~Context() noexcept
568
{
212✔
569
    ssl_destroy();
212✔
570
}
212✔
571

572
inline void Context::use_certificate_chain_file(const std::string& path)
573
{
106✔
574
    std::error_code ec;
106✔
575
    ssl_use_certificate_chain_file(path, ec); // Throws
106✔
576
    if (ec)
106✔
577
        throw std::system_error(ec);
×
578
}
106✔
579

580
inline void Context::use_private_key_file(const std::string& path)
581
{
106✔
582
    std::error_code ec;
106✔
583
    ssl_use_private_key_file(path, ec); // Throws
106✔
584
    if (ec)
106✔
585
        throw std::system_error(ec);
×
586
}
106✔
587

588
inline void Context::use_default_verify()
589
{
4✔
590
    std::error_code ec;
4✔
591
    ssl_use_default_verify(ec);
4✔
592
    if (ec)
4✔
593
        throw std::system_error(ec);
×
594
}
4✔
595

596
inline void Context::use_verify_file(const std::string& path)
597
{
28✔
598
    std::error_code ec;
28✔
599
    ssl_use_verify_file(path, ec);
28✔
600
    if (ec) {
28✔
601
        throw std::system_error(ec);
×
602
    }
×
603
}
28✔
604

605
class Stream::HandshakeOperBase : public Service::IoOper {
606
public:
607
    HandshakeOperBase(std::size_t size, Stream& stream)
608
        : IoOper{size}
609
        , m_stream{&stream}
610
    {
152✔
611
    }
152✔
612
    Want initiate()
613
    {
152✔
614
        REALM_ASSERT(this == m_stream->m_tcp_socket.m_read_oper.get());
152✔
615
        REALM_ASSERT(!is_complete());
152✔
616
        m_stream->m_tcp_socket.m_desc.ensure_nonblocking_mode(); // Throws
152✔
617
        return advance();
152✔
618
    }
152✔
619
    Want advance() noexcept override final
620
    {
434✔
621
        REALM_ASSERT(!is_complete());
434✔
622
        REALM_ASSERT(!is_canceled());
434✔
623
        REALM_ASSERT(!m_error_code);
434✔
624
        Want want = Want::nothing;
434✔
625
        m_stream->ssl_handshake(m_error_code, want);
434✔
626
        set_is_complete(want == Want::nothing);
434✔
627
        return want;
434✔
628
    }
434✔
629
    void recycle() noexcept override final
630
    {
×
631
        bool orphaned = !m_stream;
×
632
        REALM_ASSERT(orphaned);
×
633
        // Note: do_recycle() commits suicide.
634
        do_recycle(orphaned);
×
635
    }
×
636
    void orphan() noexcept override final
637
    {
×
638
        m_stream = nullptr;
×
639
    }
×
640
    Service::Descriptor& descriptor() noexcept override final
641
    {
92✔
642
        return m_stream->lowest_layer().m_desc;
92✔
643
    }
92✔
644

645
protected:
646
    Stream* m_stream;
647
    std::error_code m_error_code;
648
};
649

650
template <class H>
651
class Stream::HandshakeOper : public HandshakeOperBase {
652
public:
653
    HandshakeOper(std::size_t size, Stream& stream, H handler)
654
        : HandshakeOperBase{size, stream}
655
        , m_handler{std::move(handler)}
656
    {
100✔
657
    }
100✔
658
    void recycle_and_execute() override final
659
    {
100✔
660
        REALM_ASSERT(is_complete() || is_canceled());
100!
661
        bool orphaned = !m_stream;
100✔
662
        std::error_code ec = m_error_code;
100✔
663
        if (is_canceled())
100✔
664
            ec = util::error::operation_aborted;
×
665
        // Note: do_recycle_and_execute() commits suicide.
52✔
666
        do_recycle_and_execute<H>(orphaned, m_handler, ec); // Throws
100✔
667
    }
100✔
668

669
private:
670
    H m_handler;
671
};
672

673
class Stream::ShutdownOperBase : public Service::IoOper {
674
public:
675
    ShutdownOperBase(std::size_t size, Stream& stream)
676
        : IoOper{size}
677
        , m_stream{&stream}
678
    {
4✔
679
    }
4✔
680
    Want initiate()
681
    {
4✔
682
        REALM_ASSERT(this == m_stream->m_tcp_socket.m_write_oper.get());
4✔
683
        REALM_ASSERT(!is_complete());
4✔
684
        m_stream->m_tcp_socket.m_desc.ensure_nonblocking_mode(); // Throws
4✔
685
        return advance();
4✔
686
    }
4✔
687
    Want advance() noexcept override final
688
    {
4✔
689
        REALM_ASSERT(!is_complete());
4✔
690
        REALM_ASSERT(!is_canceled());
4✔
691
        REALM_ASSERT(!m_error_code);
4✔
692
        Want want = Want::nothing;
4✔
693
        m_stream->ssl_shutdown(m_error_code, want);
4✔
694
        if (want == Want::nothing)
4✔
695
            set_is_complete(true);
4✔
696
        return want;
4✔
697
    }
4✔
698
    void recycle() noexcept override final
699
    {
×
700
        bool orphaned = !m_stream;
×
701
        REALM_ASSERT(orphaned);
×
702
        // Note: do_recycle() commits suicide.
×
703
        do_recycle(orphaned);
×
704
    }
×
705
    void orphan() noexcept override final
706
    {
×
707
        m_stream = nullptr;
×
708
    }
×
709
    Service::Descriptor& descriptor() noexcept override final
710
    {
×
711
        return m_stream->lowest_layer().m_desc;
×
712
    }
×
713

714
protected:
715
    Stream* m_stream;
716
    std::error_code m_error_code;
717
};
718

719
template <class H>
720
class Stream::ShutdownOper : public ShutdownOperBase {
721
public:
722
    ShutdownOper(std::size_t size, Stream& stream, H handler)
723
        : ShutdownOperBase{size, stream}
724
        , m_handler{std::move(handler)}
725
    {
4✔
726
    }
4✔
727
    void recycle_and_execute() override final
728
    {
4✔
729
        REALM_ASSERT(is_complete() || is_canceled());
4!
730
        bool orphaned = !m_stream;
4✔
731
        std::error_code ec = m_error_code;
4✔
732
        if (is_canceled())
4✔
733
            ec = util::error::operation_aborted;
×
734
        // Note: do_recycle_and_execute() commits suicide.
1✔
735
        do_recycle_and_execute<H>(orphaned, m_handler, ec); // Throws
4✔
736
    }
4✔
737

738
private:
739
    H m_handler;
740
};
741

742
inline Stream::Stream(Socket& socket, Context& context, HandshakeType type)
743
    : m_tcp_socket{socket}
744
    , m_ssl_context{context}
745
    , m_handshake_type{type}
746
{
236✔
747
    ssl_init(); // Throws
236✔
748
}
236✔
749

750
inline Stream::~Stream() noexcept
751
{
236✔
752
    m_tcp_socket.cancel();
236✔
753
    ssl_destroy();
236✔
754
}
236✔
755

756
inline void Stream::set_logger(util::Logger* logger_ptr)
757
{
188✔
758
    logger = logger_ptr;
188✔
759
}
188✔
760

761
inline void Stream::set_verify_mode(VerifyMode mode)
762
{
38✔
763
    std::error_code ec;
38✔
764
    ssl_set_verify_mode(mode, ec); // Throws
38✔
765
    if (ec)
38✔
766
        throw std::system_error(ec);
×
767
}
38✔
768

769
inline void Stream::set_host_name(std::string host_name)
770
{
42✔
771
    m_host_name = std::move(host_name);
42✔
772
    std::error_code ec;
42✔
773
    ssl_set_host_name(m_host_name, ec);
42✔
774
    if (ec)
42✔
775
        throw std::system_error(ec);
×
776
}
42✔
777

778
inline void Stream::set_server_port(port_type server_port)
779
{
20✔
780
    m_server_port = server_port;
20✔
781
}
20✔
782

783
inline void Stream::use_verify_callback(const std::function<SSLVerifyCallback>& callback)
784
{
6✔
785
    std::error_code ec;
6✔
786
    ssl_use_verify_callback(callback, ec); // Throws
6✔
787
    if (ec)
6!
788
        throw std::system_error(ec);
×
789
}
6✔
790

791
#if REALM_INCLUDE_CERTS
792
inline void Stream::use_included_certificates()
793
{
2✔
794
    std::error_code ec;
2✔
795
    ssl_use_included_certificates(ec); // Throws
2✔
796
    if (ec)
2✔
797
        throw std::system_error(ec);
798
}
2✔
799
#endif
800

801
inline void Stream::handshake()
802
{
4✔
803
    std::error_code ec;
4✔
804
    if (handshake(ec)) // Throws
4✔
805
        throw std::system_error(ec);
4✔
806
}
4✔
807

808
inline std::size_t Stream::read(char* buffer, std::size_t size)
809
{
×
810
    std::error_code ec;
×
811
    read(buffer, size, ec); // Throws
×
812
    if (ec)
×
813
        throw std::system_error(ec);
×
814
    return size;
×
815
}
×
816

817
inline std::size_t Stream::read(char* buffer, std::size_t size, std::error_code& ec)
818
{
4✔
819
    return StreamOps::read(*this, buffer, size, ec); // Throws
4✔
820
}
4✔
821

822
inline std::size_t Stream::read(char* buffer, std::size_t size, ReadAheadBuffer& rab)
823
{
×
824
    std::error_code ec;
×
825
    read(buffer, size, rab, ec); // Throws
×
826
    if (ec)
×
827
        throw std::system_error(ec);
×
828
    return size;
×
829
}
×
830

831
inline std::size_t Stream::read(char* buffer, std::size_t size, ReadAheadBuffer& rab, std::error_code& ec)
832
{
×
833
    int delim = std::char_traits<char>::eof();
×
834
    return StreamOps::buffered_read(*this, buffer, size, delim, rab, ec); // Throws
×
835
}
×
836

837
inline std::size_t Stream::read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer& rab)
838
{
×
839
    std::error_code ec;
×
840
    std::size_t n = read_until(buffer, size, delim, rab, ec); // Throws
×
841
    if (ec)
×
842
        throw std::system_error(ec);
×
843
    return n;
×
844
}
×
845

846
inline std::size_t Stream::read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer& rab,
847
                                      std::error_code& ec)
848
{
×
849
    int delim_2 = std::char_traits<char>::to_int_type(delim);
×
850
    return StreamOps::buffered_read(*this, buffer, size, delim_2, rab, ec); // Throws
×
851
}
×
852

853
inline std::size_t Stream::write(const char* data, std::size_t size)
854
{
8✔
855
    std::error_code ec;
8✔
856
    write(data, size, ec); // Throws
8✔
857
    if (ec)
8✔
858
        throw std::system_error(ec);
4✔
859
    return size;
4✔
860
}
4✔
861

862
inline std::size_t Stream::write(const char* data, std::size_t size, std::error_code& ec)
863
{
8✔
864
    return StreamOps::write(*this, data, size, ec); // Throws
8✔
865
}
8✔
866

867
inline std::size_t Stream::read_some(char* buffer, std::size_t size)
868
{
12✔
869
    std::error_code ec;
12✔
870
    std::size_t n = read_some(buffer, size, ec); // Throws
12✔
871
    if (ec)
12✔
872
        throw std::system_error(ec);
12✔
873
    return n;
×
874
}
×
875

876
inline std::size_t Stream::read_some(char* buffer, std::size_t size, std::error_code& ec)
877
{
12✔
878
    return StreamOps::read_some(*this, buffer, size, ec); // Throws
12✔
879
}
12✔
880

881
inline std::size_t Stream::write_some(const char* data, std::size_t size)
882
{
×
883
    std::error_code ec;
×
884
    std::size_t n = write_some(data, size, ec); // Throws
×
885
    if (ec)
×
886
        throw std::system_error(ec);
×
887
    return n;
×
888
}
×
889

890
inline std::size_t Stream::write_some(const char* data, std::size_t size, std::error_code& ec)
891
{
×
892
    return StreamOps::write_some(*this, data, size, ec); // Throws
×
893
}
×
894

895
inline void Stream::shutdown()
896
{
20✔
897
    std::error_code ec;
20✔
898
    if (shutdown(ec)) // Throws
20✔
899
        throw std::system_error(ec);
2✔
900
}
20✔
901

902
template <class H>
903
inline void Stream::async_handshake(H handler)
904
{
100✔
905
    LendersHandshakeOperPtr op = Service::alloc<HandshakeOper<H>>(m_tcp_socket.m_read_oper, *this,
100✔
906
                                                                  std::move(handler)); // Throws
100✔
907
    m_tcp_socket.m_desc.initiate_oper(std::move(op));                                  // Throws
100✔
908
}
100✔
909

910
template <class H>
911
inline void Stream::async_read(char* buffer, std::size_t size, H handler)
912
{
2✔
913
    bool is_read_some = false;
2✔
914
    StreamOps::async_read(*this, buffer, size, is_read_some, std::move(handler)); // Throws
2✔
915
}
2✔
916

917
template <class H>
918
inline void Stream::async_read(char* buffer, std::size_t size, ReadAheadBuffer& rab, H handler)
919
{
344✔
920
    int delim = std::char_traits<char>::eof();
344✔
921
    StreamOps::async_buffered_read(*this, buffer, size, delim, rab, std::move(handler)); // Throws
344✔
922
}
344✔
923

924
template <class H>
925
inline void Stream::async_read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer& rab, H handler)
926
{
224✔
927
    int delim_2 = std::char_traits<char>::to_int_type(delim);
224✔
928
    StreamOps::async_buffered_read(*this, buffer, size, delim_2, rab, std::move(handler)); // Throws
224✔
929
}
224✔
930

931
template <class H>
932
inline void Stream::async_write(const char* data, std::size_t size, H handler)
933
{
170✔
934
    bool is_write_some = false;
170✔
935
    StreamOps::async_write(*this, data, size, is_write_some, std::move(handler)); // Throws
170✔
936
}
170✔
937

938
template <class H>
939
inline void Stream::async_read_some(char* buffer, std::size_t size, H handler)
940
{
236,449✔
941
    bool is_read_some = true;
236,449✔
942
    StreamOps::async_read(*this, buffer, size, is_read_some, std::move(handler)); // Throws
236,449✔
943
}
236,449✔
944

945
template <class H>
946
inline void Stream::async_write_some(const char* data, std::size_t size, H handler)
947
{
153,853✔
948
    bool is_write_some = true;
153,853✔
949
    StreamOps::async_write(*this, data, size, is_write_some, std::move(handler)); // Throws
153,853✔
950
}
153,853✔
951

952
template <class H>
953
inline void Stream::async_shutdown(H handler)
954
{
4✔
955
    LendersShutdownOperPtr op = Service::alloc<ShutdownOper<H>>(m_tcp_socket.m_write_oper, *this,
4✔
956
                                                                std::move(handler)); // Throws
4✔
957
    m_tcp_socket.m_desc.initiate_oper(std::move(op));                                // Throws
4✔
958
}
4✔
959

960
inline void Stream::do_init_read_async(std::error_code&, Want& want) noexcept
961
{
472,928✔
962
    want = Want::nothing; // Proceed immediately unless there is an error
472,928✔
963
}
472,928✔
964

965
inline void Stream::do_init_write_async(std::error_code&, Want& want) noexcept
966
{
307,764✔
967
    want = Want::nothing; // Proceed immediately unless there is an error
307,764✔
968
}
307,764✔
969

970
inline std::size_t Stream::do_read_some_sync(char* buffer, std::size_t size, std::error_code& ec) noexcept
971
{
20✔
972
    Want want = Want::nothing;
20✔
973
    std::size_t n = do_read_some_async(buffer, size, ec, want);
20✔
974
    if (n == 0 && want != Want::nothing)
20✔
975
        ec = util::error::resource_unavailable_try_again;
×
976
    return n;
20✔
977
}
20✔
978

979
inline std::size_t Stream::do_write_some_sync(const char* data, std::size_t size, std::error_code& ec) noexcept
980
{
8✔
981
    Want want = Want::nothing;
8✔
982
    std::size_t n = do_write_some_async(data, size, ec, want);
8✔
983
    if (n == 0 && want != Want::nothing)
8✔
984
        ec = util::error::resource_unavailable_try_again;
×
985
    return n;
8✔
986
}
8✔
987

988
inline std::size_t Stream::do_read_some_async(char* buffer, std::size_t size, std::error_code& ec,
989
                                              Want& want) noexcept
990
{
474,860✔
991
    return ssl_read(buffer, size, ec, want);
474,860✔
992
}
474,860✔
993

994
inline std::size_t Stream::do_write_some_async(const char* data, std::size_t size, std::error_code& ec,
995
                                               Want& want) noexcept
996
{
310,916✔
997
    return ssl_write(data, size, ec, want);
310,916✔
998
}
310,916✔
999

1000
inline Socket& Stream::lowest_layer() noexcept
1001
{
3,109,550✔
1002
    return m_tcp_socket;
3,109,550✔
1003
}
3,109,550✔
1004

1005
#if REALM_HAVE_OPENSSL
1006

1007
inline void Stream::ssl_handshake(std::error_code& ec, Want& want) noexcept
1008
{
218✔
1009
    auto perform = [this]() noexcept {
218✔
1010
        switch (m_handshake_type) {
218✔
1011
            case client:
92✔
1012
                return do_ssl_connect();
92✔
1013
            case server:
126✔
1014
                return do_ssl_accept();
126✔
1015
        }
1016
        REALM_ASSERT(false);
1017
        return 0;
1018
    };
1019
    std::size_t n = ssl_perform(std::move(perform), ec, want);
218✔
1020
    REALM_ASSERT(n == 0 || n == 1);
218✔
1021
    if (want == Want::nothing && n == 0 && !ec) {
218✔
1022
        // End of input on TCP socket
1023
        ec = util::MiscExtErrors::premature_end_of_input;
1024
    }
1025
}
218✔
1026

1027
inline std::size_t Stream::ssl_read(char* buffer, std::size_t size, std::error_code& ec, Want& want) noexcept
1028
{
257,274✔
1029
    auto perform = [this, buffer, size]() noexcept {
258,054✔
1030
        return do_ssl_read(buffer, size);
258,054✔
1031
    };
258,054✔
1032
    std::size_t n = ssl_perform(std::move(perform), ec, want);
257,274✔
1033
    if (want == Want::nothing && n == 0 && !ec) {
258,002✔
1034
        // End of input on TCP socket
18✔
1035
        if (SSL_get_shutdown(m_ssl) & SSL_RECEIVED_SHUTDOWN) {
18✔
1036
            ec = util::MiscExtErrors::end_of_input;
16✔
1037
        }
16✔
1038
        else {
2✔
1039
            ec = util::MiscExtErrors::premature_end_of_input;
2✔
1040
        }
2✔
1041
    }
18✔
1042
    return n;
257,274✔
1043
}
257,274✔
1044

1045
inline std::size_t Stream::ssl_write(const char* data, std::size_t size, std::error_code& ec, Want& want) noexcept
1046
{
175,498✔
1047
    // While OpenSSL is able to continue writing after we have received the
175,498✔
1048
    // close notify alert fro the remote peer, Apple's Secure Transport API is
175,498✔
1049
    // not, so to achieve common behaviour, we make sure that any such attempt
175,498✔
1050
    // will result in an `error::broken_pipe` error.
175,498✔
1051
    if ((SSL_get_shutdown(m_ssl) & SSL_RECEIVED_SHUTDOWN) != 0) {
175,498✔
1052
        ec = util::error::broken_pipe;
2✔
1053
        want = Want::nothing;
2✔
1054
        return 0;
2✔
1055
    }
2✔
1056
    auto perform = [this, data, size]() noexcept {
175,934✔
1057
        return do_ssl_write(data, size);
175,934✔
1058
    };
175,934✔
1059
    std::size_t n = ssl_perform(std::move(perform), ec, want);
175,496✔
1060
    if (want == Want::nothing && n == 0 && !ec) {
175,642✔
1061
        // End of input on TCP socket
1062
        ec = util::MiscExtErrors::premature_end_of_input;
1063
    }
1064
    return n;
175,496✔
1065
}
175,496✔
1066

1067
inline bool Stream::ssl_shutdown(std::error_code& ec, Want& want) noexcept
1068
{
22✔
1069
    auto perform = [this]() noexcept {
22✔
1070
        return do_ssl_shutdown();
22✔
1071
    };
22✔
1072
    std::size_t n = ssl_perform(std::move(perform), ec, want);
22✔
1073
    REALM_ASSERT(n == 0 || n == 1);
22✔
1074
    if (want == Want::nothing && n == 0 && !ec) {
22✔
1075
        // The first invocation of SSL_shutdown() does not signal completion
18✔
1076
        // until the shutdown alert has been sent to the peer, or an error
18✔
1077
        // occurred (does not wait for acknowledgment).
18✔
1078
        //
18✔
1079
        // The second invocation (after a completed first invocation) does not
18✔
1080
        // signal completion until the peers shutdown alert has been received,
18✔
1081
        // or an error occurred.
18✔
1082
        //
18✔
1083
        // It is believed that:
18✔
1084
        //
18✔
1085
        // If this is the first time SSL_shutdown() is called, and
18✔
1086
        // `SSL_get_shutdown() & SSL_SENT_SHUTDOWN` evaluates to nonzero, then a
18✔
1087
        // zero return value means "partial success" (shutdown alert was sent,
18✔
1088
        // but the peers shutdown alert was not yet received), and 1 means "full
18✔
1089
        // success" (peers shutdown alert has already been received).
18✔
1090
        //
18✔
1091
        // If this is the first time SSL_shutdown() is called, and
18✔
1092
        // `SSL_get_shutdown() & SSL_SENT_SHUTDOWN` valuates to zero, then a
18✔
1093
        // zero return value means "premature end of input", and 1 is supposedly
18✔
1094
        // not a possibility.
18✔
1095
        //
18✔
1096
        // If this is the second time SSL_shutdown() is called (after the first
18✔
1097
        // call has returned zero), then a zero return value means "premature
18✔
1098
        // end of input", and 1 means "full success" (peers shutdown alert has
18✔
1099
        // now been received).
18✔
1100
        if ((SSL_get_shutdown(m_ssl) & SSL_SENT_SHUTDOWN) == 0)
18✔
1101
            ec = util::MiscExtErrors::premature_end_of_input;
1102
    }
18✔
1103
    return (n > 0);
22✔
1104
}
22✔
1105

1106
// Provides a homogeneous, and mostly quirks-free interface across the OpenSSL
1107
// operations (handshake, read, write, shutdown).
1108
//
1109
// First of all, if the operation remains incomplete (neither successfully
1110
// completed, nor failed), ssl_perform() will set `ec` to `std::system_error()`,
1111
// `want` to something other than `Want::nothing`, and return zero. Note that
1112
// read and write operations are partial in the sense that they do not need to
1113
// read or write everything before completing successfully. They only need to
1114
// read or write at least one byte to complete successfully.
1115
//
1116
// Such a situation will normally only happen when the underlying TCP socket is
1117
// in nonblocking mode, and the read/write requirements of the operation could
1118
// not be immediately accommodated. However, as is noted in the SSL_write() man
1119
// page, it can also happen in blocking mode (at least while writing).
1120
//
1121
// If an error occurred, ssl_perform() will set `ec` to something other than
1122
// `std::system_error()`, `want` to `Want::nothing`, and return 0.
1123
//
1124
// If no error occurred, and the operation completed (`!ec && want ==
1125
// Want::nothing`), then the return value indicates the outcome of the
1126
// operation.
1127
//
1128
// In general, a nonzero value means "full" success, and a zero value means
1129
// "partial" success, however, a zero result can also generally mean "premature
1130
// end of input" / "unclean protocol termination".
1131
//
1132
// Assuming there is no premature end of input, then for reads and writes, the
1133
// returned value is the number of transferred bytes. Zero for read on end of
1134
// input. Never zero for write. For handshake it is always 1. For shutdown it is
1135
// 1 if the peer shutdown alert was already received, otherwise it is zero.
1136
//
1137
// ssl_read() should use `SSL_get_shutdown() & SSL_RECEIVED_SHUTDOWN` to
1138
// distinguish between the two possible meanings of zero.
1139
//
1140
// ssl_shutdown() should use `SSL_get_shutdown() & SSL_SENT_SHUTDOWN` to
1141
// distinguish between the two possible meanings of zero.
1142
template <class Oper>
1143
std::size_t Stream::ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept
1144
{
433,828✔
1145
    ERR_clear_error();
433,828✔
1146
    m_bio_error_code = std::error_code(); // Success
433,828✔
1147
    int ret = oper();
433,828✔
1148
    int ssl_error = SSL_get_error(m_ssl, ret);
433,828✔
1149
    int sys_error = int(ERR_peek_last_error());
433,828✔
1150

433,828✔
1151
    // Guaranteed by the documentation of SSL_get_error()
433,828✔
1152
    REALM_ASSERT((ret > 0) == (ssl_error == SSL_ERROR_NONE));
433,828✔
1153

433,828✔
1154
    REALM_ASSERT(!m_bio_error_code || ssl_error == SSL_ERROR_SYSCALL);
433,828✔
1155

433,828✔
1156
    // Judging from various comments in the man pages, and from experience with
433,828✔
1157
    // the API, it seems that,
433,828✔
1158
    //
433,828✔
1159
    //   ret=0, ssl_error=SSL_ERROR_SYSCALL, sys_error=0
433,828✔
1160
    //
433,828✔
1161
    // is supposed to be an indicator of "premature end of input" / "unclean
433,828✔
1162
    // protocol termination", while
433,828✔
1163
    //
433,828✔
1164
    //   ret=0, ssl_error=SSL_ERROR_ZERO_RETURN
433,828✔
1165
    //
433,828✔
1166
    // is supposed to be an indicator of the following success conditions:
433,828✔
1167
    //
433,828✔
1168
    //   - Mature end of input / clean protocol termination.
433,828✔
1169
    //
433,828✔
1170
    //   - Successful transmission of the shutdown alert, but no prior reception
433,828✔
1171
    //     of shutdown alert from peer.
433,828✔
1172
    //
433,828✔
1173
    // Unfortunately, as is also remarked in various places in the man pages,
433,828✔
1174
    // those two success conditions may actually result in `ret=0,
433,828✔
1175
    // ssl_error=SSL_ERROR_SYSCALL, sys_error=0` too, and it seems that they
433,828✔
1176
    // almost always do.
433,828✔
1177
    //
433,828✔
1178
    // This means that we cannot properly discriminate between these conditions
433,828✔
1179
    // in ssl_perform(), and will have to defer to the caller to interpret the
433,828✔
1180
    // situation. Since thay cannot be properly told apart, we report all
433,828✔
1181
    // `ret=0, ssl_error=SSL_ERROR_SYSCALL, sys_error=0` and `ret=0,
433,828✔
1182
    // ssl_error=SSL_ERROR_ZERO_RETURN` cases as the latter.
433,828✔
1183
    switch (ssl_error) {
433,828✔
1184
        case SSL_ERROR_NONE:
433,436✔
1185
            ec = std::error_code(); // Success
433,436✔
1186
            want = Want::nothing;
433,436✔
1187
            return std::size_t(ret); // ret > 0
433,436✔
1188
        case SSL_ERROR_ZERO_RETURN:
16✔
1189
            ec = std::error_code(); // Success
16✔
1190
            want = Want::nothing;
16✔
1191
            return 0;
16✔
1192
        case SSL_ERROR_WANT_READ:
182✔
1193
            ec = std::error_code(); // Success
182✔
1194
            want = Want::read;
182✔
1195
            return 0;
182✔
1196
        case SSL_ERROR_WANT_WRITE:
330✔
1197
            ec = std::error_code(); // Success
330✔
1198
            want = Want::write;
330✔
1199
            return 0;
330✔
1200
        case SSL_ERROR_SYSCALL:
28✔
1201
            if (REALM_UNLIKELY(sys_error != 0)) {
28✔
1202
                ec = util::make_basic_system_error_code(sys_error);
1203
            }
1204
            else if (REALM_UNLIKELY(m_bio_error_code)) {
28✔
1205
                ec = m_bio_error_code;
6✔
1206
            }
6✔
1207
            else if (ret == 0) {
22✔
1208
                // ret = 0, ssl_eror = SSL_ERROR_SYSCALL, sys_error = 0
20✔
1209
                //
20✔
1210
                // See remarks above!
20✔
1211
                ec = std::error_code(); // Success
20✔
1212
            }
20✔
1213
            else {
2✔
1214
                // ret = -1, ssl_eror = SSL_ERROR_SYSCALL, sys_error = 0
2✔
1215
                //
2✔
1216
                // This situation arises in OpenSSL version >= 1.1.
2✔
1217
                // It has been observed in the SSL_connect call if the
2✔
1218
                // other endpoint terminates the connection during
2✔
1219
                // SSL_connect. The OpenSSL documentation states
2✔
1220
                // that ret = -1 implies an underlying BIO error and
2✔
1221
                // that errno should be consulted. However,
2✔
1222
                // errno = 0(Undefined error) in the observed case.
2✔
1223
                // At the moment. we will report
2✔
1224
                // MiscExtErrors::premature_end_of_input.
2✔
1225
                // If we see this error case occurring in other situations in
2✔
1226
                // the future, we will have to update this case.
2✔
1227
                ec = util::MiscExtErrors::premature_end_of_input;
2✔
1228
            }
2✔
1229
            want = Want::nothing;
28✔
1230
            return 0;
28✔
1231
        case SSL_ERROR_SSL:
20✔
1232
            ec = std::error_code(sys_error, openssl_error_category);
20✔
1233
            want = Want::nothing;
20✔
1234
            return 0;
20✔
1235
        default:
1236
            break;
1237
    }
1238
    // We are not supposed to ever get here
1239
    REALM_ASSERT(false);
1240
    return 0;
1241
}
1242

1243
inline int Stream::do_ssl_accept() noexcept
1244
{
126✔
1245
    int ret = SSL_accept(m_ssl);
126✔
1246
    return ret;
126✔
1247
}
126✔
1248

1249
inline int Stream::do_ssl_connect() noexcept
1250
{
92✔
1251
    int ret = SSL_connect(m_ssl);
92✔
1252
    return ret;
92✔
1253
}
92✔
1254

1255
inline int Stream::do_ssl_read(char* buffer, std::size_t size) noexcept
1256
{
258,184✔
1257
    int size_2 = int(size);
258,184✔
1258
    if (size > unsigned(std::numeric_limits<int>::max()))
258,184✔
1259
        size_2 = std::size_t(std::numeric_limits<int>::max());
1260
    int ret = SSL_read(m_ssl, buffer, size_2);
258,184✔
1261
    return ret;
258,184✔
1262
}
258,184✔
1263

1264
inline int Stream::do_ssl_write(const char* data, std::size_t size) noexcept
1265
{
175,962✔
1266
    int size_2 = int(size);
175,962✔
1267
    if (size > unsigned(std::numeric_limits<int>::max()))
175,962✔
1268
        size_2 = std::size_t(std::numeric_limits<int>::max());
1269
    int ret = SSL_write(m_ssl, data, size_2);
175,962✔
1270
    return ret;
175,962✔
1271
}
175,962✔
1272

1273
inline int Stream::do_ssl_shutdown() noexcept
1274
{
22✔
1275
    int ret = SSL_shutdown(m_ssl);
22✔
1276
    return ret;
22✔
1277
}
22✔
1278

1279
#elif REALM_HAVE_SECURE_TRANSPORT
1280

1281
inline void Stream::set_mock_ssl_perform_error(std::unique_ptr<MockSSLError>&& error)
1282
{
2✔
1283
    if (!error)
2✔
1284
        m_mock_ssl_perform_error.reset();
1285
    else
2✔
1286
        m_mock_ssl_perform_error = std::move(error);
2✔
1287
}
2✔
1288

1289
// Structure for mocking the error returned by Oper called by ssl_perform()
1290
// By default, this is a one-shot error that will be cleared after it is read,
1291
// unless clear_after_access is set to false.
1292
struct Stream::MockSSLError {
1293
    using Operation = Stream::BlockingOperation;
1294

1295
    explicit MockSSLError(Operation op, int ssl_error, int bytes_processed, bool clear_after_access = true)
1296
        : operation{op}
1297
        , ssl_error{ssl_error}
1298
        , sys_error{0}
1299
        , bytes_processed{bytes_processed}
1300
        , clear_after_access{clear_after_access}
1301
    {
2✔
1302
    }
2✔
1303

1304
    explicit MockSSLError(Operation op, int ssl_error, int sys_error, int bytes_processed,
1305
                          bool clear_after_access = true)
1306
        : operation{op}
1307
        , ssl_error{ssl_error}
1308
        , sys_error{sys_error}
1309
        , bytes_processed{bytes_processed}
1310
        , clear_after_access{clear_after_access}
1311
    {
1312
    }
1313

1314
    Operation operation;
1315
    int ssl_error;
1316
    int sys_error;
1317
    int bytes_processed;
1318
    bool clear_after_access;
1319
};
1320

1321

1322
// Provides a homogeneous, and mostly quirks-free interface across the SecureTransport
1323
// operations (handshake, read, write, shutdown).
1324
//
1325
// First of all, if the operation remains incomplete (neither successfully
1326
// completed, nor failed), ssl_perform() will set `ec` to `std::system_error()`,
1327
// `want` to something other than `Want::nothing`, and return zero.
1328
//
1329
// If an error occurred, ssl_perform() will set `ec` to something other than
1330
// `std::system_error()`, `want` to `Want::nothing`, and return 0. If the error
1331
// is end_of_input, it is possible that the value returned is non-zero.
1332
//
1333
// If no error occurred, and the operation completed (`!ec && want ==
1334
// Want::nothing`), then the return value indicates the outcome of the
1335
// operation.
1336
//
1337
// In general, a nonzero value means "full" success, and a zero value means
1338
// "partial" success, however, a zero result can also generally mean "premature
1339
// end of input" / "unclean protocol termination".
1340
//
1341
// Assuming there is no premature end of input, then for reads and writes, the
1342
// returned value is the number of transferred bytes. Zero for read on end of
1343
// input. Never zero for write. For handshake it is always 1. For shutdown it is
1344
// 1 if the peer shutdown alert was already received, otherwise it is zero.
1345
template <class Oper>
1346
std::size_t Stream::ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept
1347
{
353,574✔
1348
    OSStatus result;
353,574✔
1349
    std::size_t n;
353,574✔
1350

1351
    // Use caution with MockSSLError, since errSSLWouldBlock will potentially perform
1352
    // another read that may block
1353
    if (REALM_UNLIKELY(m_mock_ssl_perform_error)) {
353,574✔
1354
        result = static_cast<OSStatus>(m_mock_ssl_perform_error->ssl_error);
4✔
1355
        n = static_cast<std::size_t>(m_mock_ssl_perform_error->bytes_processed);
4✔
1356
        if (m_mock_ssl_perform_error->clear_after_access)
4!
1357
            m_mock_ssl_perform_error.reset();
4✔
1358
    }
4✔
1359
    else {
353,570✔
1360
        // Call the operation if there is no mock error set
1361
        std::tie(result, n) = oper();
353,570✔
1362
    }
353,570✔
1363

1364
    Want blocking_want = [this]() {
353,624✔
1365
        if (!m_last_operation)
353,624✔
1366
            return Want::nothing;
10,444✔
1367
        switch (*m_last_operation) {
343,180✔
1368
            case BlockingOperation::read:
200,408✔
1369
                return Want::read;
200,408✔
1370
            case BlockingOperation::write:
142,798✔
1371
                return Want::write;
142,798✔
1372
        };
1373
    }();
1374
    m_last_operation.reset();
353,574✔
1375

1376
    if (result == noErr) {
353,574✔
1377
        ec = std::error_code();
348,226✔
1378
        want = Want::nothing;
348,226✔
1379
        return n;
348,226✔
1380
    }
348,226✔
1381

1382
    // Don't return an error, but keep reading if more data is needed
1383
    if (result == errSSLWouldBlock) {
5,370✔
1384
        REALM_ASSERT(blocking_want != Want::nothing);
5,370✔
1385
        ec = std::error_code();
5,370✔
1386
        want = blocking_want;
5,370✔
1387
        return n;
5,370✔
1388
    }
5,370✔
1389

1390
    if (result == errSSLClosedGraceful) {
2,147,483,647✔
1391
        ec = util::MiscExtErrors::end_of_input;
24✔
1392
        want = Want::nothing;
24✔
1393
        return n;
24✔
1394
    }
24✔
1395

1396
    // Always return 0 if an error (other than end_of_input) occurs
1397
    if (result == errSSLClosedAbort || result == errSSLClosedNoNotify) {
2,147,483,647✔
1398
        ec = util::MiscExtErrors::premature_end_of_input;
6✔
1399
        want = Want::nothing;
6✔
1400
        return 0;
6✔
1401
    }
6✔
1402

1403
    if (result == errSecIO) {
2,147,483,647✔
1404
        // A generic I/O error means something went wrong at a lower level. Use the error
1405
        // code we smuggled out of our lower-level functions to provide a more specific error.
1406
        REALM_ASSERT(m_last_error);
8✔
1407
        ec = m_last_error;
8✔
1408
        want = Want::nothing;
8✔
1409
        return 0;
8✔
1410
    }
8✔
1411

1412
    ec = std::error_code(result, secure_transport_error_category);
2,147,483,647✔
1413
    want = Want::nothing;
2,147,483,647✔
1414
    return 0;
2,147,483,647✔
1415
}
2,147,483,647✔
1416
#endif // REALM_HAVE_OPENSSL / REALM_HAVE_SECURE_TRANSPORT
1417

1418
} // namespace ssl
1419
} // namespace realm::sync::network
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc