ksmbd: fix null pointer dereference in alloc_preauth_hash()

The Client send malformed smb2 negotiate request. ksmbd return error
response. Subsequently, the client can send smb2 session setup even
thought conn->preauth_info is not allocated.
This patch add KSMBD_SESS_NEED_SETUP status of connection to ignore
session setup request if smb2 negotiate phase is not complete.

Cc: stable@vger.kernel.org
Tested-by: Steve French <stfrench@microsoft.com>
Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-26505
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Namjae Jeon 2025-04-02 09:11:23 +09:00 committed by Steve French
parent bf21e29d78
commit c8b5b7c5da
3 changed files with 24 additions and 5 deletions

View File

@ -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);

View File

@ -374,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);

View File

@ -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 @@ out_err:
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);
@ -2243,7 +2251,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
ksmbd_free_user(sess->user); ksmbd_free_user(sess->user);
sess->user = NULL; sess->user = NULL;
} }
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); 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));