Skip to content

Commit 49110a8

Browse files
namjaejeonsmfrench
authored andcommitted
ksmbd: validate owner of durable handle on reconnect
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: c8efcc7 ("ksmbd: add support for durable handles v1/v2") Reported-by: Davide Ornaghi <d.ornaghi97@gmail.com> Reported-by: Navaneeth K <knavaneeth786@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 235e323 commit 49110a8

6 files changed

Lines changed: 102 additions & 16 deletions

File tree

fs/smb/server/mgmt/user_session.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -382,12 +382,10 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
382382
return;
383383

384384
delete_proc_session(sess);
385-
385+
ksmbd_tree_conn_session_logoff(sess);
386+
ksmbd_destroy_file_table(sess);
386387
if (sess->user)
387388
ksmbd_free_user(sess->user);
388-
389-
ksmbd_tree_conn_session_logoff(sess);
390-
ksmbd_destroy_file_table(&sess->file_table);
391389
ksmbd_launch_ksmbd_durable_scavenger();
392390
ksmbd_session_rpc_clear_list(sess);
393391
free_channel_list(sess);
@@ -618,7 +616,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
618616
goto out;
619617
}
620618

621-
ksmbd_destroy_file_table(&prev_sess->file_table);
619+
ksmbd_destroy_file_table(prev_sess);
622620
prev_sess->state = SMB2_SESSION_EXPIRED;
623621
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
624622
ksmbd_launch_ksmbd_durable_scavenger();

fs/smb/server/oplock.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1841,6 +1841,7 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,
18411841
struct ksmbd_share_config *share,
18421842
struct ksmbd_file *fp,
18431843
struct lease_ctx_info *lctx,
1844+
struct ksmbd_user *user,
18441845
char *name)
18451846
{
18461847
struct oplock_info *opinfo = opinfo_get(fp);
@@ -1849,6 +1850,12 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,
18491850
if (!opinfo)
18501851
return 0;
18511852

1853+
if (ksmbd_vfs_compare_durable_owner(fp, user) == false) {
1854+
ksmbd_debug(SMB, "Durable handle reconnect failed: owner mismatch\n");
1855+
ret = -EBADF;
1856+
goto out;
1857+
}
1858+
18521859
if (opinfo->is_lease == false) {
18531860
if (lctx) {
18541861
pr_err("create context include lease\n");

fs/smb/server/oplock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,5 +126,6 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,
126126
struct ksmbd_share_config *share,
127127
struct ksmbd_file *fp,
128128
struct lease_ctx_info *lctx,
129+
struct ksmbd_user *user,
129130
char *name);
130131
#endif /* __KSMBD_OPLOCK_H */

fs/smb/server/smb2pdu.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3013,7 +3013,8 @@ int smb2_open(struct ksmbd_work *work)
30133013
}
30143014

30153015
if (dh_info.reconnected == true) {
3016-
rc = smb2_check_durable_oplock(conn, share, dh_info.fp, lc, name);
3016+
rc = smb2_check_durable_oplock(conn, share, dh_info.fp,
3017+
lc, sess->user, name);
30173018
if (rc) {
30183019
ksmbd_put_durable_fd(dh_info.fp);
30193020
goto err_out2;

fs/smb/server/vfs_cache.c

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "misc.h"
2020
#include "mgmt/tree_connect.h"
2121
#include "mgmt/user_session.h"
22+
#include "mgmt/user_config.h"
2223
#include "smb_common.h"
2324
#include "server.h"
2425
#include "smb2pdu.h"
@@ -476,6 +477,8 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
476477

477478
if (ksmbd_stream_fd(fp))
478479
kfree(fp->stream.name);
480+
kfree(fp->owner.name);
481+
479482
kmem_cache_free(filp_cache, fp);
480483
}
481484

@@ -787,11 +790,13 @@ void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
787790
}
788791

789792
static int
790-
__close_file_table_ids(struct ksmbd_file_table *ft,
793+
__close_file_table_ids(struct ksmbd_session *sess,
791794
struct ksmbd_tree_connect *tcon,
792795
bool (*skip)(struct ksmbd_tree_connect *tcon,
793-
struct ksmbd_file *fp))
796+
struct ksmbd_file *fp,
797+
struct ksmbd_user *user))
794798
{
799+
struct ksmbd_file_table *ft = &sess->file_table;
795800
struct ksmbd_file *fp;
796801
unsigned int id = 0;
797802
int num = 0;
@@ -804,7 +809,7 @@ __close_file_table_ids(struct ksmbd_file_table *ft,
804809
break;
805810
}
806811

807-
if (skip(tcon, fp) ||
812+
if (skip(tcon, fp, sess->user) ||
808813
!atomic_dec_and_test(&fp->refcount)) {
809814
id++;
810815
write_unlock(&ft->lock);
@@ -856,7 +861,8 @@ static inline bool is_reconnectable(struct ksmbd_file *fp)
856861
}
857862

858863
static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
859-
struct ksmbd_file *fp)
864+
struct ksmbd_file *fp,
865+
struct ksmbd_user *user)
860866
{
861867
return fp->tcon != tcon;
862868
}
@@ -991,8 +997,62 @@ void ksmbd_stop_durable_scavenger(void)
991997
kthread_stop(server_conf.dh_task);
992998
}
993999

1000+
/*
1001+
* ksmbd_vfs_copy_durable_owner - Copy owner info for durable reconnect
1002+
* @fp: ksmbd file pointer to store owner info
1003+
* @user: user pointer to copy from
1004+
*
1005+
* This function binds the current user's identity to the file handle
1006+
* to satisfy MS-SMB2 Step 8 (SecurityContext matching) during reconnect.
1007+
*
1008+
* Return: 0 on success, or negative error code on failure
1009+
*/
1010+
static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp,
1011+
struct ksmbd_user *user)
1012+
{
1013+
if (!user)
1014+
return -EINVAL;
1015+
1016+
/* Duplicate the user name to ensure identity persistence */
1017+
fp->owner.name = kstrdup(user->name, GFP_KERNEL);
1018+
if (!fp->owner.name)
1019+
return -ENOMEM;
1020+
1021+
fp->owner.uid = user->uid;
1022+
fp->owner.gid = user->gid;
1023+
1024+
return 0;
1025+
}
1026+
1027+
/**
1028+
* ksmbd_vfs_compare_durable_owner - Verify if the requester is original owner
1029+
* @fp: existing ksmbd file pointer
1030+
* @user: user pointer of the reconnect requester
1031+
*
1032+
* Compares the UID, GID, and name of the current requester against the
1033+
* original owner stored in the file handle.
1034+
*
1035+
* Return: true if the user matches, false otherwise
1036+
*/
1037+
bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp,
1038+
struct ksmbd_user *user)
1039+
{
1040+
if (!user || !fp->owner.name)
1041+
return false;
1042+
1043+
/* Check if the UID and GID match first (fast path) */
1044+
if (fp->owner.uid != user->uid || fp->owner.gid != user->gid)
1045+
return false;
1046+
1047+
/* Validate the account name to ensure the same SecurityContext */
1048+
if (strcmp(fp->owner.name, user->name))
1049+
return false;
1050+
1051+
return true;
1052+
}
1053+
9941054
static bool session_fd_check(struct ksmbd_tree_connect *tcon,
995-
struct ksmbd_file *fp)
1055+
struct ksmbd_file *fp, struct ksmbd_user *user)
9961056
{
9971057
struct ksmbd_inode *ci;
9981058
struct oplock_info *op;
@@ -1002,6 +1062,9 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
10021062
if (!is_reconnectable(fp))
10031063
return false;
10041064

1065+
if (ksmbd_vfs_copy_durable_owner(fp, user))
1066+
return false;
1067+
10051068
conn = fp->conn;
10061069
ci = fp->f_ci;
10071070
down_write(&ci->m_lock);
@@ -1033,7 +1096,7 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
10331096

10341097
void ksmbd_close_tree_conn_fds(struct ksmbd_work *work)
10351098
{
1036-
int num = __close_file_table_ids(&work->sess->file_table,
1099+
int num = __close_file_table_ids(work->sess,
10371100
work->tcon,
10381101
tree_conn_fd_check);
10391102

@@ -1042,7 +1105,7 @@ void ksmbd_close_tree_conn_fds(struct ksmbd_work *work)
10421105

10431106
void ksmbd_close_session_fds(struct ksmbd_work *work)
10441107
{
1045-
int num = __close_file_table_ids(&work->sess->file_table,
1108+
int num = __close_file_table_ids(work->sess,
10461109
work->tcon,
10471110
session_fd_check);
10481111

@@ -1140,6 +1203,10 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
11401203
}
11411204
up_write(&ci->m_lock);
11421205

1206+
fp->owner.uid = fp->owner.gid = 0;
1207+
kfree(fp->owner.name);
1208+
fp->owner.name = NULL;
1209+
11431210
return 0;
11441211
}
11451212

@@ -1154,12 +1221,14 @@ int ksmbd_init_file_table(struct ksmbd_file_table *ft)
11541221
return 0;
11551222
}
11561223

1157-
void ksmbd_destroy_file_table(struct ksmbd_file_table *ft)
1224+
void ksmbd_destroy_file_table(struct ksmbd_session *sess)
11581225
{
1226+
struct ksmbd_file_table *ft = &sess->file_table;
1227+
11591228
if (!ft->idr)
11601229
return;
11611230

1162-
__close_file_table_ids(ft, NULL, session_fd_check);
1231+
__close_file_table_ids(sess, NULL, session_fd_check);
11631232
idr_destroy(ft->idr);
11641233
kfree(ft->idr);
11651234
ft->idr = NULL;

fs/smb/server/vfs_cache.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ enum {
6868
FP_CLOSED
6969
};
7070

71+
/* Owner information for durable handle reconnect */
72+
struct durable_owner {
73+
unsigned int uid;
74+
unsigned int gid;
75+
char *name;
76+
};
77+
7178
struct ksmbd_file {
7279
struct file *filp;
7380
u64 persistent_id;
@@ -114,6 +121,7 @@ struct ksmbd_file {
114121
bool is_resilient;
115122

116123
bool is_posix_ctxt;
124+
struct durable_owner owner;
117125
};
118126

119127
static inline void set_ctx_actor(struct dir_context *ctx,
@@ -140,7 +148,7 @@ static inline bool ksmbd_stream_fd(struct ksmbd_file *fp)
140148
}
141149

142150
int ksmbd_init_file_table(struct ksmbd_file_table *ft);
143-
void ksmbd_destroy_file_table(struct ksmbd_file_table *ft);
151+
void ksmbd_destroy_file_table(struct ksmbd_session *sess);
144152
int ksmbd_close_fd(struct ksmbd_work *work, u64 id);
145153
struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id);
146154
struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id);
@@ -166,6 +174,8 @@ void ksmbd_free_global_file_table(void);
166174
void ksmbd_set_fd_limit(unsigned long limit);
167175
void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
168176
unsigned int state);
177+
bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp,
178+
struct ksmbd_user *user);
169179

170180
/*
171181
* INODE hash

0 commit comments

Comments
 (0)