Skip to content

Commit 9f635d4

Browse files
committed
Merge tag 'v6.12-rc3-ksmbd-fixes' of git://git.samba.org/ksmbd
Pull smb server fixes from Steve French: - fix race between session setup and session logoff - add supplementary group support * tag 'v6.12-rc3-ksmbd-fixes' of git://git.samba.org/ksmbd: ksmbd: add support for supplementary groups ksmbd: fix user-after-free from session log off
2 parents 6f6fc39 + a77e0e0 commit 9f635d4

File tree

11 files changed

+171
-23
lines changed

11 files changed

+171
-23
lines changed

fs/smb/server/auth.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
512512
int in_len, char *out_blob, int *out_len)
513513
{
514514
struct ksmbd_spnego_authen_response *resp;
515+
struct ksmbd_login_response_ext *resp_ext = NULL;
515516
struct ksmbd_user *user = NULL;
516517
int retval;
517518

@@ -540,7 +541,10 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
540541
goto out;
541542
}
542543

543-
user = ksmbd_alloc_user(&resp->login_response);
544+
if (resp->login_response.status & KSMBD_USER_FLAG_EXTENSION)
545+
resp_ext = ksmbd_ipc_login_request_ext(resp->login_response.account);
546+
547+
user = ksmbd_alloc_user(&resp->login_response, resp_ext);
544548
if (!user) {
545549
ksmbd_debug(AUTH, "login failure\n");
546550
retval = -ENOMEM;

fs/smb/server/ksmbd_netlink.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
* - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response)
5252
* This event is to make kerberos authentication to be processed in
5353
* userspace.
54+
*
55+
* - KSMBD_EVENT_LOGIN_REQUEST_EXT/RESPONSE_EXT(ksmbd_login_request_ext/response_ext)
56+
* This event is to get user account extension info to user IPC daemon.
5457
*/
5558

5659
#define KSMBD_GENL_NAME "SMBD_GENL"
@@ -145,6 +148,16 @@ struct ksmbd_login_response {
145148
__u32 reserved[16]; /* Reserved room */
146149
};
147150

151+
/*
152+
* IPC user login response extension.
153+
*/
154+
struct ksmbd_login_response_ext {
155+
__u32 handle;
156+
__s32 ngroups; /* supplementary group count */
157+
__s8 reserved[128]; /* Reserved room */
158+
__s8 ____payload[];
159+
};
160+
148161
/*
149162
* IPC request to fetch net share config.
150163
*/
@@ -306,6 +319,9 @@ enum ksmbd_event {
306319
KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST,
307320
KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15,
308321

322+
KSMBD_EVENT_LOGIN_REQUEST_EXT,
323+
KSMBD_EVENT_LOGIN_RESPONSE_EXT,
324+
309325
__KSMBD_EVENT_MAX,
310326
KSMBD_EVENT_MAX = __KSMBD_EVENT_MAX - 1
311327
};
@@ -336,6 +352,7 @@ enum KSMBD_TREE_CONN_STATUS {
336352
#define KSMBD_USER_FLAG_BAD_USER BIT(3)
337353
#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4)
338354
#define KSMBD_USER_FLAG_DELAY_SESSION BIT(5)
355+
#define KSMBD_USER_FLAG_EXTENSION BIT(6)
339356

340357
/*
341358
* Share config flags.

fs/smb/server/mgmt/user_config.c

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
struct ksmbd_user *ksmbd_login_user(const char *account)
1313
{
1414
struct ksmbd_login_response *resp;
15+
struct ksmbd_login_response_ext *resp_ext = NULL;
1516
struct ksmbd_user *user = NULL;
1617

1718
resp = ksmbd_ipc_login_request(account);
@@ -21,15 +22,19 @@ struct ksmbd_user *ksmbd_login_user(const char *account)
2122
if (!(resp->status & KSMBD_USER_FLAG_OK))
2223
goto out;
2324

24-
user = ksmbd_alloc_user(resp);
25+
if (resp->status & KSMBD_USER_FLAG_EXTENSION)
26+
resp_ext = ksmbd_ipc_login_request_ext(account);
27+
28+
user = ksmbd_alloc_user(resp, resp_ext);
2529
out:
2630
kvfree(resp);
2731
return user;
2832
}
2933

30-
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp)
34+
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
35+
struct ksmbd_login_response_ext *resp_ext)
3136
{
32-
struct ksmbd_user *user = NULL;
37+
struct ksmbd_user *user;
3338

3439
user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL);
3540
if (!user)
@@ -44,18 +49,42 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp)
4449
if (user->passkey)
4550
memcpy(user->passkey, resp->hash, resp->hash_sz);
4651

47-
if (!user->name || !user->passkey) {
48-
kfree(user->name);
49-
kfree(user->passkey);
50-
kfree(user);
51-
user = NULL;
52+
user->ngroups = 0;
53+
user->sgid = NULL;
54+
55+
if (!user->name || !user->passkey)
56+
goto err_free;
57+
58+
if (resp_ext) {
59+
if (resp_ext->ngroups > NGROUPS_MAX) {
60+
pr_err("ngroups(%u) from login response exceeds max groups(%d)\n",
61+
resp_ext->ngroups, NGROUPS_MAX);
62+
goto err_free;
63+
}
64+
65+
user->sgid = kmemdup(resp_ext->____payload,
66+
resp_ext->ngroups * sizeof(gid_t),
67+
GFP_KERNEL);
68+
if (!user->sgid)
69+
goto err_free;
70+
71+
user->ngroups = resp_ext->ngroups;
72+
ksmbd_debug(SMB, "supplementary groups : %d\n", user->ngroups);
5273
}
74+
5375
return user;
76+
77+
err_free:
78+
kfree(user->name);
79+
kfree(user->passkey);
80+
kfree(user);
81+
return NULL;
5482
}
5583

5684
void ksmbd_free_user(struct ksmbd_user *user)
5785
{
5886
ksmbd_ipc_logout_request(user->name, user->flags);
87+
kfree(user->sgid);
5988
kfree(user->name);
6089
kfree(user->passkey);
6190
kfree(user);

fs/smb/server/mgmt/user_config.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ struct ksmbd_user {
1818

1919
size_t passkey_sz;
2020
char *passkey;
21+
int ngroups;
22+
gid_t *sgid;
2123
};
2224

2325
static inline bool user_guest(struct ksmbd_user *user)
@@ -60,7 +62,8 @@ static inline unsigned int user_gid(struct ksmbd_user *user)
6062
}
6163

6264
struct ksmbd_user *ksmbd_login_user(const char *account);
63-
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp);
65+
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
66+
struct ksmbd_login_response_ext *resp_ext);
6467
void ksmbd_free_user(struct ksmbd_user *user);
6568
int ksmbd_anonymous_user(struct ksmbd_user *user);
6669
bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2);

fs/smb/server/mgmt/user_session.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,10 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
177177

178178
down_write(&conn->session_lock);
179179
xa_for_each(&conn->sessions, id, sess) {
180-
if (sess->state != SMB2_SESSION_VALID ||
181-
time_after(jiffies,
182-
sess->last_active + SMB2_SESSION_TIMEOUT)) {
180+
if (atomic_read(&sess->refcnt) == 0 &&
181+
(sess->state != SMB2_SESSION_VALID ||
182+
time_after(jiffies,
183+
sess->last_active + SMB2_SESSION_TIMEOUT))) {
183184
xa_erase(&conn->sessions, sess->id);
184185
hash_del(&sess->hlist);
185186
ksmbd_session_destroy(sess);
@@ -269,8 +270,6 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
269270

270271
down_read(&sessions_table_lock);
271272
sess = __session_lookup(id);
272-
if (sess)
273-
sess->last_active = jiffies;
274273
up_read(&sessions_table_lock);
275274

276275
return sess;
@@ -289,6 +288,22 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
289288
return sess;
290289
}
291290

291+
void ksmbd_user_session_get(struct ksmbd_session *sess)
292+
{
293+
atomic_inc(&sess->refcnt);
294+
}
295+
296+
void ksmbd_user_session_put(struct ksmbd_session *sess)
297+
{
298+
if (!sess)
299+
return;
300+
301+
if (atomic_read(&sess->refcnt) <= 0)
302+
WARN_ON(1);
303+
else
304+
atomic_dec(&sess->refcnt);
305+
}
306+
292307
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
293308
u64 sess_id)
294309
{
@@ -393,6 +408,7 @@ static struct ksmbd_session *__session_create(int protocol)
393408
xa_init(&sess->rpc_handle_list);
394409
sess->sequence_number = 1;
395410
rwlock_init(&sess->tree_conns_lock);
411+
atomic_set(&sess->refcnt, 1);
396412

397413
ret = __init_smb2_session(sess);
398414
if (ret)

fs/smb/server/mgmt/user_session.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ struct ksmbd_session {
6161
struct ksmbd_file_table file_table;
6262
unsigned long last_active;
6363
rwlock_t tree_conns_lock;
64+
65+
atomic_t refcnt;
6466
};
6567

6668
static inline int test_session_flag(struct ksmbd_session *sess, int bit)
@@ -104,4 +106,6 @@ void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id);
104106
int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name);
105107
void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id);
106108
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id);
109+
void ksmbd_user_session_get(struct ksmbd_session *sess);
110+
void ksmbd_user_session_put(struct ksmbd_session *sess);
107111
#endif /* __USER_SESSION_MANAGEMENT_H__ */

fs/smb/server/server.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
238238
} while (is_chained == true);
239239

240240
send:
241+
if (work->sess)
242+
ksmbd_user_session_put(work->sess);
241243
if (work->tcon)
242244
ksmbd_tree_connect_put(work->tcon);
243245
smb3_preauth_hash_rsp(work);

fs/smb/server/smb2pdu.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,10 @@ int smb2_check_user_session(struct ksmbd_work *work)
605605

606606
/* Check for validity of user session */
607607
work->sess = ksmbd_session_lookup_all(conn, sess_id);
608-
if (work->sess)
608+
if (work->sess) {
609+
ksmbd_user_session_get(work->sess);
609610
return 1;
611+
}
610612
ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id);
611613
return -ENOENT;
612614
}
@@ -1740,6 +1742,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
17401742
}
17411743

17421744
conn->binding = true;
1745+
ksmbd_user_session_get(sess);
17431746
} else if ((conn->dialect < SMB30_PROT_ID ||
17441747
server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) &&
17451748
(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
@@ -1766,6 +1769,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
17661769
}
17671770

17681771
conn->binding = false;
1772+
ksmbd_user_session_get(sess);
17691773
}
17701774
work->sess = sess;
17711775

@@ -2228,7 +2232,9 @@ int smb2_session_logoff(struct ksmbd_work *work)
22282232
}
22292233

22302234
ksmbd_destroy_file_table(&sess->file_table);
2235+
down_write(&conn->session_lock);
22312236
sess->state = SMB2_SESSION_EXPIRED;
2237+
up_write(&conn->session_lock);
22322238

22332239
ksmbd_free_user(sess->user);
22342240
sess->user = NULL;

fs/smb/server/smb_common.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -736,13 +736,15 @@ int __ksmbd_override_fsids(struct ksmbd_work *work,
736736
struct ksmbd_share_config *share)
737737
{
738738
struct ksmbd_session *sess = work->sess;
739+
struct ksmbd_user *user = sess->user;
739740
struct cred *cred;
740741
struct group_info *gi;
741742
unsigned int uid;
742743
unsigned int gid;
744+
int i;
743745

744-
uid = user_uid(sess->user);
745-
gid = user_gid(sess->user);
746+
uid = user_uid(user);
747+
gid = user_gid(user);
746748
if (share->force_uid != KSMBD_SHARE_INVALID_UID)
747749
uid = share->force_uid;
748750
if (share->force_gid != KSMBD_SHARE_INVALID_GID)
@@ -755,11 +757,18 @@ int __ksmbd_override_fsids(struct ksmbd_work *work,
755757
cred->fsuid = make_kuid(&init_user_ns, uid);
756758
cred->fsgid = make_kgid(&init_user_ns, gid);
757759

758-
gi = groups_alloc(0);
760+
gi = groups_alloc(user->ngroups);
759761
if (!gi) {
760762
abort_creds(cred);
761763
return -ENOMEM;
762764
}
765+
766+
for (i = 0; i < user->ngroups; i++)
767+
gi->gid[i] = make_kgid(&init_user_ns, user->sgid[i]);
768+
769+
if (user->ngroups)
770+
groups_sort(gi);
771+
763772
set_groups(cred, gi);
764773
put_group_info(gi);
765774

0 commit comments

Comments
 (0)