4 ksmbd SMB3 server fixes, all also for stable

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmfuvXUACgkQiiy9cAdy
 T1FoiAv9FM0WxnmB4NWk6+BOjaonbY5eWhP3Eh4H+rCvSG5kaXrZKDFs7vRqylrA
 cOg1TfF0Vop76i/bxkeI6hSXwYqApbb9sVNnP5KyEe7QB9gO0qiDXrEAz7C912cX
 HMbt9Mi3pPCEMn8/s/Z4yMKiDoIu7dIbsFLOmvSvP7+lyOrVrmuHy4m1/Od80/TP
 5jKiBhKFIDuxEnEuVPNdAxvyU/ZGG41TFJ9Q39+/85KiPnnzMUyhSNrtI87RU7EF
 v9L9LNNUShiHSvFJc3emXAIje0uKLN7SBVnFBayGsf/rkLDC/vJnZDU0h0QvDS9E
 ganTjAZJvD0et0qx01zu98PbJjNxSevPvpaCLHMTbiURKAVEZv9Wu0SJPRRLNYfm
 /wS7PtiN6xthXPBKEt6OCjfAkesBqiePPSLc082VAKjfgbH1Xl00KROE+kRPcgKd
 E19ug3g+kEzv2ohc9ZZ6THQxv4zq1whHyqMQFD01wCMUiXLhjrMpUndQgzdQm52P
 1bdPofxj
 =UsDp
 -----END PGP SIGNATURE-----

Merge tag 'v6.15rc-part2-ksmbd-server-fixes' of git://git.samba.org/ksmbd

Pull smb server fixes from Steve French:
 "Four ksmbd SMB3 server fixes, all also for stable"

* tag 'v6.15rc-part2-ksmbd-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: fix null pointer dereference in alloc_preauth_hash()
  ksmbd: validate zero num_subauth before sub_auth is accessed
  ksmbd: fix overflow in dacloffset bounds check
  ksmbd: fix session use-after-free in multichannel connection
This commit is contained in:
Linus Torvalds 2025-04-03 16:18:06 -07:00
commit 06a22366d6
5 changed files with 55 additions and 20 deletions

View File

@ -1016,9 +1016,9 @@ static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id,
ses_enc_key = enc ? sess->smb3encryptionkey :
sess->smb3decryptionkey;
if (enc)
ksmbd_user_session_get(sess);
memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE);
if (!enc)
ksmbd_user_session_put(sess);
return 0;
}

View File

@ -27,6 +27,7 @@ enum {
KSMBD_SESS_EXITING,
KSMBD_SESS_NEED_RECONNECT,
KSMBD_SESS_NEED_NEGOTIATE,
KSMBD_SESS_NEED_SETUP,
KSMBD_SESS_RELEASING
};
@ -187,6 +188,11 @@ static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn)
return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE;
}
static inline bool ksmbd_conn_need_setup(struct ksmbd_conn *conn)
{
return READ_ONCE(conn->status) == KSMBD_SESS_NEED_SETUP;
}
static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn)
{
return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT;
@ -217,6 +223,11 @@ static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_conn *conn)
WRITE_ONCE(conn->status, KSMBD_SESS_NEED_NEGOTIATE);
}
static inline void ksmbd_conn_set_need_setup(struct ksmbd_conn *conn)
{
WRITE_ONCE(conn->status, KSMBD_SESS_NEED_SETUP);
}
static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn)
{
WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT);

View File

@ -181,7 +181,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
down_write(&sessions_table_lock);
down_write(&conn->session_lock);
xa_for_each(&conn->sessions, id, sess) {
if (atomic_read(&sess->refcnt) == 0 &&
if (atomic_read(&sess->refcnt) <= 1 &&
(sess->state != SMB2_SESSION_VALID ||
time_after(jiffies,
sess->last_active + SMB2_SESSION_TIMEOUT))) {
@ -233,7 +233,8 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
down_write(&conn->session_lock);
xa_erase(&conn->sessions, sess->id);
up_write(&conn->session_lock);
ksmbd_session_destroy(sess);
if (atomic_dec_and_test(&sess->refcnt))
ksmbd_session_destroy(sess);
}
}
}
@ -252,7 +253,8 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
if (xa_empty(&sess->ksmbd_chann_list)) {
xa_erase(&conn->sessions, sess->id);
hash_del(&sess->hlist);
ksmbd_session_destroy(sess);
if (atomic_dec_and_test(&sess->refcnt))
ksmbd_session_destroy(sess);
}
}
up_write(&conn->session_lock);
@ -328,8 +330,8 @@ void ksmbd_user_session_put(struct ksmbd_session *sess)
if (atomic_read(&sess->refcnt) <= 0)
WARN_ON(1);
else
atomic_dec(&sess->refcnt);
else if (atomic_dec_and_test(&sess->refcnt))
ksmbd_session_destroy(sess);
}
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
@ -372,13 +374,13 @@ void destroy_previous_session(struct ksmbd_conn *conn,
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_RECONNECT);
err = ksmbd_conn_wait_idle_sess_id(conn, id);
if (err) {
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
goto out;
}
ksmbd_destroy_file_table(&prev_sess->file_table);
prev_sess->state = SMB2_SESSION_EXPIRED;
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
ksmbd_launch_ksmbd_durable_scavenger();
out:
up_write(&conn->session_lock);
@ -436,7 +438,7 @@ static struct ksmbd_session *__session_create(int protocol)
xa_init(&sess->rpc_handle_list);
sess->sequence_number = 1;
rwlock_init(&sess->tree_conns_lock);
atomic_set(&sess->refcnt, 1);
atomic_set(&sess->refcnt, 2);
ret = __init_smb2_session(sess);
if (ret)

View File

@ -1249,7 +1249,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
}
conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode);
ksmbd_conn_set_need_negotiate(conn);
ksmbd_conn_set_need_setup(conn);
err_out:
ksmbd_conn_unlock(conn);
@ -1271,6 +1271,9 @@ static int alloc_preauth_hash(struct ksmbd_session *sess,
if (sess->Preauth_HashValue)
return 0;
if (!conn->preauth_info)
return -ENOMEM;
sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue,
PREAUTH_HASHVALUE_SIZE, KSMBD_DEFAULT_GFP);
if (!sess->Preauth_HashValue)
@ -1674,6 +1677,11 @@ int smb2_sess_setup(struct ksmbd_work *work)
ksmbd_debug(SMB, "Received smb2 session setup request\n");
if (!ksmbd_conn_need_setup(conn) && !ksmbd_conn_good(conn)) {
work->send_no_response = 1;
return rc;
}
WORK_BUFFERS(work, req, rsp);
rsp->StructureSize = cpu_to_le16(9);
@ -1909,7 +1917,7 @@ out_err:
if (try_delay) {
ksmbd_conn_set_need_reconnect(conn);
ssleep(5);
ksmbd_conn_set_need_negotiate(conn);
ksmbd_conn_set_need_setup(conn);
}
}
smb2_set_err_rsp(work);
@ -2235,14 +2243,15 @@ int smb2_session_logoff(struct ksmbd_work *work)
return -ENOENT;
}
ksmbd_destroy_file_table(&sess->file_table);
down_write(&conn->session_lock);
sess->state = SMB2_SESSION_EXPIRED;
up_write(&conn->session_lock);
ksmbd_free_user(sess->user);
sess->user = NULL;
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE);
if (sess->user) {
ksmbd_free_user(sess->user);
sess->user = NULL;
}
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_SETUP);
rsp->StructureSize = cpu_to_le16(4);
err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));

View File

@ -270,6 +270,11 @@ static int sid_to_id(struct mnt_idmap *idmap,
return -EIO;
}
if (psid->num_subauth == 0) {
pr_err("%s: zero subauthorities!\n", __func__);
return -EIO;
}
if (sidtype == SIDOWNER) {
kuid_t uid;
uid_t id;
@ -1026,7 +1031,9 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
struct dentry *parent = path->dentry->d_parent;
struct mnt_idmap *idmap = mnt_idmap(path->mnt);
int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size;
int rc = 0, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
int rc = 0, pntsd_type, pntsd_size, acl_len, aces_size;
unsigned int dacloffset;
size_t dacl_struct_end;
u16 num_aces, ace_cnt = 0;
char *aces_base;
bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
@ -1035,8 +1042,11 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
parent, &parent_pntsd);
if (pntsd_size <= 0)
return -ENOENT;
dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) {
if (!dacloffset ||
check_add_overflow(dacloffset, sizeof(struct smb_acl), &dacl_struct_end) ||
dacl_struct_end > (size_t)pntsd_size) {
rc = -EINVAL;
goto free_parent_pntsd;
}
@ -1240,7 +1250,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
struct smb_ntsd *pntsd = NULL;
struct smb_acl *pdacl;
struct posix_acl *posix_acls;
int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset;
int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size;
unsigned int dacl_offset;
size_t dacl_struct_end;
struct smb_sid sid;
int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
struct smb_ace *ace;
@ -1259,7 +1271,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
dacl_offset = le32_to_cpu(pntsd->dacloffset);
if (!dacl_offset ||
(dacl_offset + sizeof(struct smb_acl) > pntsd_size))
check_add_overflow(dacl_offset, sizeof(struct smb_acl), &dacl_struct_end) ||
dacl_struct_end > (size_t)pntsd_size)
goto err_out;
pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));