1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-05 09:20:04 +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:
Brian Quinlan 2022-03-31 06:40:51 +00:00 committed by Commit Bot
parent 10bbfbebc9
commit c286b76c2d
10 changed files with 224 additions and 2 deletions

View File

@ -74,6 +74,10 @@
- Deprecate `SecureSocket.renegotiate` and `RawSecureSocket.renegotiate`,
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`
- Add `Isolate.run` to run a function in a new isolate.

View File

@ -137,6 +137,7 @@ namespace bin {
V(SecurityContext_SetClientAuthoritiesBytes, 3) \
V(SecurityContext_SetTrustedCertificatesBytes, 3) \
V(SecurityContext_TrustBuiltinRoots, 1) \
V(SecurityContext_SetAllowTlsRenegotiation, 2) \
V(SecurityContext_UseCertificateChainBytes, 3) \
V(ServerSocket_Accept, 2) \
V(ServerSocket_CreateBindListen, 7) \

View File

@ -504,6 +504,10 @@ void SSLFilter::Connect(const char* hostname,
SSL_set_bio(ssl_, ssl_side, ssl_side);
SSL_set_mode(ssl_, SSL_MODE_AUTO_RETRY); // TODO(whesse): Is this right?
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_);
SSL_set_ex_data(ssl_, ssl_cert_context_index, context);

View File

@ -130,6 +130,12 @@ void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)(
"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)(
Dart_NativeArguments args) {
Dart_ThrowException(DartUtils::NewDartArgumentError(

View File

@ -877,6 +877,22 @@ void FUNCTION_NAME(SecurityContext_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) {
Dart_SetReturnValue(args, X509Helper::GetDer(args));
}

View File

@ -31,7 +31,8 @@ class SSLCertContext : public ReferenceCounted<SSLCertContext> {
: ReferenceCounted(),
context_(context),
alpn_protocol_string_(nullptr),
trust_builtin_(false) {}
trust_builtin_(false),
allow_tls_renegotiation_(false) {}
~SSLCertContext() {
SSL_CTX_free(context_);
@ -82,6 +83,11 @@ class SSLCertContext : public ReferenceCounted<SSLCertContext> {
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 RegisterCallbacks(SSL* ssl);
@ -112,7 +118,7 @@ class SSLCertContext : public ReferenceCounted<SSLCertContext> {
uint8_t* alpn_protocol_string_;
bool trust_builtin_;
bool allow_tls_renegotiation_;
static bool long_ssl_cert_evaluation_;
static bool bypass_trusting_system_roots_;

View File

@ -209,6 +209,8 @@ class SecurityContext {
class _SecurityContext extends NativeFieldWrapperClass1
implements SecurityContext {
bool _allowLegacyUnsafeRenegotiation = false;
_SecurityContext(bool withTrustedRoots) {
_createNativeContext();
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")
external void _createNativeContext();
@ -266,6 +275,8 @@ class _SecurityContext extends NativeFieldWrapperClass1
external void _setAlpnProtocols(Uint8List protocols, bool isServer);
@pragma("vm:external-name", "SecurityContext_TrustBuiltinRoots")
external void _trustBuiltinRoots();
@pragma("vm:external-name", "SecurityContext_SetAllowTlsRenegotiation")
external void _setAllowTlsRenegotiation(bool allow);
}
/**

View File

@ -160,6 +160,16 @@ abstract class SecurityContext {
/// or client connections.
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.
///
/// The [protocols] list is expected to contain protocols in descending order

View File

@ -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();
}

View File

@ -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();
}