forked from mirrors/linux
		
	tls: rx: add sockopt for enabling optimistic decrypt with TLS 1.3
Since optimisitic decrypt may add extra load in case of retries require socket owner to explicitly opt-in. Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									ce61327ce9
								
							
						
					
					
						commit
						88527790c0
					
				
					 8 changed files with 122 additions and 7 deletions
				
			
		|  | @ -239,6 +239,19 @@ for the original TCP transmission and TCP retransmissions. To the receiver | ||||||
| this will look like TLS records had been tampered with and will result | this will look like TLS records had been tampered with and will result | ||||||
| in record authentication failures. | in record authentication failures. | ||||||
| 
 | 
 | ||||||
|  | TLS_RX_EXPECT_NO_PAD | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~ | ||||||
|  | 
 | ||||||
|  | TLS 1.3 only. Expect the sender to not pad records. This allows the data | ||||||
|  | to be decrypted directly into user space buffers with TLS 1.3. | ||||||
|  | 
 | ||||||
|  | This optimization is safe to enable only if the remote end is trusted, | ||||||
|  | otherwise it is an attack vector to doubling the TLS processing cost. | ||||||
|  | 
 | ||||||
|  | If the record decrypted turns out to had been padded or is not a data | ||||||
|  | record it will be decrypted again into a kernel buffer without zero copy. | ||||||
|  | Such events are counted in the ``TlsDecryptRetry`` statistic. | ||||||
|  | 
 | ||||||
| Statistics | Statistics | ||||||
| ========== | ========== | ||||||
| 
 | 
 | ||||||
|  | @ -264,3 +277,8 @@ TLS implementation exposes the following per-namespace statistics | ||||||
| 
 | 
 | ||||||
| - ``TlsDeviceRxResync`` - | - ``TlsDeviceRxResync`` - | ||||||
|   number of RX resyncs sent to NICs handling cryptography |   number of RX resyncs sent to NICs handling cryptography | ||||||
|  | 
 | ||||||
