]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ksmbd: validate payload size in ipc response
authorNamjae Jeon <linkinjeon@kernel.org>
Sun, 31 Mar 2024 12:59:10 +0000 (21:59 +0900)
committerSteve French <stfrench@microsoft.com>
Tue, 2 Apr 2024 14:21:01 +0000 (09:21 -0500)
If installing malicious ksmbd-tools, ksmbd.mountd can return invalid ipc
response to ksmbd kernel server. ksmbd should validate payload size of
ipc response from ksmbd.mountd to avoid memory overrun or
slab-out-of-bounds. This patch validate 3 ipc response that has payload.

Cc: stable@vger.kernel.org
Reported-by: Chao Ma <machao2019@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/server/ksmbd_netlink.h
fs/smb/server/mgmt/share_config.c
fs/smb/server/transport_ipc.c

index 8ca8a45c4c621c5dabf56664bec27e5df0db8356..686b321c5a8bb5f0a1189023a3311e85aaf84c9e 100644 (file)
@@ -167,7 +167,8 @@ struct ksmbd_share_config_response {
        __u16   force_uid;
        __u16   force_gid;
        __s8    share_name[KSMBD_REQ_MAX_SHARE_NAME];
-       __u32   reserved[112];          /* Reserved room */
+       __u32   reserved[111];          /* Reserved room */
+       __u32   payload_sz;
        __u32   veto_list_sz;
        __s8    ____payload[];
 };
index 328a412259dc1b935eb2ce3eee2977d39155157b..a2f0a2edceb8ae49852dde1b40cf7594df6084d9 100644 (file)
@@ -158,7 +158,12 @@ static struct ksmbd_share_config *share_config_request(struct unicode_map *um,
        share->name = kstrdup(name, GFP_KERNEL);
 
        if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
-               share->path = kstrdup(ksmbd_share_config_path(resp),
+               int path_len = PATH_MAX;
+
+               if (resp->payload_sz)
+                       path_len = resp->payload_sz - resp->veto_list_sz;
+
+               share->path = kstrndup(ksmbd_share_config_path(resp), path_len,
                                      GFP_KERNEL);
                if (share->path)
                        share->path_sz = strlen(share->path);
index f29bb03f0dc47bfcb0fe3fc5c5acff16d5a314a8..8752ac82c557bf92985bd4d87a3e37f4cd4a60dc 100644 (file)
@@ -65,6 +65,7 @@ struct ipc_msg_table_entry {
        struct hlist_node       ipc_table_hlist;
 
        void                    *response;
+       unsigned int            msg_sz;
 };
 
 static struct delayed_work ipc_timer_work;
@@ -275,6 +276,7 @@ static int handle_response(int type, void *payload, size_t sz)
                }
 
                memcpy(entry->response, payload, sz);
+               entry->msg_sz = sz;
                wake_up_interruptible(&entry->wait);
                ret = 0;
                break;
@@ -453,6 +455,34 @@ out:
        return ret;
 }
 
+static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
+{
+       unsigned int msg_sz = entry->msg_sz;
+
+       if (entry->type == KSMBD_EVENT_RPC_REQUEST) {
+               struct ksmbd_rpc_command *resp = entry->response;
+
+               msg_sz = sizeof(struct ksmbd_rpc_command) + resp->payload_sz;
+       } else if (entry->type == KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST) {
+               struct ksmbd_spnego_authen_response *resp = entry->response;
+
+               msg_sz = sizeof(struct ksmbd_spnego_authen_response) +
+                               resp->session_key_len + resp->spnego_blob_len;
+       } else if (entry->type == KSMBD_EVENT_SHARE_CONFIG_REQUEST) {
+               struct ksmbd_share_config_response *resp = entry->response;
+
+               if (resp->payload_sz) {
+                       if (resp->payload_sz < resp->veto_list_sz)
+                               return -EINVAL;
+
+                       msg_sz = sizeof(struct ksmbd_share_config_response) +
+                                       resp->payload_sz;
+               }
+       }
+
+       return entry->msg_sz != msg_sz ? -EINVAL : 0;
+}
+
 static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle)
 {
        struct ipc_msg_table_entry entry;
@@ -477,6 +507,13 @@ static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle
        ret = wait_event_interruptible_timeout(entry.wait,
                                               entry.response != NULL,
                                               IPC_WAIT_TIMEOUT);
+       if (entry.response) {
+               ret = ipc_validate_msg(&entry);
+               if (ret) {
+                       kvfree(entry.response);
+                       entry.response = NULL;
+               }
+       }
 out:
        down_write(&ipc_msg_table_lock);
        hash_del(&entry.ipc_table_hlist);