138c8a9a5SSteve French // SPDX-License-Identifier: LGPL-2.1
238c8a9a5SSteve French /*
338c8a9a5SSteve French *
438c8a9a5SSteve French * Copyright (C) International Business Machines Corp., 2007,2008
538c8a9a5SSteve French * Author(s): Steve French (sfrench@us.ibm.com)
638c8a9a5SSteve French *
738c8a9a5SSteve French * Contains the routines for mapping CIFS/NTFS ACLs
838c8a9a5SSteve French *
938c8a9a5SSteve French */
1038c8a9a5SSteve French
1138c8a9a5SSteve French #include <linux/fs.h>
1238c8a9a5SSteve French #include <linux/slab.h>
1338c8a9a5SSteve French #include <linux/string.h>
1438c8a9a5SSteve French #include <linux/keyctl.h>
1538c8a9a5SSteve French #include <linux/key-type.h>
1638c8a9a5SSteve French #include <uapi/linux/posix_acl.h>
1738c8a9a5SSteve French #include <linux/posix_acl.h>
1838c8a9a5SSteve French #include <linux/posix_acl_xattr.h>
1938c8a9a5SSteve French #include <keys/user-type.h>
2038c8a9a5SSteve French #include "cifspdu.h"
2138c8a9a5SSteve French #include "cifsglob.h"
2238c8a9a5SSteve French #include "cifsacl.h"
2338c8a9a5SSteve French #include "cifsproto.h"
2438c8a9a5SSteve French #include "cifs_debug.h"
2538c8a9a5SSteve French #include "fs_context.h"
2638c8a9a5SSteve French #include "cifs_fs_sb.h"
2738c8a9a5SSteve French #include "cifs_unicode.h"
2838c8a9a5SSteve French
2938c8a9a5SSteve French /* security id for everyone/world system group */
3046c22d37SChenXiaoSong static const struct smb_sid sid_everyone = {
3138c8a9a5SSteve French 1, 1, {0, 0, 0, 0, 0, 1}, {0} };
3238c8a9a5SSteve French /* security id for Authenticated Users system group */
3346c22d37SChenXiaoSong static const struct smb_sid sid_authusers = {
3438c8a9a5SSteve French 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} };
3538c8a9a5SSteve French
3638c8a9a5SSteve French /* S-1-22-1 Unmapped Unix users */
3746c22d37SChenXiaoSong static const struct smb_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22},
3838c8a9a5SSteve French {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
3938c8a9a5SSteve French
4038c8a9a5SSteve French /* S-1-22-2 Unmapped Unix groups */
4146c22d37SChenXiaoSong static const struct smb_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22},
4238c8a9a5SSteve French {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
4338c8a9a5SSteve French
4438c8a9a5SSteve French /*
4538c8a9a5SSteve French * See https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
4638c8a9a5SSteve French */
4738c8a9a5SSteve French
4838c8a9a5SSteve French /* S-1-5-88 MS NFS and Apple style UID/GID/mode */
4938c8a9a5SSteve French
5038c8a9a5SSteve French /* S-1-5-88-1 Unix uid */
5146c22d37SChenXiaoSong static const struct smb_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5},
5238c8a9a5SSteve French {cpu_to_le32(88),
5338c8a9a5SSteve French cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
5438c8a9a5SSteve French
5538c8a9a5SSteve French /* S-1-5-88-2 Unix gid */
5646c22d37SChenXiaoSong static const struct smb_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5},
5738c8a9a5SSteve French {cpu_to_le32(88),
5838c8a9a5SSteve French cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
5938c8a9a5SSteve French
6038c8a9a5SSteve French /* S-1-5-88-3 Unix mode */
6146c22d37SChenXiaoSong static const struct smb_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5},
6238c8a9a5SSteve French {cpu_to_le32(88),
6338c8a9a5SSteve French cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
6438c8a9a5SSteve French
6538c8a9a5SSteve French static const struct cred *root_cred;
6638c8a9a5SSteve French
6738c8a9a5SSteve French static int
cifs_idmap_key_instantiate(struct key * key,struct key_preparsed_payload * prep)6838c8a9a5SSteve French cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
6938c8a9a5SSteve French {
7038c8a9a5SSteve French char *payload;
7138c8a9a5SSteve French
7238c8a9a5SSteve French /*
7338c8a9a5SSteve French * If the payload is less than or equal to the size of a pointer, then
7438c8a9a5SSteve French * an allocation here is wasteful. Just copy the data directly to the
7538c8a9a5SSteve French * payload.value union member instead.
7638c8a9a5SSteve French *
7738c8a9a5SSteve French * With this however, you must check the datalen before trying to
7838c8a9a5SSteve French * dereference payload.data!
7938c8a9a5SSteve French */
8038c8a9a5SSteve French if (prep->datalen <= sizeof(key->payload)) {
8138c8a9a5SSteve French key->payload.data[0] = NULL;
8238c8a9a5SSteve French memcpy(&key->payload, prep->data, prep->datalen);
8338c8a9a5SSteve French } else {
8438c8a9a5SSteve French payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
8538c8a9a5SSteve French if (!payload)
8638c8a9a5SSteve French return -ENOMEM;
8738c8a9a5SSteve French key->payload.data[0] = payload;
8838c8a9a5SSteve French }
8938c8a9a5SSteve French
9038c8a9a5SSteve French key->datalen = prep->datalen;
9138c8a9a5SSteve French return 0;
9238c8a9a5SSteve French }
9338c8a9a5SSteve French
9438c8a9a5SSteve French static inline void
cifs_idmap_key_destroy(struct key * key)9538c8a9a5SSteve French cifs_idmap_key_destroy(struct key *key)
9638c8a9a5SSteve French {
9738c8a9a5SSteve French if (key->datalen > sizeof(key->payload))
9838c8a9a5SSteve French kfree(key->payload.data[0]);
9938c8a9a5SSteve French }
10038c8a9a5SSteve French
10138c8a9a5SSteve French static struct key_type cifs_idmap_key_type = {
10238c8a9a5SSteve French .name = "cifs.idmap",
10338c8a9a5SSteve French .instantiate = cifs_idmap_key_instantiate,
10438c8a9a5SSteve French .destroy = cifs_idmap_key_destroy,
10538c8a9a5SSteve French .describe = user_describe,
10638c8a9a5SSteve French };
10738c8a9a5SSteve French
10838c8a9a5SSteve French static char *
sid_to_key_str(struct smb_sid * sidptr,unsigned int type)10946c22d37SChenXiaoSong sid_to_key_str(struct smb_sid *sidptr, unsigned int type)
11038c8a9a5SSteve French {
11138c8a9a5SSteve French int i, len;
11238c8a9a5SSteve French unsigned int saval;
11338c8a9a5SSteve French char *sidstr, *strptr;
11438c8a9a5SSteve French unsigned long long id_auth_val;
11538c8a9a5SSteve French
11638c8a9a5SSteve French /* 3 bytes for prefix */
11738c8a9a5SSteve French sidstr = kmalloc(3 + SID_STRING_BASE_SIZE +
11838c8a9a5SSteve French (SID_STRING_SUBAUTH_SIZE * sidptr->num_subauth),
11938c8a9a5SSteve French GFP_KERNEL);
12038c8a9a5SSteve French if (!sidstr)
12138c8a9a5SSteve French return sidstr;
12238c8a9a5SSteve French
12338c8a9a5SSteve French strptr = sidstr;
12438c8a9a5SSteve French len = sprintf(strptr, "%cs:S-%hhu", type == SIDOWNER ? 'o' : 'g',
12538c8a9a5SSteve French sidptr->revision);
12638c8a9a5SSteve French strptr += len;
12738c8a9a5SSteve French
12838c8a9a5SSteve French /* The authority field is a single 48-bit number */
12938c8a9a5SSteve French id_auth_val = (unsigned long long)sidptr->authority[5];
13038c8a9a5SSteve French id_auth_val |= (unsigned long long)sidptr->authority[4] << 8;
13138c8a9a5SSteve French id_auth_val |= (unsigned long long)sidptr->authority[3] << 16;
13238c8a9a5SSteve French id_auth_val |= (unsigned long long)sidptr->authority[2] << 24;
13338c8a9a5SSteve French id_auth_val |= (unsigned long long)sidptr->authority[1] << 32;
13438c8a9a5SSteve French id_auth_val |= (unsigned long long)sidptr->authority[0] << 48;
13538c8a9a5SSteve French
13638c8a9a5SSteve French /*
13738c8a9a5SSteve French * MS-DTYP states that if the authority is >= 2^32, then it should be
13838c8a9a5SSteve French * expressed as a hex value.
13938c8a9a5SSteve French */
14038c8a9a5SSteve French if (id_auth_val <= UINT_MAX)
14138c8a9a5SSteve French len = sprintf(strptr, "-%llu", id_auth_val);
14238c8a9a5SSteve French else
14338c8a9a5SSteve French len = sprintf(strptr, "-0x%llx", id_auth_val);
14438c8a9a5SSteve French
14538c8a9a5SSteve French strptr += len;
14638c8a9a5SSteve French
14738c8a9a5SSteve French for (i = 0; i < sidptr->num_subauth; ++i) {
14838c8a9a5SSteve French saval = le32_to_cpu(sidptr->sub_auth[i]);
14938c8a9a5SSteve French len = sprintf(strptr, "-%u", saval);
15038c8a9a5SSteve French strptr += len;
15138c8a9a5SSteve French }
15238c8a9a5SSteve French
15338c8a9a5SSteve French return sidstr;
15438c8a9a5SSteve French }
15538c8a9a5SSteve French
15638c8a9a5SSteve French /*
15738c8a9a5SSteve French * if the two SIDs (roughly equivalent to a UUID for a user or group) are
15838c8a9a5SSteve French * the same returns zero, if they do not match returns non-zero.
15938c8a9a5SSteve French */
16038c8a9a5SSteve French static int
compare_sids(const struct smb_sid * ctsid,const struct smb_sid * cwsid)16146c22d37SChenXiaoSong compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid)
16238c8a9a5SSteve French {
16338c8a9a5SSteve French int i;
16438c8a9a5SSteve French int num_subauth, num_sat, num_saw;
16538c8a9a5SSteve French
16638c8a9a5SSteve French if ((!ctsid) || (!cwsid))
16738c8a9a5SSteve French return 1;
16838c8a9a5SSteve French
16938c8a9a5SSteve French /* compare the revision */
17038c8a9a5SSteve French if (ctsid->revision != cwsid->revision) {
17138c8a9a5SSteve French if (ctsid->revision > cwsid->revision)
17238c8a9a5SSteve French return 1;
17338c8a9a5SSteve French else
17438c8a9a5SSteve French return -1;
17538c8a9a5SSteve French }
17638c8a9a5SSteve French
17738c8a9a5SSteve French /* compare all of the six auth values */
17838c8a9a5SSteve French for (i = 0; i < NUM_AUTHS; ++i) {
17938c8a9a5SSteve French if (ctsid->authority[i] != cwsid->authority[i]) {
18038c8a9a5SSteve French if (ctsid->authority[i] > cwsid->authority[i])
18138c8a9a5SSteve French return 1;
18238c8a9a5SSteve French else
18338c8a9a5SSteve French return -1;
18438c8a9a5SSteve French }
18538c8a9a5SSteve French }
18638c8a9a5SSteve French
18738c8a9a5SSteve French /* compare all of the subauth values if any */
18838c8a9a5SSteve French num_sat = ctsid->num_subauth;
18938c8a9a5SSteve French num_saw = cwsid->num_subauth;
19038c8a9a5SSteve French num_subauth = num_sat < num_saw ? num_sat : num_saw;
19138c8a9a5SSteve French if (num_subauth) {
19238c8a9a5SSteve French for (i = 0; i < num_subauth; ++i) {
19338c8a9a5SSteve French if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
19438c8a9a5SSteve French if (le32_to_cpu(ctsid->sub_auth[i]) >
19538c8a9a5SSteve French le32_to_cpu(cwsid->sub_auth[i]))
19638c8a9a5SSteve French return 1;
19738c8a9a5SSteve French else
19838c8a9a5SSteve French return -1;
19938c8a9a5SSteve French }
20038c8a9a5SSteve French }
20138c8a9a5SSteve French }
20238c8a9a5SSteve French
20338c8a9a5SSteve French return 0; /* sids compare/match */
20438c8a9a5SSteve French }
20538c8a9a5SSteve French
20638c8a9a5SSteve French static bool
is_well_known_sid(const struct smb_sid * psid,uint32_t * puid,bool is_group)20746c22d37SChenXiaoSong is_well_known_sid(const struct smb_sid *psid, uint32_t *puid, bool is_group)
20838c8a9a5SSteve French {
20938c8a9a5SSteve French int i;
21038c8a9a5SSteve French int num_subauth;
21146c22d37SChenXiaoSong const struct smb_sid *pwell_known_sid;
21238c8a9a5SSteve French
21338c8a9a5SSteve French if (!psid || (puid == NULL))
21438c8a9a5SSteve French return false;
21538c8a9a5SSteve French
21638c8a9a5SSteve French num_subauth = psid->num_subauth;
21738c8a9a5SSteve French
21838c8a9a5SSteve French /* check if Mac (or Windows NFS) vs. Samba format for Unix owner SID */
21938c8a9a5SSteve French if (num_subauth == 2) {
22038c8a9a5SSteve French if (is_group)
22138c8a9a5SSteve French pwell_known_sid = &sid_unix_groups;
22238c8a9a5SSteve French else
22338c8a9a5SSteve French pwell_known_sid = &sid_unix_users;
22438c8a9a5SSteve French } else if (num_subauth == 3) {
22538c8a9a5SSteve French if (is_group)
22638c8a9a5SSteve French pwell_known_sid = &sid_unix_NFS_groups;
22738c8a9a5SSteve French else
22838c8a9a5SSteve French pwell_known_sid = &sid_unix_NFS_users;
22938c8a9a5SSteve French } else
23038c8a9a5SSteve French return false;
23138c8a9a5SSteve French
23238c8a9a5SSteve French /* compare the revision */
23338c8a9a5SSteve French if (psid->revision != pwell_known_sid->revision)
23438c8a9a5SSteve French return false;
23538c8a9a5SSteve French
23638c8a9a5SSteve French /* compare all of the six auth values */
23738c8a9a5SSteve French for (i = 0; i < NUM_AUTHS; ++i) {
23838c8a9a5SSteve French if (psid->authority[i] != pwell_known_sid->authority[i]) {
23938c8a9a5SSteve French cifs_dbg(FYI, "auth %d did not match\n", i);
24038c8a9a5SSteve French return false;
24138c8a9a5SSteve French }
24238c8a9a5SSteve French }
24338c8a9a5SSteve French
24438c8a9a5SSteve French if (num_subauth == 2) {
24538c8a9a5SSteve French if (psid->sub_auth[0] != pwell_known_sid->sub_auth[0])
24638c8a9a5SSteve French return false;
24738c8a9a5SSteve French
24838c8a9a5SSteve French *puid = le32_to_cpu(psid->sub_auth[1]);
24938c8a9a5SSteve French } else /* 3 subauths, ie Windows/Mac style */ {
25038c8a9a5SSteve French *puid = le32_to_cpu(psid->sub_auth[0]);
25138c8a9a5SSteve French if ((psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) ||
25238c8a9a5SSteve French (psid->sub_auth[1] != pwell_known_sid->sub_auth[1]))
25338c8a9a5SSteve French return false;
25438c8a9a5SSteve French
25538c8a9a5SSteve French *puid = le32_to_cpu(psid->sub_auth[2]);
25638c8a9a5SSteve French }
25738c8a9a5SSteve French
25838c8a9a5SSteve French cifs_dbg(FYI, "Unix UID %d returned from SID\n", *puid);
25938c8a9a5SSteve French return true; /* well known sid found, uid returned */
26038c8a9a5SSteve French }
26138c8a9a5SSteve French
26238c8a9a5SSteve French static __u16
cifs_copy_sid(struct smb_sid * dst,const struct smb_sid * src)26346c22d37SChenXiaoSong cifs_copy_sid(struct smb_sid *dst, const struct smb_sid *src)
26438c8a9a5SSteve French {
26538c8a9a5SSteve French int i;
26638c8a9a5SSteve French __u16 size = 1 + 1 + 6;
26738c8a9a5SSteve French
26838c8a9a5SSteve French dst->revision = src->revision;
26938c8a9a5SSteve French dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES);
27038c8a9a5SSteve French for (i = 0; i < NUM_AUTHS; ++i)
27138c8a9a5SSteve French dst->authority[i] = src->authority[i];
27238c8a9a5SSteve French for (i = 0; i < dst->num_subauth; ++i)
27338c8a9a5SSteve French dst->sub_auth[i] = src->sub_auth[i];
27438c8a9a5SSteve French size += (dst->num_subauth * 4);
27538c8a9a5SSteve French
27638c8a9a5SSteve French return size;
27738c8a9a5SSteve French }
27838c8a9a5SSteve French
27938c8a9a5SSteve French static int
id_to_sid(unsigned int cid,uint sidtype,struct smb_sid * ssid)28046c22d37SChenXiaoSong id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid)
28138c8a9a5SSteve French {
28238c8a9a5SSteve French int rc;
28338c8a9a5SSteve French struct key *sidkey;
28446c22d37SChenXiaoSong struct smb_sid *ksid;
28538c8a9a5SSteve French unsigned int ksid_size;
28638c8a9a5SSteve French char desc[3 + 10 + 1]; /* 3 byte prefix + 10 bytes for value + NULL */
28738c8a9a5SSteve French const struct cred *saved_cred;
28838c8a9a5SSteve French
28938c8a9a5SSteve French rc = snprintf(desc, sizeof(desc), "%ci:%u",
29038c8a9a5SSteve French sidtype == SIDOWNER ? 'o' : 'g', cid);
29138c8a9a5SSteve French if (rc >= sizeof(desc))
29238c8a9a5SSteve French return -EINVAL;
29338c8a9a5SSteve French
29438c8a9a5SSteve French rc = 0;
29538c8a9a5SSteve French saved_cred = override_creds(root_cred);
29638c8a9a5SSteve French sidkey = request_key(&cifs_idmap_key_type, desc, "");
29738c8a9a5SSteve French if (IS_ERR(sidkey)) {
29838c8a9a5SSteve French rc = -EINVAL;
29938c8a9a5SSteve French cifs_dbg(FYI, "%s: Can't map %cid %u to a SID\n",
30038c8a9a5SSteve French __func__, sidtype == SIDOWNER ? 'u' : 'g', cid);
30138c8a9a5SSteve French goto out_revert_creds;
30238c8a9a5SSteve French } else if (sidkey->datalen < CIFS_SID_BASE_SIZE) {
30338c8a9a5SSteve French rc = -EIO;
30438c8a9a5SSteve French cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n",
30538c8a9a5SSteve French __func__, sidkey->datalen);
30638c8a9a5SSteve French goto invalidate_key;
30738c8a9a5SSteve French }
30838c8a9a5SSteve French
30938c8a9a5SSteve French /*
31038c8a9a5SSteve French * A sid is usually too large to be embedded in payload.value, but if
31138c8a9a5SSteve French * there are no subauthorities and the host has 8-byte pointers, then
31238c8a9a5SSteve French * it could be.
31338c8a9a5SSteve French */
31438c8a9a5SSteve French ksid = sidkey->datalen <= sizeof(sidkey->payload) ?
31546c22d37SChenXiaoSong (struct smb_sid *)&sidkey->payload :
31646c22d37SChenXiaoSong (struct smb_sid *)sidkey->payload.data[0];
31738c8a9a5SSteve French
31838c8a9a5SSteve French ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32));
31938c8a9a5SSteve French if (ksid_size > sidkey->datalen) {
32038c8a9a5SSteve French rc = -EIO;
32138c8a9a5SSteve French cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu, ksid_size=%u)\n",
32238c8a9a5SSteve French __func__, sidkey->datalen, ksid_size);
32338c8a9a5SSteve French goto invalidate_key;
32438c8a9a5SSteve French }
32538c8a9a5SSteve French
32638c8a9a5SSteve French cifs_copy_sid(ssid, ksid);
32738c8a9a5SSteve French out_key_put:
32838c8a9a5SSteve French key_put(sidkey);
32938c8a9a5SSteve French out_revert_creds:
33038c8a9a5SSteve French revert_creds(saved_cred);
33138c8a9a5SSteve French return rc;
33238c8a9a5SSteve French
33338c8a9a5SSteve French invalidate_key:
33438c8a9a5SSteve French key_invalidate(sidkey);
33538c8a9a5SSteve French goto out_key_put;
33638c8a9a5SSteve French }
33738c8a9a5SSteve French
33838c8a9a5SSteve French int
sid_to_id(struct cifs_sb_info * cifs_sb,struct smb_sid * psid,struct cifs_fattr * fattr,uint sidtype)33946c22d37SChenXiaoSong sid_to_id(struct cifs_sb_info *cifs_sb, struct smb_sid *psid,
34038c8a9a5SSteve French struct cifs_fattr *fattr, uint sidtype)
34138c8a9a5SSteve French {
34238c8a9a5SSteve French int rc = 0;
34338c8a9a5SSteve French struct key *sidkey;
34438c8a9a5SSteve French char *sidstr;
34538c8a9a5SSteve French const struct cred *saved_cred;
34638c8a9a5SSteve French kuid_t fuid = cifs_sb->ctx->linux_uid;
34738c8a9a5SSteve French kgid_t fgid = cifs_sb->ctx->linux_gid;
34838c8a9a5SSteve French
34938c8a9a5SSteve French /*
35038c8a9a5SSteve French * If we have too many subauthorities, then something is really wrong.
35138c8a9a5SSteve French * Just return an error.
35238c8a9a5SSteve French */
35338c8a9a5SSteve French if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) {
35438c8a9a5SSteve French cifs_dbg(FYI, "%s: %u subauthorities is too many!\n",
35538c8a9a5SSteve French __func__, psid->num_subauth);
35638c8a9a5SSteve French return -EIO;
35738c8a9a5SSteve French }
35838c8a9a5SSteve French
35938c8a9a5SSteve French if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) ||
36038c8a9a5SSteve French (cifs_sb_master_tcon(cifs_sb)->posix_extensions)) {
36138c8a9a5SSteve French uint32_t unix_id;
36238c8a9a5SSteve French bool is_group;
36338c8a9a5SSteve French
36438c8a9a5SSteve French if (sidtype != SIDOWNER)
36538c8a9a5SSteve French is_group = true;
36638c8a9a5SSteve French else
36738c8a9a5SSteve French is_group = false;
36838c8a9a5SSteve French
36938c8a9a5SSteve French if (is_well_known_sid(psid, &unix_id, is_group) == false)
37038c8a9a5SSteve French goto try_upcall_to_get_id;
37138c8a9a5SSteve French
37238c8a9a5SSteve French if (is_group) {
37338c8a9a5SSteve French kgid_t gid;
37438c8a9a5SSteve French gid_t id;
37538c8a9a5SSteve French
37638c8a9a5SSteve French id = (gid_t)unix_id;
37738c8a9a5SSteve French gid = make_kgid(&init_user_ns, id);
37838c8a9a5SSteve French if (gid_valid(gid)) {
37938c8a9a5SSteve French fgid = gid;
38038c8a9a5SSteve French goto got_valid_id;
38138c8a9a5SSteve French }
38238c8a9a5SSteve French } else {
38338c8a9a5SSteve French kuid_t uid;
38438c8a9a5SSteve French uid_t id;
38538c8a9a5SSteve French
38638c8a9a5SSteve French id = (uid_t)unix_id;
38738c8a9a5SSteve French uid = make_kuid(&init_user_ns, id);
38838c8a9a5SSteve French if (uid_valid(uid)) {
38938c8a9a5SSteve French fuid = uid;
39038c8a9a5SSteve French goto got_valid_id;
39138c8a9a5SSteve French }
39238c8a9a5SSteve French }
39338c8a9a5SSteve French /* If unable to find uid/gid easily from SID try via upcall */
39438c8a9a5SSteve French }
39538c8a9a5SSteve French
39638c8a9a5SSteve French try_upcall_to_get_id:
39738c8a9a5SSteve French sidstr = sid_to_key_str(psid, sidtype);
39838c8a9a5SSteve French if (!sidstr)
39938c8a9a5SSteve French return -ENOMEM;
40038c8a9a5SSteve French
40138c8a9a5SSteve French saved_cred = override_creds(root_cred);
40238c8a9a5SSteve French sidkey = request_key(&cifs_idmap_key_type, sidstr, "");
40338c8a9a5SSteve French if (IS_ERR(sidkey)) {
40438c8a9a5SSteve French cifs_dbg(FYI, "%s: Can't map SID %s to a %cid\n",
40538c8a9a5SSteve French __func__, sidstr, sidtype == SIDOWNER ? 'u' : 'g');
40638c8a9a5SSteve French goto out_revert_creds;
40738c8a9a5SSteve French }
40838c8a9a5SSteve French
40938c8a9a5SSteve French /*
41038c8a9a5SSteve French * FIXME: Here we assume that uid_t and gid_t are same size. It's
41138c8a9a5SSteve French * probably a safe assumption but might be better to check based on
41238c8a9a5SSteve French * sidtype.
41338c8a9a5SSteve French */
41438c8a9a5SSteve French BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t));
41538c8a9a5SSteve French if (sidkey->datalen != sizeof(uid_t)) {
41638c8a9a5SSteve French cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n",
41738c8a9a5SSteve French __func__, sidkey->datalen);
41838c8a9a5SSteve French key_invalidate(sidkey);
41938c8a9a5SSteve French goto out_key_put;
42038c8a9a5SSteve French }
42138c8a9a5SSteve French
42238c8a9a5SSteve French if (sidtype == SIDOWNER) {
42338c8a9a5SSteve French kuid_t uid;
42438c8a9a5SSteve French uid_t id;
42538c8a9a5SSteve French memcpy(&id, &sidkey->payload.data[0], sizeof(uid_t));
42638c8a9a5SSteve French uid = make_kuid(&init_user_ns, id);
42738c8a9a5SSteve French if (uid_valid(uid))
42838c8a9a5SSteve French fuid = uid;
42938c8a9a5SSteve French } else {
43038c8a9a5SSteve French kgid_t gid;
43138c8a9a5SSteve French gid_t id;
43238c8a9a5SSteve French memcpy(&id, &sidkey->payload.data[0], sizeof(gid_t));
43338c8a9a5SSteve French gid = make_kgid(&init_user_ns, id);
43438c8a9a5SSteve French if (gid_valid(gid))
43538c8a9a5SSteve French fgid = gid;
43638c8a9a5SSteve French }
43738c8a9a5SSteve French
43838c8a9a5SSteve French out_key_put:
43938c8a9a5SSteve French key_put(sidkey);
44038c8a9a5SSteve French out_revert_creds:
44138c8a9a5SSteve French revert_creds(saved_cred);
44238c8a9a5SSteve French kfree(sidstr);
44338c8a9a5SSteve French
44438c8a9a5SSteve French /*
44538c8a9a5SSteve French * Note that we return 0 here unconditionally. If the mapping
44638c8a9a5SSteve French * fails then we just fall back to using the ctx->linux_uid/linux_gid.
44738c8a9a5SSteve French */
44838c8a9a5SSteve French got_valid_id:
44938c8a9a5SSteve French rc = 0;
45038c8a9a5SSteve French if (sidtype == SIDOWNER)
45138c8a9a5SSteve French fattr->cf_uid = fuid;
45238c8a9a5SSteve French else
45338c8a9a5SSteve French fattr->cf_gid = fgid;
45438c8a9a5SSteve French return rc;
45538c8a9a5SSteve French }
45638c8a9a5SSteve French
45738c8a9a5SSteve French int
init_cifs_idmap(void)45838c8a9a5SSteve French init_cifs_idmap(void)
45938c8a9a5SSteve French {
46038c8a9a5SSteve French struct cred *cred;
46138c8a9a5SSteve French struct key *keyring;
46238c8a9a5SSteve French int ret;
46338c8a9a5SSteve French
46438c8a9a5SSteve French cifs_dbg(FYI, "Registering the %s key type\n",
46538c8a9a5SSteve French cifs_idmap_key_type.name);
46638c8a9a5SSteve French
46738c8a9a5SSteve French /* create an override credential set with a special thread keyring in
46838c8a9a5SSteve French * which requests are cached
46938c8a9a5SSteve French *
47038c8a9a5SSteve French * this is used to prevent malicious redirections from being installed
47138c8a9a5SSteve French * with add_key().
47238c8a9a5SSteve French */
47338c8a9a5SSteve French cred = prepare_kernel_cred(&init_task);
47438c8a9a5SSteve French if (!cred)
47538c8a9a5SSteve French return -ENOMEM;
47638c8a9a5SSteve French
47738c8a9a5SSteve French keyring = keyring_alloc(".cifs_idmap",
47838c8a9a5SSteve French GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
47938c8a9a5SSteve French (KEY_POS_ALL & ~KEY_POS_SETATTR) |
48038c8a9a5SSteve French KEY_USR_VIEW | KEY_USR_READ,
48138c8a9a5SSteve French KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
48238c8a9a5SSteve French if (IS_ERR(keyring)) {
48338c8a9a5SSteve French ret = PTR_ERR(keyring);
48438c8a9a5SSteve French goto failed_put_cred;
48538c8a9a5SSteve French }
48638c8a9a5SSteve French
48738c8a9a5SSteve French ret = register_key_type(&cifs_idmap_key_type);
48838c8a9a5SSteve French if (ret < 0)
48938c8a9a5SSteve French goto failed_put_key;
49038c8a9a5SSteve French
49138c8a9a5SSteve French /* instruct request_key() to use this special keyring as a cache for
49238c8a9a5SSteve French * the results it looks up */
49338c8a9a5SSteve French set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
49438c8a9a5SSteve French cred->thread_keyring = keyring;
49538c8a9a5SSteve French cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
49638c8a9a5SSteve French root_cred = cred;
49738c8a9a5SSteve French
49838c8a9a5SSteve French cifs_dbg(FYI, "cifs idmap keyring: %d\n", key_serial(keyring));
49938c8a9a5SSteve French return 0;
50038c8a9a5SSteve French
50138c8a9a5SSteve French failed_put_key:
50238c8a9a5SSteve French key_put(keyring);
50338c8a9a5SSteve French failed_put_cred:
50438c8a9a5SSteve French put_cred(cred);
50538c8a9a5SSteve French return ret;
50638c8a9a5SSteve French }
50738c8a9a5SSteve French
50838c8a9a5SSteve French void
exit_cifs_idmap(void)50938c8a9a5SSteve French exit_cifs_idmap(void)
51038c8a9a5SSteve French {
51138c8a9a5SSteve French key_revoke(root_cred->thread_keyring);
51238c8a9a5SSteve French unregister_key_type(&cifs_idmap_key_type);
51338c8a9a5SSteve French put_cred(root_cred);
51438c8a9a5SSteve French cifs_dbg(FYI, "Unregistered %s key type\n", cifs_idmap_key_type.name);
51538c8a9a5SSteve French }
51638c8a9a5SSteve French
51738c8a9a5SSteve French /* copy ntsd, owner sid, and group sid from a security descriptor to another */
copy_sec_desc(const struct smb_ntsd * pntsd,struct smb_ntsd * pnntsd,__u32 sidsoffset,struct smb_sid * pownersid,struct smb_sid * pgrpsid)518386660bdSChenXiaoSong static __u32 copy_sec_desc(const struct smb_ntsd *pntsd,
519386660bdSChenXiaoSong struct smb_ntsd *pnntsd,
52038c8a9a5SSteve French __u32 sidsoffset,
52146c22d37SChenXiaoSong struct smb_sid *pownersid,
52246c22d37SChenXiaoSong struct smb_sid *pgrpsid)
52338c8a9a5SSteve French {
52446c22d37SChenXiaoSong struct smb_sid *owner_sid_ptr, *group_sid_ptr;
52546c22d37SChenXiaoSong struct smb_sid *nowner_sid_ptr, *ngroup_sid_ptr;
52638c8a9a5SSteve French
52738c8a9a5SSteve French /* copy security descriptor control portion */
52838c8a9a5SSteve French pnntsd->revision = pntsd->revision;
52938c8a9a5SSteve French pnntsd->type = pntsd->type;
530386660bdSChenXiaoSong pnntsd->dacloffset = cpu_to_le32(sizeof(struct smb_ntsd));
53138c8a9a5SSteve French pnntsd->sacloffset = 0;
53238c8a9a5SSteve French pnntsd->osidoffset = cpu_to_le32(sidsoffset);
53346c22d37SChenXiaoSong pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct smb_sid));
53438c8a9a5SSteve French
53538c8a9a5SSteve French /* copy owner sid */
53638c8a9a5SSteve French if (pownersid)
53738c8a9a5SSteve French owner_sid_ptr = pownersid;
53838c8a9a5SSteve French else
53946c22d37SChenXiaoSong owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
54038c8a9a5SSteve French le32_to_cpu(pntsd->osidoffset));
54146c22d37SChenXiaoSong nowner_sid_ptr = (struct smb_sid *)((char *)pnntsd + sidsoffset);
54238c8a9a5SSteve French cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr);
54338c8a9a5SSteve French
54438c8a9a5SSteve French /* copy group sid */
54538c8a9a5SSteve French if (pgrpsid)
54638c8a9a5SSteve French group_sid_ptr = pgrpsid;
54738c8a9a5SSteve French else
54846c22d37SChenXiaoSong group_sid_ptr = (struct smb_sid *)((char *)pntsd +
54938c8a9a5SSteve French le32_to_cpu(pntsd->gsidoffset));
55046c22d37SChenXiaoSong ngroup_sid_ptr = (struct smb_sid *)((char *)pnntsd + sidsoffset +
55146c22d37SChenXiaoSong sizeof(struct smb_sid));
55238c8a9a5SSteve French cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr);
55338c8a9a5SSteve French
55446c22d37SChenXiaoSong return sidsoffset + (2 * sizeof(struct smb_sid));
55538c8a9a5SSteve French }
55638c8a9a5SSteve French
55738c8a9a5SSteve French
55838c8a9a5SSteve French /*
55938c8a9a5SSteve French change posix mode to reflect permissions
56038c8a9a5SSteve French pmode is the existing mode (we only want to overwrite part of this
56138c8a9a5SSteve French bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007
56238c8a9a5SSteve French */
access_flags_to_mode(__le32 ace_flags,int type,umode_t * pmode,umode_t * pdenied,umode_t mask)56338c8a9a5SSteve French static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode,
56438c8a9a5SSteve French umode_t *pdenied, umode_t mask)
56538c8a9a5SSteve French {
56638c8a9a5SSteve French __u32 flags = le32_to_cpu(ace_flags);
56738c8a9a5SSteve French /*
56838c8a9a5SSteve French * Do not assume "preferred" or "canonical" order.
56938c8a9a5SSteve French * The first DENY or ALLOW ACE which matches perfectly is
57038c8a9a5SSteve French * the permission to be used. Once allowed or denied, same
57138c8a9a5SSteve French * permission in later ACEs do not matter.
57238c8a9a5SSteve French */
57338c8a9a5SSteve French
57438c8a9a5SSteve French /* If not already allowed, deny these bits */
57538c8a9a5SSteve French if (type == ACCESS_DENIED) {
57638c8a9a5SSteve French if (flags & GENERIC_ALL &&
57738c8a9a5SSteve French !(*pmode & mask & 0777))
57838c8a9a5SSteve French *pdenied |= mask & 0777;
57938c8a9a5SSteve French
58038c8a9a5SSteve French if (((flags & GENERIC_WRITE) ||
58138c8a9a5SSteve French ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) &&
58238c8a9a5SSteve French !(*pmode & mask & 0222))
58338c8a9a5SSteve French *pdenied |= mask & 0222;
58438c8a9a5SSteve French
58538c8a9a5SSteve French if (((flags & GENERIC_READ) ||
58638c8a9a5SSteve French ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) &&
58738c8a9a5SSteve French !(*pmode & mask & 0444))
58838c8a9a5SSteve French *pdenied |= mask & 0444;
58938c8a9a5SSteve French
59038c8a9a5SSteve French if (((flags & GENERIC_EXECUTE) ||
59138c8a9a5SSteve French ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) &&
59238c8a9a5SSteve French !(*pmode & mask & 0111))
59338c8a9a5SSteve French *pdenied |= mask & 0111;
59438c8a9a5SSteve French
59538c8a9a5SSteve French return;
59638c8a9a5SSteve French } else if (type != ACCESS_ALLOWED) {
59738c8a9a5SSteve French cifs_dbg(VFS, "unknown access control type %d\n", type);
59838c8a9a5SSteve French return;
59938c8a9a5SSteve French }
60038c8a9a5SSteve French /* else ACCESS_ALLOWED type */
60138c8a9a5SSteve French
60238c8a9a5SSteve French if ((flags & GENERIC_ALL) &&
60338c8a9a5SSteve French !(*pdenied & mask & 0777)) {
60438c8a9a5SSteve French *pmode |= mask & 0777;
60538c8a9a5SSteve French cifs_dbg(NOISY, "all perms\n");
60638c8a9a5SSteve French return;
60738c8a9a5SSteve French }
60838c8a9a5SSteve French
60938c8a9a5SSteve French if (((flags & GENERIC_WRITE) ||
61038c8a9a5SSteve French ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) &&
61138c8a9a5SSteve French !(*pdenied & mask & 0222))
61238c8a9a5SSteve French *pmode |= mask & 0222;
61338c8a9a5SSteve French
61438c8a9a5SSteve French if (((flags & GENERIC_READ) ||
61538c8a9a5SSteve French ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) &&
61638c8a9a5SSteve French !(*pdenied & mask & 0444))
61738c8a9a5SSteve French *pmode |= mask & 0444;
61838c8a9a5SSteve French
61938c8a9a5SSteve French if (((flags & GENERIC_EXECUTE) ||
62038c8a9a5SSteve French ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) &&
62138c8a9a5SSteve French !(*pdenied & mask & 0111))
62238c8a9a5SSteve French *pmode |= mask & 0111;
62338c8a9a5SSteve French
62438c8a9a5SSteve French /* If DELETE_CHILD is set only on an owner ACE, set sticky bit */
62538c8a9a5SSteve French if (flags & FILE_DELETE_CHILD) {
62638c8a9a5SSteve French if (mask == ACL_OWNER_MASK) {
62738c8a9a5SSteve French if (!(*pdenied & 01000))
62838c8a9a5SSteve French *pmode |= 01000;
62938c8a9a5SSteve French } else if (!(*pdenied & 01000)) {
63038c8a9a5SSteve French *pmode &= ~01000;
63138c8a9a5SSteve French *pdenied |= 01000;
63238c8a9a5SSteve French }
63338c8a9a5SSteve French }
63438c8a9a5SSteve French
63538c8a9a5SSteve French cifs_dbg(NOISY, "access flags 0x%x mode now %04o\n", flags, *pmode);
63638c8a9a5SSteve French return;
63738c8a9a5SSteve French }
63838c8a9a5SSteve French
63938c8a9a5SSteve French /*
64038c8a9a5SSteve French Generate access flags to reflect permissions mode is the existing mode.
64138c8a9a5SSteve French This function is called for every ACE in the DACL whose SID matches
64238c8a9a5SSteve French with either owner or group or everyone.
64338c8a9a5SSteve French */
64438c8a9a5SSteve French
mode_to_access_flags(umode_t mode,umode_t bits_to_use,__u32 * pace_flags)64538c8a9a5SSteve French static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
64638c8a9a5SSteve French __u32 *pace_flags)
64738c8a9a5SSteve French {
64838c8a9a5SSteve French /* reset access mask */
64938c8a9a5SSteve French *pace_flags = 0x0;
65038c8a9a5SSteve French
65138c8a9a5SSteve French /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */
65238c8a9a5SSteve French mode &= bits_to_use;
65338c8a9a5SSteve French
65438c8a9a5SSteve French /* check for R/W/X UGO since we do not know whose flags
65538c8a9a5SSteve French is this but we have cleared all the bits sans RWX for
65638c8a9a5SSteve French either user or group or other as per bits_to_use */
65738c8a9a5SSteve French if (mode & S_IRUGO)
65838c8a9a5SSteve French *pace_flags |= SET_FILE_READ_RIGHTS;
65938c8a9a5SSteve French if (mode & S_IWUGO)
66038c8a9a5SSteve French *pace_flags |= SET_FILE_WRITE_RIGHTS;
66138c8a9a5SSteve French if (mode & S_IXUGO)
66238c8a9a5SSteve French *pace_flags |= SET_FILE_EXEC_RIGHTS;
66338c8a9a5SSteve French
66438c8a9a5SSteve French cifs_dbg(NOISY, "mode: %04o, access flags now 0x%x\n",
66538c8a9a5SSteve French mode, *pace_flags);
66638c8a9a5SSteve French return;
66738c8a9a5SSteve French }
66838c8a9a5SSteve French
cifs_copy_ace(struct smb_ace * dst,struct smb_ace * src,struct smb_sid * psid)669d6442904SChenXiaoSong static __u16 cifs_copy_ace(struct smb_ace *dst, struct smb_ace *src, struct smb_sid *psid)
67038c8a9a5SSteve French {
67138c8a9a5SSteve French __u16 size = 1 + 1 + 2 + 4;
67238c8a9a5SSteve French
67338c8a9a5SSteve French dst->type = src->type;
67438c8a9a5SSteve French dst->flags = src->flags;
67538c8a9a5SSteve French dst->access_req = src->access_req;
67638c8a9a5SSteve French
67738c8a9a5SSteve French /* Check if there's a replacement sid specified */
67838c8a9a5SSteve French if (psid)
67938c8a9a5SSteve French size += cifs_copy_sid(&dst->sid, psid);
68038c8a9a5SSteve French else
68138c8a9a5SSteve French size += cifs_copy_sid(&dst->sid, &src->sid);
68238c8a9a5SSteve French
68338c8a9a5SSteve French dst->size = cpu_to_le16(size);
68438c8a9a5SSteve French
68538c8a9a5SSteve French return size;
68638c8a9a5SSteve French }
68738c8a9a5SSteve French
fill_ace_for_sid(struct smb_ace * pntace,const struct smb_sid * psid,__u64 nmode,umode_t bits,__u8 access_type,bool allow_delete_child)688d6442904SChenXiaoSong static __u16 fill_ace_for_sid(struct smb_ace *pntace,
68946c22d37SChenXiaoSong const struct smb_sid *psid, __u64 nmode,
69038c8a9a5SSteve French umode_t bits, __u8 access_type,
69138c8a9a5SSteve French bool allow_delete_child)
69238c8a9a5SSteve French {
69338c8a9a5SSteve French int i;
69438c8a9a5SSteve French __u16 size = 0;
69538c8a9a5SSteve French __u32 access_req = 0;
69638c8a9a5SSteve French
69738c8a9a5SSteve French pntace->type = access_type;
69838c8a9a5SSteve French pntace->flags = 0x0;
69938c8a9a5SSteve French mode_to_access_flags(nmode, bits, &access_req);
70038c8a9a5SSteve French
70138c8a9a5SSteve French if (access_type == ACCESS_ALLOWED && allow_delete_child)
70238c8a9a5SSteve French access_req |= FILE_DELETE_CHILD;
70338c8a9a5SSteve French
70438c8a9a5SSteve French if (access_type == ACCESS_ALLOWED && !access_req)
70538c8a9a5SSteve French access_req = SET_MINIMUM_RIGHTS;
70638c8a9a5SSteve French else if (access_type == ACCESS_DENIED)
70738c8a9a5SSteve French access_req &= ~SET_MINIMUM_RIGHTS;
70838c8a9a5SSteve French
70938c8a9a5SSteve French pntace->access_req = cpu_to_le32(access_req);
71038c8a9a5SSteve French
71138c8a9a5SSteve French pntace->sid.revision = psid->revision;
71238c8a9a5SSteve French pntace->sid.num_subauth = psid->num_subauth;
71338c8a9a5SSteve French for (i = 0; i < NUM_AUTHS; i++)
71438c8a9a5SSteve French pntace->sid.authority[i] = psid->authority[i];
71538c8a9a5SSteve French for (i = 0; i < psid->num_subauth; i++)
71638c8a9a5SSteve French pntace->sid.sub_auth[i] = psid->sub_auth[i];
71738c8a9a5SSteve French
71838c8a9a5SSteve French size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4);
71938c8a9a5SSteve French pntace->size = cpu_to_le16(size);
72038c8a9a5SSteve French
72138c8a9a5SSteve French return size;
72238c8a9a5SSteve French }
72338c8a9a5SSteve French
72438c8a9a5SSteve French
72538c8a9a5SSteve French #ifdef CONFIG_CIFS_DEBUG2
dump_ace(struct smb_ace * pace,char * end_of_acl)726d6442904SChenXiaoSong static void dump_ace(struct smb_ace *pace, char *end_of_acl)
72738c8a9a5SSteve French {
72838c8a9a5SSteve French int num_subauth;
72938c8a9a5SSteve French
73038c8a9a5SSteve French /* validate that we do not go past end of acl */
73138c8a9a5SSteve French
73238c8a9a5SSteve French if (le16_to_cpu(pace->size) < 16) {
73338c8a9a5SSteve French cifs_dbg(VFS, "ACE too small %d\n", le16_to_cpu(pace->size));
73438c8a9a5SSteve French return;
73538c8a9a5SSteve French }
73638c8a9a5SSteve French
73738c8a9a5SSteve French if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) {
73838c8a9a5SSteve French cifs_dbg(VFS, "ACL too small to parse ACE\n");
73938c8a9a5SSteve French return;
74038c8a9a5SSteve French }
74138c8a9a5SSteve French
74238c8a9a5SSteve French num_subauth = pace->sid.num_subauth;
74338c8a9a5SSteve French if (num_subauth) {
74438c8a9a5SSteve French int i;
74538c8a9a5SSteve French cifs_dbg(FYI, "ACE revision %d num_auth %d type %d flags %d size %d\n",
74638c8a9a5SSteve French pace->sid.revision, pace->sid.num_subauth, pace->type,
74738c8a9a5SSteve French pace->flags, le16_to_cpu(pace->size));
74838c8a9a5SSteve French for (i = 0; i < num_subauth; ++i) {
74938c8a9a5SSteve French cifs_dbg(FYI, "ACE sub_auth[%d]: 0x%x\n",
75038c8a9a5SSteve French i, le32_to_cpu(pace->sid.sub_auth[i]));
75138c8a9a5SSteve French }
75238c8a9a5SSteve French
75338c8a9a5SSteve French /* BB add length check to make sure that we do not have huge
75438c8a9a5SSteve French num auths and therefore go off the end */
75538c8a9a5SSteve French }
75638c8a9a5SSteve French
75738c8a9a5SSteve French return;
75838c8a9a5SSteve French }
75938c8a9a5SSteve French #endif
76038c8a9a5SSteve French
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)761298e73acSChenXiaoSong static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
76246c22d37SChenXiaoSong struct smb_sid *pownersid, struct smb_sid *pgrpsid,
76338c8a9a5SSteve French struct cifs_fattr *fattr, bool mode_from_special_sid)
76438c8a9a5SSteve French {
76538c8a9a5SSteve French int i;
76638c8a9a5SSteve French int num_aces = 0;
76738c8a9a5SSteve French int acl_size;
76838c8a9a5SSteve French char *acl_base;
769d6442904SChenXiaoSong struct smb_ace **ppace;
77038c8a9a5SSteve French
77138c8a9a5SSteve French /* BB need to add parm so we can store the SID BB */
77238c8a9a5SSteve French
77338c8a9a5SSteve French if (!pdacl) {
77438c8a9a5SSteve French /* no DACL in the security descriptor, set
77538c8a9a5SSteve French all the permissions for user/group/other */
77638c8a9a5SSteve French fattr->cf_mode |= 0777;
77738c8a9a5SSteve French return;
77838c8a9a5SSteve French }
77938c8a9a5SSteve French
78038c8a9a5SSteve French /* validate that we do not go past end of acl */
78138c8a9a5SSteve French if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
78238c8a9a5SSteve French cifs_dbg(VFS, "ACL too small to parse DACL\n");
78338c8a9a5SSteve French return;
78438c8a9a5SSteve French }
78538c8a9a5SSteve French
78638c8a9a5SSteve French cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n",
78738c8a9a5SSteve French le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
78838c8a9a5SSteve French le32_to_cpu(pdacl->num_aces));
78938c8a9a5SSteve French
79038c8a9a5SSteve French /* reset rwx permissions for user/group/other.
79138c8a9a5SSteve French Also, if num_aces is 0 i.e. DACL has no ACEs,
79238c8a9a5SSteve French user/group/other have no permissions */
79338c8a9a5SSteve French fattr->cf_mode &= ~(0777);
79438c8a9a5SSteve French
79538c8a9a5SSteve French acl_base = (char *)pdacl;
796298e73acSChenXiaoSong acl_size = sizeof(struct smb_acl);
79738c8a9a5SSteve French
79838c8a9a5SSteve French num_aces = le32_to_cpu(pdacl->num_aces);
79938c8a9a5SSteve French if (num_aces > 0) {
80038c8a9a5SSteve French umode_t denied_mode = 0;
80138c8a9a5SSteve French
802d6442904SChenXiaoSong if (num_aces > ULONG_MAX / sizeof(struct smb_ace *))
80338c8a9a5SSteve French return;
804d6442904SChenXiaoSong ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *),
80538c8a9a5SSteve French GFP_KERNEL);
80638c8a9a5SSteve French if (!ppace)
80738c8a9a5SSteve French return;
80838c8a9a5SSteve French
80938c8a9a5SSteve French for (i = 0; i < num_aces; ++i) {
810d6442904SChenXiaoSong ppace[i] = (struct smb_ace *) (acl_base + acl_size);
81138c8a9a5SSteve French #ifdef CONFIG_CIFS_DEBUG2
81238c8a9a5SSteve French dump_ace(ppace[i], end_of_acl);
81338c8a9a5SSteve French #endif
81438c8a9a5SSteve French if (mode_from_special_sid &&
81538c8a9a5SSteve French (compare_sids(&(ppace[i]->sid),
81638c8a9a5SSteve French &sid_unix_NFS_mode) == 0)) {
81738c8a9a5SSteve French /*
81838c8a9a5SSteve French * Full permissions are:
81938c8a9a5SSteve French * 07777 = S_ISUID | S_ISGID | S_ISVTX |
82038c8a9a5SSteve French * S_IRWXU | S_IRWXG | S_IRWXO
82138c8a9a5SSteve French */
82238c8a9a5SSteve French fattr->cf_mode &= ~07777;
82338c8a9a5SSteve French fattr->cf_mode |=
82438c8a9a5SSteve French le32_to_cpu(ppace[i]->sid.sub_auth[2]);
82538c8a9a5SSteve French break;
82638c8a9a5SSteve French } else {
82738c8a9a5SSteve French if (compare_sids(&(ppace[i]->sid), pownersid) == 0) {
82838c8a9a5SSteve French access_flags_to_mode(ppace[i]->access_req,
82938c8a9a5SSteve French ppace[i]->type,
83038c8a9a5SSteve French &fattr->cf_mode,
83138c8a9a5SSteve French &denied_mode,
83238c8a9a5SSteve French ACL_OWNER_MASK);
83338c8a9a5SSteve French } else if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) {
83438c8a9a5SSteve French access_flags_to_mode(ppace[i]->access_req,
83538c8a9a5SSteve French ppace[i]->type,
83638c8a9a5SSteve French &fattr->cf_mode,
83738c8a9a5SSteve French &denied_mode,
83838c8a9a5SSteve French ACL_GROUP_MASK);
83938c8a9a5SSteve French } else if ((compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) ||
84038c8a9a5SSteve French (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)) {
84138c8a9a5SSteve French access_flags_to_mode(ppace[i]->access_req,
84238c8a9a5SSteve French ppace[i]->type,
84338c8a9a5SSteve French &fattr->cf_mode,
84438c8a9a5SSteve French &denied_mode,
84538c8a9a5SSteve French ACL_EVERYONE_MASK);
84638c8a9a5SSteve French }
84738c8a9a5SSteve French }
84838c8a9a5SSteve French
84938c8a9a5SSteve French
85038c8a9a5SSteve French /* memcpy((void *)(&(cifscred->aces[i])),
85138c8a9a5SSteve French (void *)ppace[i],
852d6442904SChenXiaoSong sizeof(struct smb_ace)); */
85338c8a9a5SSteve French
85438c8a9a5SSteve French acl_base = (char *)ppace[i];
85538c8a9a5SSteve French acl_size = le16_to_cpu(ppace[i]->size);
85638c8a9a5SSteve French }
85738c8a9a5SSteve French
85838c8a9a5SSteve French kfree(ppace);
85938c8a9a5SSteve French }
86038c8a9a5SSteve French
86138c8a9a5SSteve French return;
86238c8a9a5SSteve French }
86338c8a9a5SSteve French
setup_authusers_ACE(struct smb_ace * pntace)864d6442904SChenXiaoSong unsigned int setup_authusers_ACE(struct smb_ace *pntace)
86538c8a9a5SSteve French {
86638c8a9a5SSteve French int i;
86738c8a9a5SSteve French unsigned int ace_size = 20;
86838c8a9a5SSteve French
86938c8a9a5SSteve French pntace->type = ACCESS_ALLOWED_ACE_TYPE;
87038c8a9a5SSteve French pntace->flags = 0x0;
87138c8a9a5SSteve French pntace->access_req = cpu_to_le32(GENERIC_ALL);
87238c8a9a5SSteve French pntace->sid.num_subauth = 1;
87338c8a9a5SSteve French pntace->sid.revision = 1;
87438c8a9a5SSteve French for (i = 0; i < NUM_AUTHS; i++)
87538c8a9a5SSteve French pntace->sid.authority[i] = sid_authusers.authority[i];
87638c8a9a5SSteve French
87738c8a9a5SSteve French pntace->sid.sub_auth[0] = sid_authusers.sub_auth[0];
87838c8a9a5SSteve French
87938c8a9a5SSteve French /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */
88038c8a9a5SSteve French pntace->size = cpu_to_le16(ace_size);
88138c8a9a5SSteve French return ace_size;
88238c8a9a5SSteve French }
88338c8a9a5SSteve French
88438c8a9a5SSteve French /*
88538c8a9a5SSteve French * Fill in the special SID based on the mode. See
88638c8a9a5SSteve French * https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
88738c8a9a5SSteve French */
setup_special_mode_ACE(struct smb_ace * pntace,bool posix,__u64 nmode)8885f36890dSRalph Boehme unsigned int setup_special_mode_ACE(struct smb_ace *pntace,
8895f36890dSRalph Boehme bool posix,
8905f36890dSRalph Boehme __u64 nmode)
89138c8a9a5SSteve French {
89238c8a9a5SSteve French int i;
89338c8a9a5SSteve French unsigned int ace_size = 28;
89438c8a9a5SSteve French
8955f36890dSRalph Boehme if (posix)
8965f36890dSRalph Boehme pntace->type = ACCESS_ALLOWED_ACE_TYPE;
8975f36890dSRalph Boehme else
89838c8a9a5SSteve French pntace->type = ACCESS_DENIED_ACE_TYPE;
89938c8a9a5SSteve French pntace->flags = 0x0;
90038c8a9a5SSteve French pntace->access_req = 0;
90138c8a9a5SSteve French pntace->sid.num_subauth = 3;
90238c8a9a5SSteve French pntace->sid.revision = 1;
90338c8a9a5SSteve French for (i = 0; i < NUM_AUTHS; i++)
90438c8a9a5SSteve French pntace->sid.authority[i] = sid_unix_NFS_mode.authority[i];
90538c8a9a5SSteve French
90638c8a9a5SSteve French pntace->sid.sub_auth[0] = sid_unix_NFS_mode.sub_auth[0];
90738c8a9a5SSteve French pntace->sid.sub_auth[1] = sid_unix_NFS_mode.sub_auth[1];
90838c8a9a5SSteve French pntace->sid.sub_auth[2] = cpu_to_le32(nmode & 07777);
90938c8a9a5SSteve French
91038c8a9a5SSteve French /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */
91138c8a9a5SSteve French pntace->size = cpu_to_le16(ace_size);
91238c8a9a5SSteve French return ace_size;
91338c8a9a5SSteve French }
91438c8a9a5SSteve French
setup_special_user_owner_ACE(struct smb_ace * pntace)915d6442904SChenXiaoSong unsigned int setup_special_user_owner_ACE(struct smb_ace *pntace)
91638c8a9a5SSteve French {
91738c8a9a5SSteve French int i;
91838c8a9a5SSteve French unsigned int ace_size = 28;
91938c8a9a5SSteve French
92038c8a9a5SSteve French pntace->type = ACCESS_ALLOWED_ACE_TYPE;
92138c8a9a5SSteve French pntace->flags = 0x0;
92238c8a9a5SSteve French pntace->access_req = cpu_to_le32(GENERIC_ALL);
92338c8a9a5SSteve French pntace->sid.num_subauth = 3;
92438c8a9a5SSteve French pntace->sid.revision = 1;
92538c8a9a5SSteve French for (i = 0; i < NUM_AUTHS; i++)
92638c8a9a5SSteve French pntace->sid.authority[i] = sid_unix_NFS_users.authority[i];
92738c8a9a5SSteve French
92838c8a9a5SSteve French pntace->sid.sub_auth[0] = sid_unix_NFS_users.sub_auth[0];
92938c8a9a5SSteve French pntace->sid.sub_auth[1] = sid_unix_NFS_users.sub_auth[1];
93038c8a9a5SSteve French pntace->sid.sub_auth[2] = cpu_to_le32(current_fsgid().val);
93138c8a9a5SSteve French
93238c8a9a5SSteve French /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */
93338c8a9a5SSteve French pntace->size = cpu_to_le16(ace_size);
93438c8a9a5SSteve French return ace_size;
93538c8a9a5SSteve French }
93638c8a9a5SSteve French
populate_new_aces(char * nacl_base,struct smb_sid * pownersid,struct smb_sid * pgrpsid,__u64 * pnmode,u32 * pnum_aces,u16 * pnsize,bool modefromsid,bool posix)93738c8a9a5SSteve French static void populate_new_aces(char *nacl_base,
93846c22d37SChenXiaoSong struct smb_sid *pownersid,
93946c22d37SChenXiaoSong struct smb_sid *pgrpsid,
94038c8a9a5SSteve French __u64 *pnmode, u32 *pnum_aces, u16 *pnsize,
9415f36890dSRalph Boehme bool modefromsid,
9425f36890dSRalph Boehme bool posix)
94338c8a9a5SSteve French {
94438c8a9a5SSteve French __u64 nmode;
94538c8a9a5SSteve French u32 num_aces = 0;
94638c8a9a5SSteve French u16 nsize = 0;
94738c8a9a5SSteve French __u64 user_mode;
94838c8a9a5SSteve French __u64 group_mode;
94938c8a9a5SSteve French __u64 other_mode;
95038c8a9a5SSteve French __u64 deny_user_mode = 0;
95138c8a9a5SSteve French __u64 deny_group_mode = 0;
95238c8a9a5SSteve French bool sticky_set = false;
953d6442904SChenXiaoSong struct smb_ace *pnntace = NULL;
95438c8a9a5SSteve French
95538c8a9a5SSteve French nmode = *pnmode;
95638c8a9a5SSteve French num_aces = *pnum_aces;
95738c8a9a5SSteve French nsize = *pnsize;
95838c8a9a5SSteve French
9595f36890dSRalph Boehme if (modefromsid || posix) {
960d6442904SChenXiaoSong pnntace = (struct smb_ace *) (nacl_base + nsize);
9615f36890dSRalph Boehme nsize += setup_special_mode_ACE(pnntace, posix, nmode);
96238c8a9a5SSteve French num_aces++;
9635f36890dSRalph Boehme if (modefromsid) {
964d6442904SChenXiaoSong pnntace = (struct smb_ace *) (nacl_base + nsize);
96538c8a9a5SSteve French nsize += setup_authusers_ACE(pnntace);
96638c8a9a5SSteve French num_aces++;
9675f36890dSRalph Boehme }
96838c8a9a5SSteve French goto set_size;
96938c8a9a5SSteve French }
97038c8a9a5SSteve French
97138c8a9a5SSteve French /*
97238c8a9a5SSteve French * We'll try to keep the mode as requested by the user.
97338c8a9a5SSteve French * But in cases where we cannot meaningfully convert that
97438c8a9a5SSteve French * into ACL, return back the updated mode, so that it is
97538c8a9a5SSteve French * updated in the inode.
97638c8a9a5SSteve French */
97738c8a9a5SSteve French
97846c22d37SChenXiaoSong if (!memcmp(pownersid, pgrpsid, sizeof(struct smb_sid))) {
97938c8a9a5SSteve French /*
98038c8a9a5SSteve French * Case when owner and group SIDs are the same.
98138c8a9a5SSteve French * Set the more restrictive of the two modes.
98238c8a9a5SSteve French */
98338c8a9a5SSteve French user_mode = nmode & (nmode << 3) & 0700;
98438c8a9a5SSteve French group_mode = nmode & (nmode >> 3) & 0070;
98538c8a9a5SSteve French } else {
98638c8a9a5SSteve French user_mode = nmode & 0700;
98738c8a9a5SSteve French group_mode = nmode & 0070;
98838c8a9a5SSteve French }
98938c8a9a5SSteve French
99038c8a9a5SSteve French other_mode = nmode & 0007;
99138c8a9a5SSteve French
99238c8a9a5SSteve French /* We need DENY ACE when the perm is more restrictive than the next sets. */
99338c8a9a5SSteve French deny_user_mode = ~(user_mode) & ((group_mode << 3) | (other_mode << 6)) & 0700;
99438c8a9a5SSteve French deny_group_mode = ~(group_mode) & (other_mode << 3) & 0070;
99538c8a9a5SSteve French
99638c8a9a5SSteve French *pnmode = user_mode | group_mode | other_mode | (nmode & ~0777);
99738c8a9a5SSteve French
99838c8a9a5SSteve French /* This tells if we should allow delete child for group and everyone. */
99938c8a9a5SSteve French if (nmode & 01000)
100038c8a9a5SSteve French sticky_set = true;
100138c8a9a5SSteve French
100238c8a9a5SSteve French if (deny_user_mode) {
1003d6442904SChenXiaoSong pnntace = (struct smb_ace *) (nacl_base + nsize);
100438c8a9a5SSteve French nsize += fill_ace_for_sid(pnntace, pownersid, deny_user_mode,
100538c8a9a5SSteve French 0700, ACCESS_DENIED, false);
100638c8a9a5SSteve French num_aces++;
100738c8a9a5SSteve French }
100838c8a9a5SSteve French
100938c8a9a5SSteve French /* Group DENY ACE does not conflict with owner ALLOW ACE. Keep in preferred order*/
101038c8a9a5SSteve French if (deny_group_mode && !(deny_group_mode & (user_mode >> 3))) {
1011d6442904SChenXiaoSong pnntace = (struct smb_ace *) (nacl_base + nsize);
101238c8a9a5SSteve French nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode,
101338c8a9a5SSteve French 0070, ACCESS_DENIED, false);
101438c8a9a5SSteve French num_aces++;
101538c8a9a5SSteve French }
101638c8a9a5SSteve French
1017d6442904SChenXiaoSong pnntace = (struct smb_ace *) (nacl_base + nsize);
101838c8a9a5SSteve French nsize += fill_ace_for_sid(pnntace, pownersid, user_mode,
101938c8a9a5SSteve French 0700, ACCESS_ALLOWED, true);
102038c8a9a5SSteve French num_aces++;
102138c8a9a5SSteve French
102238c8a9a5SSteve French /* Group DENY ACE conflicts with owner ALLOW ACE. So keep it after. */
102338c8a9a5SSteve French if (deny_group_mode && (deny_group_mode & (user_mode >> 3))) {
1024d6442904SChenXiaoSong pnntace = (struct smb_ace *) (nacl_base + nsize);
102538c8a9a5SSteve French nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode,
102638c8a9a5SSteve French 0070, ACCESS_DENIED, false);
102738c8a9a5SSteve French num_aces++;
102838c8a9a5SSteve French }
102938c8a9a5SSteve French
1030d6442904SChenXiaoSong pnntace = (struct smb_ace *) (nacl_base + nsize);
103138c8a9a5SSteve French nsize += fill_ace_for_sid(pnntace, pgrpsid, group_mode,
103238c8a9a5SSteve French 0070, ACCESS_ALLOWED, !sticky_set);
103338c8a9a5SSteve French num_aces++;
103438c8a9a5SSteve French
1035d6442904SChenXiaoSong pnntace = (struct smb_ace *) (nacl_base + nsize);
103638c8a9a5SSteve French nsize += fill_ace_for_sid(pnntace, &sid_everyone, other_mode,
103738c8a9a5SSteve French 0007, ACCESS_ALLOWED, !sticky_set);
103838c8a9a5SSteve French num_aces++;
103938c8a9a5SSteve French
104038c8a9a5SSteve French set_size:
104138c8a9a5SSteve French *pnum_aces = num_aces;
104238c8a9a5SSteve French *pnsize = nsize;
104338c8a9a5SSteve French }
104438c8a9a5SSteve French
replace_sids_and_copy_aces(struct smb_acl * pdacl,struct smb_acl * pndacl,struct smb_sid * pownersid,struct smb_sid * pgrpsid,struct smb_sid * pnownersid,struct smb_sid * pngrpsid)1045298e73acSChenXiaoSong static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *pndacl,
104646c22d37SChenXiaoSong struct smb_sid *pownersid, struct smb_sid *pgrpsid,
104746c22d37SChenXiaoSong struct smb_sid *pnownersid, struct smb_sid *pngrpsid)
104838c8a9a5SSteve French {
104938c8a9a5SSteve French int i;
105038c8a9a5SSteve French u16 size = 0;
1051d6442904SChenXiaoSong struct smb_ace *pntace = NULL;
105238c8a9a5SSteve French char *acl_base = NULL;
105338c8a9a5SSteve French u32 src_num_aces = 0;
105438c8a9a5SSteve French u16 nsize = 0;
1055d6442904SChenXiaoSong struct smb_ace *pnntace = NULL;
105638c8a9a5SSteve French char *nacl_base = NULL;
105738c8a9a5SSteve French u16 ace_size = 0;
105838c8a9a5SSteve French
105938c8a9a5SSteve French acl_base = (char *)pdacl;
1060298e73acSChenXiaoSong size = sizeof(struct smb_acl);
106138c8a9a5SSteve French src_num_aces = le32_to_cpu(pdacl->num_aces);
106238c8a9a5SSteve French
106338c8a9a5SSteve French nacl_base = (char *)pndacl;
1064298e73acSChenXiaoSong nsize = sizeof(struct smb_acl);
106538c8a9a5SSteve French
106638c8a9a5SSteve French /* Go through all the ACEs */
106738c8a9a5SSteve French for (i = 0; i < src_num_aces; ++i) {
1068d6442904SChenXiaoSong pntace = (struct smb_ace *) (acl_base + size);
1069d6442904SChenXiaoSong pnntace = (struct smb_ace *) (nacl_base + nsize);
107038c8a9a5SSteve French
107138c8a9a5SSteve French if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0)
107238c8a9a5SSteve French ace_size = cifs_copy_ace(pnntace, pntace, pnownersid);
107338c8a9a5SSteve French else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0)
107438c8a9a5SSteve French ace_size = cifs_copy_ace(pnntace, pntace, pngrpsid);
107538c8a9a5SSteve French else
107638c8a9a5SSteve French ace_size = cifs_copy_ace(pnntace, pntace, NULL);
107738c8a9a5SSteve French
107838c8a9a5SSteve French size += le16_to_cpu(pntace->size);
107938c8a9a5SSteve French nsize += ace_size;
108038c8a9a5SSteve French }
108138c8a9a5SSteve French
108238c8a9a5SSteve French return nsize;
108338c8a9a5SSteve French }
108438c8a9a5SSteve French
set_chmod_dacl(struct smb_acl * pdacl,struct smb_acl * pndacl,struct smb_sid * pownersid,struct smb_sid * pgrpsid,__u64 * pnmode,bool mode_from_sid,bool posix)1085298e73acSChenXiaoSong static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
108646c22d37SChenXiaoSong struct smb_sid *pownersid, struct smb_sid *pgrpsid,
10875f36890dSRalph Boehme __u64 *pnmode, bool mode_from_sid, bool posix)
108838c8a9a5SSteve French {
108938c8a9a5SSteve French int i;
109038c8a9a5SSteve French u16 size = 0;
1091d6442904SChenXiaoSong struct smb_ace *pntace = NULL;
109238c8a9a5SSteve French char *acl_base = NULL;
109338c8a9a5SSteve French u32 src_num_aces = 0;
109438c8a9a5SSteve French u16 nsize = 0;
1095d6442904SChenXiaoSong struct smb_ace *pnntace = NULL;
109638c8a9a5SSteve French char *nacl_base = NULL;
109738c8a9a5SSteve French u32 num_aces = 0;
109838c8a9a5SSteve French bool new_aces_set = false;
109938c8a9a5SSteve French
110038c8a9a5SSteve French /* Assuming that pndacl and pnmode are never NULL */
110138c8a9a5SSteve French nacl_base = (char *)pndacl;
1102298e73acSChenXiaoSong nsize = sizeof(struct smb_acl);
110338c8a9a5SSteve French
110438c8a9a5SSteve French /* If pdacl is NULL, we don't have a src. Simply populate new ACL. */
11055f36890dSRalph Boehme if (!pdacl || posix) {
110638c8a9a5SSteve French populate_new_aces(nacl_base,
110738c8a9a5SSteve French pownersid, pgrpsid,
110838c8a9a5SSteve French pnmode, &num_aces, &nsize,
11095f36890dSRalph Boehme mode_from_sid, posix);
111038c8a9a5SSteve French goto finalize_dacl;
111138c8a9a5SSteve French }
111238c8a9a5SSteve French
111338c8a9a5SSteve French acl_base = (char *)pdacl;
1114298e73acSChenXiaoSong size = sizeof(struct smb_acl);
111538c8a9a5SSteve French src_num_aces = le32_to_cpu(pdacl->num_aces);
111638c8a9a5SSteve French
111738c8a9a5SSteve French /* Retain old ACEs which we can retain */
111838c8a9a5SSteve French for (i = 0; i < src_num_aces; ++i) {
1119d6442904SChenXiaoSong pntace = (struct smb_ace *) (acl_base + size);
112038c8a9a5SSteve French
112138c8a9a5SSteve French if (!new_aces_set && (pntace->flags & INHERITED_ACE)) {
112238c8a9a5SSteve French /* Place the new ACEs in between existing explicit and inherited */
112338c8a9a5SSteve French populate_new_aces(nacl_base,
112438c8a9a5SSteve French pownersid, pgrpsid,
112538c8a9a5SSteve French pnmode, &num_aces, &nsize,
11265f36890dSRalph Boehme mode_from_sid, posix);
112738c8a9a5SSteve French
112838c8a9a5SSteve French new_aces_set = true;
112938c8a9a5SSteve French }
113038c8a9a5SSteve French
113138c8a9a5SSteve French /* If it's any one of the ACE we're replacing, skip! */
113238c8a9a5SSteve French if (((compare_sids(&pntace->sid, &sid_unix_NFS_mode) == 0) ||
113338c8a9a5SSteve French (compare_sids(&pntace->sid, pownersid) == 0) ||
113438c8a9a5SSteve French (compare_sids(&pntace->sid, pgrpsid) == 0) ||
113538c8a9a5SSteve French (compare_sids(&pntace->sid, &sid_everyone) == 0) ||
113638c8a9a5SSteve French (compare_sids(&pntace->sid, &sid_authusers) == 0))) {
113738c8a9a5SSteve French goto next_ace;
113838c8a9a5SSteve French }
113938c8a9a5SSteve French
114038c8a9a5SSteve French /* update the pointer to the next ACE to populate*/
1141d6442904SChenXiaoSong pnntace = (struct smb_ace *) (nacl_base + nsize);
114238c8a9a5SSteve French
114338c8a9a5SSteve French nsize += cifs_copy_ace(pnntace, pntace, NULL);
114438c8a9a5SSteve French num_aces++;
114538c8a9a5SSteve French
114638c8a9a5SSteve French next_ace:
114738c8a9a5SSteve French size += le16_to_cpu(pntace->size);
114838c8a9a5SSteve French }
114938c8a9a5SSteve French
115038c8a9a5SSteve French /* If inherited ACEs are not present, place the new ones at the tail */
115138c8a9a5SSteve French if (!new_aces_set) {
115238c8a9a5SSteve French populate_new_aces(nacl_base,
115338c8a9a5SSteve French pownersid, pgrpsid,
115438c8a9a5SSteve French pnmode, &num_aces, &nsize,
11555f36890dSRalph Boehme mode_from_sid, posix);
115638c8a9a5SSteve French
115738c8a9a5SSteve French new_aces_set = true;
115838c8a9a5SSteve French }
115938c8a9a5SSteve French
116038c8a9a5SSteve French finalize_dacl:
116138c8a9a5SSteve French pndacl->num_aces = cpu_to_le32(num_aces);
116238c8a9a5SSteve French pndacl->size = cpu_to_le16(nsize);
116338c8a9a5SSteve French
116438c8a9a5SSteve French return 0;
116538c8a9a5SSteve French }
116638c8a9a5SSteve French
parse_sid(struct smb_sid * psid,char * end_of_acl)116746c22d37SChenXiaoSong static int parse_sid(struct smb_sid *psid, char *end_of_acl)
116838c8a9a5SSteve French {
116938c8a9a5SSteve French /* BB need to add parm so we can store the SID BB */
117038c8a9a5SSteve French
117138c8a9a5SSteve French /* validate that we do not go past end of ACL - sid must be at least 8
117238c8a9a5SSteve French bytes long (assuming no sub-auths - e.g. the null SID */
117338c8a9a5SSteve French if (end_of_acl < (char *)psid + 8) {
117438c8a9a5SSteve French cifs_dbg(VFS, "ACL too small to parse SID %p\n", psid);
117538c8a9a5SSteve French return -EINVAL;
117638c8a9a5SSteve French }
117738c8a9a5SSteve French
117838c8a9a5SSteve French #ifdef CONFIG_CIFS_DEBUG2
117938c8a9a5SSteve French if (psid->num_subauth) {
118038c8a9a5SSteve French int i;
118138c8a9a5SSteve French cifs_dbg(FYI, "SID revision %d num_auth %d\n",
118238c8a9a5SSteve French psid->revision, psid->num_subauth);
118338c8a9a5SSteve French
118438c8a9a5SSteve French for (i = 0; i < psid->num_subauth; i++) {
118538c8a9a5SSteve French cifs_dbg(FYI, "SID sub_auth[%d]: 0x%x\n",
118638c8a9a5SSteve French i, le32_to_cpu(psid->sub_auth[i]));
118738c8a9a5SSteve French }
118838c8a9a5SSteve French
118938c8a9a5SSteve French /* BB add length check to make sure that we do not have huge
119038c8a9a5SSteve French num auths and therefore go off the end */
119138c8a9a5SSteve French cifs_dbg(FYI, "RID 0x%x\n",
119238c8a9a5SSteve French le32_to_cpu(psid->sub_auth[psid->num_subauth-1]));
119338c8a9a5SSteve French }
119438c8a9a5SSteve French #endif
119538c8a9a5SSteve French
119638c8a9a5SSteve French return 0;
119738c8a9a5SSteve French }
119838c8a9a5SSteve French
119938c8a9a5SSteve French
120038c8a9a5SSteve French /* Convert CIFS ACL to POSIX form */
parse_sec_desc(struct cifs_sb_info * cifs_sb,struct smb_ntsd * pntsd,int acl_len,struct cifs_fattr * fattr,bool get_mode_from_special_sid)120138c8a9a5SSteve French static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
1202386660bdSChenXiaoSong struct smb_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr,
120338c8a9a5SSteve French bool get_mode_from_special_sid)
120438c8a9a5SSteve French {
120538c8a9a5SSteve French int rc = 0;
120646c22d37SChenXiaoSong struct smb_sid *owner_sid_ptr, *group_sid_ptr;
1207298e73acSChenXiaoSong struct smb_acl *dacl_ptr; /* no need for SACL ptr */
120838c8a9a5SSteve French char *end_of_acl = ((char *)pntsd) + acl_len;
120938c8a9a5SSteve French __u32 dacloffset;
121038c8a9a5SSteve French
121138c8a9a5SSteve French if (pntsd == NULL)
121238c8a9a5SSteve French return -EIO;
121338c8a9a5SSteve French
121446c22d37SChenXiaoSong owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
121538c8a9a5SSteve French le32_to_cpu(pntsd->osidoffset));
121646c22d37SChenXiaoSong group_sid_ptr = (struct smb_sid *)((char *)pntsd +
121738c8a9a5SSteve French le32_to_cpu(pntsd->gsidoffset));
121838c8a9a5SSteve French dacloffset = le32_to_cpu(pntsd->dacloffset);
1219298e73acSChenXiaoSong dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
122038c8a9a5SSteve French cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n",
122138c8a9a5SSteve French pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
122238c8a9a5SSteve French le32_to_cpu(pntsd->gsidoffset),
122338c8a9a5SSteve French le32_to_cpu(pntsd->sacloffset), dacloffset);
122438c8a9a5SSteve French /* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */
122538c8a9a5SSteve French rc = parse_sid(owner_sid_ptr, end_of_acl);
122638c8a9a5SSteve French if (rc) {
122738c8a9a5SSteve French cifs_dbg(FYI, "%s: Error %d parsing Owner SID\n", __func__, rc);
122838c8a9a5SSteve French return rc;
122938c8a9a5SSteve French }
123038c8a9a5SSteve French rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER);
123138c8a9a5SSteve French if (rc) {
123238c8a9a5SSteve French cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n",
123338c8a9a5SSteve French __func__, rc);
123438c8a9a5SSteve French return rc;
123538c8a9a5SSteve French }
123638c8a9a5SSteve French
123738c8a9a5SSteve French rc = parse_sid(group_sid_ptr, end_of_acl);
123838c8a9a5SSteve French if (rc) {
123938c8a9a5SSteve French cifs_dbg(FYI, "%s: Error %d mapping Owner SID to gid\n",
124038c8a9a5SSteve French __func__, rc);
124138c8a9a5SSteve French return rc;
124238c8a9a5SSteve French }
124338c8a9a5SSteve French rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP);
124438c8a9a5SSteve French if (rc) {
124538c8a9a5SSteve French cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n",
124638c8a9a5SSteve French __func__, rc);
124738c8a9a5SSteve French return rc;
124838c8a9a5SSteve French }
124938c8a9a5SSteve French
125038c8a9a5SSteve French if (dacloffset)
125138c8a9a5SSteve French parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
125238c8a9a5SSteve French group_sid_ptr, fattr, get_mode_from_special_sid);
125338c8a9a5SSteve French else
125438c8a9a5SSteve French cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */
125538c8a9a5SSteve French
125638c8a9a5SSteve French return rc;
125738c8a9a5SSteve French }
125838c8a9a5SSteve French
125938c8a9a5SSteve French /* Convert permission bits from mode to equivalent CIFS ACL */
build_sec_desc(struct smb_ntsd * pntsd,struct smb_ntsd * pnntsd,__u32 secdesclen,__u32 * pnsecdesclen,__u64 * pnmode,kuid_t uid,kgid_t gid,bool mode_from_sid,bool id_from_sid,bool posix,int * aclflag)1260386660bdSChenXiaoSong static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
126138c8a9a5SSteve French __u32 secdesclen, __u32 *pnsecdesclen, __u64 *pnmode, kuid_t uid, kgid_t gid,
12625f36890dSRalph Boehme bool mode_from_sid, bool id_from_sid, bool posix, int *aclflag)
126338c8a9a5SSteve French {
126438c8a9a5SSteve French int rc = 0;
126538c8a9a5SSteve French __u32 dacloffset;
126638c8a9a5SSteve French __u32 ndacloffset;
126738c8a9a5SSteve French __u32 sidsoffset;
126846c22d37SChenXiaoSong struct smb_sid *owner_sid_ptr, *group_sid_ptr;
126946c22d37SChenXiaoSong struct smb_sid *nowner_sid_ptr = NULL, *ngroup_sid_ptr = NULL;
1270298e73acSChenXiaoSong struct smb_acl *dacl_ptr = NULL; /* no need for SACL ptr */
1271298e73acSChenXiaoSong struct smb_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
127238c8a9a5SSteve French char *end_of_acl = ((char *)pntsd) + secdesclen;
127338c8a9a5SSteve French u16 size = 0;
127438c8a9a5SSteve French
127538c8a9a5SSteve French dacloffset = le32_to_cpu(pntsd->dacloffset);
127638c8a9a5SSteve French if (dacloffset) {
1277298e73acSChenXiaoSong dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
127838c8a9a5SSteve French if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
127938c8a9a5SSteve French cifs_dbg(VFS, "Server returned illegal ACL size\n");
128038c8a9a5SSteve French return -EINVAL;
128138c8a9a5SSteve French }
128238c8a9a5SSteve French }
128338c8a9a5SSteve French
128446c22d37SChenXiaoSong owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
128538c8a9a5SSteve French le32_to_cpu(pntsd->osidoffset));
128646c22d37SChenXiaoSong group_sid_ptr = (struct smb_sid *)((char *)pntsd +
128738c8a9a5SSteve French le32_to_cpu(pntsd->gsidoffset));
128838c8a9a5SSteve French
128938c8a9a5SSteve French if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
1290386660bdSChenXiaoSong ndacloffset = sizeof(struct smb_ntsd);
1291298e73acSChenXiaoSong ndacl_ptr = (struct smb_acl *)((char *)pnntsd + ndacloffset);
129238c8a9a5SSteve French ndacl_ptr->revision =
129338c8a9a5SSteve French dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION);
129438c8a9a5SSteve French
129538c8a9a5SSteve French ndacl_ptr->size = cpu_to_le16(0);
129638c8a9a5SSteve French ndacl_ptr->num_aces = cpu_to_le32(0);
129738c8a9a5SSteve French
129838c8a9a5SSteve French rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr,
12995f36890dSRalph Boehme pnmode, mode_from_sid, posix);
130038c8a9a5SSteve French
130138c8a9a5SSteve French sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
130238c8a9a5SSteve French /* copy the non-dacl portion of secdesc */
130338c8a9a5SSteve French *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset,
130438c8a9a5SSteve French NULL, NULL);
130538c8a9a5SSteve French
130638c8a9a5SSteve French *aclflag |= CIFS_ACL_DACL;
130738c8a9a5SSteve French } else {
1308386660bdSChenXiaoSong ndacloffset = sizeof(struct smb_ntsd);
1309298e73acSChenXiaoSong ndacl_ptr = (struct smb_acl *)((char *)pnntsd + ndacloffset);
131038c8a9a5SSteve French ndacl_ptr->revision =
131138c8a9a5SSteve French dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION);
131238c8a9a5SSteve French ndacl_ptr->num_aces = dacl_ptr ? dacl_ptr->num_aces : 0;
131338c8a9a5SSteve French
131438c8a9a5SSteve French if (uid_valid(uid)) { /* chown */
131538c8a9a5SSteve French uid_t id;
131646c22d37SChenXiaoSong nowner_sid_ptr = kzalloc(sizeof(struct smb_sid),
131738c8a9a5SSteve French GFP_KERNEL);
131838c8a9a5SSteve French if (!nowner_sid_ptr) {
131938c8a9a5SSteve French rc = -ENOMEM;
132038c8a9a5SSteve French goto chown_chgrp_exit;
132138c8a9a5SSteve French }
132238c8a9a5SSteve French id = from_kuid(&init_user_ns, uid);
132338c8a9a5SSteve French if (id_from_sid) {
132438c8a9a5SSteve French struct owner_sid *osid = (struct owner_sid *)nowner_sid_ptr;
132538c8a9a5SSteve French /* Populate the user ownership fields S-1-5-88-1 */
132638c8a9a5SSteve French osid->Revision = 1;
132738c8a9a5SSteve French osid->NumAuth = 3;
132838c8a9a5SSteve French osid->Authority[5] = 5;
132938c8a9a5SSteve French osid->SubAuthorities[0] = cpu_to_le32(88);
133038c8a9a5SSteve French osid->SubAuthorities[1] = cpu_to_le32(1);
133138c8a9a5SSteve French osid->SubAuthorities[2] = cpu_to_le32(id);
133238c8a9a5SSteve French
133338c8a9a5SSteve French } else { /* lookup sid with upcall */
133438c8a9a5SSteve French rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr);
133538c8a9a5SSteve French if (rc) {
133638c8a9a5SSteve French cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n",
133738c8a9a5SSteve French __func__, rc, id);
133838c8a9a5SSteve French goto chown_chgrp_exit;
133938c8a9a5SSteve French }
134038c8a9a5SSteve French }
134138c8a9a5SSteve French *aclflag |= CIFS_ACL_OWNER;
134238c8a9a5SSteve French }
134338c8a9a5SSteve French if (gid_valid(gid)) { /* chgrp */
134438c8a9a5SSteve French gid_t id;
134546c22d37SChenXiaoSong ngroup_sid_ptr = kzalloc(sizeof(struct smb_sid),
134638c8a9a5SSteve French GFP_KERNEL);
134738c8a9a5SSteve French if (!ngroup_sid_ptr) {
134838c8a9a5SSteve French rc = -ENOMEM;
134938c8a9a5SSteve French goto chown_chgrp_exit;
135038c8a9a5SSteve French }
135138c8a9a5SSteve French id = from_kgid(&init_user_ns, gid);
135238c8a9a5SSteve French if (id_from_sid) {
135338c8a9a5SSteve French struct owner_sid *gsid = (struct owner_sid *)ngroup_sid_ptr;
135438c8a9a5SSteve French /* Populate the group ownership fields S-1-5-88-2 */
135538c8a9a5SSteve French gsid->Revision = 1;
135638c8a9a5SSteve French gsid->NumAuth = 3;
135738c8a9a5SSteve French gsid->Authority[5] = 5;
135838c8a9a5SSteve French gsid->SubAuthorities[0] = cpu_to_le32(88);
135938c8a9a5SSteve French gsid->SubAuthorities[1] = cpu_to_le32(2);
136038c8a9a5SSteve French gsid->SubAuthorities[2] = cpu_to_le32(id);
136138c8a9a5SSteve French
136238c8a9a5SSteve French } else { /* lookup sid with upcall */
136338c8a9a5SSteve French rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr);
136438c8a9a5SSteve French if (rc) {
136538c8a9a5SSteve French cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n",
136638c8a9a5SSteve French __func__, rc, id);
136738c8a9a5SSteve French goto chown_chgrp_exit;
136838c8a9a5SSteve French }
136938c8a9a5SSteve French }
137038c8a9a5SSteve French *aclflag |= CIFS_ACL_GROUP;
137138c8a9a5SSteve French }
137238c8a9a5SSteve French
137338c8a9a5SSteve French if (dacloffset) {
137438c8a9a5SSteve French /* Replace ACEs for old owner with new one */
137538c8a9a5SSteve French size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr,
137638c8a9a5SSteve French owner_sid_ptr, group_sid_ptr,
137738c8a9a5SSteve French nowner_sid_ptr, ngroup_sid_ptr);
137838c8a9a5SSteve French ndacl_ptr->size = cpu_to_le16(size);
137938c8a9a5SSteve French }
138038c8a9a5SSteve French
138138c8a9a5SSteve French sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
138238c8a9a5SSteve French /* copy the non-dacl portion of secdesc */
138338c8a9a5SSteve French *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset,
138438c8a9a5SSteve French nowner_sid_ptr, ngroup_sid_ptr);
138538c8a9a5SSteve French
138638c8a9a5SSteve French chown_chgrp_exit:
138738c8a9a5SSteve French /* errors could jump here. So make sure we return soon after this */
138838c8a9a5SSteve French kfree(nowner_sid_ptr);
138938c8a9a5SSteve French kfree(ngroup_sid_ptr);
139038c8a9a5SSteve French }
139138c8a9a5SSteve French
139238c8a9a5SSteve French return rc;
139338c8a9a5SSteve French }
139438c8a9a5SSteve French
139538c8a9a5SSteve French #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
get_cifs_acl_by_fid(struct cifs_sb_info * cifs_sb,const struct cifs_fid * cifsfid,u32 * pacllen,u32 info)1396386660bdSChenXiaoSong struct smb_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
139738c8a9a5SSteve French const struct cifs_fid *cifsfid, u32 *pacllen,
1398*089d1c18SPali Rohár u32 info)
139938c8a9a5SSteve French {
1400386660bdSChenXiaoSong struct smb_ntsd *pntsd = NULL;
140138c8a9a5SSteve French unsigned int xid;
140238c8a9a5SSteve French int rc;
140338c8a9a5SSteve French struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
140438c8a9a5SSteve French
140538c8a9a5SSteve French if (IS_ERR(tlink))
140638c8a9a5SSteve French return ERR_CAST(tlink);
140738c8a9a5SSteve French
140838c8a9a5SSteve French xid = get_xid();
140938c8a9a5SSteve French rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), cifsfid->netfid, &pntsd,
1410*089d1c18SPali Rohár pacllen, info);
141138c8a9a5SSteve French free_xid(xid);
141238c8a9a5SSteve French
141338c8a9a5SSteve French cifs_put_tlink(tlink);
141438c8a9a5SSteve French
141538c8a9a5SSteve French cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
141638c8a9a5SSteve French if (rc)
141738c8a9a5SSteve French return ERR_PTR(rc);
141838c8a9a5SSteve French return pntsd;
141938c8a9a5SSteve French }
142038c8a9a5SSteve French
get_cifs_acl_by_path(struct cifs_sb_info * cifs_sb,const char * path,u32 * pacllen,u32 info)1421386660bdSChenXiaoSong static struct smb_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
1422*089d1c18SPali Rohár const char *path, u32 *pacllen, u32 info)
142338c8a9a5SSteve French {
1424386660bdSChenXiaoSong struct smb_ntsd *pntsd = NULL;
142538c8a9a5SSteve French int oplock = 0;
142638c8a9a5SSteve French unsigned int xid;
142738c8a9a5SSteve French int rc;
142838c8a9a5SSteve French struct cifs_tcon *tcon;
142938c8a9a5SSteve French struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
143038c8a9a5SSteve French struct cifs_fid fid;
143138c8a9a5SSteve French struct cifs_open_parms oparms;
143238c8a9a5SSteve French
143338c8a9a5SSteve French if (IS_ERR(tlink))
143438c8a9a5SSteve French return ERR_CAST(tlink);
143538c8a9a5SSteve French
143638c8a9a5SSteve French tcon = tlink_tcon(tlink);
143738c8a9a5SSteve French xid = get_xid();
143838c8a9a5SSteve French
143938c8a9a5SSteve French oparms = (struct cifs_open_parms) {
144038c8a9a5SSteve French .tcon = tcon,
144138c8a9a5SSteve French .cifs_sb = cifs_sb,
144238c8a9a5SSteve French .desired_access = READ_CONTROL,
144338c8a9a5SSteve French .create_options = cifs_create_options(cifs_sb, 0),
144438c8a9a5SSteve French .disposition = FILE_OPEN,
144538c8a9a5SSteve French .path = path,
144638c8a9a5SSteve French .fid = &fid,
144738c8a9a5SSteve French };
144838c8a9a5SSteve French
1449*089d1c18SPali Rohár if (info & SACL_SECINFO)
1450*089d1c18SPali Rohár oparms.desired_access |= SYSTEM_SECURITY;
1451*089d1c18SPali Rohár
145238c8a9a5SSteve French rc = CIFS_open(xid, &oparms, &oplock, NULL);
145338c8a9a5SSteve French if (!rc) {
1454*089d1c18SPali Rohár rc = CIFSSMBGetCIFSACL(xid, tcon, fid.netfid, &pntsd, pacllen, info);
145538c8a9a5SSteve French CIFSSMBClose(xid, tcon, fid.netfid);
145638c8a9a5SSteve French }
145738c8a9a5SSteve French
145838c8a9a5SSteve French cifs_put_tlink(tlink);
145938c8a9a5SSteve French free_xid(xid);
146038c8a9a5SSteve French
146138c8a9a5SSteve French cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen);
146238c8a9a5SSteve French if (rc)
146338c8a9a5SSteve French return ERR_PTR(rc);
146438c8a9a5SSteve French return pntsd;
146538c8a9a5SSteve French }
146638c8a9a5SSteve French
146738c8a9a5SSteve French /* Retrieve an ACL from the server */
get_cifs_acl(struct cifs_sb_info * cifs_sb,struct inode * inode,const char * path,u32 * pacllen,u32 info)1468386660bdSChenXiaoSong struct smb_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
146938c8a9a5SSteve French struct inode *inode, const char *path,
147038c8a9a5SSteve French u32 *pacllen, u32 info)
147138c8a9a5SSteve French {
1472386660bdSChenXiaoSong struct smb_ntsd *pntsd = NULL;
147338c8a9a5SSteve French struct cifsFileInfo *open_file = NULL;
147438c8a9a5SSteve French
147538c8a9a5SSteve French if (inode)
147638c8a9a5SSteve French open_file = find_readable_file(CIFS_I(inode), true);
147738c8a9a5SSteve French if (!open_file)
1478*089d1c18SPali Rohár return get_cifs_acl_by_path(cifs_sb, path, pacllen, info);
147938c8a9a5SSteve French
148038c8a9a5SSteve French pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info);
148138c8a9a5SSteve French cifsFileInfo_put(open_file);
148238c8a9a5SSteve French return pntsd;
148338c8a9a5SSteve French }
148438c8a9a5SSteve French
148538c8a9a5SSteve French /* Set an ACL on the server */
set_cifs_acl(struct smb_ntsd * pnntsd,__u32 acllen,struct inode * inode,const char * path,int aclflag)1486386660bdSChenXiaoSong int set_cifs_acl(struct smb_ntsd *pnntsd, __u32 acllen,
148738c8a9a5SSteve French struct inode *inode, const char *path, int aclflag)
148838c8a9a5SSteve French {
148938c8a9a5SSteve French int oplock = 0;
149038c8a9a5SSteve French unsigned int xid;
1491*089d1c18SPali Rohár int rc, access_flags = 0;
149238c8a9a5SSteve French struct cifs_tcon *tcon;
149338c8a9a5SSteve French struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
149438c8a9a5SSteve French struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
149538c8a9a5SSteve French struct cifs_fid fid;
149638c8a9a5SSteve French struct cifs_open_parms oparms;
149738c8a9a5SSteve French
149838c8a9a5SSteve French if (IS_ERR(tlink))
149938c8a9a5SSteve French return PTR_ERR(tlink);
150038c8a9a5SSteve French
150138c8a9a5SSteve French tcon = tlink_tcon(tlink);
150238c8a9a5SSteve French xid = get_xid();
150338c8a9a5SSteve French
1504*089d1c18SPali Rohár if (aclflag & CIFS_ACL_OWNER || aclflag & CIFS_ACL_GROUP)
1505*089d1c18SPali Rohár access_flags |= WRITE_OWNER;
1506*089d1c18SPali Rohár if (aclflag & CIFS_ACL_SACL)
1507*089d1c18SPali Rohár access_flags |= SYSTEM_SECURITY;
1508*089d1c18SPali Rohár if (aclflag & CIFS_ACL_DACL)
1509*089d1c18SPali Rohár access_flags |= WRITE_DAC;
151038c8a9a5SSteve French
151138c8a9a5SSteve French oparms = (struct cifs_open_parms) {
151238c8a9a5SSteve French .tcon = tcon,
151338c8a9a5SSteve French .cifs_sb = cifs_sb,
151438c8a9a5SSteve French .desired_access = access_flags,
151538c8a9a5SSteve French .create_options = cifs_create_options(cifs_sb, 0),
151638c8a9a5SSteve French .disposition = FILE_OPEN,
151738c8a9a5SSteve French .path = path,
151838c8a9a5SSteve French .fid = &fid,
151938c8a9a5SSteve French };
152038c8a9a5SSteve French
152138c8a9a5SSteve French rc = CIFS_open(xid, &oparms, &oplock, NULL);
152238c8a9a5SSteve French if (rc) {
152338c8a9a5SSteve French cifs_dbg(VFS, "Unable to open file to set ACL\n");
152438c8a9a5SSteve French goto out;
152538c8a9a5SSteve French }
152638c8a9a5SSteve French
152738c8a9a5SSteve French rc = CIFSSMBSetCIFSACL(xid, tcon, fid.netfid, pnntsd, acllen, aclflag);
152838c8a9a5SSteve French cifs_dbg(NOISY, "SetCIFSACL rc = %d\n", rc);
152938c8a9a5SSteve French
153038c8a9a5SSteve French CIFSSMBClose(xid, tcon, fid.netfid);
153138c8a9a5SSteve French out:
153238c8a9a5SSteve French free_xid(xid);
153338c8a9a5SSteve French cifs_put_tlink(tlink);
153438c8a9a5SSteve French return rc;
153538c8a9a5SSteve French }
153638c8a9a5SSteve French #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
153738c8a9a5SSteve French
153838c8a9a5SSteve French /* Translate the CIFS ACL (similar to NTFS ACL) for a file into mode bits */
153938c8a9a5SSteve French int
cifs_acl_to_fattr(struct cifs_sb_info * cifs_sb,struct cifs_fattr * fattr,struct inode * inode,bool mode_from_special_sid,const char * path,const struct cifs_fid * pfid)154038c8a9a5SSteve French cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
154138c8a9a5SSteve French struct inode *inode, bool mode_from_special_sid,
154238c8a9a5SSteve French const char *path, const struct cifs_fid *pfid)
154338c8a9a5SSteve French {
1544386660bdSChenXiaoSong struct smb_ntsd *pntsd = NULL;
154538c8a9a5SSteve French u32 acllen = 0;
154638c8a9a5SSteve French int rc = 0;
154738c8a9a5SSteve French struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
154838c8a9a5SSteve French struct smb_version_operations *ops;
154938c8a9a5SSteve French const u32 info = 0;
155038c8a9a5SSteve French
155138c8a9a5SSteve French cifs_dbg(NOISY, "converting ACL to mode for %s\n", path);
155238c8a9a5SSteve French
155338c8a9a5SSteve French if (IS_ERR(tlink))
155438c8a9a5SSteve French return PTR_ERR(tlink);
155538c8a9a5SSteve French
155638c8a9a5SSteve French ops = tlink_tcon(tlink)->ses->server->ops;
155738c8a9a5SSteve French
155838c8a9a5SSteve French if (pfid && (ops->get_acl_by_fid))
155938c8a9a5SSteve French pntsd = ops->get_acl_by_fid(cifs_sb, pfid, &acllen, info);
156038c8a9a5SSteve French else if (ops->get_acl)
156138c8a9a5SSteve French pntsd = ops->get_acl(cifs_sb, inode, path, &acllen, info);
156238c8a9a5SSteve French else {
156338c8a9a5SSteve French cifs_put_tlink(tlink);
156438c8a9a5SSteve French return -EOPNOTSUPP;
156538c8a9a5SSteve French }
156638c8a9a5SSteve French /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */
156738c8a9a5SSteve French if (IS_ERR(pntsd)) {
156838c8a9a5SSteve French rc = PTR_ERR(pntsd);
156938c8a9a5SSteve French cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc);
157038c8a9a5SSteve French } else if (mode_from_special_sid) {
157138c8a9a5SSteve French rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, true);
157238c8a9a5SSteve French kfree(pntsd);
157338c8a9a5SSteve French } else {
157438c8a9a5SSteve French /* get approximated mode from ACL */
157538c8a9a5SSteve French rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, false);
157638c8a9a5SSteve French kfree(pntsd);
157738c8a9a5SSteve French if (rc)
157838c8a9a5SSteve French cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc);
157938c8a9a5SSteve French }
158038c8a9a5SSteve French
158138c8a9a5SSteve French cifs_put_tlink(tlink);
158238c8a9a5SSteve French
158338c8a9a5SSteve French return rc;
158438c8a9a5SSteve French }
158538c8a9a5SSteve French
158638c8a9a5SSteve French /* Convert mode bits to an ACL so we can update the ACL on the server */
158738c8a9a5SSteve French int
id_mode_to_cifs_acl(struct inode * inode,const char * path,__u64 * pnmode,kuid_t uid,kgid_t gid)158838c8a9a5SSteve French id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
158938c8a9a5SSteve French kuid_t uid, kgid_t gid)
159038c8a9a5SSteve French {
159138c8a9a5SSteve French int rc = 0;
159238c8a9a5SSteve French int aclflag = CIFS_ACL_DACL; /* default flag to set */
159338c8a9a5SSteve French __u32 secdesclen = 0;
159438c8a9a5SSteve French __u32 nsecdesclen = 0;
159538c8a9a5SSteve French __u32 dacloffset = 0;
1596298e73acSChenXiaoSong struct smb_acl *dacl_ptr = NULL;
1597386660bdSChenXiaoSong struct smb_ntsd *pntsd = NULL; /* acl obtained from server */
1598386660bdSChenXiaoSong struct smb_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
159938c8a9a5SSteve French struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
160038c8a9a5SSteve French struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
160138c8a9a5SSteve French struct smb_version_operations *ops;
160238c8a9a5SSteve French bool mode_from_sid, id_from_sid;
16035f36890dSRalph Boehme bool posix = tlink_tcon(tlink)->posix_extensions;
160438c8a9a5SSteve French const u32 info = 0;
160538c8a9a5SSteve French
160638c8a9a5SSteve French if (IS_ERR(tlink))
160738c8a9a5SSteve French return PTR_ERR(tlink);
160838c8a9a5SSteve French
160938c8a9a5SSteve French ops = tlink_tcon(tlink)->ses->server->ops;
161038c8a9a5SSteve French
161138c8a9a5SSteve French cifs_dbg(NOISY, "set ACL from mode for %s\n", path);
161238c8a9a5SSteve French
161338c8a9a5SSteve French /* Get the security descriptor */
161438c8a9a5SSteve French
161538c8a9a5SSteve French if (ops->get_acl == NULL) {
161638c8a9a5SSteve French cifs_put_tlink(tlink);
161738c8a9a5SSteve French return -EOPNOTSUPP;
161838c8a9a5SSteve French }
161938c8a9a5SSteve French
162038c8a9a5SSteve French pntsd = ops->get_acl(cifs_sb, inode, path, &secdesclen, info);
162138c8a9a5SSteve French if (IS_ERR(pntsd)) {
162238c8a9a5SSteve French rc = PTR_ERR(pntsd);
162338c8a9a5SSteve French cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc);
162438c8a9a5SSteve French cifs_put_tlink(tlink);
162538c8a9a5SSteve French return rc;
162638c8a9a5SSteve French }
162738c8a9a5SSteve French
162838c8a9a5SSteve French if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)
162938c8a9a5SSteve French mode_from_sid = true;
163038c8a9a5SSteve French else
163138c8a9a5SSteve French mode_from_sid = false;
163238c8a9a5SSteve French
163338c8a9a5SSteve French if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL)
163438c8a9a5SSteve French id_from_sid = true;
163538c8a9a5SSteve French else
163638c8a9a5SSteve French id_from_sid = false;
163738c8a9a5SSteve French
163838c8a9a5SSteve French /* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */
163938c8a9a5SSteve French if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
16405f36890dSRalph Boehme if (posix)
16415f36890dSRalph Boehme nsecdesclen = 1 * sizeof(struct smb_ace);
16425f36890dSRalph Boehme else if (mode_from_sid)
16435f36890dSRalph Boehme nsecdesclen = secdesclen + (2 * sizeof(struct smb_ace));
164438c8a9a5SSteve French else /* cifsacl */
16455f36890dSRalph Boehme nsecdesclen = secdesclen + (5 * sizeof(struct smb_ace));
164638c8a9a5SSteve French } else { /* chown */
164738c8a9a5SSteve French /* When ownership changes, changes new owner sid length could be different */
164846c22d37SChenXiaoSong nsecdesclen = sizeof(struct smb_ntsd) + (sizeof(struct smb_sid) * 2);
164938c8a9a5SSteve French dacloffset = le32_to_cpu(pntsd->dacloffset);
165038c8a9a5SSteve French if (dacloffset) {
1651298e73acSChenXiaoSong dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
165238c8a9a5SSteve French if (mode_from_sid)
165338c8a9a5SSteve French nsecdesclen +=
1654d6442904SChenXiaoSong le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
165538c8a9a5SSteve French else /* cifsacl */
165638c8a9a5SSteve French nsecdesclen += le16_to_cpu(dacl_ptr->size);
165738c8a9a5SSteve French }
165838c8a9a5SSteve French }
165938c8a9a5SSteve French
166038c8a9a5SSteve French /*
166138c8a9a5SSteve French * Add three ACEs for owner, group, everyone getting rid of other ACEs
166238c8a9a5SSteve French * as chmod disables ACEs and set the security descriptor. Allocate
166338c8a9a5SSteve French * memory for the smb header, set security descriptor request security
166438c8a9a5SSteve French * descriptor parameters, and security descriptor itself
166538c8a9a5SSteve French */
166638c8a9a5SSteve French nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN);
166738c8a9a5SSteve French pnntsd = kmalloc(nsecdesclen, GFP_KERNEL);
166838c8a9a5SSteve French if (!pnntsd) {
166938c8a9a5SSteve French kfree(pntsd);
167038c8a9a5SSteve French cifs_put_tlink(tlink);
167138c8a9a5SSteve French return -ENOMEM;
167238c8a9a5SSteve French }
167338c8a9a5SSteve French
167438c8a9a5SSteve French rc = build_sec_desc(pntsd, pnntsd, secdesclen, &nsecdesclen, pnmode, uid, gid,
16755f36890dSRalph Boehme mode_from_sid, id_from_sid, posix, &aclflag);
167638c8a9a5SSteve French
167738c8a9a5SSteve French cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc);
167838c8a9a5SSteve French
167938c8a9a5SSteve French if (ops->set_acl == NULL)
168038c8a9a5SSteve French rc = -EOPNOTSUPP;
168138c8a9a5SSteve French
168238c8a9a5SSteve French if (!rc) {
168338c8a9a5SSteve French /* Set the security descriptor */
168438c8a9a5SSteve French rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag);
168538c8a9a5SSteve French cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
168638c8a9a5SSteve French }
168738c8a9a5SSteve French cifs_put_tlink(tlink);
168838c8a9a5SSteve French
168938c8a9a5SSteve French kfree(pnntsd);
169038c8a9a5SSteve French kfree(pntsd);
169138c8a9a5SSteve French return rc;
169238c8a9a5SSteve French }
169338c8a9a5SSteve French
cifs_get_acl(struct mnt_idmap * idmap,struct dentry * dentry,int type)169438c8a9a5SSteve French struct posix_acl *cifs_get_acl(struct mnt_idmap *idmap,
169538c8a9a5SSteve French struct dentry *dentry, int type)
169638c8a9a5SSteve French {
169738c8a9a5SSteve French #if defined(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) && defined(CONFIG_CIFS_POSIX)
169838c8a9a5SSteve French struct posix_acl *acl = NULL;
169938c8a9a5SSteve French ssize_t rc = -EOPNOTSUPP;
170038c8a9a5SSteve French unsigned int xid;
170138c8a9a5SSteve French struct super_block *sb = dentry->d_sb;
170238c8a9a5SSteve French struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
170338c8a9a5SSteve French struct tcon_link *tlink;
170438c8a9a5SSteve French struct cifs_tcon *pTcon;
170538c8a9a5SSteve French const char *full_path;
170638c8a9a5SSteve French void *page;
170738c8a9a5SSteve French
170838c8a9a5SSteve French tlink = cifs_sb_tlink(cifs_sb);
170938c8a9a5SSteve French if (IS_ERR(tlink))
171038c8a9a5SSteve French return ERR_CAST(tlink);
171138c8a9a5SSteve French pTcon = tlink_tcon(tlink);
171238c8a9a5SSteve French
171338c8a9a5SSteve French xid = get_xid();
171438c8a9a5SSteve French page = alloc_dentry_path();
171538c8a9a5SSteve French
171638c8a9a5SSteve French full_path = build_path_from_dentry(dentry, page);
171738c8a9a5SSteve French if (IS_ERR(full_path)) {
171838c8a9a5SSteve French acl = ERR_CAST(full_path);
171938c8a9a5SSteve French goto out;
172038c8a9a5SSteve French }
172138c8a9a5SSteve French
172238c8a9a5SSteve French /* return alt name if available as pseudo attr */
172338c8a9a5SSteve French switch (type) {
172438c8a9a5SSteve French case ACL_TYPE_ACCESS:
172538c8a9a5SSteve French if (sb->s_flags & SB_POSIXACL)
172638c8a9a5SSteve French rc = cifs_do_get_acl(xid, pTcon, full_path, &acl,
172738c8a9a5SSteve French ACL_TYPE_ACCESS,
172838c8a9a5SSteve French cifs_sb->local_nls,
172938c8a9a5SSteve French cifs_remap(cifs_sb));
173038c8a9a5SSteve French break;
173138c8a9a5SSteve French
173238c8a9a5SSteve French case ACL_TYPE_DEFAULT:
173338c8a9a5SSteve French if (sb->s_flags & SB_POSIXACL)
173438c8a9a5SSteve French rc = cifs_do_get_acl(xid, pTcon, full_path, &acl,
173538c8a9a5SSteve French ACL_TYPE_DEFAULT,
173638c8a9a5SSteve French cifs_sb->local_nls,
173738c8a9a5SSteve French cifs_remap(cifs_sb));
173838c8a9a5SSteve French break;
173938c8a9a5SSteve French }
174038c8a9a5SSteve French
174138c8a9a5SSteve French if (rc < 0) {
174238c8a9a5SSteve French if (rc == -EINVAL)
174338c8a9a5SSteve French acl = ERR_PTR(-EOPNOTSUPP);
174438c8a9a5SSteve French else
174538c8a9a5SSteve French acl = ERR_PTR(rc);
174638c8a9a5SSteve French }
174738c8a9a5SSteve French
174838c8a9a5SSteve French out:
174938c8a9a5SSteve French free_dentry_path(page);
175038c8a9a5SSteve French free_xid(xid);
175138c8a9a5SSteve French cifs_put_tlink(tlink);
175238c8a9a5SSteve French return acl;
175338c8a9a5SSteve French #else
175438c8a9a5SSteve French return ERR_PTR(-EOPNOTSUPP);
175538c8a9a5SSteve French #endif
175638c8a9a5SSteve French }
175738c8a9a5SSteve French
cifs_set_acl(struct mnt_idmap * idmap,struct dentry * dentry,struct posix_acl * acl,int type)175838c8a9a5SSteve French int cifs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
175938c8a9a5SSteve French struct posix_acl *acl, int type)
176038c8a9a5SSteve French {
176138c8a9a5SSteve French #if defined(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) && defined(CONFIG_CIFS_POSIX)
176238c8a9a5SSteve French int rc = -EOPNOTSUPP;
176338c8a9a5SSteve French unsigned int xid;
176438c8a9a5SSteve French struct super_block *sb = dentry->d_sb;
176538c8a9a5SSteve French struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
176638c8a9a5SSteve French struct tcon_link *tlink;
176738c8a9a5SSteve French struct cifs_tcon *pTcon;
176838c8a9a5SSteve French const char *full_path;
176938c8a9a5SSteve French void *page;
177038c8a9a5SSteve French
177138c8a9a5SSteve French tlink = cifs_sb_tlink(cifs_sb);
177238c8a9a5SSteve French if (IS_ERR(tlink))
177338c8a9a5SSteve French return PTR_ERR(tlink);
177438c8a9a5SSteve French pTcon = tlink_tcon(tlink);
177538c8a9a5SSteve French
177638c8a9a5SSteve French xid = get_xid();
177738c8a9a5SSteve French page = alloc_dentry_path();
177838c8a9a5SSteve French
177938c8a9a5SSteve French full_path = build_path_from_dentry(dentry, page);
178038c8a9a5SSteve French if (IS_ERR(full_path)) {
178138c8a9a5SSteve French rc = PTR_ERR(full_path);
178238c8a9a5SSteve French goto out;
178338c8a9a5SSteve French }
178438c8a9a5SSteve French
178538c8a9a5SSteve French if (!acl)
178638c8a9a5SSteve French goto out;
178738c8a9a5SSteve French
178838c8a9a5SSteve French /* return dos attributes as pseudo xattr */
178938c8a9a5SSteve French /* return alt name if available as pseudo attr */
179038c8a9a5SSteve French
179138c8a9a5SSteve French /* if proc/fs/cifs/streamstoxattr is set then
179238c8a9a5SSteve French search server for EAs or streams to
179338c8a9a5SSteve French returns as xattrs */
179438c8a9a5SSteve French if (posix_acl_xattr_size(acl->a_count) > CIFSMaxBufSize) {
179538c8a9a5SSteve French cifs_dbg(FYI, "size of EA value too large\n");
179638c8a9a5SSteve French rc = -EOPNOTSUPP;
179738c8a9a5SSteve French goto out;
179838c8a9a5SSteve French }
179938c8a9a5SSteve French
180038c8a9a5SSteve French switch (type) {
180138c8a9a5SSteve French case ACL_TYPE_ACCESS:
180238c8a9a5SSteve French if (sb->s_flags & SB_POSIXACL)
180338c8a9a5SSteve French rc = cifs_do_set_acl(xid, pTcon, full_path, acl,
180438c8a9a5SSteve French ACL_TYPE_ACCESS,
180538c8a9a5SSteve French cifs_sb->local_nls,
180638c8a9a5SSteve French cifs_remap(cifs_sb));
180738c8a9a5SSteve French break;
180838c8a9a5SSteve French
180938c8a9a5SSteve French case ACL_TYPE_DEFAULT:
181038c8a9a5SSteve French if (sb->s_flags & SB_POSIXACL)
181138c8a9a5SSteve French rc = cifs_do_set_acl(xid, pTcon, full_path, acl,
181238c8a9a5SSteve French ACL_TYPE_DEFAULT,
181338c8a9a5SSteve French cifs_sb->local_nls,
181438c8a9a5SSteve French cifs_remap(cifs_sb));
181538c8a9a5SSteve French break;
181638c8a9a5SSteve French }
181738c8a9a5SSteve French
181838c8a9a5SSteve French out:
181938c8a9a5SSteve French free_dentry_path(page);
182038c8a9a5SSteve French free_xid(xid);
182138c8a9a5SSteve French cifs_put_tlink(tlink);
182238c8a9a5SSteve French return rc;
182338c8a9a5SSteve French #else
182438c8a9a5SSteve French return -EOPNOTSUPP;
182538c8a9a5SSteve French #endif
182638c8a9a5SSteve French }
1827