mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-29 10:26:00 -04:00
ipc_validate_msg() computes the expected message size for each
response type by adding (or multiplying) attacker-controlled fields
from the daemon response to a fixed struct size in unsigned int
arithmetic. Three cases can overflow:
KSMBD_EVENT_RPC_REQUEST:
msg_sz = sizeof(struct ksmbd_rpc_command) + resp->payload_sz;
KSMBD_EVENT_SHARE_CONFIG_REQUEST:
msg_sz = sizeof(struct ksmbd_share_config_response) +
resp->payload_sz;
KSMBD_EVENT_LOGIN_REQUEST_EXT:
msg_sz = sizeof(struct ksmbd_login_response_ext) +
resp->ngroups * sizeof(gid_t);
resp->payload_sz is __u32 and resp->ngroups is __s32. Each addition
can wrap in unsigned int; the multiplication by sizeof(gid_t) mixes
signed and size_t, so a negative ngroups is converted to SIZE_MAX
before the multiply. A wrapped value of msg_sz that happens to
equal entry->msg_sz bypasses the size check on the next line, and
downstream consumers (smb2pdu.c:6742 memcpy using rpc_resp->payload_sz,
kmemdup in ksmbd_alloc_user using resp_ext->ngroups) then trust the
unverified length.
Use check_add_overflow() on the RPC_REQUEST and SHARE_CONFIG_REQUEST
paths to detect integer overflow without constraining functional
payload size; userspace ksmbd-tools grows NDR responses in 4096-byte
chunks for calls like NetShareEnumAll, so a hard transport cap is
unworkable on the response side. For LOGIN_REQUEST_EXT, reject
resp->ngroups outside the signed [0, NGROUPS_MAX] range up front and
report the error from ipc_validate_msg() so it fires at the IPC
boundary; with that bound the subsequent multiplication and addition
stay well below UINT_MAX. The now-redundant ngroups check and
pr_err in ksmbd_alloc_user() are removed.
This is the response-side analogue of aab98e2dbd ("ksmbd: fix
integer overflows on 32 bit systems"), which hardened the request
side.
Fixes: 0626e6641f ("cifsd: add server handler for central processing and tranport layers")
Fixes: a77e0e02af ("ksmbd: add support for supplementary groups")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-6
Assisted-by: Codex:gpt-5-4
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
101 lines
2.1 KiB
C
101 lines
2.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/mm.h>
|
|
|
|
#include "user_config.h"
|
|
#include "../transport_ipc.h"
|
|
|
|
struct ksmbd_user *ksmbd_login_user(const char *account)
|
|
{
|
|
struct ksmbd_login_response *resp;
|
|
struct ksmbd_login_response_ext *resp_ext = NULL;
|
|
struct ksmbd_user *user = NULL;
|
|
|
|
resp = ksmbd_ipc_login_request(account);
|
|
if (!resp)
|
|
return NULL;
|
|
|
|
if (!(resp->status & KSMBD_USER_FLAG_OK))
|
|
goto out;
|
|
|
|
if (resp->status & KSMBD_USER_FLAG_EXTENSION)
|
|
resp_ext = ksmbd_ipc_login_request_ext(account);
|
|
|
|
user = ksmbd_alloc_user(resp, resp_ext);
|
|
out:
|
|
kvfree(resp);
|
|
return user;
|
|
}
|
|
|
|
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
|
|
struct ksmbd_login_response_ext *resp_ext)
|
|
{
|
|
struct ksmbd_user *user;
|
|
|
|
user = kmalloc_obj(struct ksmbd_user, KSMBD_DEFAULT_GFP);
|
|
if (!user)
|
|
return NULL;
|
|
|
|
user->name = kstrdup(resp->account, KSMBD_DEFAULT_GFP);
|
|
user->flags = resp->status;
|
|
user->gid = resp->gid;
|
|
user->uid = resp->uid;
|
|
user->passkey_sz = resp->hash_sz;
|
|
user->passkey = kmalloc(resp->hash_sz, KSMBD_DEFAULT_GFP);
|
|
if (user->passkey)
|
|
memcpy(user->passkey, resp->hash, resp->hash_sz);
|
|
|
|
user->ngroups = 0;
|
|
user->sgid = NULL;
|
|
|
|
if (!user->name || !user->passkey)
|
|
goto err_free;
|
|
|
|
if (resp_ext) {
|
|
user->sgid = kmemdup(resp_ext->____payload,
|
|
resp_ext->ngroups * sizeof(gid_t),
|
|
KSMBD_DEFAULT_GFP);
|
|
if (!user->sgid)
|
|
goto err_free;
|
|
|
|
user->ngroups = resp_ext->ngroups;
|
|
ksmbd_debug(SMB, "supplementary groups : %d\n", user->ngroups);
|
|
}
|
|
|
|
return user;
|
|
|
|
err_free:
|
|
kfree(user->name);
|
|
kfree(user->passkey);
|
|
kfree(user);
|
|
return NULL;
|
|
}
|
|
|
|
void ksmbd_free_user(struct ksmbd_user *user)
|
|
{
|
|
ksmbd_ipc_logout_request(user->name, user->flags);
|
|
kfree(user->sgid);
|
|
kfree(user->name);
|
|
kfree(user->passkey);
|
|
kfree(user);
|
|
}
|
|
|
|
bool ksmbd_anonymous_user(struct ksmbd_user *user)
|
|
{
|
|
return user->name[0] == '\0';
|
|
}
|
|
|
|
bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2)
|
|
{
|
|
if (strcmp(u1->name, u2->name))
|
|
return false;
|
|
if (memcmp(u1->passkey, u2->passkey, u1->passkey_sz))
|
|
return false;
|
|
|
|
return true;
|
|
}
|