]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ksmbd: destroy expired sessions
authorNamjae Jeon <linkinjeon@kernel.org>
Tue, 2 May 2023 23:42:21 +0000 (08:42 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 May 2023 11:58:55 +0000 (13:58 +0200)
[ 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: Sasha Levin <sashal@kernel.org>
fs/ksmbd/mgmt/user_session.c
fs/ksmbd/mgmt/user_session.h
fs/ksmbd/smb2pdu.c
fs/ksmbd/smb2pdu.h

index 69b85a98e2c358b26cde630970a9fa18674249d1..b809f7987b9f4ffeb0f7734b3377b62ad69486d4 100644 (file)
@@ -174,70 +174,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)
@@ -246,6 +249,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;
@@ -324,6 +329,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 44a3c67b2bd9232714660df959ad098c593d07fa..51f38e5b61abb48f235ccdc34aa4a8b96e1901f0 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 51e95ea37195bfb72ced239d1cf1511b835cb2ec..d6423f2fae6d9f7dad2274e05b32c112b3d9229f 100644 (file)
@@ -1862,6 +1862,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) {
                                ksmbd_conn_set_need_reconnect(conn);
index 0c8a770fe3189b0caeea15c6e04009f5864560c3..df05c9b2504d4060b13e9b86f59edc754849a5e7 100644 (file)
@@ -61,6 +61,8 @@ struct preauth_integrity_info {
 #define SMB2_SESSION_IN_PROGRESS       BIT(0)
 #define SMB2_SESSION_VALID             BIT(1)
 
+#define SMB2_SESSION_TIMEOUT           (10 * HZ)
+
 struct create_durable_req_v2 {
        struct create_context ccontext;
        __u8   Name[8];