ssl: Use default ALPN handler

This default ALPN handler is called when ALPN protocols are configured
without a ALPN selection callback on a server.

Update-Note: Calling `SSL_CTX_set_alpn_protos` or `SSL_set_alpn_protos`
without calling `SSL_CTX_set_alpn_select_cb` will no longer fail ALPN
anymore. Instead, the protocol will be picked by walking down the client
protocol list.

Bug: 411171274

Signed-off-by: Xiangfei Ding <xfding@google.com>
Change-Id: I81cb3820c7aa1f97fe90fd7c7fe5b4616a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/98987
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index d9eca9b..56682da 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -3425,7 +3425,13 @@
 // `protos`. `protos` must be in wire-format (i.e. a series of non-empty, 8-bit
 // length-prefixed strings), or the empty string to disable ALPN. It returns
 // zero on success and one on failure. Configuring a non-empty string enables
-// ALPN on a client.
+// ALPN on a client. On a client, it will advertise these protocols. On a
+// server, it will use them to automatically select a protocol if the client
+// supports ALPN and no select callback is set.
+//
+// Those configuring HTTP/2 must also configure the cipher suite set on the
+// SSL_CTX so that the selected cipher is compliant with RFC 7540 Section 9.2.
+// Servers should install their own ALPN callback to comply with the RFC.
 //
 // WARNING: this function is dangerous because it breaks the usual return value
 // convention.
@@ -3436,7 +3442,13 @@
 // `protos` must be in wire-format (i.e. a series of non-empty, 8-bit
 // length-prefixed strings), or the empty string to disable ALPN. It returns
 // zero on success and one on failure. Configuring a non-empty string enables
-// ALPN on a client.
+// ALPN on a client. On a client, it will advertise these protocols. On a
+// server, it will use them to automatically select a protocol if the client
+// supports ALPN and no select callback is set.
+//
+// Those configuring HTTP/2 must also configure the cipher suite set on the
+// SSL_CTX so that the selected cipher is compliant with RFC 7540 Section 9.2.
+// Servers should install their own ALPN callback to comply with the RFC.
 //
 // WARNING: this function is dangerous because it breaks the usual return value
 // convention.
diff --git a/ssl/extensions.cc b/ssl/extensions.cc
index 022714a..8787d9f 100644
--- a/ssl/extensions.cc
+++ b/ssl/extensions.cc
@@ -16,6 +16,7 @@
 
 #include <assert.h>
 #include <limits.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -38,6 +39,7 @@
 #include <openssl/nid.h>
 #include <openssl/pool.h>
 #include <openssl/rand.h>
+#include <openssl/span.h>
 
 #include "../crypto/bytestring/internal.h"
 #include "../crypto/internal.h"
@@ -1031,7 +1033,7 @@
   if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_signature_algorithms) ||
       !CBB_add_u16_length_prefixed(out_compressible, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &sigalgs_cbb)) {
-        return false;
+    return false;
   }
   // Add a fake signature algorithm. See RFC 8701.
   if (hs->ssl->ctx->grease_sigalgs_enabled &&
@@ -1501,14 +1503,51 @@
   return false;
 }
 