|  | - ``TlsDecryptRetry`` - | ||||||
|  |   number of RX records which had to be re-decrypted due to | ||||||
|  |   ``TLS_RX_EXPECT_NO_PAD`` mis-prediction. Note that this counter will | ||||||
|  |   also increment for non-data records. | ||||||
|  |  | ||||||
|  | @ -102,4 +102,12 @@ static inline long strncpy_from_sockptr(char *dst, sockptr_t src, size_t count) | ||||||
| 	return strncpy_from_user(dst, src.user, count); | 	return strncpy_from_user(dst, src.user, count); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline int check_zeroed_sockptr(sockptr_t src, size_t offset, | ||||||
|  | 				       size_t size) | ||||||
|  | { | ||||||
|  | 	if (!sockptr_is_kernel(src)) | ||||||
|  | 		return check_zeroed_user(src.user + offset, size); | ||||||
|  | 	return memchr_inv(src.kernel + offset, 0, size) == NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #endif /* _LINUX_SOCKPTR_H */ | #endif /* _LINUX_SOCKPTR_H */ | ||||||
|  |  | ||||||
|  | @ -149,6 +149,7 @@ struct tls_sw_context_rx { | ||||||
| 
 | 
 | ||||||
| 	struct sk_buff *recv_pkt; | 	struct sk_buff *recv_pkt; | ||||||
| 	u8 async_capable:1; | 	u8 async_capable:1; | ||||||
|  | 	u8 zc_capable:1; | ||||||
| 	atomic_t decrypt_pending; | 	atomic_t decrypt_pending; | ||||||
| 	/* protect crypto_wait with decrypt_pending*/ | 	/* protect crypto_wait with decrypt_pending*/ | ||||||
| 	spinlock_t decrypt_compl_lock; | 	spinlock_t decrypt_compl_lock; | ||||||
|  | @ -239,6 +240,7 @@ struct tls_context { | ||||||
| 	u8 tx_conf:3; | 	u8 tx_conf:3; | ||||||
| 	u8 rx_conf:3; | 	u8 rx_conf:3; | ||||||
| 	u8 zerocopy_sendfile:1; | 	u8 zerocopy_sendfile:1; | ||||||
|  | 	u8 rx_no_pad:1; | ||||||
| 
 | 
 | ||||||
| 	int (*push_pending_record)(struct sock *sk, int flags); | 	int (*push_pending_record)(struct sock *sk, int flags); | ||||||
| 	void (*sk_write_space)(struct sock *sk); | 	void (*sk_write_space)(struct sock *sk); | ||||||
|  | @ -358,6 +360,7 @@ int tls_sk_attach(struct sock *sk, int optname, char __user *optval, | ||||||
| void tls_err_abort(struct sock *sk, int err); | void tls_err_abort(struct sock *sk, int err); | ||||||
| 
 | 
 | ||||||
| int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx); | int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx); | ||||||
|  | void tls_update_rx_zc_capable(struct tls_context *tls_ctx); | ||||||
| void tls_sw_strparser_arm(struct sock *sk, struct tls_context *ctx); | void tls_sw_strparser_arm(struct sock *sk, struct tls_context *ctx); | ||||||
| void tls_sw_strparser_done(struct tls_context *tls_ctx); | void tls_sw_strparser_done(struct tls_context *tls_ctx); | ||||||
| int tls_sw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size); | int tls_sw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size); | ||||||
|  |  | ||||||
|  | @ -344,6 +344,7 @@ enum | ||||||
| 	LINUX_MIB_TLSRXDEVICE,			/* TlsRxDevice */ | 	LINUX_MIB_TLSRXDEVICE,			/* TlsRxDevice */ | ||||||
| 	LINUX_MIB_TLSDECRYPTERROR,		/* TlsDecryptError */ | 	LINUX_MIB_TLSDECRYPTERROR,		/* TlsDecryptError */ | ||||||
| 	LINUX_MIB_TLSRXDEVICERESYNC,		/* TlsRxDeviceResync */ | 	LINUX_MIB_TLSRXDEVICERESYNC,		/* TlsRxDeviceResync */ | ||||||
|  | 	LINUX_MIN_TLSDECRYPTRETRY,		/* TlsDecryptRetry */ | ||||||
| 	__LINUX_MIB_TLSMAX | 	__LINUX_MIB_TLSMAX | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ | ||||||
| #define TLS_TX			1	/* Set transmit parameters */ | #define TLS_TX			1	/* Set transmit parameters */ | ||||||
| #define TLS_RX			2	/* Set receive parameters */ | #define TLS_RX			2	/* Set receive parameters */ | ||||||
| #define TLS_TX_ZEROCOPY_RO	3	/* TX zerocopy (only sendfile now) */ | #define TLS_TX_ZEROCOPY_RO	3	/* TX zerocopy (only sendfile now) */ | ||||||
|  | #define TLS_RX_EXPECT_NO_PAD	4	/* Attempt opportunistic zero-copy */ | ||||||
| 
 | 
 | ||||||
| /* Supported versions */ | /* Supported versions */ | ||||||
| #define TLS_VERSION_MINOR(ver)	((ver) & 0xFF) | #define TLS_VERSION_MINOR(ver)	((ver) & 0xFF) | ||||||
|  | @ -162,6 +163,7 @@ enum { | ||||||
| 	TLS_INFO_TXCONF, | 	TLS_INFO_TXCONF, | ||||||
| 	TLS_INFO_RXCONF, | 	TLS_INFO_RXCONF, | ||||||
| 	TLS_INFO_ZC_RO_TX, | 	TLS_INFO_ZC_RO_TX, | ||||||
|  | 	TLS_INFO_RX_NO_PAD, | ||||||
| 	__TLS_INFO_MAX, | 	__TLS_INFO_MAX, | ||||||
| }; | }; | ||||||
| #define TLS_INFO_MAX (__TLS_INFO_MAX - 1) | #define TLS_INFO_MAX (__TLS_INFO_MAX - 1) | ||||||
|  |  | ||||||
|  | @ -533,6 +533,37 @@ static int do_tls_getsockopt_tx_zc(struct sock *sk, char __user *optval, | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int do_tls_getsockopt_no_pad(struct sock *sk, char __user *optval, | ||||||
|  | 				    int __user *optlen) | ||||||
|  | { | ||||||
|  | 	struct tls_context *ctx = tls_get_ctx(sk); | ||||||
|  | 	unsigned int value; | ||||||
|  | 	int err, len; | ||||||
|  | 
 | ||||||
|  | 	if (ctx->prot_info.version != TLS_1_3_VERSION) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	if (get_user(len, optlen)) | ||||||
|  | 		return -EFAULT; | ||||||
|  | 	if (len < sizeof(value)) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	lock_sock(sk); | ||||||
|  | 	err = -EINVAL; | ||||||
|  | 	if (ctx->rx_conf == TLS_SW || ctx->rx_conf == TLS_HW) | ||||||
|  | 		value = ctx->rx_no_pad; | ||||||
|  | 	release_sock(sk); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	if (put_user(sizeof(value), optlen)) | ||||||
|  | 		return -EFAULT; | ||||||
|  | 	if (copy_to_user(optval, &value, sizeof(value))) | ||||||
|  | 		return -EFAULT; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int do_tls_getsockopt(struct sock *sk, int optname, | static int do_tls_getsockopt(struct sock *sk, int optname, | ||||||
| 			     char __user *optval, int __user *optlen) | 			     char __user *optval, int __user *optlen) | ||||||
| { | { | ||||||
|  | @ -547,6 +578,9 @@ static int do_tls_getsockopt(struct sock *sk, int optname, | ||||||
| 	case TLS_TX_ZEROCOPY_RO: | 	case TLS_TX_ZEROCOPY_RO: | ||||||
| 		rc = do_tls_getsockopt_tx_zc(sk, optval, optlen); | 		rc = do_tls_getsockopt_tx_zc(sk, optval, optlen); | ||||||
| 		break; | 		break; | ||||||
|  | 	case TLS_RX_EXPECT_NO_PAD: | ||||||
|  | 		rc = do_tls_getsockopt_no_pad(sk, optval, optlen); | ||||||
|  | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		rc = -ENOPROTOOPT; | 		rc = -ENOPROTOOPT; | ||||||
| 		break; | 		break; | ||||||
|  | @ -718,6 +752,38 @@ static int do_tls_setsockopt_tx_zc(struct sock *sk, sockptr_t optval, | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int do_tls_setsockopt_no_pad(struct sock *sk, sockptr_t optval, | ||||||
|  | 				    unsigned int optlen) | ||||||
|  | { | ||||||
|  | 	struct tls_context *ctx = tls_get_ctx(sk); | ||||||
|  | 	u32 val; | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	if (ctx->prot_info.version != TLS_1_3_VERSION || | ||||||
|  | 	    sockptr_is_null(optval) || optlen < sizeof(val)) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	rc = copy_from_sockptr(&val, optval, sizeof(val)); | ||||||
|  | 	if (rc) | ||||||
|  | 		return -EFAULT; | ||||||
|  | 	if (val > 1) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	rc = check_zeroed_sockptr(optval, sizeof(val), optlen - sizeof(val)); | ||||||
|  | 	if (rc < 1) | ||||||
|  | 		return rc == 0 ? -EINVAL : rc; | ||||||
|  | 
 | ||||||
|  | 	lock_sock(sk); | ||||||
|  | 	rc = -EINVAL; | ||||||
|  | 	if (ctx->rx_conf == TLS_SW || ctx->rx_conf == TLS_HW) { | ||||||
|  | 		ctx->rx_no_pad = val; | ||||||
|  | 		tls_update_rx_zc_capable(ctx); | ||||||
|  | 		rc = 0; | ||||||
|  | 	} | ||||||
|  | 	release_sock(sk); | ||||||
|  | 
 | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int do_tls_setsockopt(struct sock *sk, int optname, sockptr_t optval, | static int do_tls_setsockopt(struct sock *sk, int optname, sockptr_t optval, | ||||||
| 			     unsigned int optlen) | 			     unsigned int optlen) | ||||||
| { | { | ||||||
|  | @ -736,6 +802,9 @@ static int do_tls_setsockopt(struct sock *sk, int optname, sockptr_t optval, | ||||||
| 		rc = do_tls_setsockopt_tx_zc(sk, optval, optlen); | 		rc = do_tls_setsockopt_tx_zc(sk, optval, optlen); | ||||||
| 		release_sock(sk); | 		release_sock(sk); | ||||||
| 		break; | 		break; | ||||||
|  | 	case TLS_RX_EXPECT_NO_PAD: | ||||||
|  | 		rc = do_tls_setsockopt_no_pad(sk, optval, optlen); | ||||||
|  | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		rc = -ENOPROTOOPT; | 		rc = -ENOPROTOOPT; | ||||||
| 		break; | 		break; | ||||||
|  | @ -976,6 +1045,11 @@ static int tls_get_info(const struct sock *sk, struct sk_buff *skb) | ||||||
| 		if (err) | 		if (err) | ||||||
| 			goto nla_failure; | 			goto nla_failure; | ||||||
| 	} | 	} | ||||||
|  | 	if (ctx->rx_no_pad) { | ||||||
|  | 		err = nla_put_flag(skb, TLS_INFO_RX_NO_PAD); | ||||||
|  | 		if (err) | ||||||
|  | 			goto nla_failure; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	rcu_read_unlock(); | 	rcu_read_unlock(); | ||||||
| 	nla_nest_end(skb, start); | 	nla_nest_end(skb, start); | ||||||
|  | @ -997,6 +1071,7 @@ static size_t tls_get_info_size(const struct sock *sk) | ||||||
| 		nla_total_size(sizeof(u16)) +	/* TLS_INFO_RXCONF */ | 		nla_total_size(sizeof(u16)) +	/* TLS_INFO_RXCONF */ | ||||||
| 		nla_total_size(sizeof(u16)) +	/* TLS_INFO_TXCONF */ | 		nla_total_size(sizeof(u16)) +	/* TLS_INFO_TXCONF */ | ||||||
| 		nla_total_size(0) +		/* TLS_INFO_ZC_RO_TX */ | 		nla_total_size(0) +		/* TLS_INFO_ZC_RO_TX */ | ||||||
|  | 		nla_total_size(0) +		/* TLS_INFO_RX_NO_PAD */ | ||||||
| 		0; | 		0; | ||||||
| 
 | 
 | ||||||
| 	return size; | 	return size; | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ static const struct snmp_mib tls_mib_list[] = { | ||||||
| 	SNMP_MIB_ITEM("TlsRxDevice", LINUX_MIB_TLSRXDEVICE), | 	SNMP_MIB_ITEM("TlsRxDevice", LINUX_MIB_TLSRXDEVICE), | ||||||
| 	SNMP_MIB_ITEM("TlsDecryptError", LINUX_MIB_TLSDECRYPTERROR), | 	SNMP_MIB_ITEM("TlsDecryptError", LINUX_MIB_TLSDECRYPTERROR), | ||||||
| 	SNMP_MIB_ITEM("TlsRxDeviceResync", LINUX_MIB_TLSRXDEVICERESYNC), | 	SNMP_MIB_ITEM("TlsRxDeviceResync", LINUX_MIB_TLSRXDEVICERESYNC), | ||||||
|  | 	SNMP_MIB_ITEM("TlsDecryptRetry", LINUX_MIN_TLSDECRYPTRETRY), | ||||||
| 	SNMP_MIB_SENTINEL | 	SNMP_MIB_SENTINEL | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1601,6 +1601,7 @@ static int decrypt_skb_update(struct sock *sk, struct sk_buff *skb, | ||||||
| 	if (unlikely(darg->zc && prot->version == TLS_1_3_VERSION && | 	if (unlikely(darg->zc && prot->version == TLS_1_3_VERSION && | ||||||
| 		     darg->tail != TLS_RECORD_TYPE_DATA)) { | 		     darg->tail != TLS_RECORD_TYPE_DATA)) { | ||||||
| 		darg->zc = false; | 		darg->zc = false; | ||||||
|  | 		TLS_INC_STATS(sock_net(sk), LINUX_MIN_TLSDECRYPTRETRY); | ||||||
| 		return decrypt_skb_update(sk, skb, dest, darg); | 		return decrypt_skb_update(sk, skb, dest, darg); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -1787,7 +1788,7 @@ int tls_sw_recvmsg(struct sock *sk, | ||||||
| 	timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); | 	timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); | ||||||
| 
 | 
 | ||||||
| 	zc_capable = !bpf_strp_enabled && !is_kvec && !is_peek && | 	zc_capable = !bpf_strp_enabled && !is_kvec && !is_peek && | ||||||
| 		     prot->version != TLS_1_3_VERSION; | 		ctx->zc_capable; | ||||||
| 	decrypted = 0; | 	decrypted = 0; | ||||||
| 	while (len && (decrypted + copied < target || ctx->recv_pkt)) { | 	while (len && (decrypted + copied < target || ctx->recv_pkt)) { | ||||||
| 		struct tls_decrypt_arg darg = {}; | 		struct tls_decrypt_arg darg = {}; | ||||||
|  | @ -2269,6 +2270,14 @@ void tls_sw_strparser_arm(struct sock *sk, struct tls_context *tls_ctx) | ||||||
| 	strp_check_rcv(&rx_ctx->strp); | 	strp_check_rcv(&rx_ctx->strp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void tls_update_rx_zc_capable(struct tls_context *tls_ctx) | ||||||
|  | { | ||||||
|  | 	struct tls_sw_context_rx *rx_ctx = tls_sw_ctx_rx(tls_ctx); | ||||||
|  | 
 | ||||||
|  | 	rx_ctx->zc_capable = tls_ctx->rx_no_pad || | ||||||
|  | 		tls_ctx->prot_info.version != TLS_1_3_VERSION; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx) | int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx) | ||||||
| { | { | ||||||
| 	struct tls_context *tls_ctx = tls_get_ctx(sk); | 	struct tls_context *tls_ctx = tls_get_ctx(sk); | ||||||
|  | @ -2504,12 +2513,10 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx) | ||||||
| 	if (sw_ctx_rx) { | 	if (sw_ctx_rx) { | ||||||
| 		tfm = crypto_aead_tfm(sw_ctx_rx->aead_recv); | 		tfm = crypto_aead_tfm(sw_ctx_rx->aead_recv); | ||||||
| 
 | 
 | ||||||
| 		if (crypto_info->version == TLS_1_3_VERSION) | 		tls_update_rx_zc_capable(ctx); | ||||||
| 			sw_ctx_rx->async_capable = 0; |  | ||||||
| 		else |  | ||||||
| 		sw_ctx_rx->async_capable = | 		sw_ctx_rx->async_capable = | ||||||
| 				!!(tfm->__crt_alg->cra_flags & | 			crypto_info->version != TLS_1_3_VERSION && | ||||||
| 				   CRYPTO_ALG_ASYNC); | 			!!(tfm->__crt_alg->cra_flags & CRYPTO_ALG_ASYNC); | ||||||
| 
 | 
 | ||||||
| 		/* Set up strparser */ | 		/* Set up strparser */ | ||||||
| 		memset(&cb, 0, sizeof(cb)); | 		memset(&cb, 0, sizeof(cb)); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jakub Kicinski
						Jakub Kicinski