mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 18:41:58 +09:00
ksmbd: destroy expired sessions
[ Upstream commit ea174a9189 ]
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: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
f623f627ad
commit
1fc8a2b14e
@@ -174,70 +174,73 @@ static struct ksmbd_session *__session_lookup(unsigned long long id)
|
|||||||
struct ksmbd_session *sess;
|
struct ksmbd_session *sess;
|
||||||
|
|
||||||
hash_for_each_possible(sessions_table, sess, hlist, id) {
|
hash_for_each_possible(sessions_table, sess, hlist, id) {
|
||||||
if (id == sess->id)
|
if (id == sess->id) {
|
||||||
|
sess->last_active = jiffies;
|
||||||
return sess;
|
return sess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
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,
|
int ksmbd_session_register(struct ksmbd_conn *conn,
|
||||||
struct ksmbd_session *sess)
|
struct ksmbd_session *sess)
|
||||||
{
|
{
|
||||||
sess->dialect = conn->dialect;
|
sess->dialect = conn->dialect;
|
||||||
memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
|
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));
|
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;
|
struct channel *chann;
|
||||||
|
|
||||||
chann = xa_erase(&sess->ksmbd_chann_list, (long)conn);
|
chann = xa_erase(&sess->ksmbd_chann_list, (long)conn);
|
||||||
if (!chann)
|
if (!chann)
|
||||||
return -ENOENT;
|
return;
|
||||||
|
|
||||||
kfree(chann);
|
kfree(chann);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
|
void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
|
||||||
{
|
{
|
||||||
struct ksmbd_session *sess;
|
struct ksmbd_session *sess;
|
||||||
|
unsigned long id;
|
||||||
|
|
||||||
if (conn->binding) {
|
xa_for_each(&conn->sessions, id, sess) {
|
||||||
int bkt;
|
ksmbd_chann_del(conn, sess);
|
||||||
|
if (xa_empty(&sess->ksmbd_chann_list)) {
|
||||||
down_write(&sessions_table_lock);
|
xa_erase(&conn->sessions, sess->id);
|
||||||
hash_for_each(sessions_table, bkt, sess, hlist) {
|
ksmbd_session_destroy(sess);
|
||||||
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:
|
|
||||||
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,
|
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
|
||||||
unsigned long long id)
|
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)
|
struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
|
||||||
@@ -246,6 +249,8 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
|
|||||||
|
|
||||||
down_read(&sessions_table_lock);
|
down_read(&sessions_table_lock);
|
||||||
sess = __session_lookup(id);
|
sess = __session_lookup(id);
|
||||||
|
if (sess)
|
||||||
|
sess->last_active = jiffies;
|
||||||
up_read(&sessions_table_lock);
|
up_read(&sessions_table_lock);
|
||||||
|
|
||||||
return sess;
|
return sess;
|
||||||
@@ -324,6 +329,7 @@ static struct ksmbd_session *__session_create(int protocol)
|
|||||||
if (ksmbd_init_file_table(&sess->file_table))
|
if (ksmbd_init_file_table(&sess->file_table))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
sess->last_active = jiffies;
|
||||||
sess->state = SMB2_SESSION_IN_PROGRESS;
|
sess->state = SMB2_SESSION_IN_PROGRESS;
|
||||||
set_session_flag(sess, protocol);
|
set_session_flag(sess, protocol);
|
||||||
xa_init(&sess->tree_conns);
|
xa_init(&sess->tree_conns);
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ struct ksmbd_session {
|
|||||||
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
|
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
|
||||||
|
|
||||||
struct ksmbd_file_table file_table;
|
struct ksmbd_file_table file_table;
|
||||||
|
unsigned long last_active;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int test_session_flag(struct ksmbd_session *sess, int bit)
|
static inline int test_session_flag(struct ksmbd_session *sess, int bit)
|
||||||
|
|||||||
@@ -1860,6 +1860,7 @@ out_err:
|
|||||||
if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION)
|
if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION)
|
||||||
try_delay = true;
|
try_delay = true;
|
||||||
|
|
||||||
|
sess->last_active = jiffies;
|
||||||
sess->state = SMB2_SESSION_EXPIRED;
|
sess->state = SMB2_SESSION_EXPIRED;
|
||||||
if (try_delay)
|
if (try_delay)
|
||||||
ssleep(5);
|
ssleep(5);
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ struct preauth_integrity_info {
|
|||||||
#define SMB2_SESSION_IN_PROGRESS BIT(0)
|
#define SMB2_SESSION_IN_PROGRESS BIT(0)
|
||||||
#define SMB2_SESSION_VALID BIT(1)
|
#define SMB2_SESSION_VALID BIT(1)
|
||||||
|
|
||||||
|
#define SMB2_SESSION_TIMEOUT (10 * HZ)
|
||||||
|
|
||||||
struct create_durable_req_v2 {
|
struct create_durable_req_v2 {
|
||||||
struct create_context ccontext;
|
struct create_context ccontext;
|
||||||
__u8 Name[8];
|
__u8 Name[8];
|
||||||
|
|||||||
Reference in New Issue
Block a user