+static int default_alpn_select_cb(SSL *ssl, const uint8_t **out,
+                                  uint8_t *out_len, const uint8_t *in,
+                                  unsigned inlen, void *arg) {
+  auto *ssl_impl = FromOpaque(ssl);
+  SSL_HANDSHAKE *hs = ssl_impl->s3->hs.get();
+  if (hs == nullptr) {
+    return SSL_TLSEXT_ERR_ALERT_FATAL;
+  }
+
+  bssl::Span<const uint8_t> client_protos(in, inlen);
+  CBS cbs = client_protos;
+  CBS proto;
+  while (CBS_len(&cbs) != 0) {
+    if (!CBS_get_u8_length_prefixed(&cbs, &proto) || CBS_len(&proto) == 0) {
+      return SSL_TLSEXT_ERR_ALERT_FATAL;
+    }
+    if (ssl_alpn_list_contains_protocol(hs->config->alpn_client_proto_list,
+                                        proto)) {
+      *out = CBS_data(&proto);
+      *out_len = static_cast<uint8_t>(CBS_len(&proto));
+      return SSL_TLSEXT_ERR_OK;
+    }
+  }
+
+  return SSL_TLSEXT_ERR_ALERT_FATAL;
+}
+
 bool ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                         const SSL_CLIENT_HELLO *client_hello) {
   SSLImpl *const ssl = hs->ssl;
   CBS contents;
-  if (ssl->ctx->alpn_select_cb == nullptr ||
-      !ssl_client_hello_get_extension(
-          client_hello, &contents,
-          TLSEXT_TYPE_application_layer_protocol_negotiation)) {
+  const bool has_extension = ssl_client_hello_get_extension(
+      client_hello, &contents,
+      TLSEXT_TYPE_application_layer_protocol_negotiation);
+
+  auto alpn_select_cb = ssl->ctx->alpn_select_cb;
+  void *alpn_select_cb_arg = ssl->ctx->alpn_select_cb_arg;
+
+  if (alpn_select_cb == nullptr &&
+      !hs->config->alpn_client_proto_list.empty()) {
+    alpn_select_cb = default_alpn_select_cb;
+    alpn_select_cb_arg = nullptr;
+  }
+
+  if (!has_extension || alpn_select_cb == nullptr) {
     if (SSL_is_quic(ssl)) {
       // ALPN is required when QUIC is used.
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_APPLICATION_PROTOCOL);
@@ -1533,12 +1572,11 @@
 
   // `protocol_name_list` fits in `unsigned` because TLS extensions use 16-bit
   // lengths.
-  const uint8_t *selected;
-  uint8_t selected_len;
-  int ret = ssl->ctx->alpn_select_cb(
+  const uint8_t *selected = nullptr;
+  uint8_t selected_len = 0;
+  int ret = alpn_select_cb(
       ssl, &selected, &selected_len, CBS_data(&protocol_name_list),
-      static_cast<unsigned>(CBS_len(&protocol_name_list)),
-      ssl->ctx->alpn_select_cb_arg);
+      static_cast<unsigned>(CBS_len(&protocol_name_list)), alpn_select_cb_arg);
   // ALPN is required when QUIC is used.
   if (SSL_is_quic(ssl) &&
       (ret == SSL_TLSEXT_ERR_NOACK || ret == SSL_TLSEXT_ERR_ALERT_WARNING)) {
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index d9185ff..b1a8941 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <iterator>
 #include <limits>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -43,6 +44,7 @@
 #include <openssl/pem.h>
 #include <openssl/rand.h>
 #include <openssl/sha.h>
+#include <openssl/span.h>
 #include <openssl/ssl.h>
 #include <openssl/x509.h>
 
@@ -9589,6 +9591,90 @@
   check_alpn_proto({});
 }
 
+TEST(SSLTest, DeclarativeServerALPN) {
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(client_ctx);
+  ASSERT_TRUE(server_ctx);
+  bssl::UniquePtr<X509> cert = GetTestCertificate();
+  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+  ASSERT_TRUE(cert);
+  ASSERT_TRUE(key);
+  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
+  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
+  SSL_CTX_set_custom_verify(client_ctx.get(), SSL_VERIFY_PEER,
+                            AcceptAnyCertificate);
+
+  // Helper to run connection and check negotiated ALPN.
+  auto check_negotiation =
+      [&](bssl::Span<const uint8_t> client_protos,
+          bssl::Span<const uint8_t> server_protos,
+          std::optional<bssl::Span<const uint8_t>> expected_alpn) {
+        ASSERT_EQ(
+            0, SSL_CTX_set_alpn_protos(client_ctx.get(), client_protos.data(),
+                                       client_protos.size()));
+        ASSERT_EQ(
+            0, SSL_CTX_set_alpn_protos(server_ctx.get(), server_protos.data(),
+                                       server_protos.size()));
+
+        bssl::UniquePtr<SSL> client, server;
+        bool success = ConnectClientAndServer(
+            &client, &server, client_ctx.get(), server_ctx.get());
+        if (!expected_alpn.has_value()) {
+          EXPECT_FALSE(success);
+          return;
+        }
+
+        ASSERT_TRUE(success);
+        const uint8_t *alpn = nullptr;
+        unsigned alpn_len = 0;
+        SSL_get0_alpn_selected(client.get(), &alpn, &alpn_len);
+        EXPECT_EQ(Bytes(*expected_alpn), Bytes(alpn, alpn_len));
+        SSL_get0_alpn_selected(server.get(), &alpn, &alpn_len);
+        EXPECT_EQ(Bytes(*expected_alpn), Bytes(alpn, alpn_len));
+      };
+  const auto kBar = bssl::StringAsBytes("bar");
+  const auto kH2 = bssl::StringAsBytes("h2");
+
+  // 1. Basic match: Client ["foo", "bar"], Server ["bar", "baz"] ->
+  // negotiates "bar"
+  const uint8_t kClient1[] = {3, 'f', 'o', 'o', 3, 'b', 'a', 'r'};
+  const uint8_t kServer1[] = {3, 'b', 'a', 'r', 3, 'b', 'a', 'z'};
+  check_negotiation(kClient1, kServer1, kBar);
+
+  // 2. Client preference: Client ["bar", "foo"], Server ["foo", "bar"] ->
+  // negotiates "bar"
+  const uint8_t kClient2[] = {3, 'b', 'a', 'r', 3, 'f', 'o', 'o'};
+  const uint8_t kServer2[] = {3, 'f', 'o', 'o', 3, 'b', 'a', 'r'};
+  check_negotiation(kClient2, kServer2, kBar);
+
+  // 3. No overlap -> Handshake fails
+  const uint8_t kClient3[] = {3, 'f', 'o', 'o'};
+  const uint8_t kServer3[] = {3, 'b', 'a', 'r'};
+  check_negotiation(kClient3, kServer3, std::nullopt);
+
+  // 4. HTTP/2 example.
+  SSL_CTX_set_min_proto_version(client_ctx.get(), TLS1_2_VERSION);
+  SSL_CTX_set_max_proto_version(client_ctx.get(), TLS1_2_VERSION);
+  SSL_CTX_set_min_proto_version(server_ctx.get(), TLS1_2_VERSION);
+  SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_2_VERSION);
+
+  const uint8_t kClientH2[] = {2,   'h', '2', 8,   'h', 't',
+                               't', 'p', '/', '1', '.', '1'};
+  const uint8_t kServerH2[] = {2, 'h', '2'};
+
+  ASSERT_TRUE(
+      SSL_CTX_set_cipher_list(client_ctx.get(), "ECDHE-RSA-AES128-SHA256"));
+  ASSERT_TRUE(
+      SSL_CTX_set_cipher_list(server_ctx.get(), "ECDHE-RSA-AES128-SHA256"));
+
+  // Yes, this is a violation of RFC 7540, Section 9.2.
+  // However, we made a conscious decision to negotiate "h2" anyway.
+  // Users should install their own ALPN callback to enforce the cipher suite
+  // restrictions.
+  check_negotiation(kClientH2, kServerH2, kH2);
+}
+
 TEST(SSLTest, AcceptedPeerCertTypesConfig) {
   bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
   ASSERT_TRUE(ctx);