From nobody Sun May 24 18:45:11 2026 Received: from out162-62-58-211.mail.qq.com (out162-62-58-211.mail.qq.com [162.62.58.211]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3B3143E6385; Fri, 22 May 2026 11:40:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.62.58.211 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779450032; cv=none; b=e8rTJz8vfb+oa3aNo5sSOvm9O1OaSMul1vBGytnzulsFab+2CEoxO3/c3LxOS/2Hk4FDPCwniwS2V7NDBqlQe4IwQTbdbuVgr30Q0TvELYFVJMl+zP7XXTRiaTQc6OPpsm0TEOyVAl54mtLUtpNXnnnzhViDxofkn5DHRiAQ4Aw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779450032; c=relaxed/simple; bh=Vw2Iu8ThWjYwEHGOEUVOayEhG+qqZFIY7GBVvO//9UU=; h=Message-ID:From:To:Cc:Subject:Date:In-Reply-To:References: MIME-Version; b=tMdM6eIs8NX7U8phK77uewqkL+sRKWkN+qww//DZY/MxZqdja3TBrGTuKJrV4BsxLXuaKqoSaDomZPiMZ7J24ICAnjzdM0WHb28f/JMEmKZaPdXi5lW3zzNCiOU/C2gQKcW7wqdQl+VSHjqbMObYCMQFlwLCqC1NJ0B+3WoEWvI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=foxmail.com; spf=pass smtp.mailfrom=foxmail.com; dkim=pass (1024-bit key) header.d=foxmail.com header.i=@foxmail.com header.b=RxbR1EOJ; arc=none smtp.client-ip=162.62.58.211 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=foxmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=foxmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=foxmail.com header.i=@foxmail.com header.b="RxbR1EOJ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=foxmail.com; s=s201512; t=1779450014; bh=ZlTDc2xumRt65tXeydu4EoO/XcL1V6+hbxk7nn+6W+4=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=RxbR1EOJKJeF9w+qHxkHIzig+CzEkpJQ+aTXOev5pRfVs6i+5ScfnL7iqAMm0FUgQ u7yfsnkbvcNuVlXX1N0ovjB7s4Qc5trQ+ZBP+MUpb0GRZ/9m/mwRaXmF92ZXMRJUPl XNidxebIyd0CeUEsR7woWeAHNwrqxtbKUl7aqMqo= Received: from China-team ([120.244.195.95]) by newxmesmtplogicsvrszc50-0.qq.com (NewEsmtp) with SMTP id 9F6874FC; Fri, 22 May 2026 19:39:54 +0800 X-QQ-mid: xmsmtpt1779449994ts3fczyw0 Message-ID: X-QQ-XMAILINFO: OETOaIzh8b0bo24Hby9WSVlY/gKtawlgKoHnjDFAe6MQF5e7sYWPs/QxS6QD9r Aoyt8cpCj7qqqPXLKQ7eMHYs0TSZwlAxgyIEwfqM6DI6Iy3SUrrXRKW2F7DY/xghSD2Sn6ufGuPh rqSe1R80utrO73e9XsNTsjj7TcM/P3Fa/bv4XhgadSCs+2WG5eg6D/V9iw/dY0aSjE4r9ylYy+LO MVTs3b7yvdptR47QKmKbbpHoV8vKp5yKM0Wl9DQJGDjMad9Gko6yA+emnaeTm2BMHvIFQkLpjlv+ g3Q2tNPzgAU4sINaZvwHSMhZDqGAyoPnMoaCn1dJx8vBTwvzCnM5CpCzSB/6aApBWHDS9Z5vT2hU tT4j2n8gKqfykLEeR8qQ74a94ZFelv3kA2cfOvug5zSGsjzJzxGEOU4YHZ1sIYB/m1WMRBb+Ij2v ujlu8TPjfUQF4s6/f4BTZWVU6utGRrwX5Sr6DFO9/w0EeXrc2amyqdQhDzT7JLxZ7WCA1rMceMn7 iJoFC1yzeJr9ITeMZDET6V3E4q1Ba3XScri9nQWWjm9NyVW4qtctrQzDP0p1mMDzSXNWrRcxC0KH t7S/Uw9hPAobyB9Fra2kkQJJQIF0QG0cQ4sOdLAXSAsrOeLI3qayoqF8VXrS5XKYCv92yAOn5F8X BjzF32zM57Ew4Sm1aHWWZWRqS40/Hu4VpQlCTHnm68KlVbmSlwWF+fQR6LtRqjHlK4lbxlbkjLMi pVI2Ot/FhsyXbVgVJJM87MKwC47BKXwN0ZEZ94KCF9/z0w79SCu23/LjI+UmMmU89l5gUmGLODdJ 2O0+eu4bL4mv42ukRK0MOstnzniMMukNHQUKa9hADAl4cRm0+G2gXJc/e6dtIbFC3WYxMvYaNHxZ tsFtVBzG4B3K8/oEVgRJEUUXunpnaiaTNDJOSyRB5AQNiuPP23L7ZpODNoxnL6JjMEzurJnXCp2j Ni0IBe+BazIVG1SEti70T2G27dUDic7syyaaoDCMJDPfkJQ2lI5cias9vSVB5w1/STV9tnwP7h2B IBHvFwZv9YvtVBbC5DDm03dFeu0Qs+gvvn7IDNCD1ytoJwZ5plPGGt0yXvzK1T83kJFv1FzAdR+5 MlINtOga0KdiJVuj53mFlAGU14puA5zLB12wa/PK+OfAFBshpcv5q/b/qkdaE/NhuCyYjJ X-QQ-XMRINFO: Nq+8W0+stu50tPAe92KXseR0ZZmBTk3gLg== From: Alva Lan To: gregkh@linuxfoundation.org, sashal@kernel.org, stable@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linkinjeon@kernel.org, stfrench@microsoft.com, d.ornaghi97@gmail.com, knavaneeth786@gmail.com, Alva Lan Subject: [PATCH 6.6.y 1/1] ksmbd: validate owner of durable handle on reconnect Date: Fri, 22 May 2026 19:39:46 +0800 X-OQ-MSGID: <20260522113946.20601-1-alvalan9@foxmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Namjae Jeon [ Upstream commit 49110a8ce654bbe56bef7c5e44cce31f4b102b8a ] Currently, ksmbd does not verify if the user attempting to reconnect to a durable handle is the same user who originally opened the file. This allows any authenticated user to hijack an orphaned durable handle by predicting or brute-forcing the persistent ID. According to MS-SMB2, the server MUST verify that the SecurityContext of the reconnect request matches the SecurityContext associated with the existing open. Add a durable_owner structure to ksmbd_file to store the original opener's UID, GID, and account name. and catpure the owner information when a file handle becomes orphaned. and implementing ksmbd_vfs_compare_durable_owner() to validate the identity of the requester during SMB2_CREATE (DHnC). Fixes: c8efcc786146 ("ksmbd: add support for durable handles v1/v2") Reported-by: Davide Ornaghi Reported-by: Navaneeth K Signed-off-by: Namjae Jeon Signed-off-by: Steve French [ In the function ksmbd_free_global_file_table(), replace ksmbd_destroy_file_table(&global_ft) with idr_destroy(global_ft.idr) kfree(global_ft.idr) per commit d484d621d40f ("ksmbd: add durable scavenger timer"). ] Signed-off-by: Alva Lan --- fs/smb/server/mgmt/user_session.c | 8 +-- fs/smb/server/oplock.c | 7 +++ fs/smb/server/oplock.h | 1 + fs/smb/server/smb2pdu.c | 3 +- fs/smb/server/vfs_cache.c | 90 +++++++++++++++++++++++++++---- fs/smb/server/vfs_cache.h | 12 ++++- 6 files changed, 105 insertions(+), 16 deletions(-) diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_se= ssion.c index e344475a41bd..b39a87e4a746 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -159,11 +159,10 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) if (!sess) return; =20 + ksmbd_tree_conn_session_logoff(sess); + ksmbd_destroy_file_table(sess); if (sess->user) ksmbd_free_user(sess->user); - - ksmbd_tree_conn_session_logoff(sess); - ksmbd_destroy_file_table(&sess->file_table); ksmbd_session_rpc_clear_list(sess); free_channel_list(sess); kfree(sess->Preauth_HashValue); @@ -390,7 +389,8 @@ void destroy_previous_session(struct ksmbd_conn *conn, ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP); goto out; } - ksmbd_destroy_file_table(&prev_sess->file_table); + + ksmbd_destroy_file_table(prev_sess); prev_sess->state =3D SMB2_SESSION_EXPIRED; ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP); out: diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index c49a75ff5fbb..8487fca425bd 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -1841,6 +1841,7 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn, struct ksmbd_share_config *share, struct ksmbd_file *fp, struct lease_ctx_info *lctx, + struct ksmbd_user *user, char *name) { struct oplock_info *opinfo =3D opinfo_get(fp); @@ -1849,6 +1850,12 @@ int smb2_check_durable_oplock(struct ksmbd_conn *con= n, if (!opinfo) return 0; =20 + if (ksmbd_vfs_compare_durable_owner(fp, user) =3D=3D false) { + ksmbd_debug(SMB, "Durable handle reconnect failed: owner mismatch\n"); + ret =3D -EBADF; + goto out; + } + if (opinfo->is_lease =3D=3D false) { if (lctx) { pr_err("create context include lease\n"); diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h index f8da0bba766b..e6c4fbe5cf4e 100644 --- a/fs/smb/server/oplock.h +++ b/fs/smb/server/oplock.h @@ -133,5 +133,6 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn, struct ksmbd_share_config *share, struct ksmbd_file *fp, struct lease_ctx_info *lctx, + struct ksmbd_user *user, char *name); #endif /* __KSMBD_OPLOCK_H */ diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 6c41a67be725..e14a126c3273 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2992,7 +2992,8 @@ int smb2_open(struct ksmbd_work *work) } =20 if (dh_info.reconnected =3D=3D true) { - rc =3D smb2_check_durable_oplock(conn, share, dh_info.fp, lc, name); + rc =3D smb2_check_durable_oplock(conn, share, dh_info.fp, + lc, sess->user, name); if (rc) { ksmbd_put_durable_fd(dh_info.fp); goto err_out2; diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index eacc6ef41db0..cf53142afe7c 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -16,6 +16,7 @@ #include "connection.h" #include "mgmt/tree_connect.h" #include "mgmt/user_session.h" +#include "mgmt/user_config.h" #include "smb_common.h" =20 #define S_DEL_PENDING 1 @@ -369,6 +370,8 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *f= t, struct ksmbd_file *fp) =20 if (ksmbd_stream_fd(fp)) kfree(fp->stream.name); + kfree(fp->owner.name); + kmem_cache_free(filp_cache, fp); } =20 @@ -677,11 +680,13 @@ void ksmbd_update_fstate(struct ksmbd_file_table *ft,= struct ksmbd_file *fp, } =20 static int -__close_file_table_ids(struct ksmbd_file_table *ft, +__close_file_table_ids(struct ksmbd_session *sess, struct ksmbd_tree_connect *tcon, bool (*skip)(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp)) + struct ksmbd_file *fp, + struct ksmbd_user *user)) { + struct ksmbd_file_table *ft =3D &sess->file_table; struct ksmbd_file *fp; unsigned int id =3D 0; int num =3D 0; @@ -694,7 +699,7 @@ __close_file_table_ids(struct ksmbd_file_table *ft, break; } =20 - if (skip(tcon, fp) || + if (skip(tcon, fp, sess->user) || !atomic_dec_and_test(&fp->refcount)) { id++; write_unlock(&ft->lock); @@ -746,13 +751,68 @@ static inline bool is_reconnectable(struct ksmbd_file= *fp) } =20 static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp) + struct ksmbd_file *fp, + struct ksmbd_user *user) { return fp->tcon !=3D tcon; } =20 +/* + * ksmbd_vfs_copy_durable_owner - Copy owner info for durable reconnect + * @fp: ksmbd file pointer to store owner info + * @user: user pointer to copy from + * + * This function binds the current user's identity to the file handle + * to satisfy MS-SMB2 Step 8 (SecurityContext matching) during reconnect. + * + * Return: 0 on success, or negative error code on failure + */ +static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp, + struct ksmbd_user *user) +{ + if (!user) + return -EINVAL; + + /* Duplicate the user name to ensure identity persistence */ + fp->owner.name =3D kstrdup(user->name, GFP_KERNEL); + if (!fp->owner.name) + return -ENOMEM; + + fp->owner.uid =3D user->uid; + fp->owner.gid =3D user->gid; + + return 0; +} + +/** + * ksmbd_vfs_compare_durable_owner - Verify if the requester is original o= wner + * @fp: existing ksmbd file pointer + * @user: user pointer of the reconnect requester + * + * Compares the UID, GID, and name of the current requester against the + * original owner stored in the file handle. + * + * Return: true if the user matches, false otherwise + */ +bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp, + struct ksmbd_user *user) +{ + if (!user || !fp->owner.name) + return false; + + /* Check if the UID and GID match first (fast path) */ + if (fp->owner.uid !=3D user->uid || fp->owner.gid !=3D user->gid) + return false; + + /* Validate the account name to ensure the same SecurityContext */ + if (strcmp(fp->owner.name, user->name)) + return false; + + return true; +} + static bool session_fd_check(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp) + struct ksmbd_file *fp, struct ksmbd_user *user) { struct ksmbd_inode *ci; struct oplock_info *op; @@ -762,6 +822,9 @@ static bool session_fd_check(struct ksmbd_tree_connect = *tcon, if (!is_reconnectable(fp)) return false; =20 + if (ksmbd_vfs_copy_durable_owner(fp, user)) + return false; + conn =3D fp->conn; ci =3D fp->f_ci; down_write(&ci->m_lock); @@ -789,7 +852,7 @@ static bool session_fd_check(struct ksmbd_tree_connect = *tcon, =20 void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) { - int num =3D __close_file_table_ids(&work->sess->file_table, + int num =3D __close_file_table_ids(work->sess, work->tcon, tree_conn_fd_check); =20 @@ -798,7 +861,7 @@ void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) =20 void ksmbd_close_session_fds(struct ksmbd_work *work) { - int num =3D __close_file_table_ids(&work->sess->file_table, + int num =3D __close_file_table_ids(work->sess, work->tcon, session_fd_check); =20 @@ -820,7 +883,8 @@ void ksmbd_free_global_file_table(void) kmem_cache_free(filp_cache, fp); } =20 - ksmbd_destroy_file_table(&global_ft); + idr_destroy(global_ft.idr); + kfree(global_ft.idr); } =20 int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share, @@ -894,6 +958,10 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, s= truct ksmbd_file *fp) } up_write(&ci->m_lock); =20 + fp->owner.uid =3D fp->owner.gid =3D 0; + kfree(fp->owner.name); + fp->owner.name =3D NULL; + return 0; } =20 @@ -908,12 +976,14 @@ int ksmbd_init_file_table(struct ksmbd_file_table *ft) return 0; } =20 -void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) +void ksmbd_destroy_file_table(struct ksmbd_session *sess) { + struct ksmbd_file_table *ft =3D &sess->file_table; + if (!ft->idr) return; =20 - __close_file_table_ids(ft, NULL, session_fd_check); + __close_file_table_ids(sess, NULL, session_fd_check); idr_destroy(ft->idr); kfree(ft->idr); ft->idr =3D NULL; diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 5a225e7055f1..1f13cfdf4cac 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -67,6 +67,13 @@ enum { FP_CLOSED }; =20 +/* Owner information for durable handle reconnect */ +struct durable_owner { + unsigned int uid; + unsigned int gid; + char *name; +}; + struct ksmbd_file { struct file *filp; u64 persistent_id; @@ -110,6 +117,7 @@ struct ksmbd_file { bool is_durable; bool is_persistent; bool is_resilient; + struct durable_owner owner; }; =20 static inline void set_ctx_actor(struct dir_context *ctx, @@ -136,7 +144,7 @@ static inline bool ksmbd_stream_fd(struct ksmbd_file *f= p) } =20 int ksmbd_init_file_table(struct ksmbd_file_table *ft); -void ksmbd_destroy_file_table(struct ksmbd_file_table *ft); +void ksmbd_destroy_file_table(struct ksmbd_session *sess); int ksmbd_close_fd(struct ksmbd_work *work, u64 id); struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id); struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id= ); @@ -160,6 +168,8 @@ void ksmbd_free_global_file_table(void); void ksmbd_set_fd_limit(unsigned long limit); void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *f= p, unsigned int state); +bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp, + struct ksmbd_user *user); =20 /* * INODE hash --=20 2.43.0