Commit | Line | Data |
---|---|---|
929be906 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
bcb02034 | 2 | /* |
bcb02034 | 3 | * |
8b1327f6 | 4 | * Copyright (C) International Business Machines Corp., 2007,2008 |
bcb02034 SF |
5 | * Author(s): Steve French (sfrench@us.ibm.com) |
6 | * | |
7 | * Contains the routines for mapping CIFS/NTFS ACLs | |
8 | * | |
bcb02034 SF |
9 | */ |
10 | ||
65874007 | 11 | #include <linux/fs.h> |
5a0e3ad6 | 12 | #include <linux/slab.h> |
4d79dba0 SP |
13 | #include <linux/string.h> |
14 | #include <linux/keyctl.h> | |
15 | #include <linux/key-type.h> | |
16 | #include <keys/user-type.h> | |
65874007 SF |
17 | #include "cifspdu.h" |
18 | #include "cifsglob.h" | |
d0d66c44 | 19 | #include "cifsacl.h" |
65874007 SF |
20 | #include "cifsproto.h" |
21 | #include "cifs_debug.h" | |
8401e936 | 22 | #include "fs_context.h" |
65874007 | 23 | |
2fbc2f17 | 24 | /* security id for everyone/world system group */ |
e01b6400 SP |
25 | static const struct cifs_sid sid_everyone = { |
26 | 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; | |
2fbc2f17 SP |
27 | /* security id for Authenticated Users system group */ |
28 | static const struct cifs_sid sid_authusers = { | |
bc09d141 | 29 | 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; |
d0d66c44 | 30 | |
3514de3f SF |
31 | /* S-1-22-1 Unmapped Unix users */ |
32 | static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, | |
33 | {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | |
34 | ||
35 | /* S-1-22-2 Unmapped Unix groups */ | |
36 | static const struct cifs_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, | |
37 | {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | |
38 | ||
39 | /* | |
cba22b1c | 40 | * See https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx |
3514de3f SF |
41 | */ |
42 | ||
43 | /* S-1-5-88 MS NFS and Apple style UID/GID/mode */ | |
44 | ||
45 | /* S-1-5-88-1 Unix uid */ | |
46 | static const struct cifs_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, | |
47 | {cpu_to_le32(88), | |
48 | cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | |
49 | ||
50 | /* S-1-5-88-2 Unix gid */ | |
51 | static const struct cifs_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, | |
52 | {cpu_to_le32(88), | |
53 | cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | |
54 | ||
55 | /* S-1-5-88-3 Unix mode */ | |
56 | static const struct cifs_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, | |
57 | {cpu_to_le32(88), | |
58 | cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | |
59 | ||
b1a6dc21 | 60 | static const struct cred *root_cred; |
9409ae58 | 61 | |
4d79dba0 | 62 | static int |
cf7f601c | 63 | cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep) |
4d79dba0 SP |
64 | { |
65 | char *payload; | |
66 | ||
41a9f1f6 JL |
67 | /* |
68 | * If the payload is less than or equal to the size of a pointer, then | |
69 | * an allocation here is wasteful. Just copy the data directly to the | |
70 | * payload.value union member instead. | |
71 | * | |
72 | * With this however, you must check the datalen before trying to | |
73 | * dereference payload.data! | |
74 | */ | |
1f630680 | 75 | if (prep->datalen <= sizeof(key->payload)) { |
146aa8b1 DH |
76 | key->payload.data[0] = NULL; |
77 | memcpy(&key->payload, prep->data, prep->datalen); | |
78 | } else { | |
79 | payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); | |
80 | if (!payload) | |
81 | return -ENOMEM; | |
82 | key->payload.data[0] = payload; | |
41a9f1f6 | 83 | } |
4d79dba0 | 84 | |
cf7f601c | 85 | key->datalen = prep->datalen; |
4d79dba0 SP |
86 | return 0; |
87 | } | |
88 | ||
89 | static inline void | |
90 | cifs_idmap_key_destroy(struct key *key) | |
91 | { | |
1f630680 | 92 | if (key->datalen > sizeof(key->payload)) |
146aa8b1 | 93 | kfree(key->payload.data[0]); |
4d79dba0 SP |
94 | } |
95 | ||
b1a6dc21 | 96 | static struct key_type cifs_idmap_key_type = { |
c4aca0c0 | 97 | .name = "cifs.idmap", |
4d79dba0 SP |
98 | .instantiate = cifs_idmap_key_instantiate, |
99 | .destroy = cifs_idmap_key_destroy, | |
100 | .describe = user_describe, | |
4d79dba0 SP |
101 | }; |
102 | ||
faa65f07 JL |
103 | static char * |
104 | sid_to_key_str(struct cifs_sid *sidptr, unsigned int type) | |
9409ae58 | 105 | { |
faa65f07 | 106 | int i, len; |
ee13b2ba | 107 | unsigned int saval; |
faa65f07 | 108 | char *sidstr, *strptr; |
193cdd8a | 109 | unsigned long long id_auth_val; |
9409ae58 | 110 | |
faa65f07 JL |
111 | /* 3 bytes for prefix */ |
112 | sidstr = kmalloc(3 + SID_STRING_BASE_SIZE + | |
113 | (SID_STRING_SUBAUTH_SIZE * sidptr->num_subauth), | |
114 | GFP_KERNEL); | |
115 | if (!sidstr) | |
116 | return sidstr; | |
9409ae58 | 117 | |
faa65f07 JL |
118 | strptr = sidstr; |
119 | len = sprintf(strptr, "%cs:S-%hhu", type == SIDOWNER ? 'o' : 'g', | |
120 | sidptr->revision); | |
121 | strptr += len; | |
9409ae58 | 122 | |
193cdd8a JL |
123 | /* The authority field is a single 48-bit number */ |
124 | id_auth_val = (unsigned long long)sidptr->authority[5]; | |
125 | id_auth_val |= (unsigned long long)sidptr->authority[4] << 8; | |
126 | id_auth_val |= (unsigned long long)sidptr->authority[3] << 16; | |
127 | id_auth_val |= (unsigned long long)sidptr->authority[2] << 24; | |
128 | id_auth_val |= (unsigned long long)sidptr->authority[1] << 32; | |
129 | id_auth_val |= (unsigned long long)sidptr->authority[0] << 48; | |
130 | ||
131 | /* | |
132 | * MS-DTYP states that if the authority is >= 2^32, then it should be | |
133 | * expressed as a hex value. | |
134 | */ | |
135 | if (id_auth_val <= UINT_MAX) | |
136 | len = sprintf(strptr, "-%llu", id_auth_val); | |
137 | else | |
138 | len = sprintf(strptr, "-0x%llx", id_auth_val); | |
139 | ||
140 | strptr += len; | |
9409ae58 SP |
141 | |
142 | for (i = 0; i < sidptr->num_subauth; ++i) { | |
143 | saval = le32_to_cpu(sidptr->sub_auth[i]); | |
faa65f07 JL |
144 | len = sprintf(strptr, "-%u", saval); |
145 | strptr += len; | |
9409ae58 | 146 | } |
faa65f07 JL |
147 | |
148 | return sidstr; | |
9409ae58 SP |
149 | } |
150 | ||
436bb435 JL |
151 | /* |
152 | * if the two SIDs (roughly equivalent to a UUID for a user or group) are | |
153 | * the same returns zero, if they do not match returns non-zero. | |
154 | */ | |
155 | static int | |
156 | compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) | |
157 | { | |
158 | int i; | |
159 | int num_subauth, num_sat, num_saw; | |
160 | ||
161 | if ((!ctsid) || (!cwsid)) | |
162 | return 1; | |
163 | ||
164 | /* compare the revision */ | |
165 | if (ctsid->revision != cwsid->revision) { | |
166 | if (ctsid->revision > cwsid->revision) | |
167 | return 1; | |
168 | else | |
169 | return -1; | |
170 | } | |
171 | ||
172 | /* compare all of the six auth values */ | |
173 | for (i = 0; i < NUM_AUTHS; ++i) { | |
174 | if (ctsid->authority[i] != cwsid->authority[i]) { | |
175 | if (ctsid->authority[i] > cwsid->authority[i]) | |
176 | return 1; | |
177 | else | |
178 | return -1; | |
179 | } | |
180 | } | |
181 | ||
182 | /* compare all of the subauth values if any */ | |
183 | num_sat = ctsid->num_subauth; | |
184 | num_saw = cwsid->num_subauth; | |
185 | num_subauth = num_sat < num_saw ? num_sat : num_saw; | |
186 | if (num_subauth) { | |
187 | for (i = 0; i < num_subauth; ++i) { | |
188 | if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { | |
189 | if (le32_to_cpu(ctsid->sub_auth[i]) > | |
190 | le32_to_cpu(cwsid->sub_auth[i])) | |
191 | return 1; | |
192 | else | |
193 | return -1; | |
194 | } | |
195 | } | |
196 | } | |
197 | ||
198 | return 0; /* sids compare/match */ | |
199 | } | |
200 | ||
3514de3f SF |
201 | static bool |
202 | is_well_known_sid(const struct cifs_sid *psid, uint32_t *puid, bool is_group) | |
203 | { | |
204 | int i; | |
205 | int num_subauth; | |
206 | const struct cifs_sid *pwell_known_sid; | |
207 | ||
208 | if (!psid || (puid == NULL)) | |
209 | return false; | |
210 | ||
211 | num_subauth = psid->num_subauth; | |
212 | ||
213 | /* check if Mac (or Windows NFS) vs. Samba format for Unix owner SID */ | |
214 | if (num_subauth == 2) { | |
215 | if (is_group) | |
216 | pwell_known_sid = &sid_unix_groups; | |
217 | else | |
218 | pwell_known_sid = &sid_unix_users; | |
219 | } else if (num_subauth == 3) { | |
220 | if (is_group) | |
221 | pwell_known_sid = &sid_unix_NFS_groups; | |
222 | else | |
223 | pwell_known_sid = &sid_unix_NFS_users; | |
224 | } else | |
225 | return false; | |
226 | ||
227 | /* compare the revision */ | |
228 | if (psid->revision != pwell_known_sid->revision) | |
229 | return false; | |
230 | ||
231 | /* compare all of the six auth values */ | |
232 | for (i = 0; i < NUM_AUTHS; ++i) { | |
233 | if (psid->authority[i] != pwell_known_sid->authority[i]) { | |
234 | cifs_dbg(FYI, "auth %d did not match\n", i); | |
235 | return false; | |
236 | } | |
237 | } | |
238 | ||
239 | if (num_subauth == 2) { | |
240 | if (psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) | |
241 | return false; | |
242 | ||
243 | *puid = le32_to_cpu(psid->sub_auth[1]); | |
244 | } else /* 3 subauths, ie Windows/Mac style */ { | |
245 | *puid = le32_to_cpu(psid->sub_auth[0]); | |
246 | if ((psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) || | |
247 | (psid->sub_auth[1] != pwell_known_sid->sub_auth[1])) | |
248 | return false; | |
249 | ||
250 | *puid = le32_to_cpu(psid->sub_auth[2]); | |
251 | } | |
252 | ||
253 | cifs_dbg(FYI, "Unix UID %d returned from SID\n", *puid); | |
254 | return true; /* well known sid found, uid returned */ | |
255 | } | |
256 | ||
f5065508 | 257 | static __u16 |
36960e44 JL |
258 | cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src) |
259 | { | |
36f87ee7 | 260 | int i; |
f5065508 | 261 | __u16 size = 1 + 1 + 6; |
36f87ee7 JL |
262 | |
263 | dst->revision = src->revision; | |
30c9d6cc | 264 | dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); |
36f87ee7 JL |
265 | for (i = 0; i < NUM_AUTHS; ++i) |
266 | dst->authority[i] = src->authority[i]; | |
267 | for (i = 0; i < dst->num_subauth; ++i) | |
268 | dst->sub_auth[i] = src->sub_auth[i]; | |
f5065508 SP |
269 | size += (dst->num_subauth * 4); |
270 | ||
271 | return size; | |
36960e44 JL |
272 | } |
273 | ||
9409ae58 | 274 | static int |
faa65f07 | 275 | id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid) |
9409ae58 | 276 | { |
faa65f07 | 277 | int rc; |
21fed0d5 | 278 | struct key *sidkey; |
2ae03025 JL |
279 | struct cifs_sid *ksid; |
280 | unsigned int ksid_size; | |
faa65f07 | 281 | char desc[3 + 10 + 1]; /* 3 byte prefix + 10 bytes for value + NULL */ |
21fed0d5 | 282 | const struct cred *saved_cred; |
21fed0d5 | 283 | |
faa65f07 JL |
284 | rc = snprintf(desc, sizeof(desc), "%ci:%u", |
285 | sidtype == SIDOWNER ? 'o' : 'g', cid); | |
286 | if (rc >= sizeof(desc)) | |
287 | return -EINVAL; | |
21fed0d5 | 288 | |
faa65f07 JL |
289 | rc = 0; |
290 | saved_cred = override_creds(root_cred); | |
028db3e2 | 291 | sidkey = request_key(&cifs_idmap_key_type, desc, ""); |
faa65f07 | 292 | if (IS_ERR(sidkey)) { |
21fed0d5 | 293 | rc = -EINVAL; |
f96637be JP |
294 | cifs_dbg(FYI, "%s: Can't map %cid %u to a SID\n", |
295 | __func__, sidtype == SIDOWNER ? 'u' : 'g', cid); | |
faa65f07 JL |
296 | goto out_revert_creds; |
297 | } else if (sidkey->datalen < CIFS_SID_BASE_SIZE) { | |
298 | rc = -EIO; | |
f96637be JP |
299 | cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", |
300 | __func__, sidkey->datalen); | |
2ae03025 | 301 | goto invalidate_key; |
21fed0d5 | 302 | } |
2ae03025 | 303 | |
1f630680 JL |
304 | /* |
305 | * A sid is usually too large to be embedded in payload.value, but if | |
306 | * there are no subauthorities and the host has 8-byte pointers, then | |
307 | * it could be. | |
308 | */ | |
309 | ksid = sidkey->datalen <= sizeof(sidkey->payload) ? | |
146aa8b1 DH |
310 | (struct cifs_sid *)&sidkey->payload : |
311 | (struct cifs_sid *)sidkey->payload.data[0]; | |
1f630680 | 312 | |
2ae03025 JL |
313 | ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32)); |
314 | if (ksid_size > sidkey->datalen) { | |
315 | rc = -EIO; | |
f96637be JP |
316 | cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu, ksid_size=%u)\n", |
317 | __func__, sidkey->datalen, ksid_size); | |
2ae03025 JL |
318 | goto invalidate_key; |
319 | } | |
1f630680 | 320 | |
2ae03025 | 321 | cifs_copy_sid(ssid, ksid); |
faa65f07 JL |
322 | out_key_put: |
323 | key_put(sidkey); | |
324 | out_revert_creds: | |
325 | revert_creds(saved_cred); | |
21fed0d5 | 326 | return rc; |
2ae03025 JL |
327 | |
328 | invalidate_key: | |
329 | key_invalidate(sidkey); | |
330 | goto out_key_put; | |
21fed0d5 SP |
331 | } |
332 | ||
9934430e | 333 | int |
9409ae58 SP |
334 | sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, |
335 | struct cifs_fattr *fattr, uint sidtype) | |
336 | { | |
f2d67931 | 337 | int rc = 0; |
faa65f07 JL |
338 | struct key *sidkey; |
339 | char *sidstr; | |
9409ae58 | 340 | const struct cred *saved_cred; |
8401e936 RS |
341 | kuid_t fuid = cifs_sb->ctx->linux_uid; |
342 | kgid_t fgid = cifs_sb->ctx->linux_gid; | |
9409ae58 SP |
343 | |
344 | /* | |
faa65f07 JL |
345 | * If we have too many subauthorities, then something is really wrong. |
346 | * Just return an error. | |
9409ae58 | 347 | */ |
faa65f07 | 348 | if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { |
f96637be JP |
349 | cifs_dbg(FYI, "%s: %u subauthorities is too many!\n", |
350 | __func__, psid->num_subauth); | |
faa65f07 | 351 | return -EIO; |
9409ae58 SP |
352 | } |
353 | ||
9934430e SF |
354 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) || |
355 | (cifs_sb_master_tcon(cifs_sb)->posix_extensions)) { | |
3514de3f SF |
356 | uint32_t unix_id; |
357 | bool is_group; | |
358 | ||
359 | if (sidtype != SIDOWNER) | |
360 | is_group = true; | |
361 | else | |
362 | is_group = false; | |
363 | ||
364 | if (is_well_known_sid(psid, &unix_id, is_group) == false) | |
365 | goto try_upcall_to_get_id; | |
366 | ||
367 | if (is_group) { | |
368 | kgid_t gid; | |
369 | gid_t id; | |
370 | ||
371 | id = (gid_t)unix_id; | |
372 | gid = make_kgid(&init_user_ns, id); | |
373 | if (gid_valid(gid)) { | |
374 | fgid = gid; | |
375 | goto got_valid_id; | |
376 | } | |
377 | } else { | |
378 | kuid_t uid; | |
379 | uid_t id; | |
380 | ||
381 | id = (uid_t)unix_id; | |
382 | uid = make_kuid(&init_user_ns, id); | |
383 | if (uid_valid(uid)) { | |
384 | fuid = uid; | |
385 | goto got_valid_id; | |
386 | } | |
387 | } | |
388 | /* If unable to find uid/gid easily from SID try via upcall */ | |
389 | } | |
390 | ||
391 | try_upcall_to_get_id: | |
faa65f07 JL |
392 | sidstr = sid_to_key_str(psid, sidtype); |
393 | if (!sidstr) | |
394 | return -ENOMEM; | |
395 | ||
396 | saved_cred = override_creds(root_cred); | |
028db3e2 | 397 | sidkey = request_key(&cifs_idmap_key_type, sidstr, ""); |
faa65f07 | 398 | if (IS_ERR(sidkey)) { |
f96637be JP |
399 | cifs_dbg(FYI, "%s: Can't map SID %s to a %cid\n", |
400 | __func__, sidstr, sidtype == SIDOWNER ? 'u' : 'g'); | |
faa65f07 JL |
401 | goto out_revert_creds; |
402 | } | |
403 | ||
404 | /* | |
405 | * FIXME: Here we assume that uid_t and gid_t are same size. It's | |
406 | * probably a safe assumption but might be better to check based on | |
407 | * sidtype. | |
408 | */ | |
355958f2 | 409 | BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t)); |
41a9f1f6 | 410 | if (sidkey->datalen != sizeof(uid_t)) { |
f96637be JP |
411 | cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", |
412 | __func__, sidkey->datalen); | |
2ae03025 | 413 | key_invalidate(sidkey); |
faa65f07 | 414 | goto out_key_put; |
9409ae58 SP |
415 | } |
416 | ||
8abf2775 EB |
417 | if (sidtype == SIDOWNER) { |
418 | kuid_t uid; | |
419 | uid_t id; | |
146aa8b1 | 420 | memcpy(&id, &sidkey->payload.data[0], sizeof(uid_t)); |
8abf2775 EB |
421 | uid = make_kuid(&init_user_ns, id); |
422 | if (uid_valid(uid)) | |
423 | fuid = uid; | |
424 | } else { | |
425 | kgid_t gid; | |
426 | gid_t id; | |
146aa8b1 | 427 | memcpy(&id, &sidkey->payload.data[0], sizeof(gid_t)); |
8abf2775 EB |
428 | gid = make_kgid(&init_user_ns, id); |
429 | if (gid_valid(gid)) | |
430 | fgid = gid; | |
431 | } | |
faa65f07 JL |
432 | |
433 | out_key_put: | |
434 | key_put(sidkey); | |
435 | out_revert_creds: | |
436 | revert_creds(saved_cred); | |
437 | kfree(sidstr); | |
9409ae58 | 438 | |
faa65f07 JL |
439 | /* |
440 | * Note that we return 0 here unconditionally. If the mapping | |
8401e936 | 441 | * fails then we just fall back to using the ctx->linux_uid/linux_gid. |
faa65f07 | 442 | */ |
3514de3f | 443 | got_valid_id: |
f2d67931 | 444 | rc = 0; |
faa65f07 JL |
445 | if (sidtype == SIDOWNER) |
446 | fattr->cf_uid = fuid; | |
447 | else | |
448 | fattr->cf_gid = fgid; | |
f2d67931 | 449 | return rc; |
9409ae58 SP |
450 | } |
451 | ||
4d79dba0 SP |
452 | int |
453 | init_cifs_idmap(void) | |
454 | { | |
455 | struct cred *cred; | |
456 | struct key *keyring; | |
457 | int ret; | |
458 | ||
f96637be JP |
459 | cifs_dbg(FYI, "Registering the %s key type\n", |
460 | cifs_idmap_key_type.name); | |
4d79dba0 SP |
461 | |
462 | /* create an override credential set with a special thread keyring in | |
463 | * which requests are cached | |
464 | * | |
465 | * this is used to prevent malicious redirections from being installed | |
466 | * with add_key(). | |
467 | */ | |
468 | cred = prepare_kernel_cred(NULL); | |
469 | if (!cred) | |
470 | return -ENOMEM; | |
471 | ||
8e3028b9 EB |
472 | keyring = keyring_alloc(".cifs_idmap", |
473 | GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, | |
028db3e2 LT |
474 | (KEY_POS_ALL & ~KEY_POS_SETATTR) | |
475 | KEY_USR_VIEW | KEY_USR_READ, | |
5ac7eace | 476 | KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); |
4d79dba0 SP |
477 | if (IS_ERR(keyring)) { |
478 | ret = PTR_ERR(keyring); | |
479 | goto failed_put_cred; | |
480 | } | |
481 | ||
4d79dba0 SP |
482 | ret = register_key_type(&cifs_idmap_key_type); |
483 | if (ret < 0) | |
484 | goto failed_put_key; | |
485 | ||
486 | /* instruct request_key() to use this special keyring as a cache for | |
487 | * the results it looks up */ | |
700920eb | 488 | set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); |
4d79dba0 SP |
489 | cred->thread_keyring = keyring; |
490 | cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; | |
491 | root_cred = cred; | |
492 | ||
f96637be | 493 | cifs_dbg(FYI, "cifs idmap keyring: %d\n", key_serial(keyring)); |
4d79dba0 SP |
494 | return 0; |
495 | ||
496 | failed_put_key: | |
497 | key_put(keyring); | |
498 | failed_put_cred: | |
499 | put_cred(cred); | |
500 | return ret; | |
501 | } | |
502 | ||
503 | void | |
504 | exit_cifs_idmap(void) | |
505 | { | |
506 | key_revoke(root_cred->thread_keyring); | |
507 | unregister_key_type(&cifs_idmap_key_type); | |
508 | put_cred(root_cred); | |
f96637be | 509 | cifs_dbg(FYI, "Unregistered %s key type\n", cifs_idmap_key_type.name); |
4d79dba0 SP |
510 | } |
511 | ||
97837582 | 512 | /* copy ntsd, owner sid, and group sid from a security descriptor to another */ |
bc3e9dd9 SP |
513 | static __u32 copy_sec_desc(const struct cifs_ntsd *pntsd, |
514 | struct cifs_ntsd *pnntsd, | |
515 | __u32 sidsoffset, | |
516 | struct cifs_sid *pownersid, | |
517 | struct cifs_sid *pgrpsid) | |
97837582 | 518 | { |
97837582 SF |
519 | struct cifs_sid *owner_sid_ptr, *group_sid_ptr; |
520 | struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr; | |
521 | ||
522 | /* copy security descriptor control portion */ | |
523 | pnntsd->revision = pntsd->revision; | |
524 | pnntsd->type = pntsd->type; | |
525 | pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd)); | |
526 | pnntsd->sacloffset = 0; | |
527 | pnntsd->osidoffset = cpu_to_le32(sidsoffset); | |
528 | pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid)); | |
529 | ||
530 | /* copy owner sid */ | |
bc3e9dd9 SP |
531 | if (pownersid) |
532 | owner_sid_ptr = pownersid; | |
533 | else | |
534 | owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + | |
97837582 SF |
535 | le32_to_cpu(pntsd->osidoffset)); |
536 | nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset); | |
36960e44 | 537 | cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr); |
97837582 SF |
538 | |
539 | /* copy group sid */ | |
bc3e9dd9 SP |
540 | if (pgrpsid) |
541 | group_sid_ptr = pgrpsid; | |
542 | else | |
543 | group_sid_ptr = (struct cifs_sid *)((char *)pntsd + | |
97837582 SF |
544 | le32_to_cpu(pntsd->gsidoffset)); |
545 | ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset + | |
546 | sizeof(struct cifs_sid)); | |
36960e44 | 547 | cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr); |
97837582 | 548 | |
bc3e9dd9 | 549 | return sidsoffset + (2 * sizeof(struct cifs_sid)); |
97837582 SF |
550 | } |
551 | ||
552 | ||
630f3f0c SF |
553 | /* |
554 | change posix mode to reflect permissions | |
555 | pmode is the existing mode (we only want to overwrite part of this | |
556 | bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 | |
557 | */ | |
9b5e6857 | 558 | static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode, |
0f22053e | 559 | umode_t *pdenied, umode_t mask) |
630f3f0c | 560 | { |
9b5e6857 | 561 | __u32 flags = le32_to_cpu(ace_flags); |
0f22053e SP |
562 | /* |
563 | * Do not assume "preferred" or "canonical" order. | |
564 | * The first DENY or ALLOW ACE which matches perfectly is | |
565 | * the permission to be used. Once allowed or denied, same | |
566 | * permission in later ACEs do not matter. | |
567 | */ | |
568 | ||
569 | /* If not already allowed, deny these bits */ | |
15b03959 | 570 | if (type == ACCESS_DENIED) { |
0f22053e SP |
571 | if (flags & GENERIC_ALL && |
572 | !(*pmode & mask & 0777)) | |
573 | *pdenied |= mask & 0777; | |
574 | ||
575 | if (((flags & GENERIC_WRITE) || | |
576 | ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) && | |
577 | !(*pmode & mask & 0222)) | |
578 | *pdenied |= mask & 0222; | |
579 | ||
580 | if (((flags & GENERIC_READ) || | |
581 | ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) && | |
582 | !(*pmode & mask & 0444)) | |
583 | *pdenied |= mask & 0444; | |
584 | ||
585 | if (((flags & GENERIC_EXECUTE) || | |
586 | ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) && | |
587 | !(*pmode & mask & 0111)) | |
588 | *pdenied |= mask & 0111; | |
589 | ||
15b03959 SF |
590 | return; |
591 | } else if (type != ACCESS_ALLOWED) { | |
f96637be | 592 | cifs_dbg(VFS, "unknown access control type %d\n", type); |
15b03959 SF |
593 | return; |
594 | } | |
595 | /* else ACCESS_ALLOWED type */ | |
630f3f0c | 596 | |
0f22053e SP |
597 | if ((flags & GENERIC_ALL) && |
598 | !(*pdenied & mask & 0777)) { | |
599 | *pmode |= mask & 0777; | |
f96637be | 600 | cifs_dbg(NOISY, "all perms\n"); |
d61e5808 SF |
601 | return; |
602 | } | |
0f22053e SP |
603 | |
604 | if (((flags & GENERIC_WRITE) || | |
605 | ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) && | |
606 | !(*pdenied & mask & 0222)) | |
607 | *pmode |= mask & 0222; | |
608 | ||
609 | if (((flags & GENERIC_READ) || | |
610 | ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) && | |
611 | !(*pdenied & mask & 0444)) | |
612 | *pmode |= mask & 0444; | |
613 | ||
614 | if (((flags & GENERIC_EXECUTE) || | |
615 | ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) && | |
616 | !(*pdenied & mask & 0111)) | |
617 | *pmode |= mask & 0111; | |
630f3f0c | 618 | |
f2156d35 SP |
619 | /* If DELETE_CHILD is set only on an owner ACE, set sticky bit */ |
620 | if (flags & FILE_DELETE_CHILD) { | |
621 | if (mask == ACL_OWNER_MASK) { | |
622 | if (!(*pdenied & 01000)) | |
623 | *pmode |= 01000; | |
624 | } else if (!(*pdenied & 01000)) { | |
625 | *pmode &= ~01000; | |
626 | *pdenied |= 01000; | |
627 | } | |
628 | } | |
629 | ||
f52aa79d | 630 | cifs_dbg(NOISY, "access flags 0x%x mode now %04o\n", flags, *pmode); |
630f3f0c SF |
631 | return; |
632 | } | |
633 | ||
ce06c9f0 SF |
634 | /* |
635 | Generate access flags to reflect permissions mode is the existing mode. | |
636 | This function is called for every ACE in the DACL whose SID matches | |
637 | with either owner or group or everyone. | |
638 | */ | |
639 | ||
640 | static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, | |
641 | __u32 *pace_flags) | |
642 | { | |
643 | /* reset access mask */ | |
644 | *pace_flags = 0x0; | |
645 | ||
646 | /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ | |
647 | mode &= bits_to_use; | |
648 | ||
649 | /* check for R/W/X UGO since we do not know whose flags | |
650 | is this but we have cleared all the bits sans RWX for | |
651 | either user or group or other as per bits_to_use */ | |
652 | if (mode & S_IRUGO) | |
653 | *pace_flags |= SET_FILE_READ_RIGHTS; | |
654 | if (mode & S_IWUGO) | |
655 | *pace_flags |= SET_FILE_WRITE_RIGHTS; | |
656 | if (mode & S_IXUGO) | |
657 | *pace_flags |= SET_FILE_EXEC_RIGHTS; | |
658 | ||
f52aa79d | 659 | cifs_dbg(NOISY, "mode: %04o, access flags now 0x%x\n", |
f96637be | 660 | mode, *pace_flags); |
ce06c9f0 SF |
661 | return; |
662 | } | |
663 | ||
bc3e9dd9 | 664 | static __u16 cifs_copy_ace(struct cifs_ace *dst, struct cifs_ace *src, struct cifs_sid *psid) |
f5065508 SP |
665 | { |
666 | __u16 size = 1 + 1 + 2 + 4; | |
667 | ||
668 | dst->type = src->type; | |
669 | dst->flags = src->flags; | |
f5065508 | 670 | dst->access_req = src->access_req; |
bc3e9dd9 SP |
671 | |
672 | /* Check if there's a replacement sid specified */ | |
673 | if (psid) | |
674 | size += cifs_copy_sid(&dst->sid, psid); | |
675 | else | |
676 | size += cifs_copy_sid(&dst->sid, &src->sid); | |
677 | ||
678 | dst->size = cpu_to_le16(size); | |
f5065508 SP |
679 | |
680 | return size; | |
681 | } | |
682 | ||
2b210adc | 683 | static __u16 fill_ace_for_sid(struct cifs_ace *pntace, |
f2156d35 SP |
684 | const struct cifs_sid *psid, __u64 nmode, |
685 | umode_t bits, __u8 access_type, | |
686 | bool allow_delete_child) | |
97837582 SF |
687 | { |
688 | int i; | |
689 | __u16 size = 0; | |
690 | __u32 access_req = 0; | |
691 | ||
0f22053e | 692 | pntace->type = access_type; |
97837582 SF |
693 | pntace->flags = 0x0; |
694 | mode_to_access_flags(nmode, bits, &access_req); | |
f2156d35 SP |
695 | |
696 | if (access_type == ACCESS_ALLOWED && allow_delete_child) | |
697 | access_req |= FILE_DELETE_CHILD; | |
698 | ||
0f22053e | 699 | if (access_type == ACCESS_ALLOWED && !access_req) |
97837582 | 700 | access_req = SET_MINIMUM_RIGHTS; |
0f22053e SP |
701 | else if (access_type == ACCESS_DENIED) |
702 | access_req &= ~SET_MINIMUM_RIGHTS; | |
f2156d35 | 703 | |
97837582 SF |
704 | pntace->access_req = cpu_to_le32(access_req); |
705 | ||
706 | pntace->sid.revision = psid->revision; | |
707 | pntace->sid.num_subauth = psid->num_subauth; | |
852e2295 | 708 | for (i = 0; i < NUM_AUTHS; i++) |
97837582 SF |
709 | pntace->sid.authority[i] = psid->authority[i]; |
710 | for (i = 0; i < psid->num_subauth; i++) | |
711 | pntace->sid.sub_auth[i] = psid->sub_auth[i]; | |
712 | ||
713 | size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); | |
714 | pntace->size = cpu_to_le16(size); | |
715 | ||
ef571cad | 716 | return size; |
97837582 SF |
717 | } |
718 | ||
297647c2 | 719 | |
953f8681 SF |
720 | #ifdef CONFIG_CIFS_DEBUG2 |
721 | static void dump_ace(struct cifs_ace *pace, char *end_of_acl) | |
d0d66c44 | 722 | { |
d0d66c44 | 723 | int num_subauth; |
d0d66c44 SP |
724 | |
725 | /* validate that we do not go past end of acl */ | |
297647c2 | 726 | |
44093ca2 | 727 | if (le16_to_cpu(pace->size) < 16) { |
f96637be | 728 | cifs_dbg(VFS, "ACE too small %d\n", le16_to_cpu(pace->size)); |
44093ca2 SF |
729 | return; |
730 | } | |
731 | ||
732 | if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) { | |
f96637be | 733 | cifs_dbg(VFS, "ACL too small to parse ACE\n"); |
d0d66c44 | 734 | return; |
44093ca2 | 735 | } |
d0d66c44 | 736 | |
44093ca2 | 737 | num_subauth = pace->sid.num_subauth; |
d0d66c44 | 738 | if (num_subauth) { |
8f18c131 | 739 | int i; |
f96637be JP |
740 | cifs_dbg(FYI, "ACE revision %d num_auth %d type %d flags %d size %d\n", |
741 | pace->sid.revision, pace->sid.num_subauth, pace->type, | |
742 | pace->flags, le16_to_cpu(pace->size)); | |
d12fd121 | 743 | for (i = 0; i < num_subauth; ++i) { |
f96637be JP |
744 | cifs_dbg(FYI, "ACE sub_auth[%d]: 0x%x\n", |
745 | i, le32_to_cpu(pace->sid.sub_auth[i])); | |
d12fd121 SF |
746 | } |
747 | ||
748 | /* BB add length check to make sure that we do not have huge | |
749 | num auths and therefore go off the end */ | |
d12fd121 SF |
750 | } |
751 | ||
752 | return; | |
753 | } | |
953f8681 | 754 | #endif |
d12fd121 | 755 | |
a750e77c | 756 | static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, |
d61e5808 | 757 | struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, |
e2f8fbfb | 758 | struct cifs_fattr *fattr, bool mode_from_special_sid) |
d0d66c44 SP |
759 | { |
760 | int i; | |
761 | int num_aces = 0; | |
762 | int acl_size; | |
763 | char *acl_base; | |
d0d66c44 SP |
764 | struct cifs_ace **ppace; |
765 | ||
766 | /* BB need to add parm so we can store the SID BB */ | |
767 | ||
2b83457b SF |
768 | if (!pdacl) { |
769 | /* no DACL in the security descriptor, set | |
770 | all the permissions for user/group/other */ | |
0f22053e | 771 | fattr->cf_mode |= 0777; |
2b83457b SF |
772 | return; |
773 | } | |
774 | ||
d0d66c44 | 775 | /* validate that we do not go past end of acl */ |
af6f4612 | 776 | if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { |
f96637be | 777 | cifs_dbg(VFS, "ACL too small to parse DACL\n"); |
d0d66c44 SP |
778 | return; |
779 | } | |
780 | ||
f96637be JP |
781 | cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n", |
782 | le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), | |
783 | le32_to_cpu(pdacl->num_aces)); | |
d0d66c44 | 784 | |
7505e052 SF |
785 | /* reset rwx permissions for user/group/other. |
786 | Also, if num_aces is 0 i.e. DACL has no ACEs, | |
787 | user/group/other have no permissions */ | |
0f22053e | 788 | fattr->cf_mode &= ~(0777); |
7505e052 | 789 | |
d0d66c44 SP |
790 | acl_base = (char *)pdacl; |
791 | acl_size = sizeof(struct cifs_acl); | |
792 | ||
adbc0358 | 793 | num_aces = le32_to_cpu(pdacl->num_aces); |
a5ff3769 | 794 | if (num_aces > 0) { |
0f22053e | 795 | umode_t denied_mode = 0; |
15b03959 | 796 | |
7250170c DC |
797 | if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *)) |
798 | return; | |
6da2ec56 KC |
799 | ppace = kmalloc_array(num_aces, sizeof(struct cifs_ace *), |
800 | GFP_KERNEL); | |
f96637be | 801 | if (!ppace) |
8132b65b | 802 | return; |
d0d66c44 | 803 | |
d0d66c44 | 804 | for (i = 0; i < num_aces; ++i) { |
44093ca2 | 805 | ppace[i] = (struct cifs_ace *) (acl_base + acl_size); |
953f8681 SF |
806 | #ifdef CONFIG_CIFS_DEBUG2 |
807 | dump_ace(ppace[i], end_of_acl); | |
808 | #endif | |
e2f8fbfb SF |
809 | if (mode_from_special_sid && |
810 | (compare_sids(&(ppace[i]->sid), | |
811 | &sid_unix_NFS_mode) == 0)) { | |
812 | /* | |
813 | * Full permissions are: | |
814 | * 07777 = S_ISUID | S_ISGID | S_ISVTX | | |
815 | * S_IRWXU | S_IRWXG | S_IRWXO | |
816 | */ | |
817 | fattr->cf_mode &= ~07777; | |
818 | fattr->cf_mode |= | |
819 | le32_to_cpu(ppace[i]->sid.sub_auth[2]); | |
820 | break; | |
0f22053e SP |
821 | } else { |
822 | if (compare_sids(&(ppace[i]->sid), pownersid) == 0) { | |
823 | access_flags_to_mode(ppace[i]->access_req, | |
824 | ppace[i]->type, | |
825 | &fattr->cf_mode, | |
826 | &denied_mode, | |
827 | ACL_OWNER_MASK); | |
828 | } else if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) { | |
829 | access_flags_to_mode(ppace[i]->access_req, | |
830 | ppace[i]->type, | |
831 | &fattr->cf_mode, | |
832 | &denied_mode, | |
833 | ACL_GROUP_MASK); | |
834 | } else if ((compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) || | |
835 | (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)) { | |
836 | access_flags_to_mode(ppace[i]->access_req, | |
837 | ppace[i]->type, | |
838 | &fattr->cf_mode, | |
839 | &denied_mode, | |
840 | ACL_EVERYONE_MASK); | |
841 | } | |
842 | } | |
2fbc2f17 | 843 | |
e01b6400 | 844 | |
44093ca2 | 845 | /* memcpy((void *)(&(cifscred->aces[i])), |
d12fd121 SF |
846 | (void *)ppace[i], |
847 | sizeof(struct cifs_ace)); */ | |
d0d66c44 | 848 | |
44093ca2 SF |
849 | acl_base = (char *)ppace[i]; |
850 | acl_size = le16_to_cpu(ppace[i]->size); | |
d0d66c44 SP |
851 | } |
852 | ||
853 | kfree(ppace); | |
d0d66c44 SP |
854 | } |
855 | ||
856 | return; | |
857 | } | |
858 | ||
643fbcee SF |
859 | unsigned int setup_authusers_ACE(struct cifs_ace *pntace) |
860 | { | |
861 | int i; | |
862 | unsigned int ace_size = 20; | |
863 | ||
864 | pntace->type = ACCESS_ALLOWED_ACE_TYPE; | |
865 | pntace->flags = 0x0; | |
866 | pntace->access_req = cpu_to_le32(GENERIC_ALL); | |
867 | pntace->sid.num_subauth = 1; | |
868 | pntace->sid.revision = 1; | |
869 | for (i = 0; i < NUM_AUTHS; i++) | |
870 | pntace->sid.authority[i] = sid_authusers.authority[i]; | |
871 | ||
872 | pntace->sid.sub_auth[0] = sid_authusers.sub_auth[0]; | |
873 | ||
874 | /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ | |
875 | pntace->size = cpu_to_le16(ace_size); | |
876 | return ace_size; | |
877 | } | |
878 | ||
fdef665b SF |
879 | /* |
880 | * Fill in the special SID based on the mode. See | |
cba22b1c | 881 | * https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx |
fdef665b SF |
882 | */ |
883 | unsigned int setup_special_mode_ACE(struct cifs_ace *pntace, __u64 nmode) | |
884 | { | |
885 | int i; | |
886 | unsigned int ace_size = 28; | |
887 | ||
888 | pntace->type = ACCESS_DENIED_ACE_TYPE; | |
889 | pntace->flags = 0x0; | |
890 | pntace->access_req = 0; | |
891 | pntace->sid.num_subauth = 3; | |
892 | pntace->sid.revision = 1; | |
893 | for (i = 0; i < NUM_AUTHS; i++) | |
894 | pntace->sid.authority[i] = sid_unix_NFS_mode.authority[i]; | |
895 | ||
896 | pntace->sid.sub_auth[0] = sid_unix_NFS_mode.sub_auth[0]; | |
897 | pntace->sid.sub_auth[1] = sid_unix_NFS_mode.sub_auth[1]; | |
898 | pntace->sid.sub_auth[2] = cpu_to_le32(nmode & 07777); | |
899 | ||
900 | /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ | |
901 | pntace->size = cpu_to_le16(ace_size); | |
902 | return ace_size; | |
903 | } | |
bcb02034 | 904 | |
975221ec SF |
905 | unsigned int setup_special_user_owner_ACE(struct cifs_ace *pntace) |
906 | { | |
907 | int i; | |
908 | unsigned int ace_size = 28; | |
909 | ||
910 | pntace->type = ACCESS_ALLOWED_ACE_TYPE; | |
911 | pntace->flags = 0x0; | |
912 | pntace->access_req = cpu_to_le32(GENERIC_ALL); | |
913 | pntace->sid.num_subauth = 3; | |
914 | pntace->sid.revision = 1; | |
915 | for (i = 0; i < NUM_AUTHS; i++) | |
916 | pntace->sid.authority[i] = sid_unix_NFS_users.authority[i]; | |
917 | ||
918 | pntace->sid.sub_auth[0] = sid_unix_NFS_users.sub_auth[0]; | |
919 | pntace->sid.sub_auth[1] = sid_unix_NFS_users.sub_auth[1]; | |
920 | pntace->sid.sub_auth[2] = cpu_to_le32(current_fsgid().val); | |
921 | ||
922 | /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ | |
923 | pntace->size = cpu_to_le16(ace_size); | |
924 | return ace_size; | |
925 | } | |
926 | ||
f5065508 SP |
927 | static void populate_new_aces(char *nacl_base, |
928 | struct cifs_sid *pownersid, | |
929 | struct cifs_sid *pgrpsid, | |
930 | __u64 *pnmode, u32 *pnum_aces, u16 *pnsize, | |
931 | bool modefromsid) | |
97837582 | 932 | { |
0f22053e | 933 | __u64 nmode; |
f5065508 SP |
934 | u32 num_aces = 0; |
935 | u16 nsize = 0; | |
0f22053e SP |
936 | __u64 user_mode; |
937 | __u64 group_mode; | |
938 | __u64 other_mode; | |
939 | __u64 deny_user_mode = 0; | |
940 | __u64 deny_group_mode = 0; | |
f2156d35 | 941 | bool sticky_set = false; |
f5065508 | 942 | struct cifs_ace *pnntace = NULL; |
97837582 | 943 | |
0f22053e | 944 | nmode = *pnmode; |
f5065508 SP |
945 | num_aces = *pnum_aces; |
946 | nsize = *pnsize; | |
0f22053e | 947 | |
22442179 | 948 | if (modefromsid) { |
f5065508 SP |
949 | pnntace = (struct cifs_ace *) (nacl_base + nsize); |
950 | nsize += setup_special_mode_ACE(pnntace, nmode); | |
e37a02c7 | 951 | num_aces++; |
0c6f4ebf RS |
952 | pnntace = (struct cifs_ace *) (nacl_base + nsize); |
953 | nsize += setup_authusers_ACE(pnntace); | |
954 | num_aces++; | |
0f22053e | 955 | goto set_size; |
e37a02c7 AA |
956 | } |
957 | ||
0f22053e SP |
958 | /* |
959 | * We'll try to keep the mode as requested by the user. | |
960 | * But in cases where we cannot meaningfully convert that | |
961 | * into ACL, return back the updated mode, so that it is | |
962 | * updated in the inode. | |
963 | */ | |
964 | ||
965 | if (!memcmp(pownersid, pgrpsid, sizeof(struct cifs_sid))) { | |
966 | /* | |
967 | * Case when owner and group SIDs are the same. | |
968 | * Set the more restrictive of the two modes. | |
969 | */ | |
970 | user_mode = nmode & (nmode << 3) & 0700; | |
971 | group_mode = nmode & (nmode >> 3) & 0070; | |
972 | } else { | |
973 | user_mode = nmode & 0700; | |
974 | group_mode = nmode & 0070; | |
975 | } | |
976 | ||
977 | other_mode = nmode & 0007; | |
978 | ||
979 | /* We need DENY ACE when the perm is more restrictive than the next sets. */ | |
980 | deny_user_mode = ~(user_mode) & ((group_mode << 3) | (other_mode << 6)) & 0700; | |
981 | deny_group_mode = ~(group_mode) & (other_mode << 3) & 0070; | |
982 | ||
983 | *pnmode = user_mode | group_mode | other_mode | (nmode & ~0777); | |
984 | ||
f2156d35 SP |
985 | /* This tells if we should allow delete child for group and everyone. */ |
986 | if (nmode & 01000) | |
987 | sticky_set = true; | |
988 | ||
0f22053e | 989 | if (deny_user_mode) { |
f5065508 SP |
990 | pnntace = (struct cifs_ace *) (nacl_base + nsize); |
991 | nsize += fill_ace_for_sid(pnntace, pownersid, deny_user_mode, | |
992 | 0700, ACCESS_DENIED, false); | |
0f22053e SP |
993 | num_aces++; |
994 | } | |
f5065508 | 995 | |
0f22053e SP |
996 | /* Group DENY ACE does not conflict with owner ALLOW ACE. Keep in preferred order*/ |
997 | if (deny_group_mode && !(deny_group_mode & (user_mode >> 3))) { | |
f5065508 SP |
998 | pnntace = (struct cifs_ace *) (nacl_base + nsize); |
999 | nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode, | |
1000 | 0070, ACCESS_DENIED, false); | |
0f22053e SP |
1001 | num_aces++; |
1002 | } | |
f5065508 SP |
1003 | |
1004 | pnntace = (struct cifs_ace *) (nacl_base + nsize); | |
1005 | nsize += fill_ace_for_sid(pnntace, pownersid, user_mode, | |
1006 | 0700, ACCESS_ALLOWED, true); | |
e37a02c7 | 1007 | num_aces++; |
f5065508 | 1008 | |
0f22053e SP |
1009 | /* Group DENY ACE conflicts with owner ALLOW ACE. So keep it after. */ |
1010 | if (deny_group_mode && (deny_group_mode & (user_mode >> 3))) { | |
f5065508 SP |
1011 | pnntace = (struct cifs_ace *) (nacl_base + nsize); |
1012 | nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode, | |
1013 | 0070, ACCESS_DENIED, false); | |
0f22053e SP |
1014 | num_aces++; |
1015 | } | |
f5065508 SP |
1016 | |
1017 | pnntace = (struct cifs_ace *) (nacl_base + nsize); | |
1018 | nsize += fill_ace_for_sid(pnntace, pgrpsid, group_mode, | |
1019 | 0070, ACCESS_ALLOWED, !sticky_set); | |
e37a02c7 | 1020 | num_aces++; |
f5065508 SP |
1021 | |
1022 | pnntace = (struct cifs_ace *) (nacl_base + nsize); | |
1023 | nsize += fill_ace_for_sid(pnntace, &sid_everyone, other_mode, | |
1024 | 0007, ACCESS_ALLOWED, !sticky_set); | |
e37a02c7 | 1025 | num_aces++; |
22442179 | 1026 | |
0f22053e | 1027 | set_size: |
f5065508 SP |
1028 | *pnum_aces = num_aces; |
1029 | *pnsize = nsize; | |
1030 | } | |
1031 | ||
1032 | static __u16 replace_sids_and_copy_aces(struct cifs_acl *pdacl, struct cifs_acl *pndacl, | |
1033 | struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, | |
1034 | struct cifs_sid *pnownersid, struct cifs_sid *pngrpsid) | |
1035 | { | |
1036 | int i; | |
1037 | u16 size = 0; | |
1038 | struct cifs_ace *pntace = NULL; | |
1039 | char *acl_base = NULL; | |
1040 | u32 src_num_aces = 0; | |
1041 | u16 nsize = 0; | |
1042 | struct cifs_ace *pnntace = NULL; | |
1043 | char *nacl_base = NULL; | |
1044 | u16 ace_size = 0; | |
1045 | ||
1046 | acl_base = (char *)pdacl; | |
1047 | size = sizeof(struct cifs_acl); | |
1048 | src_num_aces = le32_to_cpu(pdacl->num_aces); | |
1049 | ||
1050 | nacl_base = (char *)pndacl; | |
1051 | nsize = sizeof(struct cifs_acl); | |
1052 | ||
1053 | /* Go through all the ACEs */ | |
1054 | for (i = 0; i < src_num_aces; ++i) { | |
1055 | pntace = (struct cifs_ace *) (acl_base + size); | |
1056 | pnntace = (struct cifs_ace *) (nacl_base + nsize); | |
1057 | ||
1058 | if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0) | |
bc3e9dd9 | 1059 | ace_size = cifs_copy_ace(pnntace, pntace, pnownersid); |
f5065508 | 1060 | else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0) |
bc3e9dd9 | 1061 | ace_size = cifs_copy_ace(pnntace, pntace, pngrpsid); |
f5065508 | 1062 | else |
bc3e9dd9 | 1063 | ace_size = cifs_copy_ace(pnntace, pntace, NULL); |
f5065508 SP |
1064 | |
1065 | size += le16_to_cpu(pntace->size); | |
1066 | nsize += ace_size; | |
1067 | } | |
1068 | ||
1069 | return nsize; | |
1070 | } | |
1071 | ||
1072 | static int set_chmod_dacl(struct cifs_acl *pdacl, struct cifs_acl *pndacl, | |
1073 | struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, | |
1074 | __u64 *pnmode, bool mode_from_sid) | |
1075 | { | |
1076 | int i; | |
1077 | u16 size = 0; | |
1078 | struct cifs_ace *pntace = NULL; | |
1079 | char *acl_base = NULL; | |
1080 | u32 src_num_aces = 0; | |
1081 | u16 nsize = 0; | |
1082 | struct cifs_ace *pnntace = NULL; | |
1083 | char *nacl_base = NULL; | |
1084 | u32 num_aces = 0; | |
f5065508 SP |
1085 | bool new_aces_set = false; |
1086 | ||
1087 | /* Assuming that pndacl and pnmode are never NULL */ | |
f5065508 SP |
1088 | nacl_base = (char *)pndacl; |
1089 | nsize = sizeof(struct cifs_acl); | |
1090 | ||
1091 | /* If pdacl is NULL, we don't have a src. Simply populate new ACL. */ | |
1092 | if (!pdacl) { | |
1093 | populate_new_aces(nacl_base, | |
1094 | pownersid, pgrpsid, | |
1095 | pnmode, &num_aces, &nsize, | |
1096 | mode_from_sid); | |
1097 | goto finalize_dacl; | |
1098 | } | |
1099 | ||
1100 | acl_base = (char *)pdacl; | |
1101 | size = sizeof(struct cifs_acl); | |
1102 | src_num_aces = le32_to_cpu(pdacl->num_aces); | |
1103 | ||
1104 | /* Retain old ACEs which we can retain */ | |
1105 | for (i = 0; i < src_num_aces; ++i) { | |
1106 | pntace = (struct cifs_ace *) (acl_base + size); | |
f5065508 SP |
1107 | |
1108 | if (!new_aces_set && (pntace->flags & INHERITED_ACE)) { | |
1109 | /* Place the new ACEs in between existing explicit and inherited */ | |
1110 | populate_new_aces(nacl_base, | |
1111 | pownersid, pgrpsid, | |
1112 | pnmode, &num_aces, &nsize, | |
1113 | mode_from_sid); | |
1114 | ||
1115 | new_aces_set = true; | |
1116 | } | |
1117 | ||
1118 | /* If it's any one of the ACE we're replacing, skip! */ | |
3bffbe9e | 1119 | if (((compare_sids(&pntace->sid, &sid_unix_NFS_mode) == 0) || |
f5065508 SP |
1120 | (compare_sids(&pntace->sid, pownersid) == 0) || |
1121 | (compare_sids(&pntace->sid, pgrpsid) == 0) || | |
1122 | (compare_sids(&pntace->sid, &sid_everyone) == 0) || | |
5171317d | 1123 | (compare_sids(&pntace->sid, &sid_authusers) == 0))) { |
f5065508 SP |
1124 | goto next_ace; |
1125 | } | |
1126 | ||
5171317d SP |
1127 | /* update the pointer to the next ACE to populate*/ |
1128 | pnntace = (struct cifs_ace *) (nacl_base + nsize); | |
1129 | ||
bc3e9dd9 | 1130 | nsize += cifs_copy_ace(pnntace, pntace, NULL); |
f5065508 SP |
1131 | num_aces++; |
1132 | ||
1133 | next_ace: | |
23bda5e6 | 1134 | size += le16_to_cpu(pntace->size); |
f5065508 SP |
1135 | } |
1136 | ||
1137 | /* If inherited ACEs are not present, place the new ones at the tail */ | |
1138 | if (!new_aces_set) { | |
1139 | populate_new_aces(nacl_base, | |
1140 | pownersid, pgrpsid, | |
1141 | pnmode, &num_aces, &nsize, | |
1142 | mode_from_sid); | |
1143 | ||
1144 | new_aces_set = true; | |
1145 | } | |
1146 | ||
1147 | finalize_dacl: | |
e37a02c7 | 1148 | pndacl->num_aces = cpu_to_le32(num_aces); |
f5065508 | 1149 | pndacl->size = cpu_to_le16(nsize); |
97837582 | 1150 | |
ef571cad | 1151 | return 0; |
97837582 SF |
1152 | } |
1153 | ||
bcb02034 SF |
1154 | static int parse_sid(struct cifs_sid *psid, char *end_of_acl) |
1155 | { | |
1156 | /* BB need to add parm so we can store the SID BB */ | |
1157 | ||
b9c7a2bb SF |
1158 | /* validate that we do not go past end of ACL - sid must be at least 8 |
1159 | bytes long (assuming no sub-auths - e.g. the null SID */ | |
1160 | if (end_of_acl < (char *)psid + 8) { | |
f96637be | 1161 | cifs_dbg(VFS, "ACL too small to parse SID %p\n", psid); |
bcb02034 SF |
1162 | return -EINVAL; |
1163 | } | |
d0d66c44 | 1164 | |
bcb02034 | 1165 | #ifdef CONFIG_CIFS_DEBUG2 |
fc03d8a5 | 1166 | if (psid->num_subauth) { |
8f18c131 | 1167 | int i; |
f96637be JP |
1168 | cifs_dbg(FYI, "SID revision %d num_auth %d\n", |
1169 | psid->revision, psid->num_subauth); | |
bcb02034 | 1170 | |
af6f4612 | 1171 | for (i = 0; i < psid->num_subauth; i++) { |
f96637be JP |
1172 | cifs_dbg(FYI, "SID sub_auth[%d]: 0x%x\n", |
1173 | i, le32_to_cpu(psid->sub_auth[i])); | |
d0d66c44 SP |
1174 | } |
1175 | ||
d12fd121 | 1176 | /* BB add length check to make sure that we do not have huge |
d0d66c44 | 1177 | num auths and therefore go off the end */ |
f96637be JP |
1178 | cifs_dbg(FYI, "RID 0x%x\n", |
1179 | le32_to_cpu(psid->sub_auth[psid->num_subauth-1])); | |
d0d66c44 | 1180 | } |
fc03d8a5 | 1181 | #endif |
d0d66c44 | 1182 | |
bcb02034 SF |
1183 | return 0; |
1184 | } | |
1185 | ||
d0d66c44 | 1186 | |
bcb02034 | 1187 | /* Convert CIFS ACL to POSIX form */ |
9409ae58 | 1188 | static int parse_sec_desc(struct cifs_sb_info *cifs_sb, |
e2f8fbfb SF |
1189 | struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr, |
1190 | bool get_mode_from_special_sid) | |
bcb02034 | 1191 | { |
9409ae58 | 1192 | int rc = 0; |
bcb02034 SF |
1193 | struct cifs_sid *owner_sid_ptr, *group_sid_ptr; |
1194 | struct cifs_acl *dacl_ptr; /* no need for SACL ptr */ | |
bcb02034 | 1195 | char *end_of_acl = ((char *)pntsd) + acl_len; |
7505e052 | 1196 | __u32 dacloffset; |
bcb02034 | 1197 | |
0b8f18e3 | 1198 | if (pntsd == NULL) |
b9c7a2bb SF |
1199 | return -EIO; |
1200 | ||
bcb02034 | 1201 | owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + |
af6f4612 | 1202 | le32_to_cpu(pntsd->osidoffset)); |
bcb02034 | 1203 | group_sid_ptr = (struct cifs_sid *)((char *)pntsd + |
af6f4612 | 1204 | le32_to_cpu(pntsd->gsidoffset)); |
7505e052 | 1205 | dacloffset = le32_to_cpu(pntsd->dacloffset); |
63d2583f | 1206 | dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); |
f96637be | 1207 | cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", |
af6f4612 SF |
1208 | pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), |
1209 | le32_to_cpu(pntsd->gsidoffset), | |
b6b38f70 | 1210 | le32_to_cpu(pntsd->sacloffset), dacloffset); |
b9c7a2bb | 1211 | /* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ |
bcb02034 | 1212 | rc = parse_sid(owner_sid_ptr, end_of_acl); |
9409ae58 | 1213 | if (rc) { |
f96637be | 1214 | cifs_dbg(FYI, "%s: Error %d parsing Owner SID\n", __func__, rc); |
9409ae58 SP |
1215 | return rc; |
1216 | } | |
1217 | rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER); | |
1218 | if (rc) { | |
f96637be JP |
1219 | cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n", |
1220 | __func__, rc); | |
bcb02034 | 1221 | return rc; |
9409ae58 | 1222 | } |
bcb02034 SF |
1223 | |
1224 | rc = parse_sid(group_sid_ptr, end_of_acl); | |
9409ae58 | 1225 | if (rc) { |
f96637be JP |
1226 | cifs_dbg(FYI, "%s: Error %d mapping Owner SID to gid\n", |
1227 | __func__, rc); | |
bcb02034 | 1228 | return rc; |
9409ae58 SP |
1229 | } |
1230 | rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP); | |
1231 | if (rc) { | |
f96637be JP |
1232 | cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n", |
1233 | __func__, rc); | |
9409ae58 SP |
1234 | return rc; |
1235 | } | |
bcb02034 | 1236 | |
7505e052 SF |
1237 | if (dacloffset) |
1238 | parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, | |
e2f8fbfb | 1239 | group_sid_ptr, fattr, get_mode_from_special_sid); |
7505e052 | 1240 | else |
f96637be | 1241 | cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */ |
d0d66c44 | 1242 | |
9409ae58 | 1243 | return rc; |
bcb02034 | 1244 | } |
b9c7a2bb | 1245 | |
97837582 SF |
1246 | /* Convert permission bits from mode to equivalent CIFS ACL */ |
1247 | static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, | |
f5065508 | 1248 | __u32 secdesclen, __u32 *pnsecdesclen, __u64 *pnmode, kuid_t uid, kgid_t gid, |
a6603398 | 1249 | bool mode_from_sid, bool id_from_sid, int *aclflag) |
97837582 SF |
1250 | { |
1251 | int rc = 0; | |
1252 | __u32 dacloffset; | |
1253 | __u32 ndacloffset; | |
1254 | __u32 sidsoffset; | |
1255 | struct cifs_sid *owner_sid_ptr, *group_sid_ptr; | |
bc3e9dd9 | 1256 | struct cifs_sid *nowner_sid_ptr = NULL, *ngroup_sid_ptr = NULL; |
97837582 SF |
1257 | struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */ |
1258 | struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */ | |
f5065508 | 1259 | char *end_of_acl = ((char *)pntsd) + secdesclen; |
bc3e9dd9 | 1260 | u16 size = 0; |
97837582 | 1261 | |
bc3e9dd9 SP |
1262 | dacloffset = le32_to_cpu(pntsd->dacloffset); |
1263 | if (dacloffset) { | |
1264 | dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); | |
1265 | if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) { | |
f1ebe48d SP |
1266 | cifs_dbg(VFS, "Server returned illegal ACL size\n"); |
1267 | return -EINVAL; | |
f5065508 | 1268 | } |
bc3e9dd9 SP |
1269 | } |
1270 | ||
1271 | owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + | |
1272 | le32_to_cpu(pntsd->osidoffset)); | |
1273 | group_sid_ptr = (struct cifs_sid *)((char *)pntsd + | |
1274 | le32_to_cpu(pntsd->gsidoffset)); | |
f5065508 | 1275 | |
bc3e9dd9 | 1276 | if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */ |
a5ff3769 SP |
1277 | ndacloffset = sizeof(struct cifs_ntsd); |
1278 | ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); | |
f5065508 SP |
1279 | ndacl_ptr->revision = |
1280 | dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); | |
a5ff3769 | 1281 | |
23bda5e6 | 1282 | ndacl_ptr->size = cpu_to_le16(0); |
f5065508 SP |
1283 | ndacl_ptr->num_aces = cpu_to_le32(0); |
1284 | ||
1285 | rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, | |
0f22053e | 1286 | pnmode, mode_from_sid); |
bc3e9dd9 | 1287 | |
a5ff3769 | 1288 | sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); |
bc3e9dd9 SP |
1289 | /* copy the non-dacl portion of secdesc */ |
1290 | *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset, | |
1291 | NULL, NULL); | |
1292 | ||
1293 | *aclflag |= CIFS_ACL_DACL; | |
a5ff3769 | 1294 | } else { |
f5065508 SP |
1295 | ndacloffset = sizeof(struct cifs_ntsd); |
1296 | ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); | |
1297 | ndacl_ptr->revision = | |
1298 | dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); | |
a5628263 | 1299 | ndacl_ptr->num_aces = dacl_ptr ? dacl_ptr->num_aces : 0; |
f5065508 | 1300 | |
8abf2775 EB |
1301 | if (uid_valid(uid)) { /* chown */ |
1302 | uid_t id; | |
26d3dade | 1303 | nowner_sid_ptr = kzalloc(sizeof(struct cifs_sid), |
a5ff3769 | 1304 | GFP_KERNEL); |
bc3e9dd9 SP |
1305 | if (!nowner_sid_ptr) { |
1306 | rc = -ENOMEM; | |
1307 | goto chown_chgrp_exit; | |
1308 | } | |
8abf2775 | 1309 | id = from_kuid(&init_user_ns, uid); |
a6603398 SF |
1310 | if (id_from_sid) { |
1311 | struct owner_sid *osid = (struct owner_sid *)nowner_sid_ptr; | |
1312 | /* Populate the user ownership fields S-1-5-88-1 */ | |
1313 | osid->Revision = 1; | |
1314 | osid->NumAuth = 3; | |
1315 | osid->Authority[5] = 5; | |
1316 | osid->SubAuthorities[0] = cpu_to_le32(88); | |
1317 | osid->SubAuthorities[1] = cpu_to_le32(1); | |
1318 | osid->SubAuthorities[2] = cpu_to_le32(id); | |
bc3e9dd9 | 1319 | |
a6603398 SF |
1320 | } else { /* lookup sid with upcall */ |
1321 | rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr); | |
1322 | if (rc) { | |
1323 | cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n", | |
1324 | __func__, rc, id); | |
bc3e9dd9 | 1325 | goto chown_chgrp_exit; |
a6603398 | 1326 | } |
a5ff3769 | 1327 | } |
bc3e9dd9 | 1328 | *aclflag |= CIFS_ACL_OWNER; |
a5ff3769 | 1329 | } |
8abf2775 EB |
1330 | if (gid_valid(gid)) { /* chgrp */ |
1331 | gid_t id; | |
26d3dade | 1332 | ngroup_sid_ptr = kzalloc(sizeof(struct cifs_sid), |
a5ff3769 | 1333 | GFP_KERNEL); |
bc3e9dd9 SP |
1334 | if (!ngroup_sid_ptr) { |
1335 | rc = -ENOMEM; | |
1336 | goto chown_chgrp_exit; | |
1337 | } | |
8abf2775 | 1338 | id = from_kgid(&init_user_ns, gid); |
a6603398 SF |
1339 | if (id_from_sid) { |
1340 | struct owner_sid *gsid = (struct owner_sid *)ngroup_sid_ptr; | |
1341 | /* Populate the group ownership fields S-1-5-88-2 */ | |
1342 | gsid->Revision = 1; | |
1343 | gsid->NumAuth = 3; | |
1344 | gsid->Authority[5] = 5; | |
1345 | gsid->SubAuthorities[0] = cpu_to_le32(88); | |
1346 | gsid->SubAuthorities[1] = cpu_to_le32(2); | |
1347 | gsid->SubAuthorities[2] = cpu_to_le32(id); | |
bc3e9dd9 | 1348 | |
a6603398 SF |
1349 | } else { /* lookup sid with upcall */ |
1350 | rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr); | |
1351 | if (rc) { | |
1352 | cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n", | |
1353 | __func__, rc, id); | |
bc3e9dd9 | 1354 | goto chown_chgrp_exit; |
a6603398 | 1355 | } |
a5ff3769 | 1356 | } |
bc3e9dd9 | 1357 | *aclflag |= CIFS_ACL_GROUP; |
a5ff3769 | 1358 | } |
bc3e9dd9 SP |
1359 | |
1360 | if (dacloffset) { | |
1361 | /* Replace ACEs for old owner with new one */ | |
1362 | size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, | |
1363 | owner_sid_ptr, group_sid_ptr, | |
1364 | nowner_sid_ptr, ngroup_sid_ptr); | |
1365 | ndacl_ptr->size = cpu_to_le16(size); | |
1366 | } | |
1367 | ||
1368 | sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); | |
1369 | /* copy the non-dacl portion of secdesc */ | |
1370 | *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset, | |
1371 | nowner_sid_ptr, ngroup_sid_ptr); | |
1372 | ||
1373 | chown_chgrp_exit: | |
1374 | /* errors could jump here. So make sure we return soon after this */ | |
1375 | kfree(nowner_sid_ptr); | |
1376 | kfree(ngroup_sid_ptr); | |
a5ff3769 | 1377 | } |
97837582 | 1378 | |
ef571cad | 1379 | return rc; |
97837582 SF |
1380 | } |
1381 | ||
42eacf9e | 1382 | struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, |
3970acf7 BP |
1383 | const struct cifs_fid *cifsfid, u32 *pacllen, |
1384 | u32 __maybe_unused unused) | |
b9c7a2bb | 1385 | { |
b9c7a2bb | 1386 | struct cifs_ntsd *pntsd = NULL; |
6d5786a3 PS |
1387 | unsigned int xid; |
1388 | int rc; | |
7ffec372 JL |
1389 | struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); |
1390 | ||
1391 | if (IS_ERR(tlink)) | |
987b21d7 | 1392 | return ERR_CAST(tlink); |
b9c7a2bb | 1393 | |
6d5786a3 | 1394 | xid = get_xid(); |
42eacf9e SF |
1395 | rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), cifsfid->netfid, &pntsd, |
1396 | pacllen); | |
6d5786a3 | 1397 | free_xid(xid); |
b9c7a2bb | 1398 | |
7ffec372 | 1399 | cifs_put_tlink(tlink); |
b9c7a2bb | 1400 | |
f96637be | 1401 | cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); |
987b21d7 SP |
1402 | if (rc) |
1403 | return ERR_PTR(rc); | |
1bf4072d CH |
1404 | return pntsd; |
1405 | } | |
8b1327f6 | 1406 | |
1bf4072d CH |
1407 | static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, |
1408 | const char *path, u32 *pacllen) | |
1409 | { | |
1410 | struct cifs_ntsd *pntsd = NULL; | |
1411 | int oplock = 0; | |
6d5786a3 | 1412 | unsigned int xid; |
0f060936 | 1413 | int rc; |
96daf2b0 | 1414 | struct cifs_tcon *tcon; |
7ffec372 | 1415 | struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); |
d81b8a40 PS |
1416 | struct cifs_fid fid; |
1417 | struct cifs_open_parms oparms; | |
7ffec372 JL |
1418 | |
1419 | if (IS_ERR(tlink)) | |
987b21d7 | 1420 | return ERR_CAST(tlink); |
b9c7a2bb | 1421 | |
7ffec372 | 1422 | tcon = tlink_tcon(tlink); |
6d5786a3 | 1423 | xid = get_xid(); |
1bf4072d | 1424 | |
d81b8a40 PS |
1425 | oparms.tcon = tcon; |
1426 | oparms.cifs_sb = cifs_sb; | |
1427 | oparms.desired_access = READ_CONTROL; | |
0f060936 | 1428 | oparms.create_options = cifs_create_options(cifs_sb, 0); |
d81b8a40 PS |
1429 | oparms.disposition = FILE_OPEN; |
1430 | oparms.path = path; | |
1431 | oparms.fid = &fid; | |
1432 | oparms.reconnect = false; | |
1433 | ||
1434 | rc = CIFS_open(xid, &oparms, &oplock, NULL); | |
987b21d7 | 1435 | if (!rc) { |
d81b8a40 PS |
1436 | rc = CIFSSMBGetCIFSACL(xid, tcon, fid.netfid, &pntsd, pacllen); |
1437 | CIFSSMBClose(xid, tcon, fid.netfid); | |
b9c7a2bb SF |
1438 | } |
1439 | ||
7ffec372 | 1440 | cifs_put_tlink(tlink); |
6d5786a3 | 1441 | free_xid(xid); |
987b21d7 | 1442 | |
f96637be | 1443 | cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); |
987b21d7 SP |
1444 | if (rc) |
1445 | return ERR_PTR(rc); | |
7505e052 SF |
1446 | return pntsd; |
1447 | } | |
1448 | ||
1bf4072d | 1449 | /* Retrieve an ACL from the server */ |
fbeba8bb | 1450 | struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb, |
1bf4072d | 1451 | struct inode *inode, const char *path, |
3970acf7 | 1452 | u32 *pacllen, u32 info) |
1bf4072d CH |
1453 | { |
1454 | struct cifs_ntsd *pntsd = NULL; | |
1455 | struct cifsFileInfo *open_file = NULL; | |
1456 | ||
1457 | if (inode) | |
6508d904 | 1458 | open_file = find_readable_file(CIFS_I(inode), true); |
1bf4072d CH |
1459 | if (!open_file) |
1460 | return get_cifs_acl_by_path(cifs_sb, path, pacllen); | |
1461 | ||
3970acf7 | 1462 | pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info); |
6ab409b5 | 1463 | cifsFileInfo_put(open_file); |
1bf4072d CH |
1464 | return pntsd; |
1465 | } | |
1466 | ||
a5ff3769 SP |
1467 | /* Set an ACL on the server */ |
1468 | int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, | |
1469 | struct inode *inode, const char *path, int aclflag) | |
b96d31a6 CH |
1470 | { |
1471 | int oplock = 0; | |
6d5786a3 | 1472 | unsigned int xid; |
0f060936 | 1473 | int rc, access_flags; |
96daf2b0 | 1474 | struct cifs_tcon *tcon; |
a5ff3769 | 1475 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
7ffec372 | 1476 | struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); |
d81b8a40 PS |
1477 | struct cifs_fid fid; |
1478 | struct cifs_open_parms oparms; | |
97837582 | 1479 | |
7ffec372 JL |
1480 | if (IS_ERR(tlink)) |
1481 | return PTR_ERR(tlink); | |
1482 | ||
1483 | tcon = tlink_tcon(tlink); | |
6d5786a3 | 1484 | xid = get_xid(); |
97837582 | 1485 | |
a5ff3769 SP |
1486 | if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP) |
1487 | access_flags = WRITE_OWNER; | |
1488 | else | |
1489 | access_flags = WRITE_DAC; | |
1490 | ||
d81b8a40 PS |
1491 | oparms.tcon = tcon; |
1492 | oparms.cifs_sb = cifs_sb; | |
1493 | oparms.desired_access = access_flags; | |
0f060936 | 1494 | oparms.create_options = cifs_create_options(cifs_sb, 0); |
d81b8a40 PS |
1495 | oparms.disposition = FILE_OPEN; |
1496 | oparms.path = path; | |
1497 | oparms.fid = &fid; | |
1498 | oparms.reconnect = false; | |
1499 | ||
1500 | rc = CIFS_open(xid, &oparms, &oplock, NULL); | |
b96d31a6 | 1501 | if (rc) { |
f96637be | 1502 | cifs_dbg(VFS, "Unable to open file to set ACL\n"); |
b96d31a6 | 1503 | goto out; |
97837582 SF |
1504 | } |
1505 | ||
d81b8a40 | 1506 | rc = CIFSSMBSetCIFSACL(xid, tcon, fid.netfid, pnntsd, acllen, aclflag); |
f96637be | 1507 | cifs_dbg(NOISY, "SetCIFSACL rc = %d\n", rc); |
97837582 | 1508 | |
d81b8a40 | 1509 | CIFSSMBClose(xid, tcon, fid.netfid); |
7ffec372 | 1510 | out: |
6d5786a3 | 1511 | free_xid(xid); |
7ffec372 | 1512 | cifs_put_tlink(tlink); |
b96d31a6 CH |
1513 | return rc; |
1514 | } | |
97837582 | 1515 | |
36c7ce4a | 1516 | /* Translate the CIFS ACL (similar to NTFS ACL) for a file into mode bits */ |
987b21d7 | 1517 | int |
0b8f18e3 | 1518 | cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, |
e2f8fbfb SF |
1519 | struct inode *inode, bool mode_from_special_sid, |
1520 | const char *path, const struct cifs_fid *pfid) | |
7505e052 SF |
1521 | { |
1522 | struct cifs_ntsd *pntsd = NULL; | |
1523 | u32 acllen = 0; | |
1524 | int rc = 0; | |
42eacf9e | 1525 | struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); |
ecdcf622 | 1526 | struct smb_version_operations *ops; |
3970acf7 | 1527 | const u32 info = 0; |
7505e052 | 1528 | |
f96637be | 1529 | cifs_dbg(NOISY, "converting ACL to mode for %s\n", path); |
1bf4072d | 1530 | |
42eacf9e SF |
1531 | if (IS_ERR(tlink)) |
1532 | return PTR_ERR(tlink); | |
7505e052 | 1533 | |
ecdcf622 JP |
1534 | ops = tlink_tcon(tlink)->ses->server->ops; |
1535 | ||
1536 | if (pfid && (ops->get_acl_by_fid)) | |
3970acf7 | 1537 | pntsd = ops->get_acl_by_fid(cifs_sb, pfid, &acllen, info); |
ecdcf622 | 1538 | else if (ops->get_acl) |
3970acf7 | 1539 | pntsd = ops->get_acl(cifs_sb, inode, path, &acllen, info); |
42eacf9e SF |
1540 | else { |
1541 | cifs_put_tlink(tlink); | |
1542 | return -EOPNOTSUPP; | |
1543 | } | |
7505e052 | 1544 | /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */ |
987b21d7 SP |
1545 | if (IS_ERR(pntsd)) { |
1546 | rc = PTR_ERR(pntsd); | |
f96637be | 1547 | cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); |
e2f8fbfb SF |
1548 | } else if (mode_from_special_sid) { |
1549 | rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, true); | |
98128572 | 1550 | kfree(pntsd); |
987b21d7 | 1551 | } else { |
e2f8fbfb SF |
1552 | /* get approximated mode from ACL */ |
1553 | rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, false); | |
987b21d7 SP |
1554 | kfree(pntsd); |
1555 | if (rc) | |
f96637be | 1556 | cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc); |
987b21d7 | 1557 | } |
7505e052 | 1558 | |
42eacf9e SF |
1559 | cifs_put_tlink(tlink); |
1560 | ||
987b21d7 | 1561 | return rc; |
b9c7a2bb | 1562 | } |
953f8681 | 1563 | |
7505e052 | 1564 | /* Convert mode bits to an ACL so we can update the ACL on the server */ |
a5ff3769 | 1565 | int |
0f22053e | 1566 | id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, |
8abf2775 | 1567 | kuid_t uid, kgid_t gid) |
953f8681 SF |
1568 | { |
1569 | int rc = 0; | |
a5ff3769 | 1570 | int aclflag = CIFS_ACL_DACL; /* default flag to set */ |
cce246ee | 1571 | __u32 secdesclen = 0; |
f5065508 SP |
1572 | __u32 nsecdesclen = 0; |
1573 | __u32 dacloffset = 0; | |
1574 | struct cifs_acl *dacl_ptr = NULL; | |
97837582 SF |
1575 | struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */ |
1576 | struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ | |
83e3bc23 SF |
1577 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
1578 | struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); | |
ecdcf622 | 1579 | struct smb_version_operations *ops; |
a6603398 | 1580 | bool mode_from_sid, id_from_sid; |
3970acf7 | 1581 | const u32 info = 0; |
83e3bc23 SF |
1582 | |
1583 | if (IS_ERR(tlink)) | |
1584 | return PTR_ERR(tlink); | |
ecdcf622 JP |
1585 | |
1586 | ops = tlink_tcon(tlink)->ses->server->ops; | |
953f8681 | 1587 | |
f96637be | 1588 | cifs_dbg(NOISY, "set ACL from mode for %s\n", path); |
953f8681 SF |
1589 | |
1590 | /* Get the security descriptor */ | |
83e3bc23 | 1591 | |
ecdcf622 | 1592 | if (ops->get_acl == NULL) { |
83e3bc23 SF |
1593 | cifs_put_tlink(tlink); |
1594 | return -EOPNOTSUPP; | |
1595 | } | |
1596 | ||
3970acf7 | 1597 | pntsd = ops->get_acl(cifs_sb, inode, path, &secdesclen, info); |
987b21d7 SP |
1598 | if (IS_ERR(pntsd)) { |
1599 | rc = PTR_ERR(pntsd); | |
f96637be | 1600 | cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); |
83e3bc23 SF |
1601 | cifs_put_tlink(tlink); |
1602 | return rc; | |
c78cd838 | 1603 | } |
7505e052 | 1604 | |
f5065508 SP |
1605 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) |
1606 | mode_from_sid = true; | |
1607 | else | |
1608 | mode_from_sid = false; | |
1609 | ||
1610 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) | |
1611 | id_from_sid = true; | |
1612 | else | |
1613 | id_from_sid = false; | |
1614 | ||
1615 | /* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */ | |
1616 | nsecdesclen = secdesclen; | |
1617 | if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */ | |
1618 | if (mode_from_sid) | |
0c6f4ebf | 1619 | nsecdesclen += 2 * sizeof(struct cifs_ace); |
f5065508 SP |
1620 | else /* cifsacl */ |
1621 | nsecdesclen += 5 * sizeof(struct cifs_ace); | |
bc3e9dd9 SP |
1622 | } else { /* chown */ |
1623 | /* When ownership changes, changes new owner sid length could be different */ | |
1624 | nsecdesclen = sizeof(struct cifs_ntsd) + (sizeof(struct cifs_sid) * 2); | |
1625 | dacloffset = le32_to_cpu(pntsd->dacloffset); | |
1626 | if (dacloffset) { | |
1627 | dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); | |
1628 | if (mode_from_sid) | |
1629 | nsecdesclen += | |
23bda5e6 | 1630 | le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct cifs_ace); |
bc3e9dd9 SP |
1631 | else /* cifsacl */ |
1632 | nsecdesclen += le16_to_cpu(dacl_ptr->size); | |
1633 | } | |
f5065508 SP |
1634 | } |
1635 | ||
c78cd838 JL |
1636 | /* |
1637 | * Add three ACEs for owner, group, everyone getting rid of other ACEs | |
1638 | * as chmod disables ACEs and set the security descriptor. Allocate | |
1639 | * memory for the smb header, set security descriptor request security | |
c45adff7 | 1640 | * descriptor parameters, and security descriptor itself |
c78cd838 | 1641 | */ |
f5065508 SP |
1642 | nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN); |
1643 | pnntsd = kmalloc(nsecdesclen, GFP_KERNEL); | |
c78cd838 | 1644 | if (!pnntsd) { |
c78cd838 | 1645 | kfree(pntsd); |
83e3bc23 | 1646 | cifs_put_tlink(tlink); |
c78cd838 JL |
1647 | return -ENOMEM; |
1648 | } | |
97837582 | 1649 | |
f5065508 | 1650 | rc = build_sec_desc(pntsd, pnntsd, secdesclen, &nsecdesclen, pnmode, uid, gid, |
a6603398 | 1651 | mode_from_sid, id_from_sid, &aclflag); |
97837582 | 1652 | |
f96637be | 1653 | cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); |
97837582 | 1654 | |
ecdcf622 | 1655 | if (ops->set_acl == NULL) |
83e3bc23 SF |
1656 | rc = -EOPNOTSUPP; |
1657 | ||
c78cd838 JL |
1658 | if (!rc) { |
1659 | /* Set the security descriptor */ | |
f5065508 | 1660 | rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag); |
f96637be | 1661 | cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc); |
97837582 | 1662 | } |
83e3bc23 | 1663 | cifs_put_tlink(tlink); |
97837582 | 1664 | |
c78cd838 JL |
1665 | kfree(pnntsd); |
1666 | kfree(pntsd); | |
ef571cad | 1667 | return rc; |
953f8681 | 1668 | } |