sunrpc: fix client side handling of tls alerts
A security exploit was discovered in NFS over TLS in tls_alert_recv
due to its assumption that there is valid data in the msghdr's
iterator's kvec.
Instead, this patch proposes the rework how control messages are
setup and used by sock_recvmsg().
If no control message structure is setup, kTLS layer will read and
process TLS data record types. As soon as it encounters a TLS control
message, it would return an error. At that point, NFS can setup a kvec
backed control buffer and read in the control message such as a TLS
alert. Scott found that a msg iterator can advance the kvec pointer
as a part of the copy process thus we need to revert the iterator
before calling into the tls_alert_recv.
Fixes: dea034b963 ("SUNRPC: Capture CMSG metadata on client-side receive")
Suggested-by: Trond Myklebust <trondmy@hammerspace.com>
Suggested-by: Scott Mayhew <smayhew@redhat.com>
Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
Link: https://lore.kernel.org/r/20250731180058.4669-3-okorniev@redhat.com
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
pull/1340/head
parent
533210f239
commit
cc5d59081f
|
|
@ -358,7 +358,7 @@ xs_alloc_sparse_pages(struct xdr_buf *buf, size_t want, gfp_t gfp)
|
||||||
|
|
||||||
static int
|
static int
|
||||||
xs_sock_process_cmsg(struct socket *sock, struct msghdr *msg,
|
xs_sock_process_cmsg(struct socket *sock, struct msghdr *msg,
|
||||||
struct cmsghdr *cmsg, int ret)
|
unsigned int *msg_flags, struct cmsghdr *cmsg, int ret)
|
||||||
{
|
{
|
||||||
u8 content_type = tls_get_record_type(sock->sk, cmsg);
|
u8 content_type = tls_get_record_type(sock->sk, cmsg);
|
||||||
u8 level, description;
|
u8 level, description;
|
||||||
|
|
@ -371,7 +371,7 @@ xs_sock_process_cmsg(struct socket *sock, struct msghdr *msg,
|
||||||
* record, even though there might be more frames
|
* record, even though there might be more frames
|
||||||
* waiting to be decrypted.
|
* waiting to be decrypted.
|
||||||
*/
|
*/
|
||||||
msg->msg_flags &= ~MSG_EOR;
|
*msg_flags &= ~MSG_EOR;
|
||||||
break;
|
break;
|
||||||
case TLS_RECORD_TYPE_ALERT:
|
case TLS_RECORD_TYPE_ALERT:
|
||||||
tls_alert_recv(sock->sk, msg, &level, &description);
|
tls_alert_recv(sock->sk, msg, &level, &description);
|
||||||
|
|
@ -386,19 +386,33 @@ xs_sock_process_cmsg(struct socket *sock, struct msghdr *msg,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
xs_sock_recv_cmsg(struct socket *sock, struct msghdr *msg, int flags)
|
xs_sock_recv_cmsg(struct socket *sock, unsigned int *msg_flags, int flags)
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
struct cmsghdr cmsg;
|
struct cmsghdr cmsg;
|
||||||
u8 buf[CMSG_SPACE(sizeof(u8))];
|
u8 buf[CMSG_SPACE(sizeof(u8))];
|
||||||
} u;
|
} u;
|
||||||
|
u8 alert[2];
|
||||||
|
struct kvec alert_kvec = {
|
||||||
|
.iov_base = alert,
|
||||||
|
.iov_len = sizeof(alert),
|
||||||
|
};
|
||||||
|
struct msghdr msg = {
|
||||||
|
.msg_flags = *msg_flags,
|
||||||
|
.msg_control = &u,
|
||||||
|
.msg_controllen = sizeof(u),
|
||||||
|
};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
msg->msg_control = &u;
|
iov_iter_kvec(&msg.msg_iter, ITER_DEST, &alert_kvec, 1,
|
||||||
msg->msg_controllen = sizeof(u);
|
alert_kvec.iov_len);
|
||||||
ret = sock_recvmsg(sock, msg, flags);
|
ret = sock_recvmsg(sock, &msg, flags);
|
||||||
if (msg->msg_controllen != sizeof(u))
|
if (ret > 0 &&
|
||||||
ret = xs_sock_process_cmsg(sock, msg, &u.cmsg, ret);
|
tls_get_record_type(sock->sk, &u.cmsg) == TLS_RECORD_TYPE_ALERT) {
|
||||||
|
iov_iter_revert(&msg.msg_iter, ret);
|
||||||
|
ret = xs_sock_process_cmsg(sock, &msg, msg_flags, &u.cmsg,
|
||||||
|
-EAGAIN);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -408,7 +422,13 @@ xs_sock_recvmsg(struct socket *sock, struct msghdr *msg, int flags, size_t seek)
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
if (seek != 0)
|
if (seek != 0)
|
||||||
iov_iter_advance(&msg->msg_iter, seek);
|
iov_iter_advance(&msg->msg_iter, seek);
|
||||||
ret = xs_sock_recv_cmsg(sock, msg, flags);
|
ret = sock_recvmsg(sock, msg, flags);
|
||||||
|
/* Handle TLS inband control message lazily */
|
||||||
|
if (msg->msg_flags & MSG_CTRUNC) {
|
||||||
|
msg->msg_flags &= ~(MSG_CTRUNC | MSG_EOR);
|
||||||
|
if (ret == 0 || ret == -EIO)
|
||||||
|
ret = xs_sock_recv_cmsg(sock, &msg->msg_flags, flags);
|
||||||
|
}
|
||||||
return ret > 0 ? ret + seek : ret;
|
return ret > 0 ? ret + seek : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -434,7 +454,7 @@ xs_read_discard(struct socket *sock, struct msghdr *msg, int flags,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
iov_iter_discard(&msg->msg_iter, ITER_DEST, count);
|
iov_iter_discard(&msg->msg_iter, ITER_DEST, count);
|
||||||
return xs_sock_recv_cmsg(sock, msg, flags);
|
return xs_sock_recvmsg(sock, msg, flags, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE
|
#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue