Skip to content

Commit

Permalink
cifs: Change SIDs in ACEs while transferring file ownership.
Browse files Browse the repository at this point in the history
With cifsacl, when a file/dir ownership is transferred (chown/chgrp),
the ACEs in the DACL for that file will need to replace the old owner
SIDs with the new owner SID.

Signed-off-by: Shyam Prasad N <[email protected]>
  • Loading branch information
sprasad-microsoft committed Feb 22, 2021
1 parent eaec39b commit 52d0afe
Showing 1 changed file with 91 additions and 46 deletions.
137 changes: 91 additions & 46 deletions fs/cifs/cifsacl.c
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,11 @@ exit_cifs_idmap(void)
}

/* copy ntsd, owner sid, and group sid from a security descriptor to another */
static void copy_sec_desc(const struct cifs_ntsd *pntsd,
struct cifs_ntsd *pnntsd, __u32 sidsoffset)
static __u32 copy_sec_desc(const struct cifs_ntsd *pntsd,
struct cifs_ntsd *pnntsd,
__u32 sidsoffset,
struct cifs_sid *pownersid,
struct cifs_sid *pgrpsid)
{
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
Expand All @@ -540,19 +543,25 @@ static void copy_sec_desc(const struct cifs_ntsd *pntsd,
pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid));

/* copy owner sid */
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
if (pownersid)
owner_sid_ptr = pownersid;
else
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->osidoffset));
nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr);

/* copy group sid */
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
if (pgrpsid)
group_sid_ptr = pgrpsid;
else
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset));
ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
sizeof(struct cifs_sid));
cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr);

return;
return sidsoffset + (2 * sizeof(struct cifs_sid));
}


Expand Down Expand Up @@ -667,15 +676,21 @@ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
return;
}

static __u16 cifs_copy_ace(struct cifs_ace *dst, struct cifs_ace *src)
static __u16 cifs_copy_ace(struct cifs_ace *dst, struct cifs_ace *src, struct cifs_sid *psid)
{
__u16 size = 1 + 1 + 2 + 4;

dst->type = src->type;
dst->flags = src->flags;
dst->size = src->size;
dst->access_req = src->access_req;
size += cifs_copy_sid(&dst->sid, &src->sid);

/* Check if there's a replacement sid specified */
if (psid)
size += cifs_copy_sid(&dst->sid, psid);
else
size += cifs_copy_sid(&dst->sid, &src->sid);

dst->size = cpu_to_le16(size);

return size;
}
Expand Down Expand Up @@ -1026,7 +1041,7 @@ static void populate_new_aces(char *nacl_base,
*pnsize = nsize;
}

static __u16 replace_sids_and_copy_aces(struct cifs_acl *pdacl, struct cifs_acl *pndacl,
static __u16 replace_sids_and_copy_aces(struct cifs_acl *pdacl, struct cifs_acl *pndacl,
struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
struct cifs_sid *pnownersid, struct cifs_sid *pngrpsid)
{
Expand Down Expand Up @@ -1124,7 +1139,7 @@ static int set_chmod_dacl(struct cifs_acl *pdacl, struct cifs_acl *pndacl,
goto next_ace;
}

nsize += cifs_copy_ace(pnntace, pntace);
nsize += cifs_copy_ace(pnntace, pntace, NULL);
num_aces++;

next_ace:
Expand Down Expand Up @@ -1250,40 +1265,44 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
__u32 ndacloffset;
__u32 sidsoffset;
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
struct cifs_sid *nowner_sid_ptr = NULL, *ngroup_sid_ptr = NULL;
struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */
struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
char *end_of_acl = ((char *)pntsd) + secdesclen;
u16 size = 0;

if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->osidoffset));
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset));
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
cifs_dbg(VFS, "Existing ACL size is wrong. Discarding old ACL\n");
dacl_ptr = NULL;
}
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
cifs_dbg(VFS, "Existing ACL size is wrong. Discarding old ACL\n");
dacl_ptr = NULL;
}
}

owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->osidoffset));
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset));

if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
ndacloffset = sizeof(struct cifs_ntsd);
ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
ndacl_ptr->revision =
ndacl_ptr->revision =
dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION);

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

rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr,
pnmode, mode_from_sid);

sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
/* copy sec desc control portion & owner and group sids */
copy_sec_desc(pntsd, pnntsd, sidsoffset);
*pnsecdesclen = sidsoffset + (2 * sizeof(struct cifs_sid));
*aclflag = CIFS_ACL_DACL;
/* copy the non-dacl portion of secdesc */
*pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset,
NULL, NULL);

*aclflag |= CIFS_ACL_DACL;
} else {
ndacloffset = sizeof(struct cifs_ntsd);
ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
Expand All @@ -1293,12 +1312,12 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,

if (uid_valid(uid)) { /* chown */
uid_t id;
owner_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
le32_to_cpu(pnntsd->osidoffset));
nowner_sid_ptr = kmalloc(sizeof(struct cifs_sid),
GFP_KERNEL);
if (!nowner_sid_ptr)
return -ENOMEM;
if (!nowner_sid_ptr) {
rc = -ENOMEM;
goto chown_chgrp_exit;
}
id = from_kuid(&init_user_ns, uid);
if (id_from_sid) {
struct owner_sid *osid = (struct owner_sid *)nowner_sid_ptr;
Expand All @@ -1309,27 +1328,25 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
osid->SubAuthorities[0] = cpu_to_le32(88);
osid->SubAuthorities[1] = cpu_to_le32(1);
osid->SubAuthorities[2] = cpu_to_le32(id);

} else { /* lookup sid with upcall */
rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr);
if (rc) {
cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n",
__func__, rc, id);
kfree(nowner_sid_ptr);
return rc;
goto chown_chgrp_exit;
}
}
cifs_copy_sid(owner_sid_ptr, nowner_sid_ptr);
kfree(nowner_sid_ptr);
*aclflag = CIFS_ACL_OWNER;
*aclflag |= CIFS_ACL_OWNER;
}
if (gid_valid(gid)) { /* chgrp */
gid_t id;
group_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
le32_to_cpu(pnntsd->gsidoffset));
ngroup_sid_ptr = kmalloc(sizeof(struct cifs_sid),
GFP_KERNEL);
if (!ngroup_sid_ptr)
return -ENOMEM;
if (!ngroup_sid_ptr) {
rc = -ENOMEM;
goto chown_chgrp_exit;
}
id = from_kgid(&init_user_ns, gid);
if (id_from_sid) {
struct owner_sid *gsid = (struct owner_sid *)ngroup_sid_ptr;
Expand All @@ -1340,19 +1357,35 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
gsid->SubAuthorities[0] = cpu_to_le32(88);
gsid->SubAuthorities[1] = cpu_to_le32(2);
gsid->SubAuthorities[2] = cpu_to_le32(id);

} else { /* lookup sid with upcall */
rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr);
if (rc) {
cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n",
__func__, rc, id);
kfree(ngroup_sid_ptr);
return rc;
goto chown_chgrp_exit;
}
}
cifs_copy_sid(group_sid_ptr, ngroup_sid_ptr);
kfree(ngroup_sid_ptr);
*aclflag = CIFS_ACL_GROUP;
*aclflag |= CIFS_ACL_GROUP;
}

if (dacloffset) {
/* Replace ACEs for old owner with new one */
size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr,
owner_sid_ptr, group_sid_ptr,
nowner_sid_ptr, ngroup_sid_ptr);
ndacl_ptr->size = cpu_to_le16(size);
}

sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
/* copy the non-dacl portion of secdesc */
*pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset,
nowner_sid_ptr, ngroup_sid_ptr);

chown_chgrp_exit:
/* errors could jump here. So make sure we return soon after this */
kfree(nowner_sid_ptr);
kfree(ngroup_sid_ptr);
}

return rc;
Expand Down Expand Up @@ -1598,6 +1631,18 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
nsecdesclen += sizeof(struct cifs_ace);
else /* cifsacl */
nsecdesclen += 5 * sizeof(struct cifs_ace);
} else { /* chown */
/* When ownership changes, changes new owner sid length could be different */
nsecdesclen = sizeof(struct cifs_ntsd) + (sizeof(struct cifs_sid) * 2);
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
if (mode_from_sid)
nsecdesclen +=
le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct cifs_ace);
else /* cifsacl */
nsecdesclen += le16_to_cpu(dacl_ptr->size);
}
}

/*
Expand Down

0 comments on commit 52d0afe

Please sign in to comment.