Commit 1238f0af authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'v6.14-rc5-smb3-fixes' of git://git.samba.org/ksmbd

Pull smb fixes from Steve French:
 "Five SMB server fixes, two related client fixes, and minor MAINTAINERS
  update:

   - Two SMB3 lock fixes fixes (including use after free and bug on fix)

   - Fix to race condition that can happen in processing IPC responses

   - Four ACL related fixes: one related to endianness of num_aces, and
     two related fixes to the checks for num_aces (for both client and
     server), and one fixing missing check for num_subauths which can
     cause memory corruption

   - And minor update to email addresses in MAINTAINERS file"

* tag 'v6.14-rc5-smb3-fixes' of git://git.samba.org/ksmbd:
  cifs: fix incorrect validation for num_aces field of smb_acl
  ksmbd: fix incorrect validation for num_aces field of smb_acl
  smb: common: change the data type of num_aces to le16
  ksmbd: fix bug on trap in smb2_lock
  ksmbd: fix use-after-free in smb2_lock
  ksmbd: fix type confusion via race condition when using ipc_msg_send_request
  ksmbd: fix out-of-bounds in parse_sec_desc()
  MAINTAINERS: update email address in cifs and ksmbd entry
parents 5872cca2 aa2a739a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -5775,6 +5775,7 @@ X: drivers/clk/clkdev.c
COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3)
M:	Steve French <sfrench@samba.org>
M:	Steve French <smfrench@gmail.com>
R:	Paulo Alcantara <pc@manguebit.com> (DFS, global name space)
R:	Ronnie Sahlberg <ronniesahlberg@gmail.com> (directory leases, sparse files)
R:	Shyam Prasad N <sprasad@microsoft.com> (multichannel)
@@ -12655,7 +12656,9 @@ F: tools/testing/selftests/
KERNEL SMB3 SERVER (KSMBD)
M:	Namjae Jeon <linkinjeon@kernel.org>
M:	Namjae Jeon <linkinjeon@samba.org>
M:	Steve French <sfrench@samba.org>
M:	Steve French <smfrench@gmail.com>
R:	Sergey Senozhatsky <senozhatsky@chromium.org>
R:	Tom Talpey <tom@talpey.com>
L:	linux-cifs@vger.kernel.org
+19 −15
Original line number Diff line number Diff line
@@ -763,7 +763,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
		       struct cifs_fattr *fattr, bool mode_from_special_sid)
{
	int i;
	int num_aces = 0;
	u16 num_aces = 0;
	int acl_size;
	char *acl_base;
	struct smb_ace **ppace;
@@ -778,14 +778,15 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
	}

	/* validate that we do not go past end of acl */
	if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
	if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) ||
	    end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
		cifs_dbg(VFS, "ACL too small to parse DACL\n");
		return;
	}

	cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n",
		 le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
		 le32_to_cpu(pdacl->num_aces));
		 le16_to_cpu(pdacl->num_aces));

	/* reset rwx permissions for user/group/other.
	   Also, if num_aces is 0 i.e. DACL has no ACEs,
@@ -795,12 +796,15 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
	acl_base = (char *)pdacl;
	acl_size = sizeof(struct smb_acl);

	num_aces = le32_to_cpu(pdacl->num_aces);
	num_aces = le16_to_cpu(pdacl->num_aces);
	if (num_aces > 0) {
		umode_t denied_mode = 0;

		if (num_aces > ULONG_MAX / sizeof(struct smb_ace *))
		if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) /
				(offsetof(struct smb_ace, sid) +
				 offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
			return;

		ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *),
				      GFP_KERNEL);
		if (!ppace)
@@ -937,12 +941,12 @@ unsigned int setup_special_user_owner_ACE(struct smb_ace *pntace)
static void populate_new_aces(char *nacl_base,
		struct smb_sid *pownersid,
		struct smb_sid *pgrpsid,
		__u64 *pnmode, u32 *pnum_aces, u16 *pnsize,
		__u64 *pnmode, u16 *pnum_aces, u16 *pnsize,
		bool modefromsid,
		bool posix)
{
	__u64 nmode;
	u32 num_aces = 0;
	u16 num_aces = 0;
	u16 nsize = 0;
	__u64 user_mode;
	__u64 group_mode;
@@ -1050,7 +1054,7 @@ static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *p
	u16 size = 0;
	struct smb_ace *pntace = NULL;
	char *acl_base = NULL;
	u32 src_num_aces = 0;
	u16 src_num_aces = 0;
	u16 nsize = 0;
	struct smb_ace *pnntace = NULL;
	char *nacl_base = NULL;
@@ -1058,7 +1062,7 @@ static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *p

	acl_base = (char *)pdacl;
	size = sizeof(struct smb_acl);
	src_num_aces = le32_to_cpu(pdacl->num_aces);
	src_num_aces = le16_to_cpu(pdacl->num_aces);

	nacl_base = (char *)pndacl;
	nsize = sizeof(struct smb_acl);
@@ -1090,11 +1094,11 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
	u16 size = 0;
	struct smb_ace *pntace = NULL;
	char *acl_base = NULL;
	u32 src_num_aces = 0;
	u16 src_num_aces = 0;
	u16 nsize = 0;
	struct smb_ace *pnntace = NULL;
	char *nacl_base = NULL;
	u32 num_aces = 0;
	u16 num_aces = 0;
	bool new_aces_set = false;

	/* Assuming that pndacl and pnmode are never NULL */
@@ -1112,7 +1116,7 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,

	acl_base = (char *)pdacl;
	size = sizeof(struct smb_acl);
	src_num_aces = le32_to_cpu(pdacl->num_aces);
	src_num_aces = le16_to_cpu(pdacl->num_aces);

	/* Retain old ACEs which we can retain */
	for (i = 0; i < src_num_aces; ++i) {
@@ -1158,7 +1162,7 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
	}

finalize_dacl:
	pndacl->num_aces = cpu_to_le32(num_aces);
	pndacl->num_aces = cpu_to_le16(num_aces);
	pndacl->size = cpu_to_le16(nsize);

	return 0;
@@ -1293,7 +1297,7 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
			dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION);

		ndacl_ptr->size = cpu_to_le16(0);
		ndacl_ptr->num_aces = cpu_to_le32(0);
		ndacl_ptr->num_aces = cpu_to_le16(0);

		rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr,
				    pnmode, mode_from_sid, posix);
@@ -1653,7 +1657,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
			dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
			if (mode_from_sid)
				nsecdesclen +=
					le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
					le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
			else /* cifsacl */
				nsecdesclen += le16_to_cpu(dacl_ptr->size);
		}
+2 −1
Original line number Diff line number Diff line
@@ -107,7 +107,8 @@ struct smb_sid {
struct smb_acl {
	__le16 revision; /* revision level */
	__le16 size;
	__le32 num_aces;
	__le16 num_aces;
	__le16 reserved;
} __attribute__((packed));

struct smb_ace {
+4 −4
Original line number Diff line number Diff line
@@ -7458,17 +7458,17 @@ int smb2_lock(struct ksmbd_work *work)
		}

no_check_cl:
		flock = smb_lock->fl;
		list_del(&smb_lock->llist);

		if (smb_lock->zero_len) {
			err = 0;
			goto skip;
		}

		flock = smb_lock->fl;
		list_del(&smb_lock->llist);
retry:
		rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL);
skip:
		if (flags & SMB2_LOCKFLAG_UNLOCK) {
		if (smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) {
			if (!rc) {
				ksmbd_debug(SMB, "File unlocked\n");
			} else if (rc == -ENOENT) {
+36 −16
Original line number Diff line number Diff line
@@ -333,7 +333,7 @@ void posix_state_to_acl(struct posix_acl_state *state,
	pace->e_perm = state->other.allow;
}

int init_acl_state(struct posix_acl_state *state, int cnt)
int init_acl_state(struct posix_acl_state *state, u16 cnt)
{
	int alloc;

@@ -368,7 +368,7 @@ static void parse_dacl(struct mnt_idmap *idmap,
		       struct smb_fattr *fattr)
{
	int i, ret;
	int num_aces = 0;
	u16 num_aces = 0;
	unsigned int acl_size;
	char *acl_base;
	struct smb_ace **ppace;
@@ -389,16 +389,18 @@ static void parse_dacl(struct mnt_idmap *idmap,

	ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n",
		    le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
		    le32_to_cpu(pdacl->num_aces));
		    le16_to_cpu(pdacl->num_aces));

	acl_base = (char *)pdacl;
	acl_size = sizeof(struct smb_acl);

	num_aces = le32_to_cpu(pdacl->num_aces);
	num_aces = le16_to_cpu(pdacl->num_aces);
	if (num_aces <= 0)
		return;

	if (num_aces > ULONG_MAX / sizeof(struct smb_ace *))
	if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) /
			(offsetof(struct smb_ace, sid) +
			 offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
		return;

	ret = init_acl_state(&acl_state, num_aces);
@@ -432,6 +434,7 @@ static void parse_dacl(struct mnt_idmap *idmap,
			offsetof(struct smb_sid, sub_auth);

		if (end_of_acl - acl_base < acl_size ||
		    ppace[i]->sid.num_subauth == 0 ||
		    ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
		    (end_of_acl - acl_base <
		     acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) ||
@@ -580,7 +583,7 @@ 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, u32 *num_aces,
				       struct smb_fattr *fattr, u16 *num_aces,
				       u16 *size, u32 nt_aces_num)
{
	struct posix_acl_entry *pace;
@@ -701,7 +704,7 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,
			   struct smb_fattr *fattr)
{
	struct smb_ace *ntace, *pndace;
	int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0;
	u16 nt_num_aces = le16_to_cpu(nt_dacl->num_aces), num_aces = 0;
	unsigned short size = 0;
	int i;

@@ -728,7 +731,7 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,

	set_posix_acl_entries_dacl(idmap, pndace, fattr,
				   &num_aces, &size, nt_num_aces);
	pndacl->num_aces = cpu_to_le32(num_aces);
	pndacl->num_aces = cpu_to_le16(num_aces);
	pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size);
}

@@ -736,7 +739,7 @@ static void set_mode_dacl(struct mnt_idmap *idmap,
			  struct smb_acl *pndacl, struct smb_fattr *fattr)
{
	struct smb_ace *pace, *pndace;
	u32 num_aces = 0;
	u16 num_aces = 0;
	u16 size = 0, ace_size = 0;
	uid_t uid;
	const struct smb_sid *sid;
@@ -792,7 +795,7 @@ static void set_mode_dacl(struct mnt_idmap *idmap,
				 fattr->cf_mode, 0007);

out:
	pndacl->num_aces = cpu_to_le32(num_aces);
	pndacl->num_aces = cpu_to_le16(num_aces);
	pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size);
}

@@ -807,6 +810,13 @@ static int parse_sid(struct smb_sid *psid, char *end_of_acl)
		return -EINVAL;
	}

	if (!psid->num_subauth)
		return 0;

	if (psid->num_subauth > SID_MAX_SUB_AUTHORITIES ||
	    end_of_acl < (char *)psid + 8 + sizeof(__le32) * psid->num_subauth)
		return -EINVAL;

	return 0;
}

@@ -848,6 +858,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
	pntsd->type = cpu_to_le16(DACL_PRESENT);

	if (pntsd->osidoffset) {
		if (le32_to_cpu(pntsd->osidoffset) < sizeof(struct smb_ntsd))
			return -EINVAL;

		rc = parse_sid(owner_sid_ptr, end_of_acl);
		if (rc) {
			pr_err("%s: Error %d parsing Owner SID\n", __func__, rc);
@@ -863,6 +876,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
	}

	if (pntsd->gsidoffset) {
		if (le32_to_cpu(pntsd->gsidoffset) < sizeof(struct smb_ntsd))
			return -EINVAL;

		rc = parse_sid(group_sid_ptr, end_of_acl);
		if (rc) {
			pr_err("%s: Error %d mapping Owner SID to gid\n",
@@ -884,6 +900,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
		pntsd->type |= cpu_to_le16(DACL_PROTECTED);

	if (dacloffset) {
		if (dacloffset < sizeof(struct smb_ntsd))
			return -EINVAL;

		parse_dacl(idmap, dacl_ptr, end_of_acl,
			   owner_sid_ptr, group_sid_ptr, fattr);
	}
@@ -1006,8 +1025,9 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
	struct smb_sid owner_sid, group_sid;
	struct dentry *parent = path->dentry->d_parent;
	struct mnt_idmap *idmap = mnt_idmap(path->mnt);
	int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size;
	int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
	int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size;
	int rc = 0, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
	u16 num_aces, ace_cnt = 0;
	char *aces_base;
	bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);

@@ -1023,7 +1043,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,

	parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset);
	acl_len = pntsd_size - dacloffset;
	num_aces = le32_to_cpu(parent_pdacl->num_aces);
	num_aces = le16_to_cpu(parent_pdacl->num_aces);
	pntsd_type = le16_to_cpu(parent_pntsd->type);
	pdacl_size = le16_to_cpu(parent_pdacl->size);

@@ -1183,7 +1203,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
			pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
			pdacl->revision = cpu_to_le16(2);
			pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size);
			pdacl->num_aces = cpu_to_le32(ace_cnt);
			pdacl->num_aces = cpu_to_le16(ace_cnt);
			pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
			memcpy(pace, aces_base, nt_size);
			pntsd_size += sizeof(struct smb_acl) + nt_size;
@@ -1264,7 +1284,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,

		ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
		aces_size = acl_size - sizeof(struct smb_acl);
		for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) {
		for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
			if (offsetof(struct smb_ace, access_req) > aces_size)
				break;
			ace_size = le16_to_cpu(ace->size);
@@ -1285,7 +1305,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,

	ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
	aces_size = acl_size - sizeof(struct smb_acl);
	for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) {
	for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
		if (offsetof(struct smb_ace, access_req) > aces_size)
			break;
		ace_size = le16_to_cpu(ace->size);
Loading