mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 05:42:43 +00:00
Some progress on unbreaking things
This commit is contained in:
@@ -897,7 +897,7 @@ pub const Listener = struct {
|
||||
socket.setTimeout(120);
|
||||
}
|
||||
|
||||
pub fn addServerName(this: *Listener, global: *JSC.JSGlobalObject, hostname: JSValue, tls: JSValue) bun.JSError!JSValue {
|
||||
pub fn addServerName(this: *Listener, global: *JSC.JSGlobalObject, hostname: JSValue, context: JSValue) bun.JSError!JSValue {
|
||||
if (!this.ssl) {
|
||||
return global.throwInvalidArguments("addServerName requires SSL support", .{});
|
||||
}
|
||||
@@ -915,7 +915,7 @@ pub const Listener = struct {
|
||||
return global.throwInvalidArguments("hostname pattern cannot be empty", .{});
|
||||
}
|
||||
|
||||
if (try JSC.API.ServerConfig.SSLConfig.fromJS(JSC.VirtualMachine.get(), global, tls)) |ssl_config| {
|
||||
if (try JSC.API.ServerConfig.SSLConfig.fromJS(JSC.VirtualMachine.get(), global, context)) |ssl_config| {
|
||||
// to keep nodejs compatibility, we allow to replace the server name
|
||||
this.socket_context.?.removeServerName(true, server_name);
|
||||
this.socket_context.?.addServerName(true, server_name, ssl_config.asUSockets());
|
||||
@@ -2846,7 +2846,7 @@ fn NewSocket(comptime ssl: bool) type {
|
||||
}
|
||||
|
||||
const request_cert = request_cert_js.toBoolean();
|
||||
const reject_unauthorized = request_cert_js.toBoolean();
|
||||
const reject_unauthorized = reject_unauthorized_js.toBoolean();
|
||||
var verify_mode: c_int = BoringSSL.SSL_VERIFY_NONE;
|
||||
if (this.handlers.is_server) {
|
||||
if (request_cert) {
|
||||
|
||||
@@ -136,11 +136,9 @@ NodeTLSSecureContext::~NodeTLSSecureContext() = default;
|
||||
|
||||
void NodeTLSSecureContext::setCACert(const ncrypto::BIOPointer& bio)
|
||||
{
|
||||
if (!bio) {
|
||||
return;
|
||||
}
|
||||
ASSERT(bio);
|
||||
|
||||
while (ncrypto::X509Pointer x509 = ncrypto::X509Pointer(PEM_read_bio_X509_AUX(bio.get(), nullptr, ncrypto::NoPasswordCallback, nullptr))) {
|
||||
while (ncrypto::X509Pointer x509 { PEM_read_bio_X509_AUX(bio.get(), nullptr, ncrypto::NoPasswordCallback, nullptr) }) {
|
||||
RELEASE_ASSERT(X509_STORE_add_cert(getCertStore(), x509.get()) == 1);
|
||||
RELEASE_ASSERT(SSL_CTX_add_client_CA(context(), x509.get()) == 1);
|
||||
}
|
||||
@@ -156,13 +154,17 @@ void NodeTLSSecureContext::setRootCerts()
|
||||
|
||||
bool NodeTLSSecureContext::applySNI(SSL* ssl)
|
||||
{
|
||||
// TODO(@heimskr): ERR_clear_error()?
|
||||
X509* x509 = SSL_get_certificate(ssl);
|
||||
SSL_CTX* ctx = context();
|
||||
|
||||
X509* x509 = [ctx] {
|
||||
ncrypto::ClearErrorOnReturn clearErrorOnReturn;
|
||||
return SSL_CTX_get0_certificate(ctx);
|
||||
}();
|
||||
|
||||
if (!x509) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SSL_CTX* ctx = context();
|
||||
EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx);
|
||||
STACK_OF(X509) * chain;
|
||||
|
||||
@@ -235,7 +237,7 @@ int NodeTLSSecureContext::ticketCompatibilityCallback(SSL* ssl, unsigned char* n
|
||||
return 1;
|
||||
}
|
||||
|
||||
// https://github.com/190n/node/blob/5812a61a68d50c65127beb68dd4dfb0242e3c5c9/src/crypto/crypto_context.cc#L112
|
||||
// https://github.com/nodejs/node/blob/5812a61a68d50c65127beb68dd4dfb0242e3c5c9/src/crypto/crypto_context.cc#L112
|
||||
static int useCertificateChain(SSL_CTX* ctx, ncrypto::X509Pointer&& x, STACK_OF(X509) * extra_certs, ncrypto::X509Pointer* cert, ncrypto::X509Pointer* issuer_)
|
||||
{
|
||||
RELEASE_ASSERT(!*issuer_);
|
||||
@@ -285,7 +287,7 @@ static int useCertificateChain(SSL_CTX* ctx, ncrypto::X509Pointer&& x, STACK_OF(
|
||||
return ret;
|
||||
}
|
||||
|
||||
// https://github.com/190n/node/blob/5812a61a68d50c65127beb68dd4dfb0242e3c5c9/src/crypto/crypto_context.cc#L183
|
||||
// https://github.com/nodejs/node/blob/5812a61a68d50c65127beb68dd4dfb0242e3c5c9/src/crypto/crypto_context.cc#L183
|
||||
static int useCertificateChain(SSL_CTX* ctx, ncrypto::BIOPointer&& in, ncrypto::X509Pointer* cert, ncrypto::X509Pointer* issuer)
|
||||
{
|
||||
ERR_clear_error();
|
||||
@@ -366,7 +368,7 @@ bool NodeTLSSecureContext::addCert(JSGlobalObject* globalObject, ThrowScope& sco
|
||||
}
|
||||
|
||||
if (useCertificateChain(context(), std::move(bio), &m_cert, &m_issuer) == 0) {
|
||||
return throwCryptoError(globalObject, scope, ERR_get_error(), "Failed to set certificate");
|
||||
throwCryptoError(globalObject, scope, ERR_get_error(), "Failed to set certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -380,14 +382,23 @@ JSC_DEFINE_HOST_FUNCTION(secureContextInit, (JSGlobalObject * globalObject, Call
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
ArgList args(callFrame);
|
||||
JSValue secureProtocolValue = args.at(0);
|
||||
JSValue optionsValue = args.at(0);
|
||||
JSValue minVersionValue = args.at(1);
|
||||
JSValue maxVersionValue = args.at(2);
|
||||
|
||||
if (!optionsValue.isObject()) {
|
||||
return throwArgumentTypeError(*globalObject, scope, 0, "options"_s, "SecureContext"_s, "init"_s, "object"_s);
|
||||
}
|
||||
|
||||
int minVersion = minVersionValue.toInt32(globalObject);
|
||||
int maxVersion = maxVersionValue.toInt32(globalObject);
|
||||
const SSL_METHOD* method = TLS_method();
|
||||
|
||||
JSObject* options = JSC::asObject(optionsValue);
|
||||
|
||||
JSValue secureProtocolValue = options->get(globalObject, Identifier::fromString(vm, "secureProtocol"_s));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (secureProtocolValue.isString()) {
|
||||
String secureProtocol = secureProtocolValue.toWTFString(globalObject);
|
||||
|
||||
@@ -461,6 +472,24 @@ JSC_DEFINE_HOST_FUNCTION(secureContextInit, (JSGlobalObject * globalObject, Call
|
||||
}
|
||||
}
|
||||
|
||||
auto getTriState = [&](ASCIILiteral name) -> WTF::TriState {
|
||||
JSValue value = options->get(globalObject, Identifier::fromString(vm, name));
|
||||
RETURN_IF_EXCEPTION(scope, WTF::TriState::Indeterminate);
|
||||
|
||||
if (value.isBoolean()) {
|
||||
return triState(value.asBoolean());
|
||||
}
|
||||
|
||||
if (!value.isUndefined()) {
|
||||
Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, makeString("options."_s, name), "boolean"_s, value);
|
||||
}
|
||||
|
||||
return WTF::TriState::Indeterminate;
|
||||
};
|
||||
|
||||
WTF::TriState requestCert = getTriState("requestCert");
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
thisObject->context(SSL_CTX_new(method));
|
||||
SSL_CTX* context = thisObject->context();
|
||||
|
||||
@@ -472,6 +501,18 @@ JSC_DEFINE_HOST_FUNCTION(secureContextInit, (JSGlobalObject * globalObject, Call
|
||||
SSL_CTX_set_options(context, SSL_OP_NO_SSLv2);
|
||||
SSL_CTX_set_options(context, SSL_OP_NO_SSLv3);
|
||||
|
||||
if (requestCert != TriState::True) {
|
||||
SSL_CTX_set_verify(context, SSL_VERIFY_NONE, nullptr);
|
||||
} else {
|
||||
WTF::TriState rejectUnauthorized = getTriState("rejectUnauthorized");
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (rejectUnauthorized == WTF::TriState::True) {
|
||||
SSL_CTX_set_verify(context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
|
||||
} else {
|
||||
SSL_CTX_set_verify(context, SSL_VERIFY_PEER, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
// TODO(@heimskr): OPENSSL_VERSION_MAJOR doesn't appear to be defined anywhere.
|
||||
SSL_CTX_set_options(context, SSL_OP_ALLOW_CLIENT_RENEGOTIATION);
|
||||
@@ -554,11 +595,11 @@ JSC_DEFINE_HOST_FUNCTION(secureContextAddCACert, (JSGlobalObject * globalObject,
|
||||
|
||||
int written = ncrypto::BIOPointer::Write(&bio, cert.span());
|
||||
if (written < 0 || static_cast<size_t>(written) != cert.length()) {
|
||||
return JSC::encodedJSUndefined();
|
||||
return JSValue::encode(jsBoolean(false));
|
||||
}
|
||||
|
||||
thisObject->setCACert(bio);
|
||||
return JSC::encodedJSUndefined();
|
||||
return JSValue::encode(jsBoolean(true));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(secureContextSetECDHCurve, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
@@ -632,7 +673,7 @@ JSC_DEFINE_HOST_FUNCTION(secureContextSetKey, (JSGlobalObject * globalObject, Ca
|
||||
return throwCryptoError(globalObject, scope, ERR_get_error(), "SSL_CTX_use_PrivateKey");
|
||||
}
|
||||
|
||||
return JSC::encodedJSUndefined();
|
||||
return JSValue::encode(jsBoolean(true));
|
||||
}
|
||||
|
||||
static const HashTableValue NodeTLSSecureContextPrototypeTableValues[] = {
|
||||
|
||||
@@ -361,10 +361,10 @@ const ServerHandlers: SocketHandler<NetSocket> = {
|
||||
socket[kServerSocket] = self._handle;
|
||||
const options = self[bunSocketServerOptions];
|
||||
const { pauseOnConnect, connectionListener, [kSocketClass]: SClass, requestCert, rejectUnauthorized } = options;
|
||||
const _socket = new SClass(options) as NetSocket | TLSSocket;
|
||||
const _socket = new SClass({ ...options, isServer: true }) as NetSocket | TLSSocket;
|
||||
_socket.isServer = true;
|
||||
_socket._requestCert = requestCert;
|
||||
_socket._rejectUnauthorized = rejectUnauthorized;
|
||||
_socket._requestCert = requestCert ?? _socket._requestCert;
|
||||
_socket._rejectUnauthorized = rejectUnauthorized ?? _socket._rejectUnauthorized;
|
||||
|
||||
_socket[kAttach](this.localPort, socket);
|
||||
|
||||
@@ -2597,6 +2597,7 @@ function initSocketHandle(self) {
|
||||
// Handle creation may be deferred to bind() or connect() time.
|
||||
if (self._handle) {
|
||||
self._handle[owner_symbol] = self;
|
||||
self._configureHandle?.();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -222,9 +222,9 @@ var InternalSecureContext = class SecureContext {
|
||||
secureOptions;
|
||||
|
||||
constructor(options) {
|
||||
const { honorCipherOrder, minVersion, maxVersion, secureProtocol } = options;
|
||||
const { honorCipherOrder, minVersion, maxVersion } = options;
|
||||
|
||||
this.context = new NodeTLSSecureContext(secureProtocol, minVersion, maxVersion);
|
||||
this.context = new NodeTLSSecureContext(options);
|
||||
|
||||
if (options) {
|
||||
let { cert } = options;
|
||||
@@ -285,7 +285,7 @@ var InternalSecureContext = class SecureContext {
|
||||
}
|
||||
|
||||
this.context.init(
|
||||
secureProtocol,
|
||||
options,
|
||||
toV("minimum", minVersion, DEFAULT_MIN_VERSION),
|
||||
toV("maximum", maxVersion, DEFAULT_MAX_VERSION),
|
||||
);
|
||||
@@ -504,7 +504,6 @@ function toBuf(val, encoding?: BufferEncoding | "buffer") {
|
||||
function addCACerts(context, certs, name) {
|
||||
ArrayPrototypeForEach.$call(certs, cert => {
|
||||
validateKeyOrCertOption(name, cert);
|
||||
context.addCACert(cert);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -526,7 +525,6 @@ function setKey(context, key, passphrase, name) {
|
||||
if (passphrase !== undefined && passphrase !== null) {
|
||||
validateString(passphrase, `${name}.passphrase`);
|
||||
}
|
||||
context.setKey(key, passphrase);
|
||||
}
|
||||
|
||||
function processCiphers(ciphers, name) {
|
||||
@@ -608,7 +606,7 @@ function TLSSocket(socket?, options?) {
|
||||
}
|
||||
|
||||
if (typeof options === "object") {
|
||||
const { ALPNProtocols, SNICallback: sni } = options;
|
||||
const { ALPNProtocols, SNICallback: sni, rejectUnauthorized, requestCert } = options;
|
||||
if (ALPNProtocols) {
|
||||
convertALPNProtocols(ALPNProtocols, this);
|
||||
}
|
||||
@@ -618,12 +616,21 @@ function TLSSocket(socket?, options?) {
|
||||
this._SNICallback = sni;
|
||||
}
|
||||
|
||||
if (typeof rejectUnauthorized !== "undefined") {
|
||||
this._rejectUnauthorized = rejectUnauthorized;
|
||||
}
|
||||
|
||||
if (typeof requestCert !== "undefined") {
|
||||
this._requestCert = requestCert;
|
||||
}
|
||||
|
||||
if (isNetSocketOrDuplex) {
|
||||
this._handle = socket;
|
||||
// keep compatibility with http2-wrapper or other places that try to grab JSStreamSocket in node.js, with here is just the TLSSocket
|
||||
this._handle._parentWrap = this;
|
||||
}
|
||||
}
|
||||
|
||||
this[ksecureContext] = options.secureContext || createSecureContext(options);
|
||||
this.authorized = false;
|
||||
this.secureConnecting = true;
|
||||
@@ -633,9 +640,12 @@ function TLSSocket(socket?, options?) {
|
||||
this[ksession] = options.session || null;
|
||||
|
||||
this.once("connect", socket => {
|
||||
if (socket?.isServer) {
|
||||
this._handle.certCallback = loadSNI.bind(this);
|
||||
this._handle.enableCertCallback();
|
||||
if (socket) {
|
||||
socket._handle?.setVerifyMode(!!socket._requestCert || !socket.isServer, !!socket._rejectUnauthorized);
|
||||
if (socket.isServer) {
|
||||
socket._handle.certCallback = loadSNI.bind(socket);
|
||||
socket._handle.enableCertCallback();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -646,6 +656,12 @@ TLSSocket.prototype._start = function _start() {
|
||||
this.connect();
|
||||
};
|
||||
|
||||
TLSSocket.prototype._configureHandle = function _configureHandle() {
|
||||
if (typeof this._rejectUnauthorized !== "undefined") {
|
||||
this._handle.setVerifyMode(!!this._requestCert || !this.isServer, !!this._rejectUnauthorized);
|
||||
}
|
||||
};
|
||||
|
||||
TLSSocket.prototype.getSession = function getSession() {
|
||||
return this._handle?.getSession?.();
|
||||
};
|
||||
@@ -815,6 +831,7 @@ function Server(options, secureConnectionListener): void {
|
||||
this.ALPNProtocols = undefined;
|
||||
this._SNICallback = SNICallback;
|
||||
this.server = this;
|
||||
this._contexts = [];
|
||||
|
||||
let contexts: Map<string, typeof InternalSecureContext> | null = null;
|
||||
|
||||
@@ -830,6 +847,7 @@ function Server(options, secureConnectionListener): void {
|
||||
} else {
|
||||
if (!contexts) contexts = new Map();
|
||||
contexts.set(hostname, context);
|
||||
this._contexts.push(context);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -933,7 +951,6 @@ function Server(options, secureConnectionListener): void {
|
||||
clientRenegotiationLimit: CLIENT_RENEG_LIMIT,
|
||||
clientRenegotiationWindow: CLIENT_RENEG_WINDOW,
|
||||
contexts: contexts,
|
||||
foo: "bar",
|
||||
},
|
||||
TLSSocket,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user