Enforce presence of ALPN when QUIC is in use.
Update-Note: If an SSL_QUIC_METHOD is set, connections will now fail if
ALPN is not negotiated. This new behavior can be detected by checking
if the value of BORINGSSL_API_VERSION is greater than 10.
Bug: 294
Change-Id: I42fb80aa09268e77cec4a51e49cdad79bd72fa58
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/42304
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 3c2d852..eb45700 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -5070,6 +5070,22 @@
SSL_CTX_set_max_proto_version(server_ctx_.get(), TLS1_3_VERSION);
SSL_CTX_set_min_proto_version(client_ctx_.get(), TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(client_ctx_.get(), TLS1_3_VERSION);
+
+ static const uint8_t kALPNProtos[] = {0x03, 'f', 'o', 'o'};
+ ASSERT_EQ(SSL_CTX_set_alpn_protos(client_ctx_.get(), kALPNProtos,
+ sizeof(kALPNProtos)),
+ 0);
+ SSL_CTX_set_alpn_select_cb(
+ server_ctx_.get(),
+ [](SSL *ssl, const uint8_t **out, uint8_t *out_len, const uint8_t *in,
+ unsigned in_len, void *arg) -> int {
+ return SSL_select_next_proto(
+ const_cast<uint8_t **>(out), out_len, in, in_len,
+ kALPNProtos, sizeof(kALPNProtos)) == OPENSSL_NPN_NEGOTIATED
+ ? SSL_TLSEXT_ERR_OK
+ : SSL_TLSEXT_ERR_NOACK;
+ },
+ nullptr);
}
static MockQUICTransport *TransportFromSSL(const SSL *ssl) {
diff --git a/ssl/t1_lib.cc b/ssl/t1_lib.cc
index 5032af5..f274b11 100644
--- a/ssl/t1_lib.cc
+++ b/ssl/t1_lib.cc
@@ -1245,6 +1245,12 @@
static bool ext_alpn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
+ if (hs->config->alpn_client_proto_list.empty() && ssl->quic_method) {
+ // ALPN MUST be used with QUIC.
+ OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_ALPN);
+ return false;
+ }
+
if (hs->config->alpn_client_proto_list.empty() ||
ssl->s3->initial_handshake_complete) {
return true;
@@ -1267,6 +1273,12 @@
CBS *contents) {
SSL *const ssl = hs->ssl;
if (contents == NULL) {
+ if (ssl->quic_method) {
+ // ALPN is required when QUIC is used.
+ OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_ALPN);
+ *out_alert = SSL_AD_NO_APPLICATION_PROTOCOL;
+ return false;
+ }
return true;
}
@@ -1342,6 +1354,12 @@
!ssl_client_hello_get_extension(
client_hello, &contents,
TLSEXT_TYPE_application_layer_protocol_negotiation)) {
+ if (ssl->quic_method) {
+ // ALPN is required when QUIC is used.
+ OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_ALPN);
+ *out_alert = SSL_AD_NO_APPLICATION_PROTOCOL;
+ return false;
+ }
// Ignore ALPN if not configured or no extension was supplied.
return true;
}
@@ -1388,6 +1406,11 @@
*out_alert = SSL_AD_INTERNAL_ERROR;
return false;
}
+ } else if (ssl->quic_method) {
+ // ALPN is required when QUIC is used.
+ OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_ALPN);
+ *out_alert = SSL_AD_NO_APPLICATION_PROTOCOL;
+ return false;
}
return true;
diff --git a/ssl/test/runner/alert.go b/ssl/test/runner/alert.go
index c1f7f27..a6f61e9 100644
--- a/ssl/test/runner/alert.go
+++ b/ssl/test/runner/alert.go
@@ -45,6 +45,7 @@
alertBadCertificateStatusResponse alert = 113
alertUnknownPSKIdentity alert = 115
alertCertificateRequired alert = 116
+ alertNoApplicationProtocol alert = 120
)
var alertText = map[alert]string{
@@ -78,6 +79,7 @@
alertUnrecognizedName: "unrecognized name",
alertUnknownPSKIdentity: "unknown PSK identity",
alertCertificateRequired: "certificate required",
+ alertNoApplicationProtocol: "no application protocol",
}
func (e alert) String() string {
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index a340e1e..5527f96 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -656,6 +656,9 @@
// skipTransportParamsConfig, if true, will skip automatic configuration of
// sending QUIC transport parameters when protocol == quic.
skipTransportParamsConfig bool
+ // skipQUICALPNConfig, if true, will skip automatic configuration of
+ // sending a fake ALPN when protocol == quic.
+ skipQUICALPNConfig bool
}
var testCases []testCase
@@ -1280,6 +1283,20 @@
base64.StdEncoding.EncodeToString([]byte{1, 2}),
}...)
}
+ if !test.skipQUICALPNConfig {
+ flags = append(flags,
+ []string{
+ "-advertise-alpn", "\x03foo",
+ "-select-alpn", "foo",
+ "-expect-alpn", "foo",
+ }...)
+ test.config.NextProtos = []string{"foo"}
+ if test.resumeConfig != nil {
+ test.resumeConfig.NextProtos = []string{"foo"}
+ }
+ test.expectedNextProto = "foo"
+ test.expectedNextProtoType = alpn
+ }
}
var resumeCount int
@@ -6816,6 +6833,68 @@
})
}
+ // Test missing ALPN in QUIC
+ if ver.version >= VersionTLS13 {
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ protocol: quic,
+ name: "QUIC-Client-ALPNMissingFromConfig-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ },
+ skipQUICALPNConfig: true,
+ shouldFail: true,
+ expectedError: ":MISSING_ALPN:",
+ })
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ protocol: quic,
+ name: "QUIC-Client-ALPNMissing-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ },
+ flags: []string{
+ "-advertise-alpn", "\x03foo",
+ },
+ skipQUICALPNConfig: true,
+ shouldFail: true,
+ expectedError: ":MISSING_ALPN:",
+ expectedLocalError: "remote error: no application protocol",
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ protocol: quic,
+ name: "QUIC-Server-ALPNMissing-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ },
+ skipQUICALPNConfig: true,
+ shouldFail: true,
+ expectedError: ":MISSING_ALPN:",
+ expectedLocalError: "remote error: no application protocol",
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ protocol: quic,
+ name: "QUIC-Server-ALPNMismatch-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ NextProtos: []string{"foo"},
+ },
+ flags: []string{
+ "-decline-alpn",
+ },
+ skipQUICALPNConfig: true,
+ shouldFail: true,
+ expectedError: ":MISSING_ALPN:",
+ expectedLocalError: "remote error: no application protocol",
+ })
+ }
+
// Test Token Binding.
const maxTokenBindingVersion = 16