Reject if the ALPN callback returned an empty protocol.

If the callback returns an empty ALPN, we forget we negotiated ALPN at
all (bssl::Array does not distinguish null and empty). Empty ALPN
protocols are forbidden anyway, so reject these ahead of time.

Change-Id: I42f1fc4c843bc865e23fb2a2e5d57424b569ee99
Reviewed-on: https://boringssl-review.googlesource.com/28546
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/t1_lib.cc b/ssl/t1_lib.cc
index 7fae0da..40b7ff0 100644
--- a/ssl/t1_lib.cc
+++ b/ssl/t1_lib.cc
@@ -1532,6 +1532,11 @@
           ssl, &selected, &selected_len, CBS_data(&protocol_name_list),
           CBS_len(&protocol_name_list),
           ssl->ctx->alpn_select_cb_arg) == SSL_TLSEXT_ERR_OK) {
+    if (selected_len == 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL);
+      *out_alert = SSL_AD_INTERNAL_ERROR;
+      return false;
+    }
     if (!ssl->s3->alpn_selected.CopyFrom(
             MakeConstSpan(selected, selected_len))) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 1d15771..87da27a 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -766,6 +766,7 @@
     exit(1);
   }
 
+  assert(config->select_alpn.empty() || !config->select_empty_alpn);
   *out = (const uint8_t*)config->select_alpn.data();
   *outlen = config->select_alpn.size();
   return SSL_TLSEXT_ERR_OK;
@@ -1228,7 +1229,8 @@
                                      NULL);
   }
 
-  if (!config->select_alpn.empty() || config->decline_alpn) {
+  if (!config->select_alpn.empty() || config->decline_alpn ||
+      config->select_empty_alpn) {
     SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL);
   }
 
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 3d8a6b0..ba52706 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -6193,6 +6193,24 @@
 			expectNoNextProto: true,
 			resumeSession:     true,
 		})
+		// Test that the server implementation catches itself if the
+		// callback tries to return an invalid empty ALPN protocol.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "ALPNServer-SelectEmpty-" + ver.name,
+			config: Config{
+				MaxVersion: ver.version,
+				NextProtos: []string{"foo", "bar", "baz"},
+			},
+			flags: []string{
+				"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
+				"-select-empty-alpn",
+			},
+			tls13Variant:       ver.tls13Variant,
+			shouldFail:         true,
+			expectedLocalError: "remote error: internal error",
+			expectedError:      ":INVALID_ALPN_PROTOCOL:",
+		})
 
 		// Test ALPN in async mode as well to ensure that extensions callbacks are only
 		// called once.
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index c5ce610..fdcb0a9 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -64,6 +64,7 @@
   { "-shim-writes-first", &TestConfig::shim_writes_first },
   { "-expect-session-miss", &TestConfig::expect_session_miss },
   { "-decline-alpn", &TestConfig::decline_alpn },
+  { "-select-empty-alpn", &TestConfig::select_empty_alpn },
   { "-expect-extended-master-secret",
     &TestConfig::expect_extended_master_secret },
   { "-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling },
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 57c4ced..6488a26 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -59,6 +59,7 @@
   std::string expected_advertised_alpn;
   std::string select_alpn;
   bool decline_alpn = false;
+  bool select_empty_alpn = false;
   std::string quic_transport_params;
   std::string expected_quic_transport_params;
   bool expect_session_miss = false;