mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 12:57:42 +00:00
Allow sockets to enable TLS renegotiation.
TESTED=unit + manually tested user issue. Bug: https://github.com/dart-lang/sdk/issues/47841 Change-Id: Iad13899135fd34f15abba3a499132d88e7f597dc Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/234821 Reviewed-by: Alexander Aprelev <aam@google.com> Commit-Queue: Brian Quinlan <bquinlan@google.com>
This commit is contained in:
parent
10bbfbebc9
commit
c286b76c2d
|
@ -74,6 +74,10 @@
|
||||||
- Deprecate `SecureSocket.renegotiate` and `RawSecureSocket.renegotiate`,
|
- Deprecate `SecureSocket.renegotiate` and `RawSecureSocket.renegotiate`,
|
||||||
which were no-ops.
|
which were no-ops.
|
||||||
|
|
||||||
|
- **Breaking Change** [#48513](https://github.com/dart-lang/sdk/issues/48513):
|
||||||
|
Add a new `allowLegacyUnsafeRenegotiation` poperty to `SecurityContext`,
|
||||||
|
which allows TLS renegotiation for client secure sockets.
|
||||||
|
|
||||||
#### `dart:isolate`
|
#### `dart:isolate`
|
||||||
|
|
||||||
- Add `Isolate.run` to run a function in a new isolate.
|
- Add `Isolate.run` to run a function in a new isolate.
|
||||||
|
|
|
@ -137,6 +137,7 @@ namespace bin {
|
||||||
V(SecurityContext_SetClientAuthoritiesBytes, 3) \
|
V(SecurityContext_SetClientAuthoritiesBytes, 3) \
|
||||||
V(SecurityContext_SetTrustedCertificatesBytes, 3) \
|
V(SecurityContext_SetTrustedCertificatesBytes, 3) \
|
||||||
V(SecurityContext_TrustBuiltinRoots, 1) \
|
V(SecurityContext_TrustBuiltinRoots, 1) \
|
||||||
|
V(SecurityContext_SetAllowTlsRenegotiation, 2) \
|
||||||
V(SecurityContext_UseCertificateChainBytes, 3) \
|
V(SecurityContext_UseCertificateChainBytes, 3) \
|
||||||
V(ServerSocket_Accept, 2) \
|
V(ServerSocket_Accept, 2) \
|
||||||
V(ServerSocket_CreateBindListen, 7) \
|
V(ServerSocket_CreateBindListen, 7) \
|
||||||
|
|
|
@ -504,6 +504,10 @@ void SSLFilter::Connect(const char* hostname,
|
||||||
SSL_set_bio(ssl_, ssl_side, ssl_side);
|
SSL_set_bio(ssl_, ssl_side, ssl_side);
|
||||||
SSL_set_mode(ssl_, SSL_MODE_AUTO_RETRY); // TODO(whesse): Is this right?
|
SSL_set_mode(ssl_, SSL_MODE_AUTO_RETRY); // TODO(whesse): Is this right?
|
||||||
SSL_set_ex_data(ssl_, filter_ssl_index, this);
|
SSL_set_ex_data(ssl_, filter_ssl_index, this);
|
||||||
|
|
||||||
|
if (context->allow_tls_renegotiation()) {
|
||||||
|
SSL_set_renegotiate_mode(ssl_, ssl_renegotiate_freely);
|
||||||
|
}
|
||||||
context->RegisterCallbacks(ssl_);
|
context->RegisterCallbacks(ssl_);
|
||||||
SSL_set_ex_data(ssl_, ssl_cert_context_index, context);
|
SSL_set_ex_data(ssl_, ssl_cert_context_index, context);
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,12 @@ void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)(
|
||||||
"Secure Sockets unsupported on this platform"));
|
"Secure Sockets unsupported on this platform"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FUNCTION_NAME(SecurityContext_SetAllowTlsRenegotiation)(
|
||||||
|
Dart_NativeArguments args) {
|
||||||
|
Dart_ThrowException(DartUtils::NewDartArgumentError(
|
||||||
|
"Secure Sockets unsupported on this platform"));
|
||||||
|
}
|
||||||
|
|
||||||
void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)(
|
void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)(
|
||||||
Dart_NativeArguments args) {
|
Dart_NativeArguments args) {
|
||||||
Dart_ThrowException(DartUtils::NewDartArgumentError(
|
Dart_ThrowException(DartUtils::NewDartArgumentError(
|
||||||
|
|
|
@ -877,6 +877,22 @@ void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)(
|
||||||
context->TrustBuiltinRoots();
|
context->TrustBuiltinRoots();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FUNCTION_NAME(SecurityContext_SetAllowTlsRenegotiation)(
|
||||||
|
Dart_NativeArguments args) {
|
||||||
|
SSLCertContext* context = SSLCertContext::GetSecurityContext(args);
|
||||||
|
Dart_Handle allow_tls_handle = ThrowIfError(Dart_GetNativeArgument(args, 1));
|
||||||
|
|
||||||
|
ASSERT(context != NULL);
|
||||||
|
ASSERT(allow_tls_handle != NULL);
|
||||||
|
|
||||||
|
if (!Dart_IsBoolean(allow_tls_handle)) {
|
||||||
|
Dart_ThrowException(DartUtils::NewDartArgumentError(
|
||||||
|
"Non-boolean argument passed to SetAllowTlsRenegotiation"));
|
||||||
|
}
|
||||||
|
bool allow = DartUtils::GetBooleanValue(allow_tls_handle);
|
||||||
|
context->set_allow_tls_renegotiation(allow);
|
||||||
|
}
|
||||||
|
|
||||||
void FUNCTION_NAME(X509_Der)(Dart_NativeArguments args) {
|
void FUNCTION_NAME(X509_Der)(Dart_NativeArguments args) {
|
||||||
Dart_SetReturnValue(args, X509Helper::GetDer(args));
|
Dart_SetReturnValue(args, X509Helper::GetDer(args));
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,8 @@ class SSLCertContext : public ReferenceCounted<SSLCertContext> {
|
||||||
: ReferenceCounted(),
|
: ReferenceCounted(),
|
||||||
context_(context),
|
context_(context),
|
||||||
alpn_protocol_string_(nullptr),
|
alpn_protocol_string_(nullptr),
|
||||||
trust_builtin_(false) {}
|
trust_builtin_(false),
|
||||||
|
allow_tls_renegotiation_(false) {}
|
||||||
|
|
||||||
~SSLCertContext() {
|
~SSLCertContext() {
|
||||||
SSL_CTX_free(context_);
|
SSL_CTX_free(context_);
|
||||||
|
@ -82,6 +83,11 @@ class SSLCertContext : public ReferenceCounted<SSLCertContext> {
|
||||||
|
|
||||||
bool trust_builtin() const { return trust_builtin_; }
|
bool trust_builtin() const { return trust_builtin_; }
|
||||||
|
|
||||||
|
void set_allow_tls_renegotiation(bool allow) {
|
||||||
|
allow_tls_renegotiation_ = allow;
|
||||||
|
}
|
||||||
|
bool allow_tls_renegotiation() const { return allow_tls_renegotiation_; }
|
||||||
|
|
||||||
void set_trust_builtin(bool trust_builtin) { trust_builtin_ = trust_builtin; }
|
void set_trust_builtin(bool trust_builtin) { trust_builtin_ = trust_builtin; }
|
||||||
|
|
||||||
void RegisterCallbacks(SSL* ssl);
|
void RegisterCallbacks(SSL* ssl);
|
||||||
|
@ -112,7 +118,7 @@ class SSLCertContext : public ReferenceCounted<SSLCertContext> {
|
||||||
uint8_t* alpn_protocol_string_;
|
uint8_t* alpn_protocol_string_;
|
||||||
|
|
||||||
bool trust_builtin_;
|
bool trust_builtin_;
|
||||||
|
bool allow_tls_renegotiation_;
|
||||||
static bool long_ssl_cert_evaluation_;
|
static bool long_ssl_cert_evaluation_;
|
||||||
static bool bypass_trusting_system_roots_;
|
static bool bypass_trusting_system_roots_;
|
||||||
|
|
||||||
|
|
|
@ -209,6 +209,8 @@ class SecurityContext {
|
||||||
|
|
||||||
class _SecurityContext extends NativeFieldWrapperClass1
|
class _SecurityContext extends NativeFieldWrapperClass1
|
||||||
implements SecurityContext {
|
implements SecurityContext {
|
||||||
|
bool _allowLegacyUnsafeRenegotiation = false;
|
||||||
|
|
||||||
_SecurityContext(bool withTrustedRoots) {
|
_SecurityContext(bool withTrustedRoots) {
|
||||||
_createNativeContext();
|
_createNativeContext();
|
||||||
if (withTrustedRoots) {
|
if (withTrustedRoots) {
|
||||||
|
@ -216,6 +218,13 @@ class _SecurityContext extends NativeFieldWrapperClass1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set allowLegacyUnsafeRenegotiation(bool allow) {
|
||||||
|
_allowLegacyUnsafeRenegotiation = allow;
|
||||||
|
_setAllowTlsRenegotiation(allow);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get allowLegacyUnsafeRenegotiation => _allowLegacyUnsafeRenegotiation;
|
||||||
|
|
||||||
@pragma("vm:external-name", "SecurityContext_Allocate")
|
@pragma("vm:external-name", "SecurityContext_Allocate")
|
||||||
external void _createNativeContext();
|
external void _createNativeContext();
|
||||||
|
|
||||||
|
@ -266,6 +275,8 @@ class _SecurityContext extends NativeFieldWrapperClass1
|
||||||
external void _setAlpnProtocols(Uint8List protocols, bool isServer);
|
external void _setAlpnProtocols(Uint8List protocols, bool isServer);
|
||||||
@pragma("vm:external-name", "SecurityContext_TrustBuiltinRoots")
|
@pragma("vm:external-name", "SecurityContext_TrustBuiltinRoots")
|
||||||
external void _trustBuiltinRoots();
|
external void _trustBuiltinRoots();
|
||||||
|
@pragma("vm:external-name", "SecurityContext_SetAllowTlsRenegotiation")
|
||||||
|
external void _setAllowTlsRenegotiation(bool allow);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -160,6 +160,16 @@ abstract class SecurityContext {
|
||||||
/// or client connections.
|
/// or client connections.
|
||||||
void setAlpnProtocols(List<String> protocols, bool isServer);
|
void setAlpnProtocols(List<String> protocols, bool isServer);
|
||||||
|
|
||||||
|
/// If `true`, the [SecurityContext] will allow TLS renegotiation.
|
||||||
|
/// Renegotiation is only supported as a client and the HelloRequest must be
|
||||||
|
/// received at a quiet point in the application protocol. This is sufficient
|
||||||
|
/// to support the legacy use case of requesting a new client certificate
|
||||||
|
/// between an HTTP request and response in (unpipelined) HTTP/1.1.
|
||||||
|
/// NOTE: Renegotiation is an extremely problematic protocol feature and
|
||||||
|
/// should only be used to communicate with legacy servers in environments
|
||||||
|
/// where it is known to be safe.
|
||||||
|
abstract bool allowLegacyUnsafeRenegotiation;
|
||||||
|
|
||||||
/// Encodes a set of supported protocols for ALPN/NPN usage.
|
/// Encodes a set of supported protocols for ALPN/NPN usage.
|
||||||
///
|
///
|
||||||
/// The [protocols] list is expected to contain protocols in descending order
|
/// The [protocols] list is expected to contain protocols in descending order
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
//
|
||||||
|
// VMOptions=
|
||||||
|
// VMOptions=--short_socket_read
|
||||||
|
// VMOptions=--short_socket_write
|
||||||
|
// VMOptions=--short_socket_read --short_socket_write
|
||||||
|
// OtherResources=certificates/server_chain.pem
|
||||||
|
// OtherResources=certificates/server_key.pem
|
||||||
|
// OtherResources=certificates/trusted_certs.pem
|
||||||
|
//
|
||||||
|
// It is not possible to initiate TLS-renegotiation from a pure-Dart server so
|
||||||
|
// just test that the `allowLegacyUnsafeRenegotiation` in `SecurityContext`
|
||||||
|
// does not affect connections that do *not* do renegotiation.
|
||||||
|
|
||||||
|
import "dart:async";
|
||||||
|
import 'dart:convert';
|
||||||
|
import "dart:io";
|
||||||
|
|
||||||
|
import "package:async_helper/async_helper.dart";
|
||||||
|
import "package:expect/expect.dart";
|
||||||
|
|
||||||
|
late InternetAddress HOST;
|
||||||
|
|
||||||
|
String localFile(path) => Platform.script.resolve(path).toFilePath();
|
||||||
|
|
||||||
|
SecurityContext serverContext = new SecurityContext()
|
||||||
|
..useCertificateChain(localFile('certificates/server_chain.pem'))
|
||||||
|
..usePrivateKey(localFile('certificates/server_key.pem'),
|
||||||
|
password: 'dartdart');
|
||||||
|
|
||||||
|
Future<SecureServerSocket> startEchoServer() {
|
||||||
|
return SecureServerSocket.bind(HOST, 0, serverContext).then((server) {
|
||||||
|
server.listen((SecureSocket client) {
|
||||||
|
client.fold<List<int>>(
|
||||||
|
<int>[], (message, data) => message..addAll(data)).then((message) {
|
||||||
|
client.add(message);
|
||||||
|
client.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return server;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
testSuccess(SecureServerSocket server) async {
|
||||||
|
// NOTE: this test only verifies that `allowLegacyUnsafeRenegotiation` does
|
||||||
|
// not cause incorrect behavior when enabled - the server does *not* actually
|
||||||
|
// trigger TLS renegotiation.
|
||||||
|
SecurityContext clientContext = new SecurityContext()
|
||||||
|
..allowLegacyUnsafeRenegotiation = true
|
||||||
|
..setTrustedCertificates(localFile('certificates/trusted_certs.pem'));
|
||||||
|
|
||||||
|
await SecureSocket.connect(HOST, server.port, context: clientContext)
|
||||||
|
.then((socket) async {
|
||||||
|
socket.write("Hello server.");
|
||||||
|
socket.close();
|
||||||
|
Expect.isTrue(await utf8.decoder.bind(socket).contains("Hello server."));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
testProperty() {
|
||||||
|
SecurityContext context = new SecurityContext();
|
||||||
|
Expect.isFalse(context.allowLegacyUnsafeRenegotiation);
|
||||||
|
context.allowLegacyUnsafeRenegotiation = true;
|
||||||
|
Expect.isTrue(context.allowLegacyUnsafeRenegotiation);
|
||||||
|
context.allowLegacyUnsafeRenegotiation = false;
|
||||||
|
Expect.isFalse(context.allowLegacyUnsafeRenegotiation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
asyncStart();
|
||||||
|
await InternetAddress.lookup("localhost").then((hosts) => HOST = hosts.first);
|
||||||
|
final server = await startEchoServer();
|
||||||
|
|
||||||
|
await testSuccess(server);
|
||||||
|
testProperty();
|
||||||
|
|
||||||
|
await server.close();
|
||||||
|
asyncEnd();
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
//
|
||||||
|
// VMOptions=
|
||||||
|
// VMOptions=--short_socket_read
|
||||||
|
// VMOptions=--short_socket_write
|
||||||
|
// VMOptions=--short_socket_read --short_socket_write
|
||||||
|
// OtherResources=certificates/server_chain.pem
|
||||||
|
// OtherResources=certificates/server_key.pem
|
||||||
|
// OtherResources=certificates/trusted_certs.pem
|
||||||
|
//
|
||||||
|
// It is not possible to initiate TLS-renegotiation from a pure-Dart server so
|
||||||
|
// just test that the `allowLegacyUnsafeRenegotiation` in `SecurityContext`
|
||||||
|
// does not affect connections that do *not* do renegotiation.
|
||||||
|
|
||||||
|
// @dart = 2.9
|
||||||
|
|
||||||
|
import "dart:async";
|
||||||
|
import 'dart:convert';
|
||||||
|
import "dart:io";
|
||||||
|
|
||||||
|
import "package:async_helper/async_helper.dart";
|
||||||
|
import "package:expect/expect.dart";
|
||||||
|
|
||||||
|
InternetAddress HOST;
|
||||||
|
|
||||||
|
String localFile(path) => Platform.script.resolve(path).toFilePath();
|
||||||
|
|
||||||
|
SecurityContext serverContext = new SecurityContext()
|
||||||
|
..useCertificateChain(localFile('certificates/server_chain.pem'))
|
||||||
|
..usePrivateKey(localFile('certificates/server_key.pem'),
|
||||||
|
password: 'dartdart');
|
||||||
|
|
||||||
|
Future<SecureServerSocket> startEchoServer() {
|
||||||
|
return SecureServerSocket.bind(HOST, 0, serverContext).then((server) {
|
||||||
|
server.listen((SecureSocket client) {
|
||||||
|
client.fold<List<int>>(
|
||||||
|
<int>[], (message, data) => message..addAll(data)).then((message) {
|
||||||
|
client.add(message);
|
||||||
|
client.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return server;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
testSuccess(SecureServerSocket server) async {
|
||||||
|
// NOTE: this test only verifies that `allowLegacyUnsafeRenegotiation` does
|
||||||
|
// not cause incorrect behavior when enabled - the server does *not* actually
|
||||||
|
// trigger TLS renegotiation.
|
||||||
|
SecurityContext clientContext = new SecurityContext()
|
||||||
|
..allowLegacyUnsafeRenegotiation = true
|
||||||
|
..setTrustedCertificates(localFile('certificates/trusted_certs.pem'));
|
||||||
|
|
||||||
|
await SecureSocket.connect(HOST, server.port, context: clientContext)
|
||||||
|
.then((socket) async {
|
||||||
|
socket.write("Hello server.");
|
||||||
|
socket.close();
|
||||||
|
Expect.isTrue(await utf8.decoder.bind(socket).contains("Hello server."));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
testProperty() {
|
||||||
|
SecurityContext context = new SecurityContext();
|
||||||
|
Expect.isFalse(context.allowLegacyUnsafeRenegotiation);
|
||||||
|
context.allowLegacyUnsafeRenegotiation = true;
|
||||||
|
Expect.isTrue(context.allowLegacyUnsafeRenegotiation);
|
||||||
|
context.allowLegacyUnsafeRenegotiation = false;
|
||||||
|
Expect.isFalse(context.allowLegacyUnsafeRenegotiation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
asyncStart();
|
||||||
|
await InternetAddress.lookup("localhost").then((hosts) => HOST = hosts.first);
|
||||||
|
final server = await startEchoServer();
|
||||||
|
|
||||||
|
await testSuccess(server);
|
||||||
|
testProperty();
|
||||||
|
|
||||||
|
await server.close();
|
||||||
|
asyncEnd();
|
||||||
|
}
|
Loading…
Reference in a new issue