CVE fixes: CVE-2023-32255 CVE-2023-32248 CVE-2023-32250 CVE-2023-32252 CVE-2023-32257 CVE-2023-32247 CVE-2023-32251 CVE-2023-32253 CVE-2023-32249 CVE-2023-32254 CVE-2023-32246 CVE-2023-32256 CVE-2023-32258 CVE-2023-2593
Chih-Yen Chang (3): ksmbd: fix wrong UserName check in session_user ksmbd: allocate one more byte for implied bcc[0] ksmbd: fix global-out-of-bounds in smb2_find_context_vals
Colin Ian King (1): ksmbd: Fix spelling mistake "excceed" -> "exceeded"
Dawei Li (1): ksmbd: Implements sess->ksmbd_chann_list as xarray
Gustav Johansson (1): ksmbd: smb2: Allow messages padded to 8byte boundary
Namjae Jeon (12): ksmbd: fix memleak in session setup ksmbd: fix NULL pointer dereference in smb2_get_info_filesystem() ksmbd: limit pdu length size according to connection status ksmbd: fix racy issue from session setup and logoff ksmbd: destroy expired sessions ksmbd: block asynchronous requests when making a delay on session setup ksmbd: fix deadlock in ksmbd_find_crypto_ctx() ksmbd: not allow guest user on multichannel ksmbd: fix racy issue under cocurrent smb2 tree disconnect ksmbd: call rcu_barrier() in ksmbd_server_exit() ksmbd: fix racy issue from smb2 close and logoff with multichannel ksmbd: do not allow the actual frame length to be smaller than the rfc1002 length
fs/ksmbd/auth.c | 19 ++-- fs/ksmbd/connection.c | 84 +++++++++++++----- fs/ksmbd/connection.h | 58 +++++++++---- fs/ksmbd/mgmt/tree_connect.c | 13 ++- fs/ksmbd/mgmt/tree_connect.h | 3 + fs/ksmbd/mgmt/user_session.c | 138 ++++++++++++++++------------- fs/ksmbd/mgmt/user_session.h | 5 +- fs/ksmbd/oplock.c | 5 +- fs/ksmbd/oplock.h | 2 +- fs/ksmbd/server.c | 4 +- fs/ksmbd/smb2misc.c | 24 +++--- fs/ksmbd/smb2pdu.c | 162 +++++++++++++++++++---------------- fs/ksmbd/smb2pdu.h | 7 +- fs/ksmbd/transport_tcp.c | 2 +- 14 files changed, 322 insertions(+), 204 deletions(-)
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.4-rc1 commit 6d7cb549c2ca20e1f07593f15e936fd54b763028 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FG3 CVE: CVE-2023-32255
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
If client send session setup request with unknown NTLMSSP message type, session that does not included channel can be created. It will cause session memleak. because ksmbd_sessions_deregister() does not destroy session if channel is not included. This patch return error response if client send the request unknown NTLMSSP message type.
Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20593 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/smb2pdu.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 15b7f876cb01..038ca5d0eeef 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -1801,6 +1801,10 @@ int smb2_sess_setup(struct ksmbd_work *work) } kfree(sess->Preauth_HashValue); sess->Preauth_HashValue = NULL; + } else { + pr_info_ratelimited("Unknown NTLMSSP message type : 0x%x\n", + le32_to_cpu(negblob->MessageType)); + rc = -EINVAL; } } else { /* TODO: need one more negotiation */
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.4-rc1 commit 3ac00a2ab69b34189942afa9e862d5170cdcb018 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FF8 CVE: CVE-2023-32248
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
If share is , share->path is NULL and it cause NULL pointer dereference issue.
Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20479 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/smb2pdu.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 038ca5d0eeef..18e176fcddf0 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -4867,6 +4867,9 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, int rc = 0, len; int fs_infoclass_size = 0;
+ if (!share->path) + return -EIO; + rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); if (rc) { pr_err("cannot create vfs path\n");
From: Dawei Li set_pte_at@outlook.com
mainline inclusion from mainline-v6.3-rc1 commit 1d9c4172110e645b383ff13eee759728d74f1a5d category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I7CETC CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
For some ops on channel: 1. lookup_chann_list(), possibly on high frequency. 2. ksmbd_chann_del().
Connection is used as indexing key to lookup channel, in that case, linear search based on list may suffer a bit for performance.
Implements sess->ksmbd_chann_list as xarray.
Signed-off-by: Dawei Li set_pte_at@outlook.com Acked-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/mgmt/user_session.c | 61 ++++++++++++++---------------------- fs/ksmbd/mgmt/user_session.h | 4 +-- fs/ksmbd/smb2pdu.c | 34 +++----------------- 3 files changed, 30 insertions(+), 69 deletions(-)
diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c index 3fa2139a0b30..96e04f1ba562 100644 --- a/fs/ksmbd/mgmt/user_session.c +++ b/fs/ksmbd/mgmt/user_session.c @@ -30,15 +30,15 @@ struct ksmbd_session_rpc {
static void free_channel_list(struct ksmbd_session *sess) { - struct channel *chann, *tmp; + struct channel *chann; + unsigned long index;
- write_lock(&sess->chann_lock); - list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list, - chann_list) { - list_del(&chann->chann_list); + xa_for_each(&sess->ksmbd_chann_list, index, chann) { + xa_erase(&sess->ksmbd_chann_list, index); kfree(chann); } - write_unlock(&sess->chann_lock); + + xa_destroy(&sess->ksmbd_chann_list); }
static void __session_rpc_close(struct ksmbd_session *sess, @@ -188,21 +188,15 @@ int ksmbd_session_register(struct ksmbd_conn *conn,
static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) { - struct channel *chann, *tmp; - - write_lock(&sess->chann_lock); - list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list, - chann_list) { - if (chann->conn == conn) { - list_del(&chann->chann_list); - kfree(chann); - write_unlock(&sess->chann_lock); - return 0; - } - } - write_unlock(&sess->chann_lock); + struct channel *chann; + + chann = xa_erase(&sess->ksmbd_chann_list, (long)conn); + if (!chann) + return -ENOENT;
- return -ENOENT; + kfree(chann); + + return 0; }
void ksmbd_sessions_deregister(struct ksmbd_conn *conn) @@ -232,7 +226,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) return;
sess_destroy: - if (list_empty(&sess->ksmbd_chann_list)) { + if (xa_empty(&sess->ksmbd_chann_list)) { xa_erase(&conn->sessions, sess->id); ksmbd_session_destroy(sess); } @@ -318,6 +312,9 @@ static struct ksmbd_session *__session_create(int protocol) struct ksmbd_session *sess; int ret;
+ if (protocol != CIFDS_SESSION_FLAG_SMB2) + return NULL; + sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL); if (!sess) return NULL; @@ -327,30 +324,20 @@ static struct ksmbd_session *__session_create(int protocol)
set_session_flag(sess, protocol); xa_init(&sess->tree_conns); - INIT_LIST_HEAD(&sess->ksmbd_chann_list); + xa_init(&sess->ksmbd_chann_list); INIT_LIST_HEAD(&sess->rpc_handle_list); sess->sequence_number = 1; - rwlock_init(&sess->chann_lock); - - switch (protocol) { - case CIFDS_SESSION_FLAG_SMB2: - ret = __init_smb2_session(sess); - break; - default: - ret = -EINVAL; - break; - }
+ ret = __init_smb2_session(sess); if (ret) goto error;
ida_init(&sess->tree_conn_ida);
- if (protocol == CIFDS_SESSION_FLAG_SMB2) { - down_write(&sessions_table_lock); - hash_add(sessions_table, &sess->hlist, sess->id); - up_write(&sessions_table_lock); - } + down_write(&sessions_table_lock); + hash_add(sessions_table, &sess->hlist, sess->id); + up_write(&sessions_table_lock); + return sess;
error: diff --git a/fs/ksmbd/mgmt/user_session.h b/fs/ksmbd/mgmt/user_session.h index 8934b8ee275b..44a3c67b2bd9 100644 --- a/fs/ksmbd/mgmt/user_session.h +++ b/fs/ksmbd/mgmt/user_session.h @@ -21,7 +21,6 @@ struct ksmbd_file_table; struct channel { __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; struct ksmbd_conn *conn; - struct list_head chann_list; };
struct preauth_session { @@ -50,8 +49,7 @@ struct ksmbd_session { char sess_key[CIFS_KEY_SIZE];
struct hlist_node hlist; - rwlock_t chann_lock; - struct list_head ksmbd_chann_list; + struct xarray ksmbd_chann_list; struct xarray tree_conns; struct ida tree_conn_ida; struct list_head rpc_handle_list; diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 18e176fcddf0..3e85d4639f86 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -74,14 +74,7 @@ static inline bool check_session_id(struct ksmbd_conn *conn, u64 id)
struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) { - struct channel *chann; - - list_for_each_entry(chann, &sess->ksmbd_chann_list, chann_list) { - if (chann->conn == conn) - return chann; - } - - return NULL; + return xa_load(&sess->ksmbd_chann_list, (long)conn); }
/** @@ -599,6 +592,7 @@ static void destroy_previous_session(struct ksmbd_conn *conn, struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); struct ksmbd_user *prev_user; struct channel *chann; + long index;
if (!prev_sess) return; @@ -612,10 +606,8 @@ static void destroy_previous_session(struct ksmbd_conn *conn, return;
prev_sess->state = SMB2_SESSION_EXPIRED; - write_lock(&prev_sess->chann_lock); - list_for_each_entry(chann, &prev_sess->ksmbd_chann_list, chann_list) + xa_for_each(&prev_sess->ksmbd_chann_list, index, chann) chann->conn->status = KSMBD_SESS_EXITING; - write_unlock(&prev_sess->chann_lock); }
/** @@ -1522,19 +1514,14 @@ static int ntlm_authenticate(struct ksmbd_work *work)
binding_session: if (conn->dialect >= SMB30_PROT_ID) { - read_lock(&sess->chann_lock); chann = lookup_chann_list(sess, conn); - read_unlock(&sess->chann_lock); if (!chann) { chann = kmalloc(sizeof(struct channel), GFP_KERNEL); if (!chann) return -ENOMEM;
chann->conn = conn; - INIT_LIST_HEAD(&chann->chann_list); - write_lock(&sess->chann_lock); - list_add(&chann->chann_list, &sess->ksmbd_chann_list); - write_unlock(&sess->chann_lock); + xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); } }
@@ -1608,19 +1595,14 @@ static int krb5_authenticate(struct ksmbd_work *work) }
if (conn->dialect >= SMB30_PROT_ID) { - read_lock(&sess->chann_lock); chann = lookup_chann_list(sess, conn); - read_unlock(&sess->chann_lock); if (!chann) { chann = kmalloc(sizeof(struct channel), GFP_KERNEL); if (!chann) return -ENOMEM;
chann->conn = conn; - INIT_LIST_HEAD(&chann->chann_list); - write_lock(&sess->chann_lock); - list_add(&chann->chann_list, &sess->ksmbd_chann_list); - write_unlock(&sess->chann_lock); + xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); } }
@@ -8349,14 +8331,11 @@ int smb3_check_sign_req(struct ksmbd_work *work) if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { signing_key = work->sess->smb3signingkey; } else { - read_lock(&work->sess->chann_lock); chann = lookup_chann_list(work->sess, conn); if (!chann) { - read_unlock(&work->sess->chann_lock); return 0; } signing_key = chann->smb3signingkey; - read_unlock(&work->sess->chann_lock); }
if (!signing_key) { @@ -8416,14 +8395,11 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { signing_key = work->sess->smb3signingkey; } else { - read_lock(&work->sess->chann_lock); chann = lookup_chann_list(work->sess, work->conn); if (!chann) { - read_unlock(&work->sess->chann_lock); return; } signing_key = chann->smb3signingkey; - read_unlock(&work->sess->chann_lock); }
if (!signing_key)
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.3-rc1 commit 62c487b53a7ff31e322cf2874d3796b8202c54a5 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I7CETC CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
Stream protocol length will never be larger than 16KB until session setup. After session setup, the size of requests will not be larger than 16KB + SMB2 MAX WRITE size. This patch limits these invalidly oversized requests and closes the connection immediately.
Fixes: 0626e6641f6b ("cifsd: add server handler for central processing and tranport layers") Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-18259 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com
Conflicts: fs/ksmbd/smb2pdu.h --- fs/ksmbd/connection.c | 17 +++++++++++++++-- fs/ksmbd/smb2pdu.h | 5 +++-- 2 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index 8e15ec9e8f43..c89169016f97 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -274,7 +274,7 @@ int ksmbd_conn_handler_loop(void *p) { struct ksmbd_conn *conn = (struct ksmbd_conn *)p; struct ksmbd_transport *t = conn->transport; - unsigned int pdu_size; + unsigned int pdu_size, max_allowed_pdu_size; char hdr_buf[4] = {0,}; int size;
@@ -299,13 +299,26 @@ int ksmbd_conn_handler_loop(void *p) pdu_size = get_rfc1002_len(hdr_buf); ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size);
+ if (conn->status == KSMBD_SESS_GOOD) + max_allowed_pdu_size = + SMB3_MAX_MSGSIZE + conn->vals->max_write_size; + else + max_allowed_pdu_size = SMB3_MAX_MSGSIZE; + + if (pdu_size > max_allowed_pdu_size) { + pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n", + pdu_size, max_allowed_pdu_size, + conn->status); + break; + } + /* * Check if pdu size is valid (min : smb header size, * max : 0x00FFFFFF). */ if (pdu_size < __SMB2_HEADER_STRUCTURE_SIZE || pdu_size > MAX_STREAM_PROT_LEN) { - continue; + break; }
/* 4 for rfc1002 length field */ diff --git a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h index 9018d919cdd8..1920963b6686 100644 --- a/fs/ksmbd/smb2pdu.h +++ b/fs/ksmbd/smb2pdu.h @@ -113,8 +113,9 @@ #define SMB21_DEFAULT_IOSIZE (1024 * 1024) #define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) #define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) -#define SMB3_MIN_IOSIZE (64 * 1024) -#define SMB3_MAX_IOSIZE (8 * 1024 * 1024) +#define SMB3_MIN_IOSIZE (64 * 1024) +#define SMB3_MAX_IOSIZE (8 * 1024 * 1024) +#define SMB3_MAX_MSGSIZE (4 * 4096)
/* * SMB2 Header Definition
From: Colin Ian King colin.i.king@gmail.com
mainline inclusion from mainline-v6.3-rc1 commit 7a17c61ee3b2683c40090179c273f4701fca9677 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I7CETC CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
There is a spelling mistake in an error message. Fix it.
Signed-off-by: Colin Ian King colin.i.king@gmail.com Acked-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index c89169016f97..3f8c756bbe08 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -306,7 +306,7 @@ int ksmbd_conn_handler_loop(void *p) max_allowed_pdu_size = SMB3_MAX_MSGSIZE;
if (pdu_size > max_allowed_pdu_size) { - pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n", + pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n", pdu_size, max_allowed_pdu_size, conn->status); break;
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.4-rc1 commit f5c779b7ddbda30866cf2a27c63e34158f858c73 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I7BG0L CVE: CVE-2023-32250,CVE-2023-32252,CVE-2023-32257
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
This racy issue is triggered by sending concurrent session setup and logoff requests. This patch does not set connection status as KSMBD_SESS_GOOD if state is KSMBD_SESS_NEED_RECONNECT in session setup. And relookup session to validate if session is deleted in logoff.
Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20481, ZDI-CAN-20590, ZDI-CAN-20596 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com --- fs/ksmbd/connection.c | 14 ++++---- fs/ksmbd/connection.h | 39 ++++++++++++--------- fs/ksmbd/mgmt/user_session.c | 1 + fs/ksmbd/server.c | 3 +- fs/ksmbd/smb2pdu.c | 67 +++++++++++++++++++++++------------- fs/ksmbd/transport_tcp.c | 2 +- 6 files changed, 77 insertions(+), 49 deletions(-)
diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index 3f8c756bbe08..991fba4377a7 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -56,7 +56,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) return NULL;
conn->need_neg = true; - conn->status = KSMBD_SESS_NEW; + ksmbd_conn_set_new(conn); conn->local_nls = load_nls("utf8"); if (!conn->local_nls) conn->local_nls = load_nls_default(); @@ -143,12 +143,12 @@ int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) return ret; }
-static void ksmbd_conn_lock(struct ksmbd_conn *conn) +void ksmbd_conn_lock(struct ksmbd_conn *conn) { mutex_lock(&conn->srv_mutex); }
-static void ksmbd_conn_unlock(struct ksmbd_conn *conn) +void ksmbd_conn_unlock(struct ksmbd_conn *conn) { mutex_unlock(&conn->srv_mutex); } @@ -239,7 +239,7 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn) if (!ksmbd_server_running()) return false;
- if (conn->status == KSMBD_SESS_EXITING) + if (ksmbd_conn_exiting(conn)) return false;
if (kthread_should_stop()) @@ -299,7 +299,7 @@ int ksmbd_conn_handler_loop(void *p) pdu_size = get_rfc1002_len(hdr_buf); ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size);
- if (conn->status == KSMBD_SESS_GOOD) + if (ksmbd_conn_good(conn)) max_allowed_pdu_size = SMB3_MAX_MSGSIZE + conn->vals->max_write_size; else @@ -308,7 +308,7 @@ int ksmbd_conn_handler_loop(void *p) if (pdu_size > max_allowed_pdu_size) { pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n", pdu_size, max_allowed_pdu_size, - conn->status); + READ_ONCE(conn->status)); break; }
@@ -413,7 +413,7 @@ static void stop_sessions(void) if (task) ksmbd_debug(CONN, "Stop session handler %s/%d\n", task->comm, task_pid_nr(task)); - conn->status = KSMBD_SESS_EXITING; + ksmbd_conn_set_exiting(conn); if (t->ops->shutdown) { read_unlock(&conn_list_lock); t->ops->shutdown(t); diff --git a/fs/ksmbd/connection.h b/fs/ksmbd/connection.h index f803e3e60d34..d493dbd200b1 100644 --- a/fs/ksmbd/connection.h +++ b/fs/ksmbd/connection.h @@ -154,6 +154,8 @@ void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); int ksmbd_conn_handler_loop(void *p); int ksmbd_conn_transport_init(void); void ksmbd_conn_transport_destroy(void); +void ksmbd_conn_lock(struct ksmbd_conn *conn); +void ksmbd_conn_unlock(struct ksmbd_conn *conn);
/* * WARNING @@ -161,43 +163,48 @@ void ksmbd_conn_transport_destroy(void); * This is a hack. We will move status to a proper place once we land * a multi-sessions support. */ -static inline bool ksmbd_conn_good(struct ksmbd_work *work) +static inline bool ksmbd_conn_good(struct ksmbd_conn *conn) { - return work->conn->status == KSMBD_SESS_GOOD; + return READ_ONCE(conn->status) == KSMBD_SESS_GOOD; }
-static inline bool ksmbd_conn_need_negotiate(struct ksmbd_work *work) +static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn) { - return work->conn->status == KSMBD_SESS_NEED_NEGOTIATE; + return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE; }
-static inline bool ksmbd_conn_need_reconnect(struct ksmbd_work *work) +static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn) { - return work->conn->status == KSMBD_SESS_NEED_RECONNECT; + return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT; }
-static inline bool ksmbd_conn_exiting(struct ksmbd_work *work) +static inline bool ksmbd_conn_exiting(struct ksmbd_conn *conn) { - return work->conn->status == KSMBD_SESS_EXITING; + return READ_ONCE(conn->status) == KSMBD_SESS_EXITING; }
-static inline void ksmbd_conn_set_good(struct ksmbd_work *work) +static inline void ksmbd_conn_set_new(struct ksmbd_conn *conn) { - work->conn->status = KSMBD_SESS_GOOD; + WRITE_ONCE(conn->status, KSMBD_SESS_NEW); }
-static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_work *work) +static inline void ksmbd_conn_set_good(struct ksmbd_conn *conn) { - work->conn->status = KSMBD_SESS_NEED_NEGOTIATE; + WRITE_ONCE(conn->status, KSMBD_SESS_GOOD); }
-static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_work *work) +static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_conn *conn) { - work->conn->status = KSMBD_SESS_NEED_RECONNECT; + WRITE_ONCE(conn->status, KSMBD_SESS_NEED_NEGOTIATE); }
-static inline void ksmbd_conn_set_exiting(struct ksmbd_work *work) +static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn) { - work->conn->status = KSMBD_SESS_EXITING; + WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT); +} + +static inline void ksmbd_conn_set_exiting(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_EXITING); } #endif /* __CONNECTION_H__ */ diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c index 96e04f1ba562..dac24affd59c 100644 --- a/fs/ksmbd/mgmt/user_session.c +++ b/fs/ksmbd/mgmt/user_session.c @@ -322,6 +322,7 @@ static struct ksmbd_session *__session_create(int protocol) if (ksmbd_init_file_table(&sess->file_table)) goto error;
+ sess->state = SMB2_SESSION_IN_PROGRESS; set_session_flag(sess, protocol); xa_init(&sess->tree_conns); xa_init(&sess->ksmbd_chann_list); diff --git a/fs/ksmbd/server.c b/fs/ksmbd/server.c index 8cd93052cfab..18feeb780965 100644 --- a/fs/ksmbd/server.c +++ b/fs/ksmbd/server.c @@ -93,7 +93,8 @@ static inline int check_conn_state(struct ksmbd_work *work) { struct smb_hdr *rsp_hdr;
- if (ksmbd_conn_exiting(work) || ksmbd_conn_need_reconnect(work)) { + if (ksmbd_conn_exiting(work->conn) || + ksmbd_conn_need_reconnect(work->conn)) { rsp_hdr = work->response_buf; rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; return 1; diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 3e85d4639f86..5a4cca091d7c 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -250,7 +250,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work)
rsp = smb2_get_msg(work->response_buf);
- WARN_ON(ksmbd_conn_good(work)); + WARN_ON(ksmbd_conn_good(conn));
rsp->StructureSize = cpu_to_le16(65); ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); @@ -280,7 +280,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; conn->use_spnego = true;
- ksmbd_conn_set_need_negotiate(work); + ksmbd_conn_set_need_negotiate(conn); return 0; }
@@ -574,7 +574,7 @@ int smb2_check_user_session(struct ksmbd_work *work) cmd == SMB2_SESSION_SETUP_HE) return 0;
- if (!ksmbd_conn_good(work)) + if (!ksmbd_conn_good(conn)) return -EINVAL;
sess_id = le64_to_cpu(req_hdr->SessionId); @@ -607,7 +607,7 @@ static void destroy_previous_session(struct ksmbd_conn *conn,
prev_sess->state = SMB2_SESSION_EXPIRED; xa_for_each(&prev_sess->ksmbd_chann_list, index, chann) - chann->conn->status = KSMBD_SESS_EXITING; + ksmbd_conn_set_exiting(chann->conn); }
/** @@ -1069,7 +1069,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
ksmbd_debug(SMB, "Received negotiate request\n"); conn->need_neg = false; - if (ksmbd_conn_good(work)) { + if (ksmbd_conn_good(conn)) { pr_err("conn->tcp_status is already in CifsGood State\n"); work->send_no_response = 1; return rc; @@ -1224,7 +1224,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) }
conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode); - ksmbd_conn_set_need_negotiate(work); + ksmbd_conn_set_need_negotiate(conn);
err_out: if (rc < 0) @@ -1645,6 +1645,7 @@ int smb2_sess_setup(struct ksmbd_work *work) rsp->SecurityBufferLength = 0; inc_rfc1001_len(work->response_buf, 9);
+ ksmbd_conn_lock(conn); if (!req->hdr.SessionId) { sess = ksmbd_smb2_session_create(); if (!sess) { @@ -1692,6 +1693,12 @@ int smb2_sess_setup(struct ksmbd_work *work) goto out_err; }
+ if (ksmbd_conn_need_reconnect(conn)) { + rc = -EFAULT; + sess = NULL; + goto out_err; + } + if (ksmbd_session_lookup(conn, sess_id)) { rc = -EACCES; goto out_err; @@ -1711,12 +1718,20 @@ int smb2_sess_setup(struct ksmbd_work *work) rc = -ENOENT; goto out_err; } + + if (sess->state == SMB2_SESSION_EXPIRED) { + rc = -EFAULT; + goto out_err; + } + + if (ksmbd_conn_need_reconnect(conn)) { + rc = -EFAULT; + sess = NULL; + goto out_err; + } } work->sess = sess;
- if (sess->state == SMB2_SESSION_EXPIRED) - sess->state = SMB2_SESSION_IN_PROGRESS; - negblob_off = le16_to_cpu(req->SecurityBufferOffset); negblob_len = le16_to_cpu(req->SecurityBufferLength); if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer) || @@ -1746,8 +1761,10 @@ int smb2_sess_setup(struct ksmbd_work *work) goto out_err; }
- ksmbd_conn_set_good(work); - sess->state = SMB2_SESSION_VALID; + if (!ksmbd_conn_need_reconnect(conn)) { + ksmbd_conn_set_good(conn); + sess->state = SMB2_SESSION_VALID; + } kfree(sess->Preauth_HashValue); sess->Preauth_HashValue = NULL; } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { @@ -1769,8 +1786,10 @@ int smb2_sess_setup(struct ksmbd_work *work) if (rc) goto out_err;
- ksmbd_conn_set_good(work); - sess->state = SMB2_SESSION_VALID; + if (!ksmbd_conn_need_reconnect(conn)) { + ksmbd_conn_set_good(conn); + sess->state = SMB2_SESSION_VALID; + } if (conn->binding) { struct preauth_session *preauth_sess;
@@ -1836,14 +1855,13 @@ int smb2_sess_setup(struct ksmbd_work *work) if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) try_delay = true;
- xa_erase(&conn->sessions, sess->id); - ksmbd_session_destroy(sess); - work->sess = NULL; + sess->state = SMB2_SESSION_EXPIRED; if (try_delay) ssleep(5); } }
+ ksmbd_conn_unlock(conn); return rc; }
@@ -2063,21 +2081,24 @@ int smb2_session_logoff(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; struct smb2_logoff_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_session *sess = work->sess; + struct ksmbd_session *sess; + struct smb2_logoff_req *req = smb2_get_msg(work->request_buf);
rsp->StructureSize = cpu_to_le16(4); inc_rfc1001_len(work->response_buf, 4);
ksmbd_debug(SMB, "request\n");
- /* setting CifsExiting here may race with start_tcp_sess */ - ksmbd_conn_set_need_reconnect(work); + ksmbd_conn_set_need_reconnect(conn); ksmbd_close_session_fds(work); ksmbd_conn_wait_idle(conn);
+ /* + * Re-lookup session to validate if session is deleted + * while waiting request complete + */ + sess = ksmbd_session_lookup(conn, le64_to_cpu(req->hdr.SessionId)); if (ksmbd_tree_conn_session_logoff(sess)) { - struct smb2_logoff_req *req = smb2_get_msg(work->request_buf); - ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; smb2_set_err_rsp(work); @@ -2089,9 +2110,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
ksmbd_free_user(sess->user); sess->user = NULL; - - /* let start_tcp_sess free connection info now */ - ksmbd_conn_set_need_negotiate(work); + ksmbd_conn_set_need_negotiate(conn); return 0; }
diff --git a/fs/ksmbd/transport_tcp.c b/fs/ksmbd/transport_tcp.c index 82a1429bbe12..2fbb4fa2f54b 100644 --- a/fs/ksmbd/transport_tcp.c +++ b/fs/ksmbd/transport_tcp.c @@ -318,7 +318,7 @@ static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig, if (length == -EINTR) { total_read = -ESHUTDOWN; break; - } else if (conn->status == KSMBD_SESS_NEED_RECONNECT) { + } else if (ksmbd_conn_need_reconnect(conn)) { total_read = -EAGAIN; break; } else if (length == -ERESTARTSYS || length == -EAGAIN) {
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.4-rc1 commit ea174a91893956450510945a0c5d1a10b5323656 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FHK CVE: CVE-2023-32247
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
client can indefinitely send smb2 session setup requests with the SessionId set to 0, thus indefinitely spawning new sessions, and causing indefinite memory usage. This patch limit to the number of sessions using expired timeout and session state.
Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20478 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/mgmt/user_session.c | 68 ++++++++++++++++++++---------------- fs/ksmbd/mgmt/user_session.h | 1 + fs/ksmbd/smb2pdu.c | 1 + fs/ksmbd/smb2pdu.h | 2 ++ 4 files changed, 41 insertions(+), 31 deletions(-)
diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c index dac24affd59c..cf83d8c3689b 100644 --- a/fs/ksmbd/mgmt/user_session.c +++ b/fs/ksmbd/mgmt/user_session.c @@ -172,70 +172,73 @@ static struct ksmbd_session *__session_lookup(unsigned long long id) struct ksmbd_session *sess;
hash_for_each_possible(sessions_table, sess, hlist, id) { - if (id == sess->id) + if (id == sess->id) { + sess->last_active = jiffies; return sess; + } } return NULL; }
+static void ksmbd_expire_session(struct ksmbd_conn *conn) +{ + unsigned long id; + struct ksmbd_session *sess; + + xa_for_each(&conn->sessions, id, sess) { + if (sess->state != SMB2_SESSION_VALID || + time_after(jiffies, + sess->last_active + SMB2_SESSION_TIMEOUT)) { + xa_erase(&conn->sessions, sess->id); + ksmbd_session_destroy(sess); + continue; + } + } +} + int ksmbd_session_register(struct ksmbd_conn *conn, struct ksmbd_session *sess) { sess->dialect = conn->dialect; memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + ksmbd_expire_session(conn); return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL)); }
-static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) +static void ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) { struct channel *chann;
chann = xa_erase(&sess->ksmbd_chann_list, (long)conn); if (!chann) - return -ENOENT; + return;
kfree(chann); - - return 0; }
void ksmbd_sessions_deregister(struct ksmbd_conn *conn) { struct ksmbd_session *sess; + unsigned long id;
- if (conn->binding) { - int bkt; - - down_write(&sessions_table_lock); - hash_for_each(sessions_table, bkt, sess, hlist) { - if (!ksmbd_chann_del(conn, sess)) { - up_write(&sessions_table_lock); - goto sess_destroy; - } + xa_for_each(&conn->sessions, id, sess) { + ksmbd_chann_del(conn, sess); + if (xa_empty(&sess->ksmbd_chann_list)) { + xa_erase(&conn->sessions, sess->id); + ksmbd_session_destroy(sess); } - up_write(&sessions_table_lock); - } else { - unsigned long id; - - xa_for_each(&conn->sessions, id, sess) { - if (!ksmbd_chann_del(conn, sess)) - goto sess_destroy; - } - } - - return; - -sess_destroy: - if (xa_empty(&sess->ksmbd_chann_list)) { - xa_erase(&conn->sessions, sess->id); - ksmbd_session_destroy(sess); } }
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, unsigned long long id) { - return xa_load(&conn->sessions, id); + struct ksmbd_session *sess; + + sess = xa_load(&conn->sessions, id); + if (sess) + sess->last_active = jiffies; + return sess; }
struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) @@ -244,6 +247,8 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
down_read(&sessions_table_lock); sess = __session_lookup(id); + if (sess) + sess->last_active = jiffies; up_read(&sessions_table_lock);
return sess; @@ -322,6 +327,7 @@ static struct ksmbd_session *__session_create(int protocol) if (ksmbd_init_file_table(&sess->file_table)) goto error;
+ sess->last_active = jiffies; sess->state = SMB2_SESSION_IN_PROGRESS; set_session_flag(sess, protocol); xa_init(&sess->tree_conns); diff --git a/fs/ksmbd/mgmt/user_session.h b/fs/ksmbd/mgmt/user_session.h index 44a3c67b2bd9..51f38e5b61ab 100644 --- a/fs/ksmbd/mgmt/user_session.h +++ b/fs/ksmbd/mgmt/user_session.h @@ -59,6 +59,7 @@ struct ksmbd_session { __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
struct ksmbd_file_table file_table; + unsigned long last_active; };
static inline int test_session_flag(struct ksmbd_session *sess, int bit) diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 5a4cca091d7c..201189e96421 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -1855,6 +1855,7 @@ int smb2_sess_setup(struct ksmbd_work *work) if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) try_delay = true;
+ sess->last_active = jiffies; sess->state = SMB2_SESSION_EXPIRED; if (try_delay) ssleep(5); diff --git a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h index 1920963b6686..59c14b2642ab 100644 --- a/fs/ksmbd/smb2pdu.h +++ b/fs/ksmbd/smb2pdu.h @@ -366,6 +366,8 @@ struct smb2_negotiate_rsp { #define SMB2_SESSION_IN_PROGRESS BIT(0) #define SMB2_SESSION_VALID BIT(1)
+#define SMB2_SESSION_TIMEOUT (10 * HZ) + /* Flags */ #define SMB2_SESSION_REQ_FLAG_BINDING 0x01 #define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.4-rc1 commit b096d97f47326b1e2dbdef1c91fab69ffda54d17 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FP0 CVE: CVE-2023-32251
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
ksmbd make a delay of 5 seconds on session setup to avoid dictionary attacks. But the 5 seconds delay can be bypassed by using asynchronous requests. This patch block all requests on current connection when making a delay on sesstion setup failure.
Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20482 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/smb2pdu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 201189e96421..f88eab8887e6 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -1857,8 +1857,11 @@ int smb2_sess_setup(struct ksmbd_work *work)
sess->last_active = jiffies; sess->state = SMB2_SESSION_EXPIRED; - if (try_delay) + if (try_delay) { + ksmbd_conn_set_need_reconnect(conn); ssleep(5); + ksmbd_conn_set_need_negotiate(conn); + } } }
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.4-rc1 commit 7b4323373d844954bb76e0e9f39c4e5fc785fa7b category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FIN CVE: CVE-2023-32253
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
Deadlock is triggered by sending multiple concurrent session setup requests. It should be reused after releasing when getting ctx for crypto. Multiple consecutive ctx uses cause deadlock while waiting for releasing due to the limited number of ctx.
Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20591 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/auth.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/fs/ksmbd/auth.c b/fs/ksmbd/auth.c index 0ac85a1a63c0..d8dabd19823f 100644 --- a/fs/ksmbd/auth.c +++ b/fs/ksmbd/auth.c @@ -220,22 +220,22 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; - struct ksmbd_crypto_ctx *ctx; + struct ksmbd_crypto_ctx *ctx = NULL; char *construct = NULL; int rc, len;
- ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); if (rc) { ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); goto out; }
+ ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); @@ -271,6 +271,8 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, ksmbd_debug(AUTH, "Could not generate md5 hash\n"); goto out; } + ksmbd_release_crypto_ctx(ctx); + ctx = NULL;
rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp); if (rc) { @@ -281,7 +283,8 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0) rc = -EINVAL; out: - ksmbd_release_crypto_ctx(ctx); + if (ctx) + ksmbd_release_crypto_ctx(ctx); kfree(construct); return rc; }
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.4-rc1 commit 3353ab2df5f68dab7da8d5ebb427a2d265a1f2b2 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FMP CVE: CVE-2023-32249
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
This patch return STATUS_NOT_SUPPORTED if binding session is guest.
Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20480 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/smb2pdu.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index f88eab8887e6..13fe21426048 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -1450,7 +1450,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) * Reuse session if anonymous try to connect * on reauthetication. */ - if (ksmbd_anonymous_user(user)) { + if (conn->binding == false && ksmbd_anonymous_user(user)) { ksmbd_free_user(user); return 0; } @@ -1464,7 +1464,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) sess->user = user; }
- if (user_guest(sess->user)) { + if (conn->binding == false && user_guest(sess->user)) { rsp->SessionFlags = SMB2_SESSION_FLAG_IS_GUEST_LE; } else { struct authenticate_message *authblob; @@ -1704,6 +1704,11 @@ int smb2_sess_setup(struct ksmbd_work *work) goto out_err; }
+ if (user_guest(sess->user)) { + rc = -EOPNOTSUPP; + goto out_err; + } + conn->binding = true; } else if ((conn->dialect < SMB30_PROT_ID || server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && @@ -1828,6 +1833,8 @@ int smb2_sess_setup(struct ksmbd_work *work) rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; else if (rc == -ENOMEM) rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; else if (rc) rsp->hdr.Status = STATUS_LOGON_FAILURE;
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.4-rc1 commit 30210947a343b6b3ca13adc9bfc88e1543e16dd5 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FJA CVE: CVE-2023-32254
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
There is UAF issue under cocurrent smb2 tree disconnect. This patch introduce TREE_CONN_EXPIRE flags for tcon to avoid cocurrent access.
Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20592 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/mgmt/tree_connect.c | 10 +++++++++- fs/ksmbd/mgmt/tree_connect.h | 3 +++ fs/ksmbd/smb2pdu.c | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/fs/ksmbd/mgmt/tree_connect.c b/fs/ksmbd/mgmt/tree_connect.c index dd262daa2c4a..e3f62df65d3a 100644 --- a/fs/ksmbd/mgmt/tree_connect.c +++ b/fs/ksmbd/mgmt/tree_connect.c @@ -95,7 +95,15 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, unsigned int id) { - return xa_load(&sess->tree_conns, id); + struct ksmbd_tree_connect *tcon; + + tcon = xa_load(&sess->tree_conns, id); + if (tcon) { + if (test_bit(TREE_CONN_EXPIRE, &tcon->status)) + tcon = NULL; + } + + return tcon; }
struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, diff --git a/fs/ksmbd/mgmt/tree_connect.h b/fs/ksmbd/mgmt/tree_connect.h index 71e50271dccf..5ef006c7d1cc 100644 --- a/fs/ksmbd/mgmt/tree_connect.h +++ b/fs/ksmbd/mgmt/tree_connect.h @@ -14,6 +14,8 @@ struct ksmbd_share_config; struct ksmbd_user; struct ksmbd_conn;
+#define TREE_CONN_EXPIRE 1 + struct ksmbd_tree_connect { int id;
@@ -25,6 +27,7 @@ struct ksmbd_tree_connect {
int maximal_access; bool posix_extensions; + unsigned long status; };
struct ksmbd_tree_conn_status { diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 13fe21426048..2320c296874b 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2066,11 +2066,12 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
ksmbd_debug(SMB, "request\n");
- if (!tcon) { + if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { struct smb2_tree_disconnect_req *req = smb2_get_msg(work->request_buf);
ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; smb2_set_err_rsp(work); return 0;
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.4-rc1 commit eb307d09fe15844fdaebeb8cc8c9b9e925430aa5 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FIB CVE: CVE-2023-32246
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
racy issue is triggered the bug by racing between closing a connection and rmmod. In ksmbd, rcu_barrier() is not called at module unload time, so nothing prevents ksmbd from getting unloaded while it still has RCU callbacks pending. It leads to trigger unintended execution of kernel code locally and use to defeat protections such as Kernel Lockdown
Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20477 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/server.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/fs/ksmbd/server.c b/fs/ksmbd/server.c index 18feeb780965..54b67dcae835 100644 --- a/fs/ksmbd/server.c +++ b/fs/ksmbd/server.c @@ -618,6 +618,7 @@ static int __init ksmbd_server_init(void) static void __exit ksmbd_server_exit(void) { ksmbd_server_shutdown(); + rcu_barrier(); ksmbd_release_inode_hash(); }
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.4-rc1 commit abcc506a9a71976a8b4c9bf3ee6efd13229c1e19 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I7BIK2 CVE: CVE-2023-32256,CVE-2023-32258
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
When smb client send concurrent smb2 close and logoff request with multichannel connection, It can cause racy issue. logoff request free tcon and can cause UAF issues in smb2 close. When receiving logoff request with multichannel, ksmbd should wait until all remaning requests complete as well as ones in the current connection, and then make session expired.
Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20796 ZDI-CAN-20595 Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com
Conflicts: fs/ksmbd/connection.c --- fs/ksmbd/connection.c | 54 +++++++++++++++++++++++++++--------- fs/ksmbd/connection.h | 19 +++++++++++-- fs/ksmbd/mgmt/tree_connect.c | 3 ++ fs/ksmbd/mgmt/user_session.c | 36 ++++++++++++++++++++---- fs/ksmbd/smb2pdu.c | 21 +++++++------- 5 files changed, 101 insertions(+), 32 deletions(-)
diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index 991fba4377a7..79742d871dbe 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -20,7 +20,7 @@ static DEFINE_MUTEX(init_lock); static struct ksmbd_conn_ops default_conn_ops;
LIST_HEAD(conn_list); -DEFINE_RWLOCK(conn_list_lock); +DECLARE_RWSEM(conn_list_lock);
/** * ksmbd_conn_free() - free resources of the connection instance @@ -32,9 +32,9 @@ DEFINE_RWLOCK(conn_list_lock); */ void ksmbd_conn_free(struct ksmbd_conn *conn) { - write_lock(&conn_list_lock); + down_write(&conn_list_lock); list_del(&conn->conns_list); - write_unlock(&conn_list_lock); + up_write(&conn_list_lock);
xa_destroy(&conn->sessions); kvfree(conn->request_buf); @@ -78,9 +78,9 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) spin_lock_init(&conn->llist_lock); INIT_LIST_HEAD(&conn->lock_list);
- write_lock(&conn_list_lock); + down_write(&conn_list_lock); list_add(&conn->conns_list, &conn_list); - write_unlock(&conn_list_lock); + up_write(&conn_list_lock); return conn; }
@@ -89,7 +89,7 @@ bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) struct ksmbd_conn *t; bool ret = false;
- read_lock(&conn_list_lock); + down_read(&conn_list_lock); list_for_each_entry(t, &conn_list, conns_list) { if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) continue; @@ -97,7 +97,7 @@ bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) ret = true; break; } - read_unlock(&conn_list_lock); + up_read(&conn_list_lock); return ret; }
@@ -153,9 +153,37 @@ void ksmbd_conn_unlock(struct ksmbd_conn *conn) mutex_unlock(&conn->srv_mutex); }
-void ksmbd_conn_wait_idle(struct ksmbd_conn *conn) +void ksmbd_all_conn_set_status(u64 sess_id, u32 status) { + struct ksmbd_conn *conn; + + down_read(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + if (conn->binding || xa_load(&conn->sessions, sess_id)) + WRITE_ONCE(conn->status, status); + } + up_read(&conn_list_lock); +} + +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id) +{ + struct ksmbd_conn *bind_conn; + wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); + + down_read(&conn_list_lock); + list_for_each_entry(bind_conn, &conn_list, conns_list) { + if (bind_conn == conn) + continue; + + if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) && + !ksmbd_conn_releasing(bind_conn) && + atomic_read(&bind_conn->req_running)) { + wait_event(bind_conn->req_running_q, + atomic_read(&bind_conn->req_running) == 0); + } + } + up_read(&conn_list_lock); }
int ksmbd_conn_write(struct ksmbd_work *work) @@ -359,10 +387,10 @@ int ksmbd_conn_handler_loop(void *p) }
out: + ksmbd_conn_set_releasing(conn); /* Wait till all reference dropped to the Server object*/ wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0);
- unload_nls(conn->local_nls); if (default_conn_ops.terminate_fn) default_conn_ops.terminate_fn(conn); @@ -404,7 +432,7 @@ static void stop_sessions(void) struct ksmbd_transport *t;
again: - read_lock(&conn_list_lock); + down_read(&conn_list_lock); list_for_each_entry(conn, &conn_list, conns_list) { struct task_struct *task;
@@ -415,12 +443,12 @@ static void stop_sessions(void) task->comm, task_pid_nr(task)); ksmbd_conn_set_exiting(conn); if (t->ops->shutdown) { - read_unlock(&conn_list_lock); + up_read(&conn_list_lock); t->ops->shutdown(t); - read_lock(&conn_list_lock); + down_read(&conn_list_lock); } } - read_unlock(&conn_list_lock); + up_read(&conn_list_lock);
if (!list_empty(&conn_list)) { schedule_timeout_interruptible(HZ / 10); /* 100ms */ diff --git a/fs/ksmbd/connection.h b/fs/ksmbd/connection.h index d493dbd200b1..2e3d96e63953 100644 --- a/fs/ksmbd/connection.h +++ b/fs/ksmbd/connection.h @@ -25,7 +25,8 @@ enum { KSMBD_SESS_GOOD, KSMBD_SESS_EXITING, KSMBD_SESS_NEED_RECONNECT, - KSMBD_SESS_NEED_NEGOTIATE + KSMBD_SESS_NEED_NEGOTIATE, + KSMBD_SESS_RELEASING };
struct ksmbd_stats { @@ -134,10 +135,10 @@ struct ksmbd_transport { #define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr))
extern struct list_head conn_list; -extern rwlock_t conn_list_lock; +extern struct rw_semaphore conn_list_lock;
bool ksmbd_conn_alive(struct ksmbd_conn *conn); -void ksmbd_conn_wait_idle(struct ksmbd_conn *conn); +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id); struct ksmbd_conn *ksmbd_conn_alloc(void); void ksmbd_conn_free(struct ksmbd_conn *conn); bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); @@ -183,6 +184,11 @@ static inline bool ksmbd_conn_exiting(struct ksmbd_conn *conn) return READ_ONCE(conn->status) == KSMBD_SESS_EXITING; }
+static inline bool ksmbd_conn_releasing(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_RELEASING; +} + static inline void ksmbd_conn_set_new(struct ksmbd_conn *conn) { WRITE_ONCE(conn->status, KSMBD_SESS_NEW); @@ -207,4 +213,11 @@ static inline void ksmbd_conn_set_exiting(struct ksmbd_conn *conn) { WRITE_ONCE(conn->status, KSMBD_SESS_EXITING); } + +static inline void ksmbd_conn_set_releasing(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_RELEASING); +} + +void ksmbd_all_conn_set_status(u64 sess_id, u32 status); #endif /* __CONNECTION_H__ */ diff --git a/fs/ksmbd/mgmt/tree_connect.c b/fs/ksmbd/mgmt/tree_connect.c index e3f62df65d3a..1cdae978d1b0 100644 --- a/fs/ksmbd/mgmt/tree_connect.c +++ b/fs/ksmbd/mgmt/tree_connect.c @@ -123,6 +123,9 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) struct ksmbd_tree_connect *tc; unsigned long id;
+ if (!sess) + return -EINVAL; + xa_for_each(&sess->tree_conns, id, tc) ret |= ksmbd_tree_conn_disconnect(sess, tc); xa_destroy(&sess->tree_conns); diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c index cf83d8c3689b..d3f8e9d93c3b 100644 --- a/fs/ksmbd/mgmt/user_session.c +++ b/fs/ksmbd/mgmt/user_session.c @@ -151,10 +151,6 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) if (!sess) return;
- down_write(&sessions_table_lock); - hash_del(&sess->hlist); - up_write(&sessions_table_lock); - if (sess->user) ksmbd_free_user(sess->user);
@@ -185,15 +181,18 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) unsigned long id; struct ksmbd_session *sess;
+ down_write(&sessions_table_lock); xa_for_each(&conn->sessions, id, sess) { if (sess->state != SMB2_SESSION_VALID || time_after(jiffies, sess->last_active + SMB2_SESSION_TIMEOUT)) { xa_erase(&conn->sessions, sess->id); + hash_del(&sess->hlist); ksmbd_session_destroy(sess); continue; } } + up_write(&sessions_table_lock); }
int ksmbd_session_register(struct ksmbd_conn *conn, @@ -205,15 +204,16 @@ int ksmbd_session_register(struct ksmbd_conn *conn, return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL)); }
-static void ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) +static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) { struct channel *chann;
chann = xa_erase(&sess->ksmbd_chann_list, (long)conn); if (!chann) - return; + return -ENOENT;
kfree(chann); + return 0; }
void ksmbd_sessions_deregister(struct ksmbd_conn *conn) @@ -221,13 +221,37 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) struct ksmbd_session *sess; unsigned long id;
+ down_write(&sessions_table_lock); + if (conn->binding) { + int bkt; + struct hlist_node *tmp; + + hash_for_each_safe(sessions_table, bkt, tmp, sess, hlist) { + if (!ksmbd_chann_del(conn, sess) && + xa_empty(&sess->ksmbd_chann_list)) { + hash_del(&sess->hlist); + ksmbd_session_destroy(sess); + } + } + } + xa_for_each(&conn->sessions, id, sess) { + unsigned long chann_id; + struct channel *chann; + + xa_for_each(&sess->ksmbd_chann_list, chann_id, chann) { + if (chann->conn != conn) + ksmbd_conn_set_exiting(chann->conn); + } + ksmbd_chann_del(conn, sess); if (xa_empty(&sess->ksmbd_chann_list)) { xa_erase(&conn->sessions, sess->id); + hash_del(&sess->hlist); ksmbd_session_destroy(sess); } } + up_write(&sessions_table_lock); }
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 2320c296874b..082350c77899 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2095,21 +2095,22 @@ int smb2_session_logoff(struct ksmbd_work *work) struct smb2_logoff_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_session *sess; struct smb2_logoff_req *req = smb2_get_msg(work->request_buf); + u64 sess_id = le64_to_cpu(req->hdr.SessionId);
rsp->StructureSize = cpu_to_le16(4); inc_rfc1001_len(work->response_buf, 4);
ksmbd_debug(SMB, "request\n");
- ksmbd_conn_set_need_reconnect(conn); + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); ksmbd_close_session_fds(work); - ksmbd_conn_wait_idle(conn); + ksmbd_conn_wait_idle(conn, sess_id);
/* * Re-lookup session to validate if session is deleted * while waiting request complete */ - sess = ksmbd_session_lookup(conn, le64_to_cpu(req->hdr.SessionId)); + sess = ksmbd_session_lookup_all(conn, sess_id); if (ksmbd_tree_conn_session_logoff(sess)) { ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; @@ -2122,7 +2123,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
ksmbd_free_user(sess->user); sess->user = NULL; - ksmbd_conn_set_need_negotiate(conn); + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); return 0; }
@@ -6905,7 +6906,7 @@ int smb2_lock(struct ksmbd_work *work)
nolock = 1; /* check locks in connection list */ - read_lock(&conn_list_lock); + down_read(&conn_list_lock); list_for_each_entry(conn, &conn_list, conns_list) { spin_lock(&conn->llist_lock); list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { @@ -6922,7 +6923,7 @@ int smb2_lock(struct ksmbd_work *work) list_del(&cmp_lock->flist); list_del(&cmp_lock->clist); spin_unlock(&conn->llist_lock); - read_unlock(&conn_list_lock); + up_read(&conn_list_lock);
locks_free_lock(cmp_lock->fl); kfree(cmp_lock); @@ -6944,7 +6945,7 @@ int smb2_lock(struct ksmbd_work *work) cmp_lock->start > smb_lock->start && cmp_lock->start < smb_lock->end) { spin_unlock(&conn->llist_lock); - read_unlock(&conn_list_lock); + up_read(&conn_list_lock); pr_err("previous lock conflict with zero byte lock range\n"); goto out; } @@ -6953,7 +6954,7 @@ int smb2_lock(struct ksmbd_work *work) smb_lock->start > cmp_lock->start && smb_lock->start < cmp_lock->end) { spin_unlock(&conn->llist_lock); - read_unlock(&conn_list_lock); + up_read(&conn_list_lock); pr_err("current lock conflict with zero byte lock range\n"); goto out; } @@ -6964,14 +6965,14 @@ int smb2_lock(struct ksmbd_work *work) cmp_lock->end >= smb_lock->end)) && !cmp_lock->zero_len && !smb_lock->zero_len) { spin_unlock(&conn->llist_lock); - read_unlock(&conn_list_lock); + up_read(&conn_list_lock); pr_err("Not allow lock operation on exclusive lock range\n"); goto out; } } spin_unlock(&conn->llist_lock); } - read_unlock(&conn_list_lock); + up_read(&conn_list_lock); out_check_cl: if (smb_lock->fl->fl_type == F_UNLCK && nolock) { pr_err("Try to unlock nolocked range\n");
From: Chih-Yen Chang cc85nod@gmail.com
mainline inclusion from mainline-v6.4-rc1 commit f0a96d1aafd8964e1f9955c830a3e5cb3c60a90f category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FNG CVE: CVE-2023-2593
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
The offset of UserName is related to the address of security buffer. To ensure the validaty of UserName, we need to compare name_off + name_len with secbuf_len instead of auth_msg_len.
[ 27.096243] ================================================================== [ 27.096890] BUG: KASAN: slab-out-of-bounds in smb_strndup_from_utf16+0x188/0x350 [ 27.097609] Read of size 2 at addr ffff888005e3b542 by task kworker/0:0/7 ... [ 27.099950] Call Trace: [ 27.100194] <TASK> [ 27.100397] dump_stack_lvl+0x33/0x50 [ 27.100752] print_report+0xcc/0x620 [ 27.102305] kasan_report+0xae/0xe0 [ 27.103072] kasan_check_range+0x35/0x1b0 [ 27.103757] smb_strndup_from_utf16+0x188/0x350 [ 27.105474] smb2_sess_setup+0xaf8/0x19c0 [ 27.107935] handle_ksmbd_work+0x274/0x810 [ 27.108315] process_one_work+0x419/0x760 [ 27.108689] worker_thread+0x2a2/0x6f0 [ 27.109385] kthread+0x160/0x190 [ 27.110129] ret_from_fork+0x1f/0x30 [ 27.110454] </TASK>
Cc: stable@vger.kernel.org Signed-off-by: Chih-Yen Chang cc85nod@gmail.com Acked-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/smb2pdu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 082350c77899..8a893532e888 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -1375,7 +1375,7 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn, struct authenticate_message *authblob; struct ksmbd_user *user; char *name; - unsigned int auth_msg_len, name_off, name_len, secbuf_len; + unsigned int name_off, name_len, secbuf_len;
secbuf_len = le16_to_cpu(req->SecurityBufferLength); if (secbuf_len < sizeof(struct authenticate_message)) { @@ -1385,9 +1385,8 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn, authblob = user_authblob(conn, req); name_off = le32_to_cpu(authblob->UserName.BufferOffset); name_len = le16_to_cpu(authblob->UserName.Length); - auth_msg_len = le16_to_cpu(req->SecurityBufferOffset) + secbuf_len;
- if (auth_msg_len < (u64)name_off + name_len) + if (secbuf_len < (u64)name_off + name_len) return NULL;
name = smb_strndup_from_utf16((const char *)authblob + name_off,
From: Chih-Yen Chang cc85nod@gmail.com
mainline inclusion from mainline-v6.4-rc1 commit 443d61d1fa9faa60ef925513d83742902390100f category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FNG CVE: CVE-2023-2593
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
ksmbd_smb2_check_message allows client to return one byte more, so we need to allocate additional memory in ksmbd_conn_handler_loop to avoid out-of-bound access.
Cc: stable@vger.kernel.org Signed-off-by: Chih-Yen Chang cc85nod@gmail.com Acked-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com
Conflicts: fs/ksmbd/connection.c --- fs/ksmbd/connection.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index 79742d871dbe..1ad330d1b337 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -350,7 +350,8 @@ int ksmbd_conn_handler_loop(void *p) }
/* 4 for rfc1002 length field */ - size = pdu_size + 4; + /* 1 for implied bcc[0] */ + size = pdu_size + 4 + 1; conn->request_buf = kvmalloc(size, GFP_KERNEL); if (!conn->request_buf) continue;
From: Chih-Yen Chang cc85nod@gmail.com
mainline inclusion from mainline-v6.4-rc1 commit 02f76c401d17e409ed45bf7887148fcc22c93c85 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FNG CVE: CVE-2023-2593
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
Add tag_len argument in smb2_find_context_vals() to avoid out-of-bound read when create_context's name_len is larger than tag length.
[ 7.995411] ================================================================== [ 7.995866] BUG: KASAN: global-out-of-bounds in memcmp+0x83/0xa0 [ 7.996248] Read of size 8 at addr ffffffff8258d940 by task kworker/0:0/7 ... [ 7.998191] Call Trace: [ 7.998358] <TASK> [ 7.998503] dump_stack_lvl+0x33/0x50 [ 7.998743] print_report+0xcc/0x620 [ 7.999458] kasan_report+0xae/0xe0 [ 7.999895] kasan_check_range+0x35/0x1b0 [ 8.000152] memcmp+0x83/0xa0 [ 8.000347] smb2_find_context_vals+0xf7/0x1e0 [ 8.000635] smb2_open+0x1df2/0x43a0 [ 8.006398] handle_ksmbd_work+0x274/0x810 [ 8.006666] process_one_work+0x419/0x760 [ 8.006922] worker_thread+0x2a2/0x6f0 [ 8.007429] kthread+0x160/0x190 [ 8.007946] ret_from_fork+0x1f/0x30 [ 8.008181] </TASK>
Cc: stable@vger.kernel.org Signed-off-by: Chih-Yen Chang cc85nod@gmail.com Acked-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/oplock.c | 5 +++-- fs/ksmbd/oplock.h | 2 +- fs/ksmbd/smb2pdu.c | 14 +++++++------- 3 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index 3721fd44a43a..fc5b2137002f 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -1449,11 +1449,12 @@ struct lease_ctx_info *parse_lease_state(void *open_req) * smb2_find_context_vals() - find a particular context info in open request * @open_req: buffer containing smb2 file open(create) request * @tag: context name to search for + * @tag_len: the length of tag * * Return: pointer to requested context, NULL if @str context not found * or error pointer if name length is invalid. */ -struct create_context *smb2_find_context_vals(void *open_req, const char *tag) +struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len) { struct create_context *cc; unsigned int next = 0; @@ -1492,7 +1493,7 @@ struct create_context *smb2_find_context_vals(void *open_req, const char *tag) return ERR_PTR(-EINVAL);
name = (char *)cc + name_off; - if (memcmp(name, tag, name_len) == 0) + if (name_len == tag_len && !memcmp(name, tag, name_len)) return cc;
remain_len -= next; diff --git a/fs/ksmbd/oplock.h b/fs/ksmbd/oplock.h index 0cf7a2b5bbc0..e1ba363b412a 100644 --- a/fs/ksmbd/oplock.h +++ b/fs/ksmbd/oplock.h @@ -118,7 +118,7 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp); void create_mxac_rsp_buf(char *cc, int maximal_access); void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); -struct create_context *smb2_find_context_vals(void *open_req, const char *str); +struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len); struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, char *lease_key); int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 8a893532e888..87e3a8488051 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -2466,7 +2466,7 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work, return -ENOENT;
/* Parse SD BUFFER create contexts */ - context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER); + context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER, 4); if (!context) return -ENOENT; else if (IS_ERR(context)) @@ -2662,7 +2662,7 @@ int smb2_open(struct ksmbd_work *work)
if (req->CreateContextsOffset) { /* Parse non-durable handle create contexts */ - context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER); + context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); goto err_out1; @@ -2682,7 +2682,7 @@ int smb2_open(struct ksmbd_work *work) }
context = smb2_find_context_vals(req, - SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); goto err_out1; @@ -2693,7 +2693,7 @@ int smb2_open(struct ksmbd_work *work) }
context = smb2_find_context_vals(req, - SMB2_CREATE_TIMEWARP_REQUEST); + SMB2_CREATE_TIMEWARP_REQUEST, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); goto err_out1; @@ -2705,7 +2705,7 @@ int smb2_open(struct ksmbd_work *work)
if (tcon->posix_extensions) { context = smb2_find_context_vals(req, - SMB2_CREATE_TAG_POSIX); + SMB2_CREATE_TAG_POSIX, 16); if (IS_ERR(context)) { rc = PTR_ERR(context); goto err_out1; @@ -3097,7 +3097,7 @@ int smb2_open(struct ksmbd_work *work) struct create_alloc_size_req *az_req;
az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, - SMB2_CREATE_ALLOCATION_SIZE); + SMB2_CREATE_ALLOCATION_SIZE, 4); if (IS_ERR(az_req)) { rc = PTR_ERR(az_req); goto err_out; @@ -3124,7 +3124,7 @@ int smb2_open(struct ksmbd_work *work) err); }
- context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID); + context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4); if (IS_ERR(context)) { rc = PTR_ERR(context); goto err_out;
From: Namjae Jeon linkinjeon@kernel.org
mainline inclusion from mainline-v6.3-rc1 commit fb533473d1595fe79ecb528fda1de33552b07178 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I7CETC CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
ksmbd allowed the actual frame length to be smaller than the rfc1002 length. If allowed, it is possible to allocates a large amount of memory that can be limited by credit management and can eventually cause memory exhaustion problem. This patch do not allow it except SMB2 Negotiate request which will be validated when message handling proceeds. Also, Allow a message that padded to 8byte boundary.
Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/smb2misc.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index 62e90b229ad7..f5691399bf9d 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -413,20 +413,19 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) goto validate_credit;
/* - * windows client also pad up to 8 bytes when compounding. - * If pad is longer than eight bytes, log the server behavior - * (once), since may indicate a problem but allow it and - * continue since the frame is parseable. + * SMB2 NEGOTIATE request will be validated when message + * handling proceeds. */ - if (clc_len < len) { - ksmbd_debug(SMB, - "cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n", - len, clc_len, command, - le64_to_cpu(hdr->MessageId)); + if (command == SMB2_NEGOTIATE_HE) + goto validate_credit; + + /* + * Allow a message that padded to 8byte boundary. + */ + if (clc_len < len && (len - clc_len) < 8) goto validate_credit; - }
- ksmbd_debug(SMB, + pr_err_ratelimited( "cli req too short, len %d not %d. cmd:%d mid:%llu\n", len, clc_len, command, le64_to_cpu(hdr->MessageId));
From: Gustav Johansson gustajo@axis.com
mainline inclusion from mainline-v6.3-rc1 commit e7b8b8ed9960bf699bf4029f482d9e869c094ed6 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I7CETC CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
clc length is now accepted to <= 8 less than length, rather than < 8.
Solve issues on some of Axis's smb clients which send messages where clc length is 8 bytes less than length.
The specific client was running kernel 4.19.217 with smb dialect 3.0.2 on armv7l.
Cc: stable@vger.kernel.org Signed-off-by: Gustav Johansson gustajo@axis.com Acked-by: Namjae Jeon linkinjeon@kernel.org Signed-off-by: Steve French stfrench@microsoft.com Signed-off-by: ZhaoLong Wang wangzhaolong1@huawei.com --- fs/ksmbd/smb2misc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index f5691399bf9d..0e951dfddc23 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -421,8 +421,11 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
/* * Allow a message that padded to 8byte boundary. + * Linux 4.19.217 with smb 3.0.2 are sometimes + * sending messages where the cls_len is exactly + * 8 bytes less than len. */ - if (clc_len < len && (len - clc_len) < 8) + if (clc_len < len && (len - clc_len) <= 8) goto validate_credit;
pr_err_ratelimited(