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 141 142 143 144 145 146 147 148
|
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2022- OpenVPN Inc.
//
// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
//
/**
* @brief Support deferred server-side state creation when client connects
*
* Creating OpenVPN protocol tracking state upon receipt of an initial client HARD_RESET
* packet invites the bad actor to flood the server with connection requests maintaining
* anonymity by spoofing the client's source address. Not only does this invite
* resource exhaustion, but, because of reliability layer retries, it creates an
* amplification attack as the server retries its un-acknowledged HARD_RESET replies to
* the spoofed address.
*
* This solution treats the server's 64-bit protocol session ID ("Psid or psid") as a
* cookie that allows the server to defer state creation. It is ported here to openvpn3
* from original work in OpenVPN. Unlike the randomly created server psid generated in
* psid.hpp for the server's HARD_RESET reply, this approach derives the server psid via
* an HMAC of information from the incoming client OpenVPN HARD_RESET control message
* (i.e., the psid cookie). This allows the server to verify the client as it returns
* the server psid in it's second packet, only then creating protocol state.
*
* Not only does this prevent the resource exhaustion, but it has the happy consequence
* of avoiding the amplification attack. Since no state is created on the first packet,
* there is no reliability layer; and, hence, no retries of the server's HARD_RESET
* reply.
*/
#pragma once
#include <openvpn/buffer/buffer.hpp> // includes rc.hpp
#include <openvpn/ssl/psid.hpp>
namespace openvpn {
/**
* @brief Interface to communicate the server's address semantics
*
* The server implementation must derive a concrete class from this abstract one.
* This encapsulates the server implementation's knowledge of the address semantics it
* needs to return the HARD_RESET packet to the client. Further, in support of the
* psid calculation, this class also needs to supply this component with a
* reproducably hashable memory slab that represents the client address.
*/
class PsidCookieAddrInfoBase
{
public:
virtual const unsigned char *get_abstract_cli_addrport(size_t &slab_size) const = 0;
virtual const void *get_impl_info() const = 0;
virtual ~PsidCookieAddrInfoBase() = default;
};
/**
* @brief Interface to provide access to the server's transport capability
*
* The server implementation must derive a concrete class from this abstract one. The
* server implementation is presumed to own the transport and must implement the
* member function to send the
*/
class PsidCookieTransportBase : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<PsidCookieTransportBase> Ptr;
virtual bool psid_cookie_send_const(Buffer &send_buf, const PsidCookieAddrInfoBase &pcaib) = 0;
virtual ~PsidCookieTransportBase() = default;
};
/**
* @brief Interface to integrate this component into the server implementation
*/
class PsidCookie : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<PsidCookie> Ptr;
/**
* @brief Values returned by the intercept() function
*
* These are status values depending upon the action that intercept() took in
* handling client's 1st and 2nd packets. Early drop indicates that the packet was
* dropped before determining whether the packet was client's 1st or 2nd.
*/
enum class Intercept
{
DECLINE_HANDLING,
EARLY_DROP,
DROP_1ST,
HANDLE_1ST,
DROP_2ND,
HANDLE_2ND,
};
/**
* @brief Called when a potential new client session packet is received
*
* Called by the server implementation when it recieves a packet for which it has no
* state information. Such a packet is potentially a client HARD_RESET or a 2nd
* client packet returning the psid cookie.
*
* @param pkt_buf The packet received by the server implementation.
* @param pcaib The address information as contained in an instance of the class
* that the server implementation derived from the PsidCookieAddrInfoBase class
* @return Intercept Status of the packet handling
*/
virtual Intercept intercept(ConstBuffer &pkt_buf, const PsidCookieAddrInfoBase &pcaib) = 0;
/**
* @brief Get the cookie psid from client's 2nd packet
*
* This provides the server's psid (a.k.a, the cookie_psid) as returned by the
* client in it's 2nd packet. It may only be called after intercept() returns
* HANDLE_2ND, indicating a valid psid cookie. Further, it may only be called once
* as it invalidates the internal data source after it sets the return value.
*
* @return ProtoSessionID
*/
virtual ProtoSessionID get_cookie_psid() = 0;
// The PsidCookie server implementation owns the transport detail for sending the psid cookie packet that the class implementing this interface creates. The intercept() method will call the derived class' psid_cookie_send_const() function above.
/**
* @brief Give this component the transport needed to send the server's HARD_RESET
*
* The server implementation must call this method before the intercept() function
* is asked to handle a packet
*
* @param pctb The transport capability as provided by the server implementation's
* object derived from the PsidCookieTransportBase class
*/
virtual void provide_psid_cookie_transport(PsidCookieTransportBase::Ptr pctb) = 0;
virtual ~PsidCookie() = default;
};
} // namespace openvpn
|