aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h
blob: ca7237bfa9a1208ec1769e300cf9e2701bf7fd6b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once

#include <vespa/vespalib/crypto/openssl_typedefs.h>
#include <vespa/vespalib/net/socket_address.h>
#include <vespa/vespalib/net/socket_spec.h>
#include <vespa/vespalib/net/tls/crypto_codec.h>
#include <vespa/vespalib/net/tls/capability_set.h>
#include <vespa/vespalib/net/tls/peer_credentials.h>
#include <vespa/vespalib/net/tls/transport_security_options.h>
#include <memory>
#include <optional>

namespace vespalib::net::tls { struct TlsContext; }

namespace vespalib::net::tls::impl {

class OpenSslTlsContextImpl;

/*
 * Frame-level OpenSSL-backed TLSv1.2/TLSv1.3 (depending on OpenSSL version)
 * crypto codec implementation.
 *
 * NOT generally thread safe per instance, but independent instances may be
 * used by different threads safely. One exception is that handshake() and
 * do_handshake_work() may be called from different threads, as long as it
 * happens with appropriate data visibility synchronization and not concurrently.
 */
class OpenSslCryptoCodecImpl : public CryptoCodec {

    struct DeferredHandshakeParams {
        const char* from_peer          = nullptr;
        size_t      from_peer_buf_size = 0;
        char*       to_peer            = nullptr;
        size_t      to_peer_buf_size   = 0;

        DeferredHandshakeParams(const char* from_peer_, size_t from_peer_buf_size_,
                                char* to_peer_, size_t to_peer_buf_size_) noexcept
            : from_peer(from_peer_),
              from_peer_buf_size(from_peer_buf_size_),
              to_peer(to_peer_),
              to_peer_buf_size(to_peer_buf_size_)
        {}

        DeferredHandshakeParams(const DeferredHandshakeParams&) noexcept = default;
        DeferredHandshakeParams& operator=(const DeferredHandshakeParams&) noexcept = default;
    };

    // The context maintains shared verification callback state, so it must be
    // kept alive explictly for at least as long as any codecs.
    std::shared_ptr<OpenSslTlsContextImpl> _ctx;
    SocketSpec     _peer_spec;
    SocketAddress  _peer_address;
    crypto::SslPtr _ssl;
    ::BIO*         _input_bio;  // Owned by _ssl
    ::BIO*         _output_bio; // Owned by _ssl
    Mode           _mode;
    std::optional<DeferredHandshakeParams> _deferred_handshake_params;
    std::optional<HandshakeResult>         _deferred_handshake_result;
    PeerCredentials _peer_credentials;
    CapabilitySet   _granted_capabilities;
public:
    ~OpenSslCryptoCodecImpl() override;

    static std::unique_ptr<OpenSslCryptoCodecImpl> make_client_codec(std::shared_ptr<OpenSslTlsContextImpl> ctx,
                                                                     const SocketSpec& peer_spec,
                                                                     const SocketAddress& peer_address);
    static std::unique_ptr<OpenSslCryptoCodecImpl> make_server_codec(std::shared_ptr<OpenSslTlsContextImpl> ctx,
                                                                     const SocketAddress& peer_address);

    /*
     * From RFC 8449 (Record Size Limit Extension for TLS), section 1:
     *   "TLS versions 1.2 [RFC5246] and earlier permit senders to
     *    generate records 16384 octets in size, plus any expansion
     *    from compression and protection up to 2048 octets (though
     *    typically this expansion is only 16 octets). TLS 1.3 reduces
     *    the allowance for expansion to 256 octets."
     *
     * We may be on TLSv1.2, so make room for the worst case.
     */
    static constexpr size_t MaximumTlsFrameSize = 16384 + 2048;
    static constexpr size_t MaximumFramePlaintextSize = 16384;

    size_t min_encode_buffer_size() const noexcept override {
        return MaximumTlsFrameSize;
    }
    size_t min_decode_buffer_size() const noexcept override {
        return MaximumFramePlaintextSize;
    }

    HandshakeResult handshake(const char* from_peer, size_t from_peer_buf_size,
                              char* to_peer, size_t to_peer_buf_size) noexcept override;

    void do_handshake_work() noexcept override;

    EncodeResult encode(const char* plaintext, size_t plaintext_size,
                        char* ciphertext, size_t ciphertext_size) noexcept override;
    DecodeResult decode(const char* ciphertext, size_t ciphertext_size,
                        char* plaintext, size_t plaintext_size) noexcept override;
    EncodeResult half_close(char* ciphertext, size_t ciphertext_size) noexcept override;

    [[nodiscard]] const PeerCredentials& peer_credentials() const noexcept override {
        return _peer_credentials;
    }

    [[nodiscard]] CapabilitySet granted_capabilities() const noexcept override {
        return _granted_capabilities;
    }

    const SocketAddress& peer_address() const noexcept { return _peer_address; }
    /*
     * If a client has sent a SNI extension field as part of the handshake,
     * returns the raw string representation of this. It only makes sense to
     * call this for codecs in server mode.
     */
    std::optional<vespalib::string> client_provided_sni_extension() const;

    // Only used by code bridging OpenSSL certificate verification callbacks and
    // evaluation of custom authorization rules.
    void set_peer_credentials(PeerCredentials peer_credentials) {
        _peer_credentials = std::move(peer_credentials);
    }
    void set_granted_capabilities(CapabilitySet granted_capabilities) {
        _granted_capabilities = granted_capabilities;
    }
private:
    OpenSslCryptoCodecImpl(std::shared_ptr<OpenSslTlsContextImpl> ctx,
                           const SocketSpec& peer_spec,
                           const SocketAddress& peer_address,
                           Mode mode);

    void enable_hostname_validation_if_requested();
    void set_server_name_indication_extension();
    HandshakeResult do_handshake_and_consume_peer_input_bytes() noexcept;
    DecodeResult drain_and_produce_plaintext_from_ssl(char* plaintext, size_t plaintext_size) noexcept;
    // Precondition: read_result < 0
    DecodeResult remap_ssl_read_failure_to_decode_result(int read_result) noexcept;
};

}