Commit c8b5b7c5 authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French
Browse files

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: default avatarSteve French <stfrench@microsoft.com>
Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-26505
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent bf21e29d
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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);
+2 −2
Original line number Diff line number Diff line
@@ -374,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);
+11 −3
Original line number Diff line number Diff line
@@ -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 @@ int smb2_sess_setup(struct ksmbd_work *work)
			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);
@@ -2243,7 +2251,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
		ksmbd_free_user(sess->user);
		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);
	err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));