Commit | Line | Data |
---|---|---|
e2f34481 NJ |
1 | // SPDX-License-Identifier: LGPL-2.1+ |
2 | /* | |
3 | * Copyright (C) International Business Machines Corp., 2007,2008 | |
4 | * Author(s): Steve French (sfrench@us.ibm.com) | |
5 | * Copyright (C) 2020 Samsung Electronics Co., Ltd. | |
6 | * Author(s): Namjae Jeon <linkinjeon@kernel.org> | |
7 | */ | |
8 | ||
9 | #include <linux/fs.h> | |
10 | #include <linux/slab.h> | |
11 | #include <linux/string.h> | |
12 | ||
13 | #include "smbacl.h" | |
14 | #include "smb_common.h" | |
15 | #include "server.h" | |
16 | #include "misc.h" | |
e2f34481 NJ |
17 | #include "mgmt/share_config.h" |
18 | ||
19 | static const struct smb_sid domain = {1, 4, {0, 0, 0, 0, 0, 5}, | |
20 | {cpu_to_le32(21), cpu_to_le32(1), cpu_to_le32(2), cpu_to_le32(3), | |
21 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | |
22 | ||
23 | /* security id for everyone/world system group */ | |
24 | static const struct smb_sid creator_owner = { | |
25 | 1, 1, {0, 0, 0, 0, 0, 3}, {0} }; | |
26 | /* security id for everyone/world system group */ | |
27 | static const struct smb_sid creator_group = { | |
28 | 1, 1, {0, 0, 0, 0, 0, 3}, {cpu_to_le32(1)} }; | |
29 | ||
30 | /* security id for everyone/world system group */ | |
31 | static const struct smb_sid sid_everyone = { | |
32 | 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; | |
33 | /* security id for Authenticated Users system group */ | |
34 | static const struct smb_sid sid_authusers = { | |
35 | 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; | |
36 | ||
37 | /* S-1-22-1 Unmapped Unix users */ | |
38 | static const struct smb_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, | |
39 | {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | |
40 | ||
41 | /* S-1-22-2 Unmapped Unix groups */ | |
42 | static const struct smb_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, | |
43 | {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | |
44 | ||
45 | /* | |
46 | * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx | |
47 | */ | |
48 | ||
49 | /* S-1-5-88 MS NFS and Apple style UID/GID/mode */ | |
50 | ||
51 | /* S-1-5-88-1 Unix uid */ | |
52 | static const struct smb_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, | |
53 | {cpu_to_le32(88), | |
54 | cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | |
55 | ||
56 | /* S-1-5-88-2 Unix gid */ | |
57 | static const struct smb_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, | |
58 | {cpu_to_le32(88), | |
59 | cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | |
60 | ||
61 | /* S-1-5-88-3 Unix mode */ | |
62 | static const struct smb_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, | |
63 | {cpu_to_le32(88), | |
64 | cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; | |
65 | ||
66 | /* | |
67 | * if the two SIDs (roughly equivalent to a UUID for a user or group) are | |
68 | * the same returns zero, if they do not match returns non-zero. | |
69 | */ | |
64b39f4a | 70 | int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) |
e2f34481 NJ |
71 | { |
72 | int i; | |
73 | int num_subauth, num_sat, num_saw; | |
74 | ||
64b39f4a | 75 | if (!ctsid || !cwsid) |
e2f34481 NJ |
76 | return 1; |
77 | ||
78 | /* compare the revision */ | |
79 | if (ctsid->revision != cwsid->revision) { | |
80 | if (ctsid->revision > cwsid->revision) | |
81 | return 1; | |
82 | else | |
83 | return -1; | |
84 | } | |
85 | ||
86 | /* compare all of the six auth values */ | |
87 | for (i = 0; i < NUM_AUTHS; ++i) { | |
88 | if (ctsid->authority[i] != cwsid->authority[i]) { | |
89 | if (ctsid->authority[i] > cwsid->authority[i]) | |
90 | return 1; | |
91 | else | |
92 | return -1; | |
93 | } | |
94 | } | |
95 | ||
96 | /* compare all of the subauth values if any */ | |
97 | num_sat = ctsid->num_subauth; | |
98 | num_saw = cwsid->num_subauth; | |
99 | num_subauth = num_sat < num_saw ? num_sat : num_saw; | |
100 | if (num_subauth) { | |
101 | for (i = 0; i < num_subauth; ++i) { | |
102 | if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { | |
103 | if (le32_to_cpu(ctsid->sub_auth[i]) > | |
64b39f4a | 104 | le32_to_cpu(cwsid->sub_auth[i])) |
e2f34481 NJ |
105 | return 1; |
106 | else | |
107 | return -1; | |
108 | } | |
109 | } | |
110 | } | |
111 | ||
112 | return 0; /* sids compare/match */ | |
113 | } | |
114 | ||
64b39f4a | 115 | static void smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) |
e2f34481 NJ |
116 | { |
117 | int i; | |
118 | ||
119 | dst->revision = src->revision; | |
120 | dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); | |
121 | for (i = 0; i < NUM_AUTHS; ++i) | |
122 | dst->authority[i] = src->authority[i]; | |
123 | for (i = 0; i < dst->num_subauth; ++i) | |
124 | dst->sub_auth[i] = src->sub_auth[i]; | |
125 | } | |
126 | ||
127 | /* | |
128 | * change posix mode to reflect permissions | |
129 | * pmode is the existing mode (we only want to overwrite part of this | |
130 | * bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 | |
131 | */ | |
132 | static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags, | |
070fb21e | 133 | int type) |
e2f34481 NJ |
134 | { |
135 | __u32 flags = le32_to_cpu(ace_flags); | |
136 | umode_t mode = 0; | |
137 | ||
138 | if (flags & GENERIC_ALL) { | |
139 | mode = 0777; | |
140 | ksmbd_debug(SMB, "all perms\n"); | |
141 | return mode; | |
142 | } | |
143 | ||
64b39f4a | 144 | if ((flags & GENERIC_READ) || (flags & FILE_READ_RIGHTS)) |
e2f34481 | 145 | mode = 0444; |
64b39f4a | 146 | if ((flags & GENERIC_WRITE) || (flags & FILE_WRITE_RIGHTS)) { |
e2f34481 NJ |
147 | mode |= 0222; |
148 | if (S_ISDIR(fattr->cf_mode)) | |
149 | mode |= 0111; | |
150 | } | |
64b39f4a | 151 | if ((flags & GENERIC_EXECUTE) || (flags & FILE_EXEC_RIGHTS)) |
e2f34481 NJ |
152 | mode |= 0111; |
153 | ||
64b39f4a | 154 | if (type == ACCESS_DENIED_ACE_TYPE || type == ACCESS_DENIED_OBJECT_ACE_TYPE) |
e2f34481 NJ |
155 | mode = ~mode; |
156 | ||
157 | ksmbd_debug(SMB, "access flags 0x%x mode now %04o\n", flags, mode); | |
158 | ||
159 | return mode; | |
160 | } | |
161 | ||
162 | /* | |
163 | * Generate access flags to reflect permissions mode is the existing mode. | |
164 | * This function is called for every ACE in the DACL whose SID matches | |
165 | * with either owner or group or everyone. | |
166 | */ | |
167 | static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, | |
070fb21e | 168 | __u32 *pace_flags) |
e2f34481 NJ |
169 | { |
170 | /* reset access mask */ | |
171 | *pace_flags = 0x0; | |
172 | ||
173 | /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ | |
174 | mode &= bits_to_use; | |
175 | ||
176 | /* | |
177 | * check for R/W/X UGO since we do not know whose flags | |
178 | * is this but we have cleared all the bits sans RWX for | |
179 | * either user or group or other as per bits_to_use | |
180 | */ | |
181 | if (mode & 0444) | |
182 | *pace_flags |= SET_FILE_READ_RIGHTS; | |
183 | if (mode & 0222) | |
184 | *pace_flags |= FILE_WRITE_RIGHTS; | |
185 | if (mode & 0111) | |
186 | *pace_flags |= SET_FILE_EXEC_RIGHTS; | |
187 | ||
188 | ksmbd_debug(SMB, "mode: %o, access flags now 0x%x\n", | |
070fb21e | 189 | mode, *pace_flags); |
e2f34481 NJ |
190 | } |
191 | ||
192 | static __u16 fill_ace_for_sid(struct smb_ace *pntace, | |
070fb21e NJ |
193 | const struct smb_sid *psid, int type, int flags, |
194 | umode_t mode, umode_t bits) | |
e2f34481 NJ |
195 | { |
196 | int i; | |
197 | __u16 size = 0; | |
198 | __u32 access_req = 0; | |
199 | ||
200 | pntace->type = type; | |
201 | pntace->flags = flags; | |
202 | mode_to_access_flags(mode, bits, &access_req); | |
203 | if (!access_req) | |
204 | access_req = SET_MINIMUM_RIGHTS; | |
205 | pntace->access_req = cpu_to_le32(access_req); | |
206 | ||
207 | pntace->sid.revision = psid->revision; | |
208 | pntace->sid.num_subauth = psid->num_subauth; | |
209 | for (i = 0; i < NUM_AUTHS; i++) | |
210 | pntace->sid.authority[i] = psid->authority[i]; | |
211 | for (i = 0; i < psid->num_subauth; i++) | |
212 | pntace->sid.sub_auth[i] = psid->sub_auth[i]; | |
213 | ||
214 | size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); | |
215 | pntace->size = cpu_to_le16(size); | |
216 | ||
217 | return size; | |
218 | } | |
219 | ||
220 | void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid) | |
221 | { | |
222 | switch (sidtype) { | |
223 | case SIDOWNER: | |
224 | smb_copy_sid(ssid, &server_conf.domain_sid); | |
225 | break; | |
226 | case SIDUNIX_USER: | |
227 | smb_copy_sid(ssid, &sid_unix_users); | |
228 | break; | |
229 | case SIDUNIX_GROUP: | |
230 | smb_copy_sid(ssid, &sid_unix_groups); | |
231 | break; | |
232 | case SIDCREATOR_OWNER: | |
233 | smb_copy_sid(ssid, &creator_owner); | |
234 | return; | |
235 | case SIDCREATOR_GROUP: | |
236 | smb_copy_sid(ssid, &creator_group); | |
237 | return; | |
238 | case SIDNFS_USER: | |
239 | smb_copy_sid(ssid, &sid_unix_NFS_users); | |
240 | break; | |
241 | case SIDNFS_GROUP: | |
242 | smb_copy_sid(ssid, &sid_unix_NFS_groups); | |
243 | break; | |
244 | case SIDNFS_MODE: | |
245 | smb_copy_sid(ssid, &sid_unix_NFS_mode); | |
246 | break; | |
247 | default: | |
248 | return; | |
249 | } | |
250 | ||
251 | /* RID */ | |
252 | ssid->sub_auth[ssid->num_subauth] = cpu_to_le32(cid); | |
253 | ssid->num_subauth++; | |
254 | } | |
255 | ||
af34983e HL |
256 | static int sid_to_id(struct user_namespace *user_ns, |
257 | struct smb_sid *psid, uint sidtype, | |
070fb21e | 258 | struct smb_fattr *fattr) |
e2f34481 NJ |
259 | { |
260 | int rc = -EINVAL; | |
261 | ||
262 | /* | |
263 | * If we have too many subauthorities, then something is really wrong. | |
264 | * Just return an error. | |
265 | */ | |
266 | if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { | |
bde1694a NJ |
267 | pr_err("%s: %u subauthorities is too many!\n", |
268 | __func__, psid->num_subauth); | |
e2f34481 NJ |
269 | return -EIO; |
270 | } | |
271 | ||
272 | if (sidtype == SIDOWNER) { | |
273 | kuid_t uid; | |
274 | uid_t id; | |
275 | ||
276 | id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); | |
277 | if (id > 0) { | |
af34983e HL |
278 | uid = make_kuid(user_ns, id); |
279 | if (uid_valid(uid) && kuid_has_mapping(user_ns, uid)) { | |
e2f34481 NJ |
280 | fattr->cf_uid = uid; |
281 | rc = 0; | |
282 | } | |
283 | } | |
284 | } else { | |
285 | kgid_t gid; | |
286 | gid_t id; | |
287 | ||
288 | id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); | |
289 | if (id > 0) { | |
af34983e HL |
290 | gid = make_kgid(user_ns, id); |
291 | if (gid_valid(gid) && kgid_has_mapping(user_ns, gid)) { | |
e2f34481 NJ |
292 | fattr->cf_gid = gid; |
293 | rc = 0; | |
294 | } | |
295 | } | |
296 | } | |
297 | ||
298 | return rc; | |
299 | } | |
300 | ||
301 | void posix_state_to_acl(struct posix_acl_state *state, | |
070fb21e | 302 | struct posix_acl_entry *pace) |
e2f34481 NJ |
303 | { |
304 | int i; | |
305 | ||
306 | pace->e_tag = ACL_USER_OBJ; | |
307 | pace->e_perm = state->owner.allow; | |
308 | for (i = 0; i < state->users->n; i++) { | |
309 | pace++; | |
310 | pace->e_tag = ACL_USER; | |
311 | pace->e_uid = state->users->aces[i].uid; | |
312 | pace->e_perm = state->users->aces[i].perms.allow; | |
313 | } | |
314 | ||
315 | pace++; | |
316 | pace->e_tag = ACL_GROUP_OBJ; | |
317 | pace->e_perm = state->group.allow; | |
318 | ||
319 | for (i = 0; i < state->groups->n; i++) { | |
320 | pace++; | |
321 | pace->e_tag = ACL_GROUP; | |
322 | pace->e_gid = state->groups->aces[i].gid; | |
323 | pace->e_perm = state->groups->aces[i].perms.allow; | |
324 | } | |
325 | ||
326 | if (state->users->n || state->groups->n) { | |
327 | pace++; | |
328 | pace->e_tag = ACL_MASK; | |
329 | pace->e_perm = state->mask.allow; | |
330 | } | |
331 | ||
332 | pace++; | |
333 | pace->e_tag = ACL_OTHER; | |
334 | pace->e_perm = state->other.allow; | |
335 | } | |
336 | ||
337 | int init_acl_state(struct posix_acl_state *state, int cnt) | |
338 | { | |
339 | int alloc; | |
340 | ||
341 | memset(state, 0, sizeof(struct posix_acl_state)); | |
342 | /* | |
343 | * In the worst case, each individual acl could be for a distinct | |
344 | * named user or group, but we don't know which, so we allocate | |
345 | * enough space for either: | |
346 | */ | |
347 | alloc = sizeof(struct posix_ace_state_array) | |
64b39f4a | 348 | + cnt * sizeof(struct posix_user_ace_state); |
e2f34481 NJ |
349 | state->users = kzalloc(alloc, GFP_KERNEL); |
350 | if (!state->users) | |
351 | return -ENOMEM; | |
352 | state->groups = kzalloc(alloc, GFP_KERNEL); | |
353 | if (!state->groups) { | |
354 | kfree(state->users); | |
355 | return -ENOMEM; | |
356 | } | |
357 | return 0; | |
358 | } | |
359 | ||
360 | void free_acl_state(struct posix_acl_state *state) | |
361 | { | |
362 | kfree(state->users); | |
363 | kfree(state->groups); | |
364 | } | |
365 | ||
af34983e HL |
366 | static void parse_dacl(struct user_namespace *user_ns, |
367 | struct smb_acl *pdacl, char *end_of_acl, | |
070fb21e NJ |
368 | struct smb_sid *pownersid, struct smb_sid *pgrpsid, |
369 | struct smb_fattr *fattr) | |
e2f34481 NJ |
370 | { |
371 | int i, ret; | |
372 | int num_aces = 0; | |
373 | int acl_size; | |
374 | char *acl_base; | |
375 | struct smb_ace **ppace; | |
376 | struct posix_acl_entry *cf_pace, *cf_pdace; | |
377 | struct posix_acl_state acl_state, default_acl_state; | |
378 | umode_t mode = 0, acl_mode; | |
379 | bool owner_found = false, group_found = false, others_found = false; | |
380 | ||
381 | if (!pdacl) | |
382 | return; | |
383 | ||
384 | /* validate that we do not go past end of acl */ | |
548e9ad3 NJ |
385 | if (end_of_acl <= (char *)pdacl || |
386 | end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { | |
bde1694a | 387 | pr_err("ACL too small to parse DACL\n"); |
e2f34481 NJ |
388 | return; |
389 | } | |
390 | ||
391 | ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", | |
070fb21e NJ |
392 | le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), |
393 | le32_to_cpu(pdacl->num_aces)); | |
e2f34481 NJ |
394 | |
395 | acl_base = (char *)pdacl; | |
396 | acl_size = sizeof(struct smb_acl); | |
397 | ||
398 | num_aces = le32_to_cpu(pdacl->num_aces); | |
399 | if (num_aces <= 0) | |
400 | return; | |
401 | ||
402 | if (num_aces > ULONG_MAX / sizeof(struct smb_ace *)) | |
403 | return; | |
404 | ||
070fb21e | 405 | ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); |
e2f34481 NJ |
406 | if (!ppace) |
407 | return; | |
408 | ||
409 | ret = init_acl_state(&acl_state, num_aces); | |
410 | if (ret) | |
411 | return; | |
412 | ret = init_acl_state(&default_acl_state, num_aces); | |
413 | if (ret) { | |
414 | free_acl_state(&acl_state); | |
415 | return; | |
416 | } | |
417 | ||
418 | /* | |
419 | * reset rwx permissions for user/group/other. | |
420 | * Also, if num_aces is 0 i.e. DACL has no ACEs, | |
421 | * user/group/other have no permissions | |
422 | */ | |
423 | for (i = 0; i < num_aces; ++i) { | |
64b39f4a | 424 | ppace[i] = (struct smb_ace *)(acl_base + acl_size); |
e2f34481 NJ |
425 | acl_base = (char *)ppace[i]; |
426 | acl_size = le16_to_cpu(ppace[i]->size); | |
427 | ppace[i]->access_req = | |
428 | smb_map_generic_desired_access(ppace[i]->access_req); | |
429 | ||
64b39f4a | 430 | if (!(compare_sids(&ppace[i]->sid, &sid_unix_NFS_mode))) { |
e2f34481 NJ |
431 | fattr->cf_mode = |
432 | le32_to_cpu(ppace[i]->sid.sub_auth[2]); | |
433 | break; | |
64b39f4a | 434 | } else if (!compare_sids(&ppace[i]->sid, pownersid)) { |
e2f34481 | 435 | acl_mode = access_flags_to_mode(fattr, |
070fb21e NJ |
436 | ppace[i]->access_req, |
437 | ppace[i]->type); | |
e2f34481 NJ |
438 | acl_mode &= 0700; |
439 | ||
440 | if (!owner_found) { | |
441 | mode &= ~(0700); | |
442 | mode |= acl_mode; | |
443 | } | |
444 | owner_found = true; | |
64b39f4a NJ |
445 | } else if (!compare_sids(&ppace[i]->sid, pgrpsid) || |
446 | ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == | |
447 | DOMAIN_USER_RID_LE) { | |
e2f34481 | 448 | acl_mode = access_flags_to_mode(fattr, |
070fb21e NJ |
449 | ppace[i]->access_req, |
450 | ppace[i]->type); | |
e2f34481 NJ |
451 | acl_mode &= 0070; |
452 | if (!group_found) { | |
453 | mode &= ~(0070); | |
454 | mode |= acl_mode; | |
455 | } | |
456 | group_found = true; | |
64b39f4a | 457 | } else if (!compare_sids(&ppace[i]->sid, &sid_everyone)) { |
e2f34481 | 458 | acl_mode = access_flags_to_mode(fattr, |
070fb21e NJ |
459 | ppace[i]->access_req, |
460 | ppace[i]->type); | |
e2f34481 NJ |
461 | acl_mode &= 0007; |
462 | if (!others_found) { | |
463 | mode &= ~(0007); | |
464 | mode |= acl_mode; | |
465 | } | |
466 | others_found = true; | |
64b39f4a | 467 | } else if (!compare_sids(&ppace[i]->sid, &creator_owner)) { |
e2f34481 | 468 | continue; |
64b39f4a | 469 | } else if (!compare_sids(&ppace[i]->sid, &creator_group)) { |
e2f34481 | 470 | continue; |
64b39f4a | 471 | } else if (!compare_sids(&ppace[i]->sid, &sid_authusers)) { |
e2f34481 | 472 | continue; |
64b39f4a | 473 | } else { |
e2f34481 NJ |
474 | struct smb_fattr temp_fattr; |
475 | ||
476 | acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, | |
070fb21e | 477 | ppace[i]->type); |
e2f34481 | 478 | temp_fattr.cf_uid = INVALID_UID; |
af34983e | 479 | ret = sid_to_id(user_ns, &ppace[i]->sid, SIDOWNER, &temp_fattr); |
e2f34481 | 480 | if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { |
bde1694a NJ |
481 | pr_err("%s: Error %d mapping Owner SID to uid\n", |
482 | __func__, ret); | |
e2f34481 NJ |
483 | continue; |
484 | } | |
485 | ||
486 | acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; | |
487 | acl_state.users->aces[acl_state.users->n].uid = | |
488 | temp_fattr.cf_uid; | |
489 | acl_state.users->aces[acl_state.users->n++].perms.allow = | |
490 | ((acl_mode & 0700) >> 6) | 0004; | |
491 | default_acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; | |
492 | default_acl_state.users->aces[default_acl_state.users->n].uid = | |
493 | temp_fattr.cf_uid; | |
494 | default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = | |
495 | ((acl_mode & 0700) >> 6) | 0004; | |
496 | } | |
497 | } | |
498 | kfree(ppace); | |
499 | ||
500 | if (owner_found) { | |
501 | /* The owner must be set to at least read-only. */ | |
502 | acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; | |
503 | acl_state.users->aces[acl_state.users->n].uid = fattr->cf_uid; | |
504 | acl_state.users->aces[acl_state.users->n++].perms.allow = | |
505 | ((mode & 0700) >> 6) | 0004; | |
506 | default_acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; | |
507 | default_acl_state.users->aces[default_acl_state.users->n].uid = | |
508 | fattr->cf_uid; | |
509 | default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = | |
510 | ((mode & 0700) >> 6) | 0004; | |
511 | } | |
512 | ||
513 | if (group_found) { | |
514 | acl_state.group.allow = (mode & 0070) >> 3; | |
515 | acl_state.groups->aces[acl_state.groups->n].gid = | |
516 | fattr->cf_gid; | |
517 | acl_state.groups->aces[acl_state.groups->n++].perms.allow = | |
518 | (mode & 0070) >> 3; | |
86df49e1 | 519 | default_acl_state.group.allow = (mode & 0070) >> 3; |
e2f34481 NJ |
520 | default_acl_state.groups->aces[default_acl_state.groups->n].gid = |
521 | fattr->cf_gid; | |
522 | default_acl_state.groups->aces[default_acl_state.groups->n++].perms.allow = | |
523 | (mode & 0070) >> 3; | |
524 | } | |
525 | ||
526 | if (others_found) { | |
527 | fattr->cf_mode &= ~(0007); | |
528 | fattr->cf_mode |= mode & 0007; | |
529 | ||
530 | acl_state.other.allow = mode & 0007; | |
531 | default_acl_state.other.allow = mode & 0007; | |
532 | } | |
533 | ||
534 | if (acl_state.users->n || acl_state.groups->n) { | |
535 | acl_state.mask.allow = 0x07; | |
67d1c432 | 536 | fattr->cf_acls = posix_acl_alloc(acl_state.users->n + |
e2f34481 NJ |
537 | acl_state.groups->n + 4, GFP_KERNEL); |
538 | if (fattr->cf_acls) { | |
539 | cf_pace = fattr->cf_acls->a_entries; | |
540 | posix_state_to_acl(&acl_state, cf_pace); | |
541 | } | |
542 | } | |
543 | ||
544 | if (default_acl_state.users->n || default_acl_state.groups->n) { | |
545 | default_acl_state.mask.allow = 0x07; | |
546 | fattr->cf_dacls = | |
67d1c432 | 547 | posix_acl_alloc(default_acl_state.users->n + |
e2f34481 NJ |
548 | default_acl_state.groups->n + 4, GFP_KERNEL); |
549 | if (fattr->cf_dacls) { | |
550 | cf_pdace = fattr->cf_dacls->a_entries; | |
551 | posix_state_to_acl(&default_acl_state, cf_pdace); | |
552 | } | |
553 | } | |
554 | free_acl_state(&acl_state); | |
555 | free_acl_state(&default_acl_state); | |
556 | } | |
557 | ||
af34983e HL |
558 | static void set_posix_acl_entries_dacl(struct user_namespace *user_ns, |
559 | struct smb_ace *pndace, | |
070fb21e NJ |
560 | struct smb_fattr *fattr, u32 *num_aces, |
561 | u16 *size, u32 nt_aces_num) | |
e2f34481 NJ |
562 | { |
563 | struct posix_acl_entry *pace; | |
564 | struct smb_sid *sid; | |
565 | struct smb_ace *ntace; | |
566 | int i, j; | |
567 | ||
568 | if (!fattr->cf_acls) | |
569 | goto posix_default_acl; | |
570 | ||
571 | pace = fattr->cf_acls->a_entries; | |
572 | for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) { | |
573 | int flags = 0; | |
574 | ||
575 | sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); | |
576 | if (!sid) | |
577 | break; | |
578 | ||
579 | if (pace->e_tag == ACL_USER) { | |
580 | uid_t uid; | |
581 | unsigned int sid_type = SIDOWNER; | |
582 | ||
af34983e | 583 | uid = from_kuid(user_ns, pace->e_uid); |
e2f34481 NJ |
584 | if (!uid) |
585 | sid_type = SIDUNIX_USER; | |
586 | id_to_sid(uid, sid_type, sid); | |
587 | } else if (pace->e_tag == ACL_GROUP) { | |
588 | gid_t gid; | |
589 | ||
af34983e | 590 | gid = from_kgid(user_ns, pace->e_gid); |
e2f34481 NJ |
591 | id_to_sid(gid, SIDUNIX_GROUP, sid); |
592 | } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) { | |
593 | smb_copy_sid(sid, &sid_everyone); | |
594 | } else { | |
595 | kfree(sid); | |
596 | continue; | |
597 | } | |
598 | ntace = pndace; | |
599 | for (j = 0; j < nt_aces_num; j++) { | |
600 | if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] == | |
601 | sid->sub_auth[sid->num_subauth - 1]) | |
602 | goto pass_same_sid; | |
603 | ntace = (struct smb_ace *)((char *)ntace + | |
604 | le16_to_cpu(ntace->size)); | |
605 | } | |
606 | ||
607 | if (S_ISDIR(fattr->cf_mode) && pace->e_tag == ACL_OTHER) | |
608 | flags = 0x03; | |
609 | ||
64b39f4a | 610 | ntace = (struct smb_ace *)((char *)pndace + *size); |
e2f34481 NJ |
611 | *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, |
612 | pace->e_perm, 0777); | |
613 | (*num_aces)++; | |
614 | if (pace->e_tag == ACL_USER) | |
615 | ntace->access_req |= | |
616 | FILE_DELETE_LE | FILE_DELETE_CHILD_LE; | |
617 | ||
618 | if (S_ISDIR(fattr->cf_mode) && | |
64b39f4a NJ |
619 | (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { |
620 | ntace = (struct smb_ace *)((char *)pndace + *size); | |
e2f34481 NJ |
621 | *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, |
622 | 0x03, pace->e_perm, 0777); | |
623 | (*num_aces)++; | |
624 | if (pace->e_tag == ACL_USER) | |
625 | ntace->access_req |= | |
626 | FILE_DELETE_LE | FILE_DELETE_CHILD_LE; | |
627 | } | |
628 | ||
629 | pass_same_sid: | |
630 | kfree(sid); | |
631 | } | |
632 | ||
633 | if (nt_aces_num) | |
634 | return; | |
635 | ||
636 | posix_default_acl: | |
637 | if (!fattr->cf_dacls) | |
638 | return; | |
639 | ||
640 | pace = fattr->cf_dacls->a_entries; | |
641 | for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) { | |
642 | sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); | |
643 | if (!sid) | |
644 | break; | |
645 | ||
646 | if (pace->e_tag == ACL_USER) { | |
647 | uid_t uid; | |
648 | ||
af34983e | 649 | uid = from_kuid(user_ns, pace->e_uid); |
e2f34481 NJ |
650 | id_to_sid(uid, SIDCREATOR_OWNER, sid); |
651 | } else if (pace->e_tag == ACL_GROUP) { | |
652 | gid_t gid; | |
653 | ||
af34983e | 654 | gid = from_kgid(user_ns, pace->e_gid); |
e2f34481 NJ |
655 | id_to_sid(gid, SIDCREATOR_GROUP, sid); |
656 | } else { | |
657 | kfree(sid); | |
658 | continue; | |
659 | } | |
660 | ||
64b39f4a | 661 | ntace = (struct smb_ace *)((char *)pndace + *size); |
e2f34481 NJ |
662 | *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, |
663 | pace->e_perm, 0777); | |
664 | (*num_aces)++; | |
665 | if (pace->e_tag == ACL_USER) | |
666 | ntace->access_req |= | |
667 | FILE_DELETE_LE | FILE_DELETE_CHILD_LE; | |
668 | kfree(sid); | |
669 | } | |
670 | } | |
671 | ||
af34983e HL |
672 | static void set_ntacl_dacl(struct user_namespace *user_ns, |
673 | struct smb_acl *pndacl, | |
674 | struct smb_acl *nt_dacl, | |
070fb21e NJ |
675 | const struct smb_sid *pownersid, |
676 | const struct smb_sid *pgrpsid, | |
677 | struct smb_fattr *fattr) | |
e2f34481 NJ |
678 | { |
679 | struct smb_ace *ntace, *pndace; | |
680 | int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; | |
681 | unsigned short size = 0; | |
682 | int i; | |
683 | ||
684 | pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); | |
685 | if (nt_num_aces) { | |
686 | ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl)); | |
687 | for (i = 0; i < nt_num_aces; i++) { | |
688 | memcpy((char *)pndace + size, ntace, le16_to_cpu(ntace->size)); | |
689 | size += le16_to_cpu(ntace->size); | |
690 | ntace = (struct smb_ace *)((char *)ntace + le16_to_cpu(ntace->size)); | |
691 | num_aces++; | |
692 | } | |
693 | } | |
694 | ||
af34983e HL |
695 | set_posix_acl_entries_dacl(user_ns, pndace, fattr, |
696 | &num_aces, &size, nt_num_aces); | |
e2f34481 NJ |
697 | pndacl->num_aces = cpu_to_le32(num_aces); |
698 | pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); | |
699 | } | |
700 | ||
af34983e HL |
701 | static void set_mode_dacl(struct user_namespace *user_ns, |
702 | struct smb_acl *pndacl, struct smb_fattr *fattr) | |
e2f34481 NJ |
703 | { |
704 | struct smb_ace *pace, *pndace; | |
705 | u32 num_aces = 0; | |
706 | u16 size = 0, ace_size = 0; | |
707 | uid_t uid; | |
708 | const struct smb_sid *sid; | |
709 | ||
710 | pace = pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); | |
711 | ||
712 | if (fattr->cf_acls) { | |
af34983e HL |
713 | set_posix_acl_entries_dacl(user_ns, pndace, fattr, |
714 | &num_aces, &size, num_aces); | |
e2f34481 NJ |
715 | goto out; |
716 | } | |
717 | ||
718 | /* owner RID */ | |
af34983e | 719 | uid = from_kuid(user_ns, fattr->cf_uid); |
e2f34481 NJ |
720 | if (uid) |
721 | sid = &server_conf.domain_sid; | |
722 | else | |
723 | sid = &sid_unix_users; | |
724 | ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0, | |
070fb21e | 725 | fattr->cf_mode, 0700); |
e2f34481 NJ |
726 | pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); |
727 | pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; | |
728 | pace->size = cpu_to_le16(ace_size + 4); | |
729 | size += le16_to_cpu(pace->size); | |
730 | pace = (struct smb_ace *)((char *)pndace + size); | |
731 | ||
732 | /* Group RID */ | |
733 | ace_size = fill_ace_for_sid(pace, &sid_unix_groups, | |
070fb21e | 734 | ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); |
e2f34481 | 735 | pace->sid.sub_auth[pace->sid.num_subauth++] = |
af34983e | 736 | cpu_to_le32(from_kgid(user_ns, fattr->cf_gid)); |
e2f34481 NJ |
737 | pace->size = cpu_to_le16(ace_size + 4); |
738 | size += le16_to_cpu(pace->size); | |
739 | pace = (struct smb_ace *)((char *)pndace + size); | |
740 | num_aces = 3; | |
741 | ||
742 | if (S_ISDIR(fattr->cf_mode)) { | |
743 | pace = (struct smb_ace *)((char *)pndace + size); | |
744 | ||
745 | /* creator owner */ | |
746 | size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED, | |
070fb21e | 747 | 0x0b, fattr->cf_mode, 0700); |
e2f34481 NJ |
748 | pace->access_req |= FILE_DELETE_LE | FILE_DELETE_CHILD_LE; |
749 | pace = (struct smb_ace *)((char *)pndace + size); | |
750 | ||
751 | /* creator group */ | |
752 | size += fill_ace_for_sid(pace, &creator_group, ACCESS_ALLOWED, | |
070fb21e | 753 | 0x0b, fattr->cf_mode, 0070); |
e2f34481 NJ |
754 | pace = (struct smb_ace *)((char *)pndace + size); |
755 | num_aces = 5; | |
756 | } | |
757 | ||
758 | /* other */ | |
759 | size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0, | |
070fb21e | 760 | fattr->cf_mode, 0007); |
e2f34481 NJ |
761 | |
762 | out: | |
763 | pndacl->num_aces = cpu_to_le32(num_aces); | |
764 | pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); | |
765 | } | |
766 | ||
767 | static int parse_sid(struct smb_sid *psid, char *end_of_acl) | |
768 | { | |
769 | /* | |
770 | * validate that we do not go past end of ACL - sid must be at least 8 | |
771 | * bytes long (assuming no sub-auths - e.g. the null SID | |
772 | */ | |
773 | if (end_of_acl < (char *)psid + 8) { | |
bde1694a | 774 | pr_err("ACL too small to parse SID %p\n", psid); |
e2f34481 NJ |
775 | return -EINVAL; |
776 | } | |
777 | ||
778 | return 0; | |
779 | } | |
780 | ||
781 | /* Convert CIFS ACL to POSIX form */ | |
af34983e HL |
782 | int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, |
783 | int acl_len, struct smb_fattr *fattr) | |
e2f34481 NJ |
784 | { |
785 | int rc = 0; | |
786 | struct smb_sid *owner_sid_ptr, *group_sid_ptr; | |
787 | struct smb_acl *dacl_ptr; /* no need for SACL ptr */ | |
788 | char *end_of_acl = ((char *)pntsd) + acl_len; | |
789 | __u32 dacloffset; | |
548e9ad3 | 790 | int pntsd_type; |
e2f34481 | 791 | |
64b39f4a | 792 | if (!pntsd) |
e2f34481 NJ |
793 | return -EIO; |
794 | ||
795 | owner_sid_ptr = (struct smb_sid *)((char *)pntsd + | |
796 | le32_to_cpu(pntsd->osidoffset)); | |
797 | group_sid_ptr = (struct smb_sid *)((char *)pntsd + | |
798 | le32_to_cpu(pntsd->gsidoffset)); | |
799 | dacloffset = le32_to_cpu(pntsd->dacloffset); | |
800 | dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); | |
801 | ksmbd_debug(SMB, | |
070fb21e NJ |
802 | "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", |
803 | pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), | |
804 | le32_to_cpu(pntsd->gsidoffset), | |
805 | le32_to_cpu(pntsd->sacloffset), dacloffset); | |
e2f34481 | 806 | |
e2f34481 | 807 | pntsd_type = le16_to_cpu(pntsd->type); |
e2f34481 NJ |
808 | if (!(pntsd_type & DACL_PRESENT)) { |
809 | ksmbd_debug(SMB, "DACL_PRESENT in DACL type is not set\n"); | |
810 | return rc; | |
811 | } | |
812 | ||
813 | pntsd->type = cpu_to_le16(DACL_PRESENT); | |
814 | ||
815 | if (pntsd->osidoffset) { | |
816 | rc = parse_sid(owner_sid_ptr, end_of_acl); | |
817 | if (rc) { | |
bde1694a | 818 | pr_err("%s: Error %d parsing Owner SID\n", __func__, rc); |
e2f34481 NJ |
819 | return rc; |
820 | } | |
821 | ||
af34983e | 822 | rc = sid_to_id(user_ns, owner_sid_ptr, SIDOWNER, fattr); |
e2f34481 | 823 | if (rc) { |
bde1694a NJ |
824 | pr_err("%s: Error %d mapping Owner SID to uid\n", |
825 | __func__, rc); | |
e2f34481 NJ |
826 | owner_sid_ptr = NULL; |
827 | } | |
828 | } | |
829 | ||
830 | if (pntsd->gsidoffset) { | |
831 | rc = parse_sid(group_sid_ptr, end_of_acl); | |
832 | if (rc) { | |
bde1694a NJ |
833 | pr_err("%s: Error %d mapping Owner SID to gid\n", |
834 | __func__, rc); | |
e2f34481 NJ |
835 | return rc; |
836 | } | |
af34983e | 837 | rc = sid_to_id(user_ns, group_sid_ptr, SIDUNIX_GROUP, fattr); |
e2f34481 | 838 | if (rc) { |
bde1694a NJ |
839 | pr_err("%s: Error %d mapping Group SID to gid\n", |
840 | __func__, rc); | |
e2f34481 NJ |
841 | group_sid_ptr = NULL; |
842 | } | |
843 | } | |
844 | ||
070fb21e | 845 | if ((pntsd_type & (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) == |
e2f34481 NJ |
846 | (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) |
847 | pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); | |
848 | if (pntsd_type & DACL_PROTECTED) | |
849 | pntsd->type |= cpu_to_le16(DACL_PROTECTED); | |
850 | ||
851 | if (dacloffset) { | |
af34983e HL |
852 | parse_dacl(user_ns, dacl_ptr, end_of_acl, |
853 | owner_sid_ptr, group_sid_ptr, fattr); | |
e2f34481 NJ |
854 | } |
855 | ||
856 | return 0; | |
857 | } | |
858 | ||
859 | /* Convert permission bits from mode to equivalent CIFS ACL */ | |
af34983e HL |
860 | int build_sec_desc(struct user_namespace *user_ns, |
861 | struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, | |
070fb21e NJ |
862 | int addition_info, __u32 *secdesclen, |
863 | struct smb_fattr *fattr) | |
e2f34481 NJ |
864 | { |
865 | int rc = 0; | |
866 | __u32 offset; | |
867 | struct smb_sid *owner_sid_ptr, *group_sid_ptr; | |
868 | struct smb_sid *nowner_sid_ptr, *ngroup_sid_ptr; | |
869 | struct smb_acl *dacl_ptr = NULL; /* no need for SACL ptr */ | |
870 | uid_t uid; | |
871 | gid_t gid; | |
872 | unsigned int sid_type = SIDOWNER; | |
873 | ||
874 | nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); | |
875 | if (!nowner_sid_ptr) | |
876 | return -ENOMEM; | |
877 | ||
af34983e | 878 | uid = from_kuid(user_ns, fattr->cf_uid); |
e2f34481 NJ |
879 | if (!uid) |
880 | sid_type = SIDUNIX_USER; | |
881 | id_to_sid(uid, sid_type, nowner_sid_ptr); | |
882 | ||
883 | ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); | |
884 | if (!ngroup_sid_ptr) { | |
885 | kfree(nowner_sid_ptr); | |
886 | return -ENOMEM; | |
887 | } | |
888 | ||
af34983e | 889 | gid = from_kgid(user_ns, fattr->cf_gid); |
e2f34481 NJ |
890 | id_to_sid(gid, SIDUNIX_GROUP, ngroup_sid_ptr); |
891 | ||
892 | offset = sizeof(struct smb_ntsd); | |
893 | pntsd->sacloffset = 0; | |
894 | pntsd->revision = cpu_to_le16(1); | |
895 | pntsd->type = cpu_to_le16(SELF_RELATIVE); | |
896 | if (ppntsd) | |
897 | pntsd->type |= ppntsd->type; | |
898 | ||
899 | if (addition_info & OWNER_SECINFO) { | |
900 | pntsd->osidoffset = cpu_to_le32(offset); | |
901 | owner_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); | |
902 | smb_copy_sid(owner_sid_ptr, nowner_sid_ptr); | |
903 | offset += 1 + 1 + 6 + (nowner_sid_ptr->num_subauth * 4); | |
904 | } | |
905 | ||
906 | if (addition_info & GROUP_SECINFO) { | |
907 | pntsd->gsidoffset = cpu_to_le32(offset); | |
908 | group_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); | |
909 | smb_copy_sid(group_sid_ptr, ngroup_sid_ptr); | |
910 | offset += 1 + 1 + 6 + (ngroup_sid_ptr->num_subauth * 4); | |
911 | } | |
912 | ||
913 | if (addition_info & DACL_SECINFO) { | |
914 | pntsd->type |= cpu_to_le16(DACL_PRESENT); | |
915 | dacl_ptr = (struct smb_acl *)((char *)pntsd + offset); | |
916 | dacl_ptr->revision = cpu_to_le16(2); | |
917 | dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl)); | |
918 | dacl_ptr->num_aces = 0; | |
919 | ||
64b39f4a | 920 | if (!ppntsd) { |
af34983e | 921 | set_mode_dacl(user_ns, dacl_ptr, fattr); |
64b39f4a | 922 | } else if (!ppntsd->dacloffset) { |
e2f34481 | 923 | goto out; |
64b39f4a | 924 | } else { |
e2f34481 NJ |
925 | struct smb_acl *ppdacl_ptr; |
926 | ||
927 | ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + | |
928 | le32_to_cpu(ppntsd->dacloffset)); | |
af34983e HL |
929 | set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr, |
930 | nowner_sid_ptr, ngroup_sid_ptr, fattr); | |
e2f34481 NJ |
931 | } |
932 | pntsd->dacloffset = cpu_to_le32(offset); | |
933 | offset += le16_to_cpu(dacl_ptr->size); | |
934 | } | |
935 | ||
936 | out: | |
937 | kfree(nowner_sid_ptr); | |
938 | kfree(ngroup_sid_ptr); | |
939 | *secdesclen = offset; | |
940 | return rc; | |
941 | } | |
942 | ||
943 | static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, | |
070fb21e | 944 | u8 flags, __le32 access_req) |
e2f34481 NJ |
945 | { |
946 | ace->type = type; | |
947 | ace->flags = flags; | |
948 | ace->access_req = access_req; | |
949 | smb_copy_sid(&ace->sid, sid); | |
950 | ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4)); | |
951 | } | |
952 | ||
ef24c962 HL |
953 | int smb_inherit_dacl(struct ksmbd_conn *conn, |
954 | struct path *path, | |
070fb21e | 955 | unsigned int uid, unsigned int gid) |
e2f34481 NJ |
956 | { |
957 | const struct smb_sid *psid, *creator = NULL; | |
958 | struct smb_ace *parent_aces, *aces; | |
959 | struct smb_acl *parent_pdacl; | |
960 | struct smb_ntsd *parent_pntsd = NULL; | |
961 | struct smb_sid owner_sid, group_sid; | |
ef24c962 | 962 | struct dentry *parent = path->dentry->d_parent; |
465d7204 | 963 | struct user_namespace *user_ns = mnt_user_ns(path->mnt); |
e2f34481 | 964 | int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0; |
a9071e3c | 965 | int rc = 0, num_aces, dacloffset, pntsd_type, acl_len; |
e2f34481 | 966 | char *aces_base; |
ef24c962 | 967 | bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); |
e2f34481 | 968 | |
465d7204 | 969 | acl_len = ksmbd_vfs_get_sd_xattr(conn, user_ns, |
af34983e | 970 | parent, &parent_pntsd); |
e2f34481 | 971 | if (acl_len <= 0) |
a9071e3c | 972 | return -ENOENT; |
e2f34481 | 973 | dacloffset = le32_to_cpu(parent_pntsd->dacloffset); |
a9071e3c NJ |
974 | if (!dacloffset) { |
975 | rc = -EINVAL; | |
976 | goto free_parent_pntsd; | |
977 | } | |
e2f34481 NJ |
978 | |
979 | parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); | |
980 | num_aces = le32_to_cpu(parent_pdacl->num_aces); | |
981 | pntsd_type = le16_to_cpu(parent_pntsd->type); | |
982 | ||
983 | aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); | |
a9071e3c NJ |
984 | if (!aces_base) { |
985 | rc = -ENOMEM; | |
986 | goto free_parent_pntsd; | |
987 | } | |
e2f34481 NJ |
988 | |
989 | aces = (struct smb_ace *)aces_base; | |
990 | parent_aces = (struct smb_ace *)((char *)parent_pdacl + | |
991 | sizeof(struct smb_acl)); | |
992 | ||
993 | if (pntsd_type & DACL_AUTO_INHERITED) | |
994 | inherited_flags = INHERITED_ACE; | |
995 | ||
996 | for (i = 0; i < num_aces; i++) { | |
997 | flags = parent_aces->flags; | |
998 | if (!smb_inherit_flags(flags, is_dir)) | |
999 | goto pass; | |
1000 | if (is_dir) { | |
1001 | flags &= ~(INHERIT_ONLY_ACE | INHERITED_ACE); | |
1002 | if (!(flags & CONTAINER_INHERIT_ACE)) | |
1003 | flags |= INHERIT_ONLY_ACE; | |
1004 | if (flags & NO_PROPAGATE_INHERIT_ACE) | |
1005 | flags = 0; | |
64b39f4a | 1006 | } else { |
e2f34481 | 1007 | flags = 0; |
64b39f4a | 1008 | } |
e2f34481 NJ |
1009 | |
1010 | if (!compare_sids(&creator_owner, &parent_aces->sid)) { | |
1011 | creator = &creator_owner; | |
1012 | id_to_sid(uid, SIDOWNER, &owner_sid); | |
1013 | psid = &owner_sid; | |
1014 | } else if (!compare_sids(&creator_group, &parent_aces->sid)) { | |
1015 | creator = &creator_group; | |
1016 | id_to_sid(gid, SIDUNIX_GROUP, &group_sid); | |
1017 | psid = &group_sid; | |
1018 | } else { | |
1019 | creator = NULL; | |
1020 | psid = &parent_aces->sid; | |
1021 | } | |
1022 | ||
1023 | if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) { | |
1024 | smb_set_ace(aces, psid, parent_aces->type, inherited_flags, | |
070fb21e | 1025 | parent_aces->access_req); |
e2f34481 NJ |
1026 | nt_size += le16_to_cpu(aces->size); |
1027 | ace_cnt++; | |
1028 | aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); | |
1029 | flags |= INHERIT_ONLY_ACE; | |
1030 | psid = creator; | |
64b39f4a | 1031 | } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) { |
e2f34481 | 1032 | psid = &parent_aces->sid; |
64b39f4a | 1033 | } |
e2f34481 NJ |
1034 | |
1035 | smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, | |
070fb21e | 1036 | parent_aces->access_req); |
e2f34481 NJ |
1037 | nt_size += le16_to_cpu(aces->size); |
1038 | aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); | |
1039 | ace_cnt++; | |
1040 | pass: | |
1041 | parent_aces = | |
1042 | (struct smb_ace *)((char *)parent_aces + le16_to_cpu(parent_aces->size)); | |
1043 | } | |
1044 | ||
1045 | if (nt_size > 0) { | |
1046 | struct smb_ntsd *pntsd; | |
1047 | struct smb_acl *pdacl; | |
1048 | struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; | |
1049 | int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; | |
1050 | ||
1051 | if (parent_pntsd->osidoffset) { | |
1052 | powner_sid = (struct smb_sid *)((char *)parent_pntsd + | |
1053 | le32_to_cpu(parent_pntsd->osidoffset)); | |
1054 | powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4); | |
1055 | } | |
1056 | if (parent_pntsd->gsidoffset) { | |
1057 | pgroup_sid = (struct smb_sid *)((char *)parent_pntsd + | |
1058 | le32_to_cpu(parent_pntsd->gsidoffset)); | |
1059 | pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); | |
1060 | } | |
1061 | ||
1062 | pntsd = kzalloc(sizeof(struct smb_ntsd) + powner_sid_size + | |
1063 | pgroup_sid_size + sizeof(struct smb_acl) + | |
1064 | nt_size, GFP_KERNEL); | |
1065 | if (!pntsd) { | |
1066 | rc = -ENOMEM; | |
a9071e3c | 1067 | goto free_aces_base; |
e2f34481 NJ |
1068 | } |
1069 | ||
1070 | pntsd->revision = cpu_to_le16(1); | |
1071 | pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PRESENT); | |
1072 | if (le16_to_cpu(parent_pntsd->type) & DACL_AUTO_INHERITED) | |
1073 | pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); | |
1074 | pntsd_size = sizeof(struct smb_ntsd); | |
1075 | pntsd->osidoffset = parent_pntsd->osidoffset; | |
1076 | pntsd->gsidoffset = parent_pntsd->gsidoffset; | |
1077 | pntsd->dacloffset = parent_pntsd->dacloffset; | |
1078 | ||
1079 | if (pntsd->osidoffset) { | |
1080 | struct smb_sid *owner_sid = (struct smb_sid *)((char *)pntsd + | |
1081 | le32_to_cpu(pntsd->osidoffset)); | |
1082 | memcpy(owner_sid, powner_sid, powner_sid_size); | |
1083 | pntsd_size += powner_sid_size; | |
1084 | } | |
1085 | ||
1086 | if (pntsd->gsidoffset) { | |
1087 | struct smb_sid *group_sid = (struct smb_sid *)((char *)pntsd + | |
1088 | le32_to_cpu(pntsd->gsidoffset)); | |
1089 | memcpy(group_sid, pgroup_sid, pgroup_sid_size); | |
1090 | pntsd_size += pgroup_sid_size; | |
1091 | } | |
1092 | ||
1093 | if (pntsd->dacloffset) { | |
1094 | struct smb_ace *pace; | |
1095 | ||
1096 | pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); | |
1097 | pdacl->revision = cpu_to_le16(2); | |
1098 | pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size); | |
1099 | pdacl->num_aces = cpu_to_le32(ace_cnt); | |
1100 | pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); | |
1101 | memcpy(pace, aces_base, nt_size); | |
1102 | pntsd_size += sizeof(struct smb_acl) + nt_size; | |
1103 | } | |
1104 | ||
465d7204 | 1105 | ksmbd_vfs_set_sd_xattr(conn, user_ns, |
af34983e | 1106 | path->dentry, pntsd, pntsd_size); |
e2f34481 | 1107 | kfree(pntsd); |
e2f34481 NJ |
1108 | } |
1109 | ||
a9071e3c | 1110 | free_aces_base: |
e2f34481 | 1111 | kfree(aces_base); |
a9071e3c NJ |
1112 | free_parent_pntsd: |
1113 | kfree(parent_pntsd); | |
e2f34481 NJ |
1114 | return rc; |
1115 | } | |
1116 | ||
1117 | bool smb_inherit_flags(int flags, bool is_dir) | |
1118 | { | |
1119 | if (!is_dir) | |
1120 | return (flags & OBJECT_INHERIT_ACE) != 0; | |
1121 | ||
1122 | if (flags & OBJECT_INHERIT_ACE && !(flags & NO_PROPAGATE_INHERIT_ACE)) | |
1123 | return true; | |
1124 | ||
1125 | if (flags & CONTAINER_INHERIT_ACE) | |
1126 | return true; | |
1127 | return false; | |
1128 | } | |
1129 | ||
ef24c962 | 1130 | int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path, |
070fb21e | 1131 | __le32 *pdaccess, int uid) |
e2f34481 | 1132 | { |
465d7204 | 1133 | struct user_namespace *user_ns = mnt_user_ns(path->mnt); |
e2f34481 NJ |
1134 | struct smb_ntsd *pntsd = NULL; |
1135 | struct smb_acl *pdacl; | |
1136 | struct posix_acl *posix_acls; | |
1137 | int rc = 0, acl_size; | |
1138 | struct smb_sid sid; | |
1139 | int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE); | |
1140 | struct smb_ace *ace; | |
1141 | int i, found = 0; | |
1142 | unsigned int access_bits = 0; | |
1143 | struct smb_ace *others_ace = NULL; | |
1144 | struct posix_acl_entry *pa_entry; | |
1145 | unsigned int sid_type = SIDOWNER; | |
50355b0b | 1146 | char *end_of_acl; |
e2f34481 NJ |
1147 | |
1148 | ksmbd_debug(SMB, "check permission using windows acl\n"); | |
465d7204 | 1149 | acl_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, |
af34983e | 1150 | path->dentry, &pntsd); |
50355b0b NJ |
1151 | if (acl_size <= 0 || !pntsd || !pntsd->dacloffset) { |
1152 | kfree(pntsd); | |
e2f34481 | 1153 | return 0; |
50355b0b | 1154 | } |
e2f34481 NJ |
1155 | |
1156 | pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); | |
50355b0b NJ |
1157 | end_of_acl = ((char *)pntsd) + acl_size; |
1158 | if (end_of_acl <= (char *)pdacl) { | |
1159 | kfree(pntsd); | |
1160 | return 0; | |
1161 | } | |
1162 | ||
1163 | if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size) || | |
1164 | le16_to_cpu(pdacl->size) < sizeof(struct smb_acl)) { | |
1165 | kfree(pntsd); | |
1166 | return 0; | |
1167 | } | |
1168 | ||
e2f34481 NJ |
1169 | if (!pdacl->num_aces) { |
1170 | if (!(le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) && | |
1171 | *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) { | |
1172 | rc = -EACCES; | |
1173 | goto err_out; | |
1174 | } | |
1175 | kfree(pntsd); | |
1176 | return 0; | |
1177 | } | |
1178 | ||
1179 | if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) { | |
1180 | granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | | |
1181 | DELETE; | |
1182 | ||
1183 | ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); | |
1184 | for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { | |
1185 | granted |= le32_to_cpu(ace->access_req); | |
64b39f4a | 1186 | ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); |
50355b0b NJ |
1187 | if (end_of_acl < (char *)ace) |
1188 | goto err_out; | |
e2f34481 NJ |
1189 | } |
1190 | ||
1191 | if (!pdacl->num_aces) | |
1192 | granted = GENERIC_ALL_FLAGS; | |
1193 | } | |
1194 | ||
1195 | if (!uid) | |
1196 | sid_type = SIDUNIX_USER; | |
1197 | id_to_sid(uid, sid_type, &sid); | |
1198 | ||
1199 | ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); | |
1200 | for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { | |
1201 | if (!compare_sids(&sid, &ace->sid) || | |
1202 | !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { | |
1203 | found = 1; | |
1204 | break; | |
1205 | } | |
1206 | if (!compare_sids(&sid_everyone, &ace->sid)) | |
1207 | others_ace = ace; | |
1208 | ||
64b39f4a | 1209 | ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); |
50355b0b NJ |
1210 | if (end_of_acl < (char *)ace) |
1211 | goto err_out; | |
e2f34481 NJ |
1212 | } |
1213 | ||
1214 | if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) { | |
1215 | granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | | |
1216 | DELETE; | |
1217 | ||
1218 | granted |= le32_to_cpu(ace->access_req); | |
1219 | ||
1220 | if (!pdacl->num_aces) | |
1221 | granted = GENERIC_ALL_FLAGS; | |
1222 | } | |
1223 | ||
ef24c962 | 1224 | posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); |
e2f34481 NJ |
1225 | if (posix_acls && !found) { |
1226 | unsigned int id = -1; | |
1227 | ||
1228 | pa_entry = posix_acls->a_entries; | |
1229 | for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { | |
1230 | if (pa_entry->e_tag == ACL_USER) | |
465d7204 | 1231 | id = from_kuid(user_ns, |
af34983e | 1232 | pa_entry->e_uid); |
e2f34481 | 1233 | else if (pa_entry->e_tag == ACL_GROUP) |
465d7204 | 1234 | id = from_kgid(user_ns, |
af34983e | 1235 | pa_entry->e_gid); |
e2f34481 NJ |
1236 | else |
1237 | continue; | |
1238 | ||
1239 | if (id == uid) { | |
1240 | mode_to_access_flags(pa_entry->e_perm, 0777, &access_bits); | |
1241 | if (!access_bits) | |
1242 | access_bits = SET_MINIMUM_RIGHTS; | |
1243 | goto check_access_bits; | |
1244 | } | |
1245 | } | |
1246 | } | |
1247 | if (posix_acls) | |
1248 | posix_acl_release(posix_acls); | |
1249 | ||
1250 | if (!found) { | |
64b39f4a | 1251 | if (others_ace) { |
e2f34481 | 1252 | ace = others_ace; |
64b39f4a | 1253 | } else { |
e2f34481 NJ |
1254 | ksmbd_debug(SMB, "Can't find corresponding sid\n"); |
1255 | rc = -EACCES; | |
1256 | goto err_out; | |
1257 | } | |
1258 | } | |
1259 | ||
1260 | switch (ace->type) { | |
1261 | case ACCESS_ALLOWED_ACE_TYPE: | |
1262 | access_bits = le32_to_cpu(ace->access_req); | |
1263 | break; | |
1264 | case ACCESS_DENIED_ACE_TYPE: | |
1265 | case ACCESS_DENIED_CALLBACK_ACE_TYPE: | |
1266 | access_bits = le32_to_cpu(~ace->access_req); | |
1267 | break; | |
1268 | } | |
1269 | ||
1270 | check_access_bits: | |
070fb21e NJ |
1271 | if (granted & |
1272 | ~(access_bits | FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | DELETE)) { | |
e2f34481 | 1273 | ksmbd_debug(SMB, "Access denied with winACL, granted : %x, access_req : %x\n", |
070fb21e | 1274 | granted, le32_to_cpu(ace->access_req)); |
e2f34481 NJ |
1275 | rc = -EACCES; |
1276 | goto err_out; | |
1277 | } | |
1278 | ||
1279 | *pdaccess = cpu_to_le32(granted); | |
1280 | err_out: | |
1281 | kfree(pntsd); | |
1282 | return rc; | |
1283 | } | |
1284 | ||
1285 | int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, | |
ef24c962 | 1286 | struct path *path, struct smb_ntsd *pntsd, int ntsd_len, |
070fb21e | 1287 | bool type_check) |
e2f34481 NJ |
1288 | { |
1289 | int rc; | |
1290 | struct smb_fattr fattr = {{0}}; | |
ef24c962 | 1291 | struct inode *inode = d_inode(path->dentry); |
465d7204 | 1292 | struct user_namespace *user_ns = mnt_user_ns(path->mnt); |
e2f34481 NJ |
1293 | |
1294 | fattr.cf_uid = INVALID_UID; | |
1295 | fattr.cf_gid = INVALID_GID; | |
1296 | fattr.cf_mode = inode->i_mode; | |
1297 | ||
465d7204 | 1298 | rc = parse_sec_desc(user_ns, pntsd, ntsd_len, &fattr); |
e2f34481 NJ |
1299 | if (rc) |
1300 | goto out; | |
1301 | ||
1302 | inode->i_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); | |
1303 | if (!uid_eq(fattr.cf_uid, INVALID_UID)) | |
1304 | inode->i_uid = fattr.cf_uid; | |
1305 | if (!gid_eq(fattr.cf_gid, INVALID_GID)) | |
1306 | inode->i_gid = fattr.cf_gid; | |
1307 | mark_inode_dirty(inode); | |
1308 | ||
465d7204 | 1309 | ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); |
e2f34481 NJ |
1310 | /* Update posix acls */ |
1311 | if (fattr.cf_dacls) { | |
465d7204 | 1312 | rc = set_posix_acl(user_ns, inode, |
af34983e | 1313 | ACL_TYPE_ACCESS, fattr.cf_acls); |
e2f34481 | 1314 | if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) |
465d7204 | 1315 | rc = set_posix_acl(user_ns, inode, |
67d1c432 | 1316 | ACL_TYPE_DEFAULT, fattr.cf_dacls); |
e2f34481 NJ |
1317 | } |
1318 | ||
1319 | /* Check it only calling from SD BUFFER context */ | |
1320 | if (type_check && !(le16_to_cpu(pntsd->type) & DACL_PRESENT)) | |
1321 | goto out; | |
1322 | ||
64b39f4a | 1323 | if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { |
e2f34481 | 1324 | /* Update WinACL in xattr */ |
465d7204 HL |
1325 | ksmbd_vfs_remove_sd_xattrs(user_ns, path->dentry); |
1326 | ksmbd_vfs_set_sd_xattr(conn, user_ns, | |
af34983e | 1327 | path->dentry, pntsd, ntsd_len); |
e2f34481 NJ |
1328 | } |
1329 | ||
1330 | out: | |
1331 | posix_acl_release(fattr.cf_acls); | |
1332 | posix_acl_release(fattr.cf_dacls); | |
1333 | mark_inode_dirty(inode); | |
1334 | return rc; | |
1335 | } | |
1336 | ||
1337 | void ksmbd_init_domain(u32 *sub_auth) | |
1338 | { | |
1339 | int i; | |
1340 | ||
1341 | memcpy(&server_conf.domain_sid, &domain, sizeof(struct smb_sid)); | |
1342 | for (i = 0; i < 3; ++i) | |
1343 | server_conf.domain_sid.sub_auth[i + 1] = cpu_to_le32(sub_auth[i]); | |
1344 | } |