mirror of https://github.com/torvalds/linux.git
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:
commit
06a22366d6
|
|
@ -1016,9 +1016,9 @@ static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id,
|
||||||
|
|
||||||
ses_enc_key = enc ? sess->smb3encryptionkey :
|
ses_enc_key = enc ? sess->smb3encryptionkey :
|
||||||
sess->smb3decryptionkey;
|
sess->smb3decryptionkey;
|
||||||
if (enc)
|
|
||||||
ksmbd_user_session_get(sess);
|
|
||||||
memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE);
|
memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE);
|
||||||
|
if (!enc)
|
||||||
|
ksmbd_user_session_put(sess);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ enum {
|
||||||
KSMBD_SESS_EXITING,
|
KSMBD_SESS_EXITING,
|
||||||
KSMBD_SESS_NEED_RECONNECT,
|
KSMBD_SESS_NEED_RECONNECT,
|
||||||
KSMBD_SESS_NEED_NEGOTIATE,
|
KSMBD_SESS_NEED_NEGOTIATE,
|
||||||
|
KSMBD_SESS_NEED_SETUP,
|
||||||
KSMBD_SESS_RELEASING
|
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;
|
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)
|
static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn)
|
||||||
{
|
{
|
||||||
return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT;
|
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);
|
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)
|
static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn)
|
||||||
{
|
{
|
||||||
WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT);
|
WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT);
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
|
||||||
down_write(&sessions_table_lock);
|
down_write(&sessions_table_lock);
|
||||||
down_write(&conn->session_lock);
|
down_write(&conn->session_lock);
|
||||||
xa_for_each(&conn->sessions, id, sess) {
|
xa_for_each(&conn->sessions, id, sess) {
|
||||||
if (atomic_read(&sess->refcnt) == 0 &&
|
if (atomic_read(&sess->refcnt) <= 1 &&
|
||||||
(sess->state != SMB2_SESSION_VALID ||
|
(sess->state != SMB2_SESSION_VALID ||
|
||||||
time_after(jiffies,
|
time_after(jiffies,
|
||||||
sess->last_active + SMB2_SESSION_TIMEOUT))) {
|
sess->last_active + SMB2_SESSION_TIMEOUT))) {
|
||||||
|
|
@ -233,7 +233,8 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
|
||||||
down_write(&conn->session_lock);
|
down_write(&conn->session_lock);
|
||||||
xa_erase(&conn->sessions, sess->id);
|
xa_erase(&conn->sessions, sess->id);
|
||||||
up_write(&conn->session_lock);
|
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)) {
|
if (xa_empty(&sess->ksmbd_chann_list)) {
|
||||||
xa_erase(&conn->sessions, sess->id);
|
xa_erase(&conn->sessions, sess->id);
|
||||||
hash_del(&sess->hlist);
|
hash_del(&sess->hlist);
|
||||||
ksmbd_session_destroy(sess);
|
if (atomic_dec_and_test(&sess->refcnt))
|
||||||
|
ksmbd_session_destroy(sess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
up_write(&conn->session_lock);
|
up_write(&conn->session_lock);
|
||||||
|
|
@ -328,8 +330,8 @@ void ksmbd_user_session_put(struct ksmbd_session *sess)
|
||||||
|
|
||||||
if (atomic_read(&sess->refcnt) <= 0)
|
if (atomic_read(&sess->refcnt) <= 0)
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
else
|
else if (atomic_dec_and_test(&sess->refcnt))
|
||||||
atomic_dec(&sess->refcnt);
|
ksmbd_session_destroy(sess);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
|
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);
|
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_RECONNECT);
|
||||||
err = ksmbd_conn_wait_idle_sess_id(conn, id);
|
err = ksmbd_conn_wait_idle_sess_id(conn, id);
|
||||||
if (err) {
|
if (err) {
|
||||||
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
|
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ksmbd_destroy_file_table(&prev_sess->file_table);
|
ksmbd_destroy_file_table(&prev_sess->file_table);
|
||||||
prev_sess->state = SMB2_SESSION_EXPIRED;
|
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();
|
ksmbd_launch_ksmbd_durable_scavenger();
|
||||||
out:
|
out:
|
||||||
up_write(&conn->session_lock);
|
up_write(&conn->session_lock);
|
||||||
|
|
@ -436,7 +438,7 @@ static struct ksmbd_session *__session_create(int protocol)
|
||||||
xa_init(&sess->rpc_handle_list);
|
xa_init(&sess->rpc_handle_list);
|
||||||
sess->sequence_number = 1;
|
sess->sequence_number = 1;
|
||||||
rwlock_init(&sess->tree_conns_lock);
|
rwlock_init(&sess->tree_conns_lock);
|
||||||
atomic_set(&sess->refcnt, 1);
|
atomic_set(&sess->refcnt, 2);
|
||||||
|
|
||||||
ret = __init_smb2_session(sess);
|
ret = __init_smb2_session(sess);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
||||||
|
|
@ -1249,7 +1249,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode);
|
conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode);
|
||||||
ksmbd_conn_set_need_negotiate(conn);
|
ksmbd_conn_set_need_setup(conn);
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
ksmbd_conn_unlock(conn);
|
ksmbd_conn_unlock(conn);
|
||||||
|
|
@ -1271,6 +1271,9 @@ static int alloc_preauth_hash(struct ksmbd_session *sess,
|
||||||
if (sess->Preauth_HashValue)
|
if (sess->Preauth_HashValue)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (!conn->preauth_info)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue,
|
sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue,
|
||||||
PREAUTH_HASHVALUE_SIZE, KSMBD_DEFAULT_GFP);
|
PREAUTH_HASHVALUE_SIZE, KSMBD_DEFAULT_GFP);
|
||||||
if (!sess->Preauth_HashValue)
|
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");
|
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);
|
WORK_BUFFERS(work, req, rsp);
|
||||||
|
|
||||||
rsp->StructureSize = cpu_to_le16(9);
|
rsp->StructureSize = cpu_to_le16(9);
|
||||||
|
|
@ -1909,7 +1917,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
|
||||||
if (try_delay) {
|
if (try_delay) {
|
||||||
ksmbd_conn_set_need_reconnect(conn);
|
ksmbd_conn_set_need_reconnect(conn);
|
||||||
ssleep(5);
|
ssleep(5);
|
||||||
ksmbd_conn_set_need_negotiate(conn);
|
ksmbd_conn_set_need_setup(conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
smb2_set_err_rsp(work);
|
smb2_set_err_rsp(work);
|
||||||
|
|
@ -2235,14 +2243,15 @@ int smb2_session_logoff(struct ksmbd_work *work)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
ksmbd_destroy_file_table(&sess->file_table);
|
|
||||||
down_write(&conn->session_lock);
|
down_write(&conn->session_lock);
|
||||||
sess->state = SMB2_SESSION_EXPIRED;
|
sess->state = SMB2_SESSION_EXPIRED;
|
||||||
up_write(&conn->session_lock);
|
up_write(&conn->session_lock);
|
||||||
|
|
||||||
ksmbd_free_user(sess->user);
|
if (sess->user) {
|
||||||
sess->user = NULL;
|
ksmbd_free_user(sess->user);
|
||||||
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE);
|
sess->user = NULL;
|
||||||
|
}
|
||||||
|
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_SETUP);
|
||||||
|
|
||||||
rsp->StructureSize = cpu_to_le16(4);
|
rsp->StructureSize = cpu_to_le16(4);
|
||||||
err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
|
err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,11 @@ static int sid_to_id(struct mnt_idmap *idmap,
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (psid->num_subauth == 0) {
|
||||||
|
pr_err("%s: zero subauthorities!\n", __func__);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
if (sidtype == SIDOWNER) {
|
if (sidtype == SIDOWNER) {
|
||||||
kuid_t uid;
|
kuid_t uid;
|
||||||
uid_t id;
|
uid_t id;
|
||||||
|
|
@ -1026,7 +1031,9 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
|
||||||
struct dentry *parent = path->dentry->d_parent;
|
struct dentry *parent = path->dentry->d_parent;
|
||||||
struct mnt_idmap *idmap = mnt_idmap(path->mnt);
|
struct mnt_idmap *idmap = mnt_idmap(path->mnt);
|
||||||
int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size;
|
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;
|
u16 num_aces, ace_cnt = 0;
|
||||||
char *aces_base;
|
char *aces_base;
|
||||||
bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
|
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);
|
parent, &parent_pntsd);
|
||||||
if (pntsd_size <= 0)
|
if (pntsd_size <= 0)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
|
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;
|
rc = -EINVAL;
|
||||||
goto free_parent_pntsd;
|
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_ntsd *pntsd = NULL;
|
||||||
struct smb_acl *pdacl;
|
struct smb_acl *pdacl;
|
||||||
struct posix_acl *posix_acls;
|
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;
|
struct smb_sid sid;
|
||||||
int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
|
int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
|
||||||
struct smb_ace *ace;
|
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);
|
dacl_offset = le32_to_cpu(pntsd->dacloffset);
|
||||||
if (!dacl_offset ||
|
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;
|
goto err_out;
|
||||||
|
|
||||||
pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
|
pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue