]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Low level code to support ALPN in DoT
authorArtem Boldariev <artem@boldariev.com>
Wed, 25 Aug 2021 12:31:20 +0000 (15:31 +0300)
committerArtem Boldariev <artem@boldariev.com>
Tue, 5 Oct 2021 08:23:47 +0000 (11:23 +0300)
This commit adds low-level code necessary to support ALPN in DoT as
XoT requires "dot" ALPN token to be negotiated on a connection for
zone transfers.

lib/isc/include/isc/tls.h
lib/isc/netmgr/http.c
lib/isc/netmgr/netmgr-int.h
lib/isc/netmgr/tlsdns.c
lib/isc/tests/netmgr_test.c
lib/isc/tls.c

index addc27ba5ca3c26ff58f9e2ce40994e9804248d5..fcc484df4741119cb271ffa94e09d36571678b73 100644 (file)
@@ -152,6 +152,7 @@ isc_tls_free(isc_tls_t **tlsp);
  *\li  'tlsp' != NULL and '*tlsp' != NULL.
  */
 
+#if HAVE_LIBNGHTTP2
 void
 isc_tlsctx_enable_http2client_alpn(isc_tlsctx_t *ctx);
 void
@@ -163,7 +164,23 @@ isc_tlsctx_enable_http2server_alpn(isc_tlsctx_t *ctx);
  * Requires:
  *\li  'ctx' is not NULL.
  */
+#endif /* HAVE_LIBNGHTTP2 */
 
 void
-isc_tls_get_http2_alpn(isc_tls_t *tls, const unsigned char **alpn,
-                      unsigned int *alpnlen);
+isc_tls_get_selected_alpn(isc_tls_t *tls, const unsigned char **alpn,
+                         unsigned int *alpnlen);
+
+#define ISC_TLS_DOT_PROTO_ALPN_ID     "dot"
+#define ISC_TLS_DOT_PROTO_ALPN_ID_LEN 3
+
+void
+isc_tlsctx_enable_dot_client_alpn(isc_tlsctx_t *ctx);
+void
+isc_tlsctx_enable_dot_server_alpn(isc_tlsctx_t *ctx);
+/*%<
+ *
+ * Enable DoT Application Layer Protocol Negotation for 'ctx'.
+ *
+ * Requires:
+ *\li  'ctx' is not NULL.
+ */
index faba79a803b59330219914183315ed97b2f92985..be3f90821c19633ab5aca72ce690e805363deeb3 100644 (file)
@@ -1362,8 +1362,8 @@ transport_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
 
                INSIST(transp_sock->type == isc_nm_tlssocket);
 
-               isc_tls_get_http2_alpn(transp_sock->tlsstream.tls, &alpn,
-                                      &alpnlen);
+               isc_tls_get_selected_alpn(transp_sock->tlsstream.tls, &alpn,
+                                         &alpnlen);
                if (alpn == NULL || alpnlen != NGHTTP2_PROTO_VERSION_ID_LEN ||
                    memcmp(NGHTTP2_PROTO_VERSION_ID, alpn,
                           NGHTTP2_PROTO_VERSION_ID_LEN) != 0)
index e5a02343b848d38aec5da239aecece80d4ca3f00..963476b64c8596d6d5819866fdbd688374ce6367 100644 (file)
@@ -881,6 +881,7 @@ struct isc_nmsocket {
                isc_result_t pending_error;
                /* List of active send requests. */
                isc__nm_uvreq_t *pending_req;
+               bool alpn_negotiated;
        } tls;
 
 #if HAVE_LIBNGHTTP2
index 0b3b43703a9e63750762ff4ffd392eb75eb04e3f..165d8be2e9d7a56aee7d8698323f8a712e49385f 100644 (file)
@@ -496,6 +496,8 @@ isc_nm_listentlsdns(isc_nm_t *mgr, isc_sockaddr_t *iface,
        sock->tid = 0;
        sock->fd = -1;
 
+       isc_tlsctx_enable_dot_server_alpn(sslctx);
+
 #if !HAVE_SO_REUSEPORT_LB
        fd = isc__nm_tlsdns_lb_socket(iface->type.sa.sa_family);
 #endif
@@ -1071,6 +1073,17 @@ tls_cycle_input(isc_nmsocket_t *sock) {
        if (sock->tls.state == TLS_STATE_HANDSHAKE &&
            SSL_is_init_finished(sock->tls.tls))
        {
+               const unsigned char *alpn = NULL;
+               unsigned int alpnlen = 0;
+
+               isc_tls_get_selected_alpn(sock->tls.tls, &alpn, &alpnlen);
+               if (alpn != NULL && alpnlen == ISC_TLS_DOT_PROTO_ALPN_ID_LEN &&
+                   memcmp(ISC_TLS_DOT_PROTO_ALPN_ID, alpn,
+                          ISC_TLS_DOT_PROTO_ALPN_ID_LEN) == 0)
+               {
+                       sock->tls.alpn_negotiated = true;
+               }
+
                sock->tls.state = TLS_STATE_IO;
 
                if (SSL_is_server(sock->tls.tls)) {
index 7c789d7b3bed005f54b6007757e9376c52828e20..469ee01ef7965f8fd597ce720672d6a91134d04c 100644 (file)
@@ -226,6 +226,8 @@ _setup(void **state __attribute__((unused))) {
                return (-1);
        }
 
+       isc_tlsctx_enable_dot_client_alpn(tcp_connect_tlsctx);
+
        return (0);
 }
 
index 94d42b7e17491d2f293c42af5b538761cc86b444..9ea8f091644cd491926781d573c1e29f22c5f5d4 100644 (file)
@@ -688,10 +688,11 @@ isc_tlsctx_enable_http2server_alpn(isc_tlsctx_t *tls) {
        SSL_CTX_set_alpn_select_cb(tls, alpn_select_proto_cb, NULL);
 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
 }
+#endif /* HAVE_LIBNGHTTP2 */
 
 void
-isc_tls_get_http2_alpn(isc_tls_t *tls, const unsigned char **alpn,
-                      unsigned int *alpnlen) {
+isc_tls_get_selected_alpn(isc_tls_t *tls, const unsigned char **alpn,
+                         unsigned int *alpnlen) {
        REQUIRE(tls != NULL);
        REQUIRE(alpn != NULL);
        REQUIRE(alpnlen != NULL);
@@ -705,4 +706,68 @@ isc_tls_get_http2_alpn(isc_tls_t *tls, const unsigned char **alpn,
        }
 #endif
 }
-#endif /* HAVE_LIBNGHTTP2 */
+
+static bool
+protoneg_check_protocol(const uint8_t **pout, uint8_t *pout_len,
+                       const uint8_t *in, size_t in_len, const uint8_t *key,
+                       size_t key_len) {
+       for (size_t i = 0; i + key_len <= in_len; i += (size_t)(in[i] + 1)) {
+               if (memcmp(&in[i], key, key_len) == 0) {
+                       *pout = (const uint8_t *)(&in[i + 1]);
+                       *pout_len = in[i];
+                       return (true);
+               }
+       }
+       return (false);
+}
+
+/* dot prepended by its length (3 bytes) */
+#define DOT_PROTO_ALPN    "\x3" ISC_TLS_DOT_PROTO_ALPN_ID
+#define DOT_PROTO_ALPN_LEN (sizeof(DOT_PROTO_ALPN) - 1)
+
+static bool
+dot_select_next_protocol(const uint8_t **pout, uint8_t *pout_len,
+                        const uint8_t *in, size_t in_len) {
+       return (protoneg_check_protocol(pout, pout_len, in, in_len,
+                                       (const uint8_t *)DOT_PROTO_ALPN,
+                                       DOT_PROTO_ALPN_LEN));
+}
+
+void
+isc_tlsctx_enable_dot_client_alpn(isc_tlsctx_t *ctx) {
+       REQUIRE(ctx != NULL);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+       SSL_CTX_set_alpn_protos(ctx, (const uint8_t *)DOT_PROTO_ALPN,
+                               DOT_PROTO_ALPN_LEN);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+static int
+dot_alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
+                        unsigned char *outlen, const unsigned char *in,
+                        unsigned int inlen, void *arg) {
+       bool ret;
+
+       UNUSED(ssl);
+       UNUSED(arg);
+
+       ret = dot_select_next_protocol(out, outlen, in, inlen);
+
+       if (!ret) {
+               return (SSL_TLSEXT_ERR_NOACK);
+       }
+
+       return (SSL_TLSEXT_ERR_OK);
+}
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+
+void
+isc_tlsctx_enable_dot_server_alpn(isc_tlsctx_t *tls) {
+       REQUIRE(tls != NULL);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+       SSL_CTX_set_alpn_select_cb(tls, dot_alpn_select_proto_cb, NULL);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+}