git: 9a673b715897 - main - ktls: Add software support for AES-CBC decryption for TLS 1.1+.

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Tue, 15 Nov 2022 20:04:17 UTC
The branch main has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=9a673b7158973d86558a5d381e4784a561576b98

commit 9a673b7158973d86558a5d381e4784a561576b98
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2022-11-15 20:02:03 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2022-11-15 20:02:03 +0000

    ktls: Add software support for AES-CBC decryption for TLS 1.1+.
    
    This is mainly intended to provide a fallback for TOE TLS which may
    need to use software decryption for an initial record at the start
    of a connection.
    
    Reviewed by:    markj
    Sponsored by:   Chelsio Communications
    Differential Revision:  https://reviews.freebsd.org/D37370
---
 sys/kern/uipc_ktls.c      |   6 ++-
 sys/opencrypto/ktls_ocf.c | 133 ++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 134 insertions(+), 5 deletions(-)

diff --git a/sys/kern/uipc_ktls.c b/sys/kern/uipc_ktls.c
index 3df1f2843c32..ae9201976988 100644
--- a/sys/kern/uipc_ktls.c
+++ b/sys/kern/uipc_ktls.c
@@ -149,7 +149,7 @@ SYSCTL_BOOL(_kern_ipc_tls, OID_AUTO, enable, CTLFLAG_RWTUN,
 static bool ktls_cbc_enable = true;
 SYSCTL_BOOL(_kern_ipc_tls, OID_AUTO, cbc_enable, CTLFLAG_RWTUN,
     &ktls_cbc_enable, 1,
-    "Enable Support of AES-CBC crypto for kernel TLS");
+    "Enable support of AES-CBC crypto for kernel TLS");
 
 static bool ktls_sw_buffer_cache = true;
 SYSCTL_BOOL(_kern_ipc_tls, OID_AUTO, sw_buffer_cache, CTLFLAG_RDTUN,
@@ -2444,8 +2444,10 @@ ktls_decrypt(struct socket *so)
 			sb->sb_ccc -= tls_len;
 			sb->sb_tlsdcc = 0;
 
+			if (error != EMSGSIZE)
+				error = EBADMSG;
 			CURVNET_SET(so->so_vnet);
-			so->so_error = EBADMSG;
+			so->so_error = error;
 			sorwakeup_locked(so);
 			CURVNET_RESTORE();
 
diff --git a/sys/opencrypto/ktls_ocf.c b/sys/opencrypto/ktls_ocf.c
index 6347ca459646..938133fa451a 100644
--- a/sys/opencrypto/ktls_ocf.c
+++ b/sys/opencrypto/ktls_ocf.c
@@ -101,6 +101,11 @@ SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls10_cbc_encrypts,
     CTLFLAG_RD, &ocf_tls10_cbc_encrypts,
     "Total number of OCF TLS 1.0 CBC encryption operations");
 
+static COUNTER_U64_DEFINE_EARLY(ocf_tls11_cbc_decrypts);
+SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls11_cbc_decrypts,
+    CTLFLAG_RD, &ocf_tls11_cbc_decrypts,
+    "Total number of OCF TLS 1.1/1.2 CBC decryption operations");
+
 static COUNTER_U64_DEFINE_EARLY(ocf_tls11_cbc_encrypts);
 SYSCTL_COUNTER_U64(_kern_ipc_tls_stats_ocf, OID_AUTO, tls11_cbc_encrypts,
     CTLFLAG_RD, &ocf_tls11_cbc_encrypts,
@@ -416,8 +421,129 @@ ktls_ocf_tls_cbc_encrypt(struct ktls_ocf_encrypt_state *state,
 	return (error);
 }
 
+static int
+check_padding(void *arg, void *data, u_int len)
+{
+	uint8_t pad = *(uint8_t *)arg;
+	const char *cp = data;
+
+	while (len > 0) {
+		if (*cp != pad)
+			return (EBADMSG);
+		cp++;
+		len--;
+	}
+	return (0);
+}
+
+static int
+ktls_ocf_tls_cbc_decrypt(struct ktls_session *tls,
+    const struct tls_record_layer *hdr, struct mbuf *m, uint64_t seqno,
+    int *trailer_len)
+{
+	struct tls_mac_data ad;
+	struct cryptop crp;
+	struct uio uio;
+	struct ktls_ocf_session *os;
+	struct iovec *iov;
+	struct mbuf *n;
+	u_int iovcnt;
+	int i, error, skip;
+	uint16_t tls_len, tls_comp_len;
+	uint8_t pad;
+
+	os = tls->ocf_session;
+
+	/*
+	 * Ensure record is a multiple of the cipher block size and
+	 * contains at least an explicit IV, MAC, and at least one
+	 * padding byte.
+	 */
+	tls_len = ntohs(hdr->tls_length);
+	if (tls_len % AES_BLOCK_LEN != 0 ||
+	    tls_len < AES_BLOCK_LEN + roundup2(os->mac_len + 1, AES_BLOCK_LEN))
+		return (EMSGSIZE);
+
+	/* First, decrypt the record. */
+	crypto_initreq(&crp, os->sid);
+	crp.crp_iv_start = sizeof(*hdr);
+	crp.crp_payload_start = tls->params.tls_hlen;
+	crp.crp_payload_length = tls_len - AES_BLOCK_LEN;
+	crypto_use_mbuf(&crp, m);
+	crp.crp_op = CRYPTO_OP_DECRYPT;
+	crp.crp_flags = CRYPTO_F_CBIMM;
+
+	counter_u64_add(ocf_tls11_cbc_decrypts, 1);
+
+	error = ktls_ocf_dispatch(os, &crp);
+	crypto_destroyreq(&crp);
+	if (error)
+		return (error);
+
+	/* Verify the padding. */
+	m_copydata(m, sizeof(*hdr) + tls_len - 1, 1, &pad);
+	*trailer_len = os->mac_len + pad + 1;
+	if (AES_BLOCK_LEN + *trailer_len > tls_len)
+		return (EBADMSG);
+	error = m_apply(m, sizeof(*hdr) + tls_len - (pad + 1), pad + 1,
+	    check_padding, &pad);
+	if (error)
+		return (error);
+
+	/* Verify the MAC. */
+	tls_comp_len = tls_len - (AES_BLOCK_LEN + *trailer_len);
+	memset(&uio, 0, sizeof(uio));
+
+	/*
+	 * Allocate and populate the iov.  Have to skip over the TLS
+	 * header in 'm' as it is not part of the MAC input.
+	 */
+	iovcnt = 1;
+	for (n = m; n != NULL; n = n->m_next)
+		iovcnt++;
+	iov = malloc(iovcnt * sizeof(*iov), M_KTLS_OCF, M_WAITOK);
+	iov[0].iov_base = &ad;
+	iov[0].iov_len = sizeof(ad);
+	skip = sizeof(*hdr) + AES_BLOCK_LEN;
+	for (i = 1, n = m; n != NULL; i++, n = n->m_next) {
+		if (n->m_len < skip) {
+			skip -= n->m_len;
+			continue;
+		}
+		iov[i].iov_base = mtod(n, char *) + skip;
+		iov[i].iov_len = n->m_len - skip;
+		skip = 0;
+	}
+	uio.uio_iov = iov;
+	uio.uio_iovcnt = i;
+	uio.uio_segflg = UIO_SYSSPACE;
+	uio.uio_td = curthread;
+	uio.uio_resid = sizeof(ad) + tls_len - AES_BLOCK_LEN;
+
+	/* Initialize the AAD. */
+	ad.seq = htobe64(seqno);
+	ad.type = hdr->tls_type;
+	ad.tls_vmajor = hdr->tls_vmajor;
+	ad.tls_vminor = hdr->tls_vminor;
+	ad.tls_length = htons(tls_comp_len);
+
+	crypto_initreq(&crp, os->mac_sid);
+	crp.crp_payload_start = 0;
+	crp.crp_payload_length = sizeof(ad) + tls_comp_len;
+	crp.crp_digest_start = crp.crp_payload_length;
+	crp.crp_op = CRYPTO_OP_VERIFY_DIGEST;
+	crp.crp_flags = CRYPTO_F_CBIMM;
+	crypto_use_uio(&crp, &uio);
+	error = ktls_ocf_dispatch(os, &crp);
+
+	crypto_destroyreq(&crp);
+	free(iov, M_KTLS_OCF);
+	return (error);
+}
+
 static const struct ktls_ocf_sw ktls_ocf_tls_cbc_sw = {
-	.encrypt = ktls_ocf_tls_cbc_encrypt
+	.encrypt = ktls_ocf_tls_cbc_encrypt,
+	.decrypt = ktls_ocf_tls_cbc_decrypt
 };
 
 static int
@@ -912,8 +1038,9 @@ ktls_ocf_try(struct socket *so, struct ktls_session *tls, int direction)
 		    tls->params.tls_vminor > TLS_MINOR_VER_TWO)
 			return (EPROTONOSUPPORT);
 
-		/* AES-CBC is not supported for receive. */
-		if (direction == KTLS_RX)
+		/* AES-CBC is not supported for receive for TLS 1.0. */
+		if (direction == KTLS_RX &&
+		    tls->params.tls_vminor == TLS_MINOR_VER_ZERO)
 			return (EPROTONOSUPPORT);
 
 		csp.csp_flags |= CSP_F_SEPARATE_OUTPUT;