eight ksmbd server fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmpj6jgACgkQiiy9cAdy
 T1F7Agv/Q0LNuCs18A0dR8SlNLwbo6lE6RVL3A8TfFKqiqpyigPBf0Ax2VqR/uuH
 6rfuwZYLMNOp9P+jgFYPug8+fx2Ya58oLlVtCY+JooASUbwEg7PG/R3oHuZe4tOS
 m6m8+DVO82JwGH88dkLXBvyrlkxCWZJtPWlS3s+os/2mGAX8M4hvIiCKGz3PAFmb
 Qul3AGlxlSgi7eKuZGMU2OslgRVmMVOrNUgW4LfkDP/c8JjwgdDAuhebJOV2sAKi
 qL8GlIk/ZsXbZ9qK2ogQ03dt3WxcikWcqbj4GX/d0191sPRx0XuRRXwhOetTUzR3
 7PtcDSk6x94+ut4vpuheEaz4Mx8MiNV73pJzNqPQ5WLbA493PUJdEVPbQE2z+y5j
 lPFRRYqNtTDAMKv5ZFCUSKs7gO52/jIgdn+RSJ29tJ2Di4IzmguTGv3Cwmq/yL7s
 lhfoo2YM55AVkgeOfSsHXNGB89XQjolGb0S5j5fHCkmtvKt5zoWVTp8eoHgG7jih
 l7h1AR1X
 =7btg
 -----END PGP SIGNATURE-----

Merge tag 'v7.2-rc4-smb3-server-fixes' of git://git.samba.org/ksmbd

Pull smb server fixes from Steve French:
 "This contains eight ksmbd fixes covering POSIX ACL handling, SMB
  signing enforcement, DACL parsing and construction hardening, session
  lifetime handling, and validation of malformed transform and
  compressed SMB2 requests:

   - preserve inherited POSIX ACL mask when creating objects.

   - enforce the session signing requirement for plaintext SMB requests.

   - harden DACL/ACE processing against size overflows, incomplete ACE
     copies, and undersized SIDs.

   - defer teardown of a previous session until NTLM authentication
     succeeds.

   - reject undersized encryption-transform and decompressed SMB2
     requests before they can reach normal SMB2 request processing"

* tag 'v7.2-rc4-smb3-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: reject undersized decompressed SMB2 requests
  ksmbd: validate minimum PDU size for transform requests
  ksmbd: defer destroy_previous_session() until after NTLM authentication
  ksmbd: validate ACE size against SID sub-authorities
  ksmbd: restore DACL size on check_add_overflow() to avoid malformed ACL
  ksmbd: bound DACL dedup walk to copied ACEs
  ksmbd: enforce signing required by the session
  ksmbd: preserve VFS inherited POSIX ACL mask
This commit is contained in:
Linus Torvalds 2026-07-24 19:50:05 -07:00
commit 8e371eff3f
6 changed files with 54 additions and 49 deletions

View File

@ -56,7 +56,8 @@ int ksmbd_decompress_request(struct ksmbd_conn *conn)
}
max_allowed_pdu_size = SMB3_MAX_MSGSIZE + conn->vals->max_write_size;
if (out_size > max_allowed_pdu_size ||
if (out_size < sizeof(struct smb2_pdu) ||
out_size > max_allowed_pdu_size ||
out_size > MAX_STREAM_PROT_LEN)
return -EINVAL;

View File

@ -441,6 +441,8 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn)
/* "+2" for BCC field (ByteCount, 2 bytes) */
#define SMB1_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb_hdr) + 2)
#define SMB2_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb2_pdu))
#define SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE \
(sizeof(struct smb2_transform_hdr) + sizeof(struct smb2_hdr))
/**
* ksmbd_conn_handler_loop() - session thread to listen on new smb requests
@ -455,6 +457,7 @@ int ksmbd_conn_handler_loop(void *p)
struct ksmbd_conn *conn = (struct ksmbd_conn *)p;
struct ksmbd_transport *t = conn->transport;
unsigned int pdu_size, max_allowed_pdu_size, max_req;
__le32 proto;
char hdr_buf[4] = {0,};
int size;
@ -546,11 +549,14 @@ int ksmbd_conn_handler_loop(void *p)
if (!ksmbd_smb_request(conn))
break;
if (((struct smb2_hdr *)smb_get_msg(conn->request_buf))->ProtocolId ==
SMB2_PROTO_NUMBER) {
if (pdu_size < SMB2_MIN_SUPPORTED_PDU_SIZE)
break;
}
proto = *(__le32 *)smb_get_msg(conn->request_buf);
if (proto == SMB2_PROTO_NUMBER &&
pdu_size < SMB2_MIN_SUPPORTED_PDU_SIZE)
break;
if (proto == SMB2_TRANSFORM_PROTO_NUM &&
pdu_size < SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE)
break;
if (!default_conn_ops.process_fn) {
pr_err("No connection request callback\n");

View File

@ -112,6 +112,7 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn,
{
struct smb_version_cmds *cmds;
u16 command;
bool signed_req;
int ret;
if (check_conn_state(work))
@ -138,7 +139,14 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn,
return SERVER_HANDLER_ABORT;
}
if (work->sess && conn->ops->is_sign_req(work, command)) {
signed_req = conn->ops->is_sign_req && conn->ops->is_sign_req(work, command);
if (work->sess && work->sess->sign && !work->encrypted &&
!signed_req) {
conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED);
return SERVER_HANDLER_ABORT;
}
if (work->sess && signed_req) {
ret = conn->ops->check_sign_req(work);
if (!ret) {
conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED);

View File

@ -1717,11 +1717,6 @@ static int ntlm_authenticate(struct ksmbd_work *work,
return -EPERM;
}
/* Check for previous session */
prev_id = le64_to_cpu(req->PreviousSessionId);
if (prev_id && prev_id != sess->id)
destroy_previous_session(conn, user, prev_id);
if (sess->state == SMB2_SESSION_VALID) {
/*
* Reuse session if anonymous try to connect
@ -1761,6 +1756,10 @@ static int ntlm_authenticate(struct ksmbd_work *work,
}
}
prev_id = le64_to_cpu(req->PreviousSessionId);
if (prev_id && prev_id != sess->id)
destroy_previous_session(conn, sess->user, prev_id);
/*
* If session state is SMB2_SESSION_VALID, We can assume
* that it is reauthentication. And the user/password
@ -9596,8 +9595,7 @@ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command)
struct smb2_hdr *rcv_hdr2 = smb_get_msg(work->request_buf);
if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) &&
command != SMB2_NEGOTIATE_HE &&
command != SMB2_OPLOCK_BREAK_HE)
command != SMB2_NEGOTIATE_HE)
return true;
return false;

View File

@ -608,7 +608,8 @@ static void parse_dacl(struct mnt_idmap *idmap,
static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
struct smb_ace *pndace,
struct smb_fattr *fattr, u16 *num_aces,
u16 *size, u32 nt_aces_num)
u16 *size, u16 existing_nt_aces,
bool had_nt_aces)
{
struct posix_acl_entry *pace;
struct smb_sid *sid;
@ -640,14 +641,14 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
gid = posix_acl_gid_translate(idmap, pace);
id_to_sid(gid, SIDUNIX_GROUP, sid);
} else if (pace->e_tag == ACL_OTHER && !nt_aces_num) {
} else if (pace->e_tag == ACL_OTHER && !had_nt_aces) {
smb_copy_sid(sid, &sid_everyone);
} else {
kfree(sid);
continue;
}
ntace = pndace;
for (j = 0; j < nt_aces_num; j++) {
for (j = 0; j < existing_nt_aces; j++) {
if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] ==
sid->sub_auth[sid->num_subauth - 1])
goto pass_same_sid;
@ -662,6 +663,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags,
pace->e_perm, 0777);
if (check_add_overflow(*size, ace_sz, size)) {
*size -= ace_sz;
kfree(sid);
break;
}
@ -676,6 +678,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED,
0x03, pace->e_perm, 0777);
if (check_add_overflow(*size, ace_sz, size)) {
*size -= ace_sz;
kfree(sid);
break;
}
@ -689,7 +692,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
kfree(sid);
}
if (nt_aces_num)
if (had_nt_aces)
return;
posix_default_acl:
@ -721,6 +724,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b,
pace->e_perm, 0777);
if (check_add_overflow(*size, ace_sz, size)) {
*size -= ace_sz;
kfree(sid);
break;
}
@ -742,6 +746,7 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,
{
struct smb_ace *ntace, *pndace;
u16 nt_num_aces = le16_to_cpu(nt_dacl->num_aces), num_aces = 0;
u16 copied_nt_aces;
unsigned short size = 0;
int i;
@ -751,20 +756,29 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,
for (i = 0; i < nt_num_aces; i++) {
unsigned short nt_ace_size;
if (offsetof(struct smb_ace, access_req) > aces_size)
if (aces_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE)
break;
nt_ace_size = le16_to_cpu(ntace->size);
if (nt_ace_size > aces_size)
if (nt_ace_size > aces_size ||
nt_ace_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE)
break;
if (ntace->sid.num_subauth == 0 ||
ntace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES)
ntace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
nt_ace_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE +
sizeof(__le32) *
ntace->sid.num_subauth)
goto next_ace;
memcpy((char *)pndace + size, ntace, nt_ace_size);
if (check_add_overflow(size, nt_ace_size, &size))
if (check_add_overflow(size, nt_ace_size, &size)) {
size -= nt_ace_size;
break;
}
num_aces++;
next_ace:
@ -773,8 +787,10 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,
}
}
copied_nt_aces = num_aces;
set_posix_acl_entries_dacl(idmap, pndace, fattr,
&num_aces, &size, nt_num_aces);
&num_aces, &size, copied_nt_aces,
nt_num_aces != 0);
pndacl->num_aces = cpu_to_le16(num_aces);
pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size);
}
@ -792,7 +808,7 @@ static void set_mode_dacl(struct mnt_idmap *idmap,
if (fattr->cf_acls) {
set_posix_acl_entries_dacl(idmap, pndace, fattr,
&num_aces, &size, num_aces);
&num_aces, &size, num_aces, false);
goto out;
}

View File

@ -1886,10 +1886,6 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
const struct path *path, struct inode *parent_inode)
{
struct posix_acl *acls;
struct posix_acl_entry *pace;
struct dentry *dentry = path->dentry;
struct inode *inode = d_inode(dentry);
int rc, i;
if (!IS_ENABLED(CONFIG_FS_POSIX_ACL))
return -EOPNOTSUPP;
@ -1897,29 +1893,9 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
acls = get_inode_acl(parent_inode, ACL_TYPE_DEFAULT);
if (IS_ERR_OR_NULL(acls))
return -ENOENT;
pace = acls->a_entries;
for (i = 0; i < acls->a_count; i++, pace++) {
if (pace->e_tag == ACL_MASK) {
pace->e_perm = 0x07;
break;
}
}
rc = set_posix_acl(idmap, dentry, ACL_TYPE_ACCESS, acls);
if (rc < 0)
ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
rc);
if (S_ISDIR(inode->i_mode)) {
rc = set_posix_acl(idmap, dentry, ACL_TYPE_DEFAULT,
acls);
if (rc < 0)
ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n",
rc);
}
posix_acl_release(acls);
return rc;
return 0;
}
void ksmbd_vfs_update_compressed_fattr(struct dentry *dentry, __le32 *fattr)