]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ksmbd: validate session id and tree id in the compound request
authorNamjae Jeon <linkinjeon@kernel.org>
Thu, 15 Jun 2023 13:05:29 +0000 (22:05 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 28 Jun 2023 09:12:18 +0000 (11:12 +0200)
commit 5005bcb4219156f1bf7587b185080ec1da08518e upstream.

This patch validate session id and tree id in compound request.
If first operation in the compound is SMB2 ECHO request, ksmbd bypass
session and tree validation. So work->sess and work->tcon could be NULL.
If secound request in the compound access work->sess or tcon, It cause
NULL pointer dereferecing error.

Cc: stable@vger.kernel.org
Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-21165
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/server.c
fs/ksmbd/smb2pdu.c

index 8a0ad399f24561abbeda6e44edba9fc69d6cd462..847ee62afb8a168986ea598167f13251e5409c01 100644 (file)
@@ -185,24 +185,31 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
                goto send;
        }
 
-       if (conn->ops->check_user_session) {
-               rc = conn->ops->check_user_session(work);
-               if (rc < 0) {
-                       command = conn->ops->get_cmd_val(work);
-                       conn->ops->set_rsp_status(work,
-                                       STATUS_USER_SESSION_DELETED);
-                       goto send;
-               } else if (rc > 0) {
-                       rc = conn->ops->get_ksmbd_tcon(work);
+       do {
+               if (conn->ops->check_user_session) {
+                       rc = conn->ops->check_user_session(work);
                        if (rc < 0) {
-                               conn->ops->set_rsp_status(work,
-                                       STATUS_NETWORK_NAME_DELETED);
+                               if (rc == -EINVAL)
+                                       conn->ops->set_rsp_status(work,
+                                               STATUS_INVALID_PARAMETER);
+                               else
+                                       conn->ops->set_rsp_status(work,
+                                               STATUS_USER_SESSION_DELETED);
                                goto send;
+                       } else if (rc > 0) {
+                               rc = conn->ops->get_ksmbd_tcon(work);
+                               if (rc < 0) {
+                                       if (rc == -EINVAL)
+                                               conn->ops->set_rsp_status(work,
+                                                       STATUS_INVALID_PARAMETER);
+                                       else
+                                               conn->ops->set_rsp_status(work,
+                                                       STATUS_NETWORK_NAME_DELETED);
+                                       goto send;
+                               }
                        }
                }
-       }
 
-       do {
                rc = __process_request(work, conn, &command);
                if (rc == SERVER_HANDLER_ABORT)
                        break;
index 56f661e5ef628de43f7c747ca4f534dfee290172..2b7c0ba6a77de760be55b6f3ff7d08dc81766673 100644 (file)
@@ -90,7 +90,6 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work)
        unsigned int cmd = le16_to_cpu(req_hdr->Command);
        int tree_id;
 
-       work->tcon = NULL;
        if (cmd == SMB2_TREE_CONNECT_HE ||
            cmd ==  SMB2_CANCEL_HE ||
            cmd ==  SMB2_LOGOFF_HE) {
@@ -104,10 +103,28 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work)
        }
 
        tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId);
+
+       /*
+        * If request is not the first in Compound request,
+        * Just validate tree id in header with work->tcon->id.
+        */
+       if (work->next_smb2_rcv_hdr_off) {
+               if (!work->tcon) {
+                       pr_err("The first operation in the compound does not have tcon\n");
+                       return -EINVAL;
+               }
+               if (work->tcon->id != tree_id) {
+                       pr_err("tree id(%u) is different with id(%u) in first operation\n",
+                                       tree_id, work->tcon->id);
+                       return -EINVAL;
+               }
+               return 1;
+       }
+
        work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id);
        if (!work->tcon) {
                pr_err("Invalid tid %d\n", tree_id);
-               return -EINVAL;
+               return -ENOENT;
        }
 
        return 1;
@@ -553,7 +570,6 @@ int smb2_check_user_session(struct ksmbd_work *work)
        unsigned int cmd = conn->ops->get_cmd_val(work);
        unsigned long long sess_id;
 
-       work->sess = NULL;
        /*
         * SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not
         * require a session id, so no need to validate user session's for
@@ -564,15 +580,33 @@ int smb2_check_user_session(struct ksmbd_work *work)
                return 0;
 
        if (!ksmbd_conn_good(conn))
-               return -EINVAL;
+               return -EIO;
 
        sess_id = le64_to_cpu(req_hdr->SessionId);
+
+       /*
+        * If request is not the first in Compound request,
+        * Just validate session id in header with work->sess->id.
+        */
+       if (work->next_smb2_rcv_hdr_off) {
+               if (!work->sess) {
+                       pr_err("The first operation in the compound does not have sess\n");
+                       return -EINVAL;
+               }
+               if (work->sess->id != sess_id) {
+                       pr_err("session id(%llu) is different with the first operation(%lld)\n",
+                                       sess_id, work->sess->id);
+                       return -EINVAL;
+               }
+               return 1;
+       }
+
        /* Check for validity of user session */
        work->sess = ksmbd_session_lookup_all(conn, sess_id);
        if (work->sess)
                return 1;
        ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id);
-       return -EINVAL;
+       return -ENOENT;
 }
 
 static void destroy_previous_session(struct ksmbd_conn *conn,