- INotify/Thread epoll dispatch was silently dropping every live core::INotify event (an isKnownINotify registry fixes it) - this had broken mail delivery for over a week, masked by routine restarts. - TCPServer::lock self-deadlocked every admin-console connection via a plain std::mutex being re-entered; switched to recursive_mutex. - TLS.cpp/h: SNI-based certificate selection (addSNIContext), the groundwork for multi-tenant TLS (qualityiron.vegas alongside barant.com on one APIServer process). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
131 lines
4.8 KiB
C++
131 lines
4.8 KiB
C++
#ifndef __TLS_h__
|
|
#define __TLS_h__
|
|
|
|
#include "ZString.h"
|
|
#include "TLSInfo.h"
|
|
#include <openssl/ssl.h>
|
|
#include <map>
|
|
#include <string>
|
|
|
|
namespace core {
|
|
|
|
///
|
|
/// TLS
|
|
///
|
|
/// This object provides the support methods to handle TLS on the server core and
|
|
/// session environment.
|
|
///
|
|
|
|
class TLS {
|
|
|
|
public:
|
|
|
|
///
|
|
///
|
|
///
|
|
|
|
TLS();
|
|
|
|
///
|
|
/// Use this constructor when the SSL context needs to be created as when opening
|
|
/// a server TCPSocket.
|
|
///
|
|
|
|
TLS(TLSInfo *tlsInfo);
|
|
|
|
///
|
|
/// Use this constructor on creation of a new TCPSocket that needs access to
|
|
/// the generating server's SSL context.
|
|
///
|
|
|
|
TLS(SSL_CTX *ctx);
|
|
|
|
///
|
|
/// The destructor for this object.
|
|
///
|
|
|
|
~TLS();
|
|
|
|
void createContext(TLSInfo *tlsInfo);
|
|
|
|
///
|
|
/// Registers a second (or third, etc.) certificate/key pair for a
|
|
/// specific hostname on THIS SAME listening socket, via TLS's
|
|
/// Server Name Indication (SNI) extension - lets one TCPServer
|
|
/// terminate TLS for multiple unrelated domains, each with its own
|
|
/// real certificate, without needing a separate listening port or
|
|
/// process per domain. Must be called after createContext() (or the
|
|
/// TLSInfo* constructor, which calls it) has already established the
|
|
/// base `ctx` - that base context keeps serving as both the
|
|
/// fallback for hostnames not registered here AND the cert offered
|
|
/// to any client that sends no SNI hostname at all (a bare-IP
|
|
/// request, or any TLS client old enough to predate SNI). Callers
|
|
/// that never call this at all (every existing single-site
|
|
/// TLSInfo* user - BARANTMail, Zephora, ClockPlanet, and a
|
|
/// single-site APIServer config) see zero behavior change: the SNI
|
|
/// callback is only ever registered on `ctx` the first time this is
|
|
/// called, not unconditionally in createContext().
|
|
///
|
|
void addSNIContext(const std::string &hostname, TLSInfo *tlsInfo);
|
|
|
|
// A never-configured TLS object (either the listening TCPServer
|
|
// itself when constructed with tlsInfo=NULL, or a TCPSession spawned
|
|
// from one via TLS(SSL_CTX*)) must default to a real NULL here, not
|
|
// garbage - TCPSocket::receiveData()/sendData() key their TLS-vs-
|
|
// plain dispatch on this exact value (see TCPSocket.cpp).
|
|
SSL_CTX *ctx = NULL;
|
|
|
|
protected:
|
|
int receiveData(coreutils::ZString &buffer);
|
|
int sendData(const char *data, int length);
|
|
void registerSocket(int fd);
|
|
void acceptSocket();
|
|
|
|
protected:
|
|
// Set by sendData() when the last SSL_write() failed specifically
|
|
// with SSL_ERROR_WANT_READ (needs a READ to make progress - e.g.
|
|
// mid-handshake application data, or a post-handshake session
|
|
// ticket exchange) rather than any other reason. TCPSocket::
|
|
// sendNeedsRead() reads this (protected, so a subclass can reach
|
|
// it directly) to tell Socket::writeSocket()/resetSocket() not to
|
|
// busy-spin retrying the write via EPOLLWRNORM - see their own
|
|
// doc comments for the confirmed real damage (a 15GB+ debug log,
|
|
// and every affected connection dropped before ever reading the
|
|
// client's first command) this caused when unaddressed.
|
|
bool lastSendWantedRead = false;
|
|
|
|
private:
|
|
char *sip_cacert = (char *)"../testkeys/certs/pbxca.crt";
|
|
char *sip_cert = (char *)"../testkeys/certs/pbxserver.crt";
|
|
char *sip_key = (char *)"../testkeys/certs/pbxserver.key";
|
|
bool initialized = false;
|
|
SSL *ssl;
|
|
|
|
// The actual cert/key-loading + cipher/session-cache setup shared
|
|
// by both the base context (createContext(), unchanged) and every
|
|
// additional per-hostname context addSNIContext() builds - factored
|
|
// out so there's exactly one place that does this, not two copies
|
|
// drifting apart.
|
|
SSL_CTX *buildContext(TLSInfo *tlsInfo);
|
|
|
|
// Owns every SSL_CTX* built by addSNIContext() (the base `ctx` is
|
|
// owned/freed the same way it always was, untouched by this map).
|
|
// Keyed by hostname exactly as configured - matched case-
|
|
// insensitively against the ClientHello's SNI value in the
|
|
// callback (see TLS.cpp).
|
|
std::map<std::string, SSL_CTX *> sniContexts;
|
|
bool sniCallbackRegistered = false;
|
|
|
|
// OpenSSL's own SSL_CTX_set_tlsext_servername_callback signature -
|
|
// a static member (not a free function) so it can reach the owning
|
|
// TLS instance's private sniContexts via the `arg` parameter
|
|
// (SSL_CTX_set_tlsext_servername_arg(ctx, this), set alongside the
|
|
// callback itself in addSNIContext()).
|
|
static int sniCallback(SSL *ssl, int *ad, void *arg);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|