]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ksmbd: destroy expired sessions
authorNamjae Jeon <linkinjeon@kernel.org>
Mon, 18 Dec 2023 15:33:49 +0000 (00:33 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 23 Dec 2023 09:41:55 +0000 (10:41 +0100)
[ Upstream commit 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: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ksmbd/mgmt/user_session.c
fs/ksmbd/mgmt/user_session.h
fs/ksmbd/smb2pdu.c
fs/ksmbd/smb2pdu.h

index 68d40025cfbf78f688d72e97eb90300f1a4a7b22..3840de7773b9c43c55287b2b5e9a4c49c60cfb09 100644 (file)
@@ -165,70 +165,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;
+       unsigned long id;
 
-       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;
-                       }
+       xa_for_each(&conn->sessions, id, sess) {
+               ksmbd_chann_del(conn, sess);
+               if (xa_empty(&sess->ksmbd_chann_list)) {
+                       xa_erase(&conn->sessions, sess->id);
+                       ksmbd_session_destroy(sess);
                }
-               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,
                                           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)
@@ -237,6 +240,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;
@@ -315,6 +320,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);
index b6a9e7a6aae45e18cb4ae5829e5330247acaccaa..f99d475b28db45f96ae559c7368220a5dcd1a69e 100644 (file)
@@ -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)
index dfe4fafd6cdef9c635b0cb6e05979e7f9d964174..3fe827e1584a0832a749f85cbc9b9ba948c17e09 100644 (file)
@@ -1875,6 +1875,7 @@ out_err:
                        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);
index 9cde1f8e842824627832abfe1120d7fb78ca4287..a774889e0aa546946e5aeb8f4290a49c8de0af65 100644 (file)
@@ -619,6 +619,8 @@ struct create_context {
        __u8 Buffer[0];
 } __packed;
 
+#define SMB2_SESSION_TIMEOUT           (10 * HZ)
+
 struct create_durable_req_v2 {
        struct create_context ccontext;
        __u8   Name[8];