Commit 8c79f1ee authored by Namjae Jeon's avatar Namjae Jeon Committed by ZhaoLong Wang
Browse files

ksmbd: destroy expired sessions

mainline inclusion
from mainline-v6.4-rc1
commit ea174a91
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I74FHK
CVE: CVE-2023-32247

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ea174a91893956450510945a0c5d1a10b5323656



--------------------------------

client can indefinitely send smb2 session setup requests with
the SessionId set to 0, thus indefinitely spawning new sessions,
and causing indefinite memory usage. This patch limit to the number
of sessions using expired timeout and session state.

Cc: stable@vger.kernel.org
Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-20478
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
Signed-off-by: default avatarZhaoLong Wang <wangzhaolong1@huawei.com>
parent e80d0dac
Loading
Loading
Loading
Loading
+37 −31
Original line number Diff line number Diff line
@@ -172,70 +172,73 @@ static struct ksmbd_session *__session_lookup(unsigned long long id)
	struct ksmbd_session *sess;

	hash_for_each_possible(sessions_table, sess, hlist, id) {
		if (id == sess->id)
		if (id == sess->id) {
			sess->last_active = jiffies;
			return sess;
		}
	}
	return NULL;
}

static void ksmbd_expire_session(struct ksmbd_conn *conn)
{
	unsigned long id;
	struct ksmbd_session *sess;

	xa_for_each(&conn->sessions, id, sess) {
		if (sess->state != SMB2_SESSION_VALID ||
		    time_after(jiffies,
			       sess->last_active + SMB2_SESSION_TIMEOUT)) {
			xa_erase(&conn->sessions, sess->id);
			ksmbd_session_destroy(sess);
			continue;
		}
	}
}

int ksmbd_session_register(struct ksmbd_conn *conn,
			   struct ksmbd_session *sess)
{
	sess->dialect = conn->dialect;
	memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
	ksmbd_expire_session(conn);
	return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL));
}

static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)
static void ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)
{
	struct channel *chann;

	chann = xa_erase(&sess->ksmbd_chann_list, (long)conn);
	if (!chann)
		return -ENOENT;
		return;

	kfree(chann);

	return 0;
}

void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
{
	struct ksmbd_session *sess;

	if (conn->binding) {
		int bkt;

		down_write(&sessions_table_lock);
		hash_for_each(sessions_table, bkt, sess, hlist) {
			if (!ksmbd_chann_del(conn, sess)) {
				up_write(&sessions_table_lock);
				goto sess_destroy;
			}
		}
		up_write(&sessions_table_lock);
	} else {
	unsigned long id;

	xa_for_each(&conn->sessions, id, sess) {
			if (!ksmbd_chann_del(conn, sess))
				goto sess_destroy;
		}
	}

	return;

sess_destroy:
		ksmbd_chann_del(conn, sess);
		if (xa_empty(&sess->ksmbd_chann_list)) {
			xa_erase(&conn->sessions, sess->id);
			ksmbd_session_destroy(sess);
		}
	}
}

struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
					   unsigned long long id)
{
	return xa_load(&conn->sessions, id);
	struct ksmbd_session *sess;

	sess = xa_load(&conn->sessions, id);
	if (sess)
		sess->last_active = jiffies;
	return sess;
}

struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
@@ -244,6 +247,8 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)

	down_read(&sessions_table_lock);
	sess = __session_lookup(id);
	if (sess)
		sess->last_active = jiffies;
	up_read(&sessions_table_lock);

	return sess;
@@ -322,6 +327,7 @@ static struct ksmbd_session *__session_create(int protocol)
	if (ksmbd_init_file_table(&sess->file_table))
		goto error;

	sess->last_active = jiffies;
	sess->state = SMB2_SESSION_IN_PROGRESS;
	set_session_flag(sess, protocol);
	xa_init(&sess->tree_conns);
+1 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ struct ksmbd_session {
	__u8				smb3signingkey[SMB3_SIGN_KEY_SIZE];

	struct ksmbd_file_table		file_table;
	unsigned long			last_active;
};

static inline int test_session_flag(struct ksmbd_session *sess, int bit)
+1 −0
Original line number Diff line number Diff line
@@ -1890,6 +1890,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
			if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION)
				try_delay = true;

			sess->last_active = jiffies;
			sess->state = SMB2_SESSION_EXPIRED;
			if (try_delay)
				ssleep(5);
+2 −0
Original line number Diff line number Diff line
@@ -367,6 +367,8 @@ struct smb2_negotiate_rsp {
#define SMB2_SESSION_IN_PROGRESS	BIT(0)
#define SMB2_SESSION_VALID		BIT(1)

#define SMB2_SESSION_TIMEOUT		(10 * HZ)

/* Flags */
#define SMB2_SESSION_REQ_FLAG_BINDING		0x01
#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA	0x04