Skip to content
12 changes: 8 additions & 4 deletions drivers/net/bonding/bond_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -5320,18 +5320,22 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave = NULL;
struct list_head *iter;
struct bond_up_slave *slaves;
bool xmit_suc = false;
bool skb_used = false;
int slaves_count, i;

bond_for_each_slave_rcu(bond, slave, iter) {
slaves = rcu_dereference(bond->all_slaves);

slaves_count = slaves ? READ_ONCE(slaves->count) : 0;
for (i = 0; i < slaves_count; i++) {
struct slave *slave = slaves->arr[i];
struct sk_buff *skb2;

if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
continue;

if (bond_is_last_slave(bond, slave)) {
if (i + 1 == slaves_count) {
skb2 = skb;
skb_used = true;
} else {
Expand Down
133 changes: 118 additions & 15 deletions fs/smb/client/cifsacl.c
Original file line number Diff line number Diff line change
Expand Up @@ -758,14 +758,85 @@ static void dump_ace(struct smb_ace *pace, char *end_of_acl)
}
#endif

static int validate_dacl(struct smb_acl *pdacl, char *end_of_acl)
{
int i, ace_hdr_size, ace_size, min_ace_size;
u16 dacl_size, num_aces;
char *acl_base, *end_of_dacl;
struct smb_ace *pace;

if (!pdacl)
return 0;

if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl)) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
return -EINVAL;
}

dacl_size = le16_to_cpu(pdacl->size);
if (dacl_size < sizeof(struct smb_acl) ||
end_of_acl < (char *)pdacl + dacl_size) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
return -EINVAL;
}

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

ace_hdr_size = offsetof(struct smb_ace, sid) +
offsetof(struct smb_sid, sub_auth);
min_ace_size = ace_hdr_size + sizeof(__le32);
if (num_aces > (dacl_size - sizeof(struct smb_acl)) / min_ace_size) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
return -EINVAL;
}

end_of_dacl = (char *)pdacl + dacl_size;
acl_base = (char *)pdacl;
ace_size = sizeof(struct smb_acl);

for (i = 0; i < num_aces; ++i) {
if (end_of_dacl - acl_base < ace_size) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}

pace = (struct smb_ace *)(acl_base + ace_size);
acl_base = (char *)pace;

if (end_of_dacl - acl_base < ace_hdr_size ||
pace->sid.num_subauth == 0 ||
pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}

ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth;
if (end_of_dacl - acl_base < ace_size ||
le16_to_cpu(pace->size) < ace_size) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}

ace_size = le16_to_cpu(pace->size);
if (end_of_dacl - acl_base < ace_size) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}
}

return 0;
}

static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
struct smb_sid *pownersid, struct smb_sid *pgrpsid,
struct cifs_fattr *fattr, bool mode_from_special_sid)
{
int i;
int num_aces = 0;
int acl_size;
char *acl_base;
char *acl_base, *end_of_dacl;
struct smb_ace **ppace;

/* BB need to add parm so we can store the SID BB */
Expand All @@ -777,11 +848,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
return;
}

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

cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n",
le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
Expand All @@ -792,26 +860,27 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
user/group/other have no permissions */
fattr->cf_mode &= ~(0777);

end_of_dacl = (char *)pdacl + le16_to_cpu(pdacl->size);
acl_base = (char *)pdacl;
acl_size = sizeof(struct smb_acl);

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

if (num_aces > ULONG_MAX / sizeof(struct smb_ace *))
return;
ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *),
GFP_KERNEL);
if (!ppace)
return;

for (i = 0; i < num_aces; ++i) {
ppace[i] = (struct smb_ace *) (acl_base + acl_size);

#ifdef CONFIG_CIFS_DEBUG2
dump_ace(ppace[i], end_of_acl);
dump_ace(ppace[i], end_of_dacl);
#endif
if (mode_from_special_sid &&
ppace[i]->sid.num_subauth >= 3 &&
(compare_sids(&(ppace[i]->sid),
&sid_unix_NFS_mode) == 0)) {
/*
Expand Down Expand Up @@ -1196,6 +1265,17 @@ static int parse_sid(struct smb_sid *psid, char *end_of_acl)
return 0;
}

static bool dacl_offset_valid(unsigned int acl_len, __u32 dacloffset)
{
if (acl_len < sizeof(struct smb_acl))
return false;

if (dacloffset < sizeof(struct smb_ntsd))
return false;

return dacloffset <= acl_len - sizeof(struct smb_acl);
}


/* Convert CIFS ACL to POSIX form */
static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
Expand All @@ -1216,7 +1296,6 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
group_sid_ptr = (struct smb_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset));
dacloffset = le32_to_cpu(pntsd->dacloffset);
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n",
pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
le32_to_cpu(pntsd->gsidoffset),
Expand Down Expand Up @@ -1247,11 +1326,18 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
return rc;
}

if (dacloffset)
if (dacloffset) {
if (!dacl_offset_valid(acl_len, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
return -EINVAL;
}

dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
group_sid_ptr, fattr, get_mode_from_special_sid);
else
} else {
cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */
}

return rc;
}
Expand All @@ -1274,11 +1360,15 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,

dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
cifs_dbg(VFS, "Server returned illegal ACL size\n");
if (!dacl_offset_valid(secdesclen, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
return -EINVAL;
}

dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
rc = validate_dacl(dacl_ptr, end_of_acl);
if (rc)
return rc;
}

owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
Expand Down Expand Up @@ -1650,7 +1740,19 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
nsecdesclen = sizeof(struct smb_ntsd) + (sizeof(struct smb_sid) * 2);
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
if (!dacl_offset_valid(secdesclen, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
rc = -EINVAL;
goto id_mode_to_cifs_acl_exit;
}

dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen);
if (rc) {
kfree(pntsd);
cifs_put_tlink(tlink);
return rc;
}
if (mode_from_sid)
nsecdesclen +=
le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
Expand All @@ -1666,7 +1768,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
* descriptor parameters, and security descriptor itself
*/
nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN);
pnntsd = kmalloc(nsecdesclen, GFP_KERNEL);
pnntsd = kzalloc(nsecdesclen, GFP_KERNEL);
if (!pnntsd) {
kfree(pntsd);
cifs_put_tlink(tlink);
Expand All @@ -1686,6 +1788,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag);
cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
}
id_mode_to_cifs_acl_exit:
cifs_put_tlink(tlink);

kfree(pnntsd);
Expand Down
Loading