blob: 076b69ceef7f4d1afe12612e6fdcdccdfe207f6f [file] [log] [blame]
Steve Frenchbcb02032007-09-25 16:17:24 +00001/*
2 * fs/cifs/cifsacl.c
3 *
Steve French8b1327f2008-03-14 22:37:16 +00004 * Copyright (C) International Business Machines Corp., 2007,2008
Steve Frenchbcb02032007-09-25 16:17:24 +00005 * Author(s): Steve French (sfrench@us.ibm.com)
6 *
7 * Contains the routines for mapping CIFS/NTFS ACLs
8 *
9 * This library is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published
11 * by the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
Steve French65874002007-09-25 19:53:44 +000024#include <linux/fs.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090025#include <linux/slab.h>
Shirish Pargaonkar4d79dba2011-04-27 23:34:35 -050026#include <linux/string.h>
27#include <linux/keyctl.h>
28#include <linux/key-type.h>
29#include <keys/user-type.h>
Steve French65874002007-09-25 19:53:44 +000030#include "cifspdu.h"
31#include "cifsglob.h"
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +000032#include "cifsacl.h"
Steve French65874002007-09-25 19:53:44 +000033#include "cifsproto.h"
34#include "cifs_debug.h"
Steve French65874002007-09-25 19:53:44 +000035
Shirish Pargaonkar2fbc2f12010-12-06 14:56:46 -060036/* security id for everyone/world system group */
Shirish Pargaonkare01b6402007-10-30 04:45:14 +000037static const struct cifs_sid sid_everyone = {
38 1, 1, {0, 0, 0, 0, 0, 1}, {0} };
Shirish Pargaonkar2fbc2f12010-12-06 14:56:46 -060039/* security id for Authenticated Users system group */
40static const struct cifs_sid sid_authusers = {
41 1, 1, {0, 0, 0, 0, 0, 5}, {11} };
Steve Frenchbcb02032007-09-25 16:17:24 +000042/* group users */
Steve Frenchad7a2922008-02-07 23:25:02 +000043static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +000044
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -050045const struct cred *root_cred;
46
47static void
48shrink_idmap_tree(struct rb_root *root, int nr_to_scan, int *nr_rem,
49 int *nr_del)
50{
51 struct rb_node *node;
52 struct rb_node *tmp;
53 struct cifs_sid_id *psidid;
54
55 node = rb_first(root);
56 while (node) {
57 tmp = node;
58 node = rb_next(tmp);
59 psidid = rb_entry(tmp, struct cifs_sid_id, rbnode);
60 if (nr_to_scan == 0 || *nr_del == nr_to_scan)
61 ++(*nr_rem);
62 else {
63 if (time_after(jiffies, psidid->time + SID_MAP_EXPIRE)
64 && psidid->refcount == 0) {
65 rb_erase(tmp, root);
66 ++(*nr_del);
67 } else
68 ++(*nr_rem);
69 }
70 }
71}
Shirish Pargaonkar4d79dba2011-04-27 23:34:35 -050072
73/*
74 * Run idmap cache shrinker.
75 */
76static int
77cifs_idmap_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask)
78{
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -050079 int nr_del = 0;
80 int nr_rem = 0;
81 struct rb_root *root;
82
83 root = &uidtree;
84 spin_lock(&siduidlock);
85 shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
86 spin_unlock(&siduidlock);
87
88 root = &gidtree;
89 spin_lock(&sidgidlock);
90 shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
91 spin_unlock(&sidgidlock);
92
93 return nr_rem;
Shirish Pargaonkar4d79dba2011-04-27 23:34:35 -050094}
95
96static struct shrinker cifs_shrinker = {
97 .shrink = cifs_idmap_shrinker,
98 .seeks = DEFAULT_SEEKS,
99};
100
101static int
102cifs_idmap_key_instantiate(struct key *key, const void *data, size_t datalen)
103{
104 char *payload;
105
106 payload = kmalloc(datalen, GFP_KERNEL);
107 if (!payload)
108 return -ENOMEM;
109
110 memcpy(payload, data, datalen);
111 key->payload.data = payload;
112 return 0;
113}
114
115static inline void
116cifs_idmap_key_destroy(struct key *key)
117{
118 kfree(key->payload.data);
119}
120
Shirish Pargaonkar4d79dba2011-04-27 23:34:35 -0500121struct key_type cifs_idmap_key_type = {
Shirish Pargaonkarc4aca0c2011-05-06 02:35:00 -0500122 .name = "cifs.idmap",
Shirish Pargaonkar4d79dba2011-04-27 23:34:35 -0500123 .instantiate = cifs_idmap_key_instantiate,
124 .destroy = cifs_idmap_key_destroy,
125 .describe = user_describe,
126 .match = user_match,
127};
128
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500129static void
130sid_to_str(struct cifs_sid *sidptr, char *sidstr)
131{
132 int i;
133 unsigned long saval;
134 char *strptr;
135
136 strptr = sidstr;
137
138 sprintf(strptr, "%s", "S");
139 strptr = sidstr + strlen(sidstr);
140
141 sprintf(strptr, "-%d", sidptr->revision);
142 strptr = sidstr + strlen(sidstr);
143
144 for (i = 0; i < 6; ++i) {
145 if (sidptr->authority[i]) {
146 sprintf(strptr, "-%d", sidptr->authority[i]);
147 strptr = sidstr + strlen(sidstr);
148 }
149 }
150
151 for (i = 0; i < sidptr->num_subauth; ++i) {
152 saval = le32_to_cpu(sidptr->sub_auth[i]);
153 sprintf(strptr, "-%ld", saval);
154 strptr = sidstr + strlen(sidstr);
155 }
156}
157
158static void
159id_rb_insert(struct rb_root *root, struct cifs_sid *sidptr,
160 struct cifs_sid_id **psidid, char *typestr)
161{
162 int rc;
163 char *strptr;
164 struct rb_node *node = root->rb_node;
165 struct rb_node *parent = NULL;
166 struct rb_node **linkto = &(root->rb_node);
167 struct cifs_sid_id *lsidid;
168
169 while (node) {
170 lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
171 parent = node;
172 rc = compare_sids(sidptr, &((lsidid)->sid));
173 if (rc > 0) {
174 linkto = &(node->rb_left);
175 node = node->rb_left;
176 } else if (rc < 0) {
177 linkto = &(node->rb_right);
178 node = node->rb_right;
179 }
180 }
181
182 memcpy(&(*psidid)->sid, sidptr, sizeof(struct cifs_sid));
183 (*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
184 (*psidid)->refcount = 0;
185
186 sprintf((*psidid)->sidstr, "%s", typestr);
187 strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
188 sid_to_str(&(*psidid)->sid, strptr);
189
190 clear_bit(SID_ID_PENDING, &(*psidid)->state);
191 clear_bit(SID_ID_MAPPED, &(*psidid)->state);
192
193 rb_link_node(&(*psidid)->rbnode, parent, linkto);
194 rb_insert_color(&(*psidid)->rbnode, root);
195}
196
197static struct cifs_sid_id *
198id_rb_search(struct rb_root *root, struct cifs_sid *sidptr)
199{
200 int rc;
201 struct rb_node *node = root->rb_node;
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500202 struct cifs_sid_id *lsidid;
203
204 while (node) {
205 lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500206 rc = compare_sids(sidptr, &((lsidid)->sid));
207 if (rc > 0) {
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500208 node = node->rb_left;
209 } else if (rc < 0) {
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500210 node = node->rb_right;
211 } else /* node found */
212 return lsidid;
213 }
214
215 return NULL;
216}
217
218static int
219sidid_pending_wait(void *unused)
220{
221 schedule();
222 return signal_pending(current) ? -ERESTARTSYS : 0;
223}
224
225static int
226sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
227 struct cifs_fattr *fattr, uint sidtype)
228{
229 int rc;
230 unsigned long cid;
231 struct key *idkey;
232 const struct cred *saved_cred;
233 struct cifs_sid_id *psidid, *npsidid;
234 struct rb_root *cidtree;
235 spinlock_t *cidlock;
236
237 if (sidtype == SIDOWNER) {
238 cid = cifs_sb->mnt_uid; /* default uid, in case upcall fails */
239 cidlock = &siduidlock;
240 cidtree = &uidtree;
241 } else if (sidtype == SIDGROUP) {
242 cid = cifs_sb->mnt_gid; /* default gid, in case upcall fails */
243 cidlock = &sidgidlock;
244 cidtree = &gidtree;
245 } else
246 return -ENOENT;
247
248 spin_lock(cidlock);
249 psidid = id_rb_search(cidtree, psid);
250
251 if (!psidid) { /* node does not exist, allocate one & attempt adding */
252 spin_unlock(cidlock);
253 npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
254 if (!npsidid)
255 return -ENOMEM;
256
257 npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
258 if (!npsidid->sidstr) {
259 kfree(npsidid);
260 return -ENOMEM;
261 }
262
263 spin_lock(cidlock);
264 psidid = id_rb_search(cidtree, psid);
265 if (psidid) { /* node happened to get inserted meanwhile */
266 ++psidid->refcount;
267 spin_unlock(cidlock);
268 kfree(npsidid->sidstr);
269 kfree(npsidid);
270 } else {
271 psidid = npsidid;
272 id_rb_insert(cidtree, psid, &psidid,
273 sidtype == SIDOWNER ? "os:" : "gs:");
274 ++psidid->refcount;
275 spin_unlock(cidlock);
276 }
277 } else {
278 ++psidid->refcount;
279 spin_unlock(cidlock);
280 }
281
282 /*
283 * If we are here, it is safe to access psidid and its fields
284 * since a reference was taken earlier while holding the spinlock.
285 * A reference on the node is put without holding the spinlock
286 * and it is OK to do so in this case, shrinker will not erase
287 * this node until all references are put and we do not access
288 * any fields of the node after a reference is put .
289 */
290 if (test_bit(SID_ID_MAPPED, &psidid->state)) {
291 cid = psidid->id;
292 psidid->time = jiffies; /* update ts for accessing */
293 goto sid_to_id_out;
294 }
295
296 if (time_after(psidid->time + SID_MAP_RETRY, jiffies))
297 goto sid_to_id_out;
298
299 if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
300 saved_cred = override_creds(root_cred);
301 idkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
302 if (IS_ERR(idkey))
303 cFYI(1, "%s: Can't map SID to an id", __func__);
304 else {
305 cid = *(unsigned long *)idkey->payload.value;
306 psidid->id = cid;
307 set_bit(SID_ID_MAPPED, &psidid->state);
308 key_put(idkey);
309 kfree(psidid->sidstr);
310 }
311 revert_creds(saved_cred);
312 psidid->time = jiffies; /* update ts for accessing */
313 clear_bit(SID_ID_PENDING, &psidid->state);
314 wake_up_bit(&psidid->state, SID_ID_PENDING);
315 } else {
316 rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
317 sidid_pending_wait, TASK_INTERRUPTIBLE);
318 if (rc) {
319 cFYI(1, "%s: sidid_pending_wait interrupted %d",
320 __func__, rc);
321 --psidid->refcount; /* decremented without spinlock */
322 return rc;
323 }
324 if (test_bit(SID_ID_MAPPED, &psidid->state))
325 cid = psidid->id;
326 }
327
328sid_to_id_out:
329 --psidid->refcount; /* decremented without spinlock */
330 if (sidtype == SIDOWNER)
331 fattr->cf_uid = cid;
332 else
333 fattr->cf_gid = cid;
334
335 return 0;
336}
337
Shirish Pargaonkar4d79dba2011-04-27 23:34:35 -0500338int
339init_cifs_idmap(void)
340{
341 struct cred *cred;
342 struct key *keyring;
343 int ret;
344
345 cFYI(1, "Registering the %s key type\n", cifs_idmap_key_type.name);
346
347 /* create an override credential set with a special thread keyring in
348 * which requests are cached
349 *
350 * this is used to prevent malicious redirections from being installed
351 * with add_key().
352 */
353 cred = prepare_kernel_cred(NULL);
354 if (!cred)
355 return -ENOMEM;
356
357 keyring = key_alloc(&key_type_keyring, ".cifs_idmap", 0, 0, cred,
358 (KEY_POS_ALL & ~KEY_POS_SETATTR) |
359 KEY_USR_VIEW | KEY_USR_READ,
360 KEY_ALLOC_NOT_IN_QUOTA);
361 if (IS_ERR(keyring)) {
362 ret = PTR_ERR(keyring);
363 goto failed_put_cred;
364 }
365
366 ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
367 if (ret < 0)
368 goto failed_put_key;
369
370 ret = register_key_type(&cifs_idmap_key_type);
371 if (ret < 0)
372 goto failed_put_key;
373
374 /* instruct request_key() to use this special keyring as a cache for
375 * the results it looks up */
376 cred->thread_keyring = keyring;
377 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
378 root_cred = cred;
379
380 spin_lock_init(&siduidlock);
381 uidtree = RB_ROOT;
382 spin_lock_init(&sidgidlock);
383 gidtree = RB_ROOT;
384
385 register_shrinker(&cifs_shrinker);
386
387 cFYI(1, "cifs idmap keyring: %d\n", key_serial(keyring));
388 return 0;
389
390failed_put_key:
391 key_put(keyring);
392failed_put_cred:
393 put_cred(cred);
394 return ret;
395}
396
397void
398exit_cifs_idmap(void)
399{
400 key_revoke(root_cred->thread_keyring);
401 unregister_key_type(&cifs_idmap_key_type);
402 put_cred(root_cred);
403 unregister_shrinker(&cifs_shrinker);
404 cFYI(1, "Unregistered %s key type\n", cifs_idmap_key_type.name);
405}
406
407void
408cifs_destroy_idmaptrees(void)
409{
410 struct rb_root *root;
411 struct rb_node *node;
412
413 root = &uidtree;
414 spin_lock(&siduidlock);
415 while ((node = rb_first(root)))
416 rb_erase(node, root);
417 spin_unlock(&siduidlock);
418
419 root = &gidtree;
420 spin_lock(&sidgidlock);
421 while ((node = rb_first(root)))
422 rb_erase(node, root);
423 spin_unlock(&sidgidlock);
424}
Steve French297647c2007-10-12 04:11:59 +0000425
Steve Frencha750e772007-10-17 22:50:39 +0000426/* if the two SIDs (roughly equivalent to a UUID for a user or group) are
427 the same returns 1, if they do not match returns 0 */
Steve French630f3f0c2007-10-25 21:17:17 +0000428int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
Steve French297647c2007-10-12 04:11:59 +0000429{
430 int i;
431 int num_subauth, num_sat, num_saw;
432
433 if ((!ctsid) || (!cwsid))
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500434 return 1;
Steve French297647c2007-10-12 04:11:59 +0000435
436 /* compare the revision */
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500437 if (ctsid->revision != cwsid->revision) {
438 if (ctsid->revision > cwsid->revision)
439 return 1;
440 else
441 return -1;
442 }
Steve French297647c2007-10-12 04:11:59 +0000443
444 /* compare all of the six auth values */
445 for (i = 0; i < 6; ++i) {
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500446 if (ctsid->authority[i] != cwsid->authority[i]) {
447 if (ctsid->authority[i] > cwsid->authority[i])
448 return 1;
449 else
450 return -1;
451 }
Steve French297647c2007-10-12 04:11:59 +0000452 }
453
454 /* compare all of the subauth values if any */
Steve Frenchadbc0352007-10-17 02:12:46 +0000455 num_sat = ctsid->num_subauth;
Steve Frenchadddd492007-10-17 02:48:17 +0000456 num_saw = cwsid->num_subauth;
Steve French297647c2007-10-12 04:11:59 +0000457 num_subauth = num_sat < num_saw ? num_sat : num_saw;
458 if (num_subauth) {
459 for (i = 0; i < num_subauth; ++i) {
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500460 if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
461 if (ctsid->sub_auth[i] > cwsid->sub_auth[i])
462 return 1;
463 else
464 return -1;
465 }
Steve French297647c2007-10-12 04:11:59 +0000466 }
467 }
468
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500469 return 0; /* sids compare/match */
Steve French297647c2007-10-12 04:11:59 +0000470}
471
Steve French97837582007-12-31 07:47:21 +0000472
473/* copy ntsd, owner sid, and group sid from a security descriptor to another */
474static void copy_sec_desc(const struct cifs_ntsd *pntsd,
475 struct cifs_ntsd *pnntsd, __u32 sidsoffset)
476{
477 int i;
478
479 struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
480 struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
481
482 /* copy security descriptor control portion */
483 pnntsd->revision = pntsd->revision;
484 pnntsd->type = pntsd->type;
485 pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd));
486 pnntsd->sacloffset = 0;
487 pnntsd->osidoffset = cpu_to_le32(sidsoffset);
488 pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid));
489
490 /* copy owner sid */
491 owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
492 le32_to_cpu(pntsd->osidoffset));
493 nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
494
495 nowner_sid_ptr->revision = owner_sid_ptr->revision;
496 nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
497 for (i = 0; i < 6; i++)
498 nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
499 for (i = 0; i < 5; i++)
500 nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
501
502 /* copy group sid */
503 group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
504 le32_to_cpu(pntsd->gsidoffset));
505 ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
506 sizeof(struct cifs_sid));
507
508 ngroup_sid_ptr->revision = group_sid_ptr->revision;
509 ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
510 for (i = 0; i < 6; i++)
511 ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
512 for (i = 0; i < 5; i++)
Shirish Pargaonkarb1910ad2008-07-24 14:53:20 +0000513 ngroup_sid_ptr->sub_auth[i] = group_sid_ptr->sub_auth[i];
Steve French97837582007-12-31 07:47:21 +0000514
515 return;
516}
517
518
Steve French630f3f0c2007-10-25 21:17:17 +0000519/*
520 change posix mode to reflect permissions
521 pmode is the existing mode (we only want to overwrite part of this
522 bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007
523*/
Al Viro9b5e6852007-12-05 08:24:38 +0000524static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode,
Steve French15b03952007-11-08 17:57:40 +0000525 umode_t *pbits_to_set)
Steve French4879b442007-10-19 21:57:39 +0000526{
Al Viro9b5e6852007-12-05 08:24:38 +0000527 __u32 flags = le32_to_cpu(ace_flags);
Steve French15b03952007-11-08 17:57:40 +0000528 /* the order of ACEs is important. The canonical order is to begin with
Steve Frenchce06c9f2007-11-08 21:12:01 +0000529 DENY entries followed by ALLOW, otherwise an allow entry could be
Steve French15b03952007-11-08 17:57:40 +0000530 encountered first, making the subsequent deny entry like "dead code"
Steve Frenchce06c9f2007-11-08 21:12:01 +0000531 which would be superflous since Windows stops when a match is made
Steve French15b03952007-11-08 17:57:40 +0000532 for the operation you are trying to perform for your user */
533
534 /* For deny ACEs we change the mask so that subsequent allow access
535 control entries do not turn on the bits we are denying */
536 if (type == ACCESS_DENIED) {
Steve Frenchad7a2922008-02-07 23:25:02 +0000537 if (flags & GENERIC_ALL)
Steve French15b03952007-11-08 17:57:40 +0000538 *pbits_to_set &= ~S_IRWXUGO;
Steve Frenchad7a2922008-02-07 23:25:02 +0000539
Al Viro9b5e6852007-12-05 08:24:38 +0000540 if ((flags & GENERIC_WRITE) ||
541 ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000542 *pbits_to_set &= ~S_IWUGO;
Al Viro9b5e6852007-12-05 08:24:38 +0000543 if ((flags & GENERIC_READ) ||
544 ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000545 *pbits_to_set &= ~S_IRUGO;
Al Viro9b5e6852007-12-05 08:24:38 +0000546 if ((flags & GENERIC_EXECUTE) ||
547 ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000548 *pbits_to_set &= ~S_IXUGO;
549 return;
550 } else if (type != ACCESS_ALLOWED) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000551 cERROR(1, "unknown access control type %d", type);
Steve French15b03952007-11-08 17:57:40 +0000552 return;
553 }
554 /* else ACCESS_ALLOWED type */
Steve French44093ca2007-10-23 21:22:55 +0000555
Al Viro9b5e6852007-12-05 08:24:38 +0000556 if (flags & GENERIC_ALL) {
Steve French15b03952007-11-08 17:57:40 +0000557 *pmode |= (S_IRWXUGO & (*pbits_to_set));
Joe Perchesb6b38f72010-04-21 03:50:45 +0000558 cFYI(DBG2, "all perms");
Steve Frenchd61e5802007-10-26 04:32:43 +0000559 return;
560 }
Al Viro9b5e6852007-12-05 08:24:38 +0000561 if ((flags & GENERIC_WRITE) ||
562 ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000563 *pmode |= (S_IWUGO & (*pbits_to_set));
Al Viro9b5e6852007-12-05 08:24:38 +0000564 if ((flags & GENERIC_READ) ||
565 ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000566 *pmode |= (S_IRUGO & (*pbits_to_set));
Al Viro9b5e6852007-12-05 08:24:38 +0000567 if ((flags & GENERIC_EXECUTE) ||
568 ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000569 *pmode |= (S_IXUGO & (*pbits_to_set));
Steve Frenchd61e5802007-10-26 04:32:43 +0000570
Joe Perchesb6b38f72010-04-21 03:50:45 +0000571 cFYI(DBG2, "access flags 0x%x mode now 0x%x", flags, *pmode);
Steve French630f3f0c2007-10-25 21:17:17 +0000572 return;
573}
574
Steve Frenchce06c9f2007-11-08 21:12:01 +0000575/*
576 Generate access flags to reflect permissions mode is the existing mode.
577 This function is called for every ACE in the DACL whose SID matches
578 with either owner or group or everyone.
579*/
580
581static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
582 __u32 *pace_flags)
583{
584 /* reset access mask */
585 *pace_flags = 0x0;
586
587 /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */
588 mode &= bits_to_use;
589
590 /* check for R/W/X UGO since we do not know whose flags
591 is this but we have cleared all the bits sans RWX for
592 either user or group or other as per bits_to_use */
593 if (mode & S_IRUGO)
594 *pace_flags |= SET_FILE_READ_RIGHTS;
595 if (mode & S_IWUGO)
596 *pace_flags |= SET_FILE_WRITE_RIGHTS;
597 if (mode & S_IXUGO)
598 *pace_flags |= SET_FILE_EXEC_RIGHTS;
599
Joe Perchesb6b38f72010-04-21 03:50:45 +0000600 cFYI(DBG2, "mode: 0x%x, access flags now 0x%x", mode, *pace_flags);
Steve Frenchce06c9f2007-11-08 21:12:01 +0000601 return;
602}
603
Al Viro2b210ad2008-03-29 03:09:18 +0000604static __u16 fill_ace_for_sid(struct cifs_ace *pntace,
Steve French97837582007-12-31 07:47:21 +0000605 const struct cifs_sid *psid, __u64 nmode, umode_t bits)
606{
607 int i;
608 __u16 size = 0;
609 __u32 access_req = 0;
610
611 pntace->type = ACCESS_ALLOWED;
612 pntace->flags = 0x0;
613 mode_to_access_flags(nmode, bits, &access_req);
614 if (!access_req)
615 access_req = SET_MINIMUM_RIGHTS;
616 pntace->access_req = cpu_to_le32(access_req);
617
618 pntace->sid.revision = psid->revision;
619 pntace->sid.num_subauth = psid->num_subauth;
620 for (i = 0; i < 6; i++)
621 pntace->sid.authority[i] = psid->authority[i];
622 for (i = 0; i < psid->num_subauth; i++)
623 pntace->sid.sub_auth[i] = psid->sub_auth[i];
624
625 size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4);
626 pntace->size = cpu_to_le16(size);
627
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000628 return size;
Steve French97837582007-12-31 07:47:21 +0000629}
630
Steve French297647c2007-10-12 04:11:59 +0000631
Steve French953f8682007-10-31 04:54:42 +0000632#ifdef CONFIG_CIFS_DEBUG2
633static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000634{
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000635 int num_subauth;
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000636
637 /* validate that we do not go past end of acl */
Steve French297647c2007-10-12 04:11:59 +0000638
Steve French44093ca2007-10-23 21:22:55 +0000639 if (le16_to_cpu(pace->size) < 16) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000640 cERROR(1, "ACE too small %d", le16_to_cpu(pace->size));
Steve French44093ca2007-10-23 21:22:55 +0000641 return;
642 }
643
644 if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000645 cERROR(1, "ACL too small to parse ACE");
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000646 return;
Steve French44093ca2007-10-23 21:22:55 +0000647 }
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000648
Steve French44093ca2007-10-23 21:22:55 +0000649 num_subauth = pace->sid.num_subauth;
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000650 if (num_subauth) {
Steve French8f18c132007-10-12 18:54:12 +0000651 int i;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000652 cFYI(1, "ACE revision %d num_auth %d type %d flags %d size %d",
Steve French44093ca2007-10-23 21:22:55 +0000653 pace->sid.revision, pace->sid.num_subauth, pace->type,
Joe Perchesb6b38f72010-04-21 03:50:45 +0000654 pace->flags, le16_to_cpu(pace->size));
Steve Frenchd12fd122007-10-03 19:43:19 +0000655 for (i = 0; i < num_subauth; ++i) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000656 cFYI(1, "ACE sub_auth[%d]: 0x%x", i,
657 le32_to_cpu(pace->sid.sub_auth[i]));
Steve Frenchd12fd122007-10-03 19:43:19 +0000658 }
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000659
Steve Frenchd12fd122007-10-03 19:43:19 +0000660 /* BB add length check to make sure that we do not have huge
661 num auths and therefore go off the end */
Steve Frenchd12fd122007-10-03 19:43:19 +0000662 }
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000663
Steve Frenchd12fd122007-10-03 19:43:19 +0000664 return;
665}
Steve French953f8682007-10-31 04:54:42 +0000666#endif
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000667
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000668
Steve Frencha750e772007-10-17 22:50:39 +0000669static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
Steve Frenchd61e5802007-10-26 04:32:43 +0000670 struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400671 struct cifs_fattr *fattr)
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000672{
673 int i;
674 int num_aces = 0;
675 int acl_size;
676 char *acl_base;
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000677 struct cifs_ace **ppace;
678
679 /* BB need to add parm so we can store the SID BB */
680
Steve French2b834572007-11-25 10:01:00 +0000681 if (!pdacl) {
682 /* no DACL in the security descriptor, set
683 all the permissions for user/group/other */
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400684 fattr->cf_mode |= S_IRWXUGO;
Steve French2b834572007-11-25 10:01:00 +0000685 return;
686 }
687
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000688 /* validate that we do not go past end of acl */
Steve Frenchaf6f4612007-10-16 18:40:37 +0000689 if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000690 cERROR(1, "ACL too small to parse DACL");
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000691 return;
692 }
693
Joe Perchesb6b38f72010-04-21 03:50:45 +0000694 cFYI(DBG2, "DACL revision %d size %d num aces %d",
Steve Frenchaf6f4612007-10-16 18:40:37 +0000695 le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
Joe Perchesb6b38f72010-04-21 03:50:45 +0000696 le32_to_cpu(pdacl->num_aces));
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000697
Steve French7505e052007-11-01 18:03:01 +0000698 /* reset rwx permissions for user/group/other.
699 Also, if num_aces is 0 i.e. DACL has no ACEs,
700 user/group/other have no permissions */
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400701 fattr->cf_mode &= ~(S_IRWXUGO);
Steve French7505e052007-11-01 18:03:01 +0000702
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000703 acl_base = (char *)pdacl;
704 acl_size = sizeof(struct cifs_acl);
705
Steve Frenchadbc0352007-10-17 02:12:46 +0000706 num_aces = le32_to_cpu(pdacl->num_aces);
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000707 if (num_aces > 0) {
Steve French15b03952007-11-08 17:57:40 +0000708 umode_t user_mask = S_IRWXU;
709 umode_t group_mask = S_IRWXG;
Shirish Pargaonkar2fbc2f12010-12-06 14:56:46 -0600710 umode_t other_mask = S_IRWXU | S_IRWXG | S_IRWXO;
Steve French15b03952007-11-08 17:57:40 +0000711
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000712 ppace = kmalloc(num_aces * sizeof(struct cifs_ace *),
713 GFP_KERNEL);
Stanislav Fomichev8132b652011-02-06 02:05:28 +0300714 if (!ppace) {
715 cERROR(1, "DACL memory allocation error");
716 return;
717 }
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000718
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000719 for (i = 0; i < num_aces; ++i) {
Steve French44093ca2007-10-23 21:22:55 +0000720 ppace[i] = (struct cifs_ace *) (acl_base + acl_size);
Steve French953f8682007-10-31 04:54:42 +0000721#ifdef CONFIG_CIFS_DEBUG2
722 dump_ace(ppace[i], end_of_acl);
723#endif
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500724 if (compare_sids(&(ppace[i]->sid), pownersid) == 0)
Shirish Pargaonkare01b6402007-10-30 04:45:14 +0000725 access_flags_to_mode(ppace[i]->access_req,
Steve French15b03952007-11-08 17:57:40 +0000726 ppace[i]->type,
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400727 &fattr->cf_mode,
Steve French15b03952007-11-08 17:57:40 +0000728 &user_mask);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500729 if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0)
Shirish Pargaonkare01b6402007-10-30 04:45:14 +0000730 access_flags_to_mode(ppace[i]->access_req,
Steve French15b03952007-11-08 17:57:40 +0000731 ppace[i]->type,
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400732 &fattr->cf_mode,
Steve French15b03952007-11-08 17:57:40 +0000733 &group_mask);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500734 if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0)
Shirish Pargaonkare01b6402007-10-30 04:45:14 +0000735 access_flags_to_mode(ppace[i]->access_req,
Steve French15b03952007-11-08 17:57:40 +0000736 ppace[i]->type,
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400737 &fattr->cf_mode,
Steve French15b03952007-11-08 17:57:40 +0000738 &other_mask);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500739 if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)
Shirish Pargaonkar2fbc2f12010-12-06 14:56:46 -0600740 access_flags_to_mode(ppace[i]->access_req,
741 ppace[i]->type,
742 &fattr->cf_mode,
743 &other_mask);
744
Shirish Pargaonkare01b6402007-10-30 04:45:14 +0000745
Steve French44093ca2007-10-23 21:22:55 +0000746/* memcpy((void *)(&(cifscred->aces[i])),
Steve Frenchd12fd122007-10-03 19:43:19 +0000747 (void *)ppace[i],
748 sizeof(struct cifs_ace)); */
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000749
Steve French44093ca2007-10-23 21:22:55 +0000750 acl_base = (char *)ppace[i];
751 acl_size = le16_to_cpu(ppace[i]->size);
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000752 }
753
754 kfree(ppace);
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000755 }
756
757 return;
758}
759
Steve Frenchbcb02032007-09-25 16:17:24 +0000760
Steve French97837582007-12-31 07:47:21 +0000761static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
762 struct cifs_sid *pgrpsid, __u64 nmode)
763{
Al Viro2b210ad2008-03-29 03:09:18 +0000764 u16 size = 0;
Steve French97837582007-12-31 07:47:21 +0000765 struct cifs_acl *pnndacl;
766
767 pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
768
769 size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
770 pownersid, nmode, S_IRWXU);
771 size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
772 pgrpsid, nmode, S_IRWXG);
773 size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
774 &sid_everyone, nmode, S_IRWXO);
775
776 pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
Shirish Pargaonkard9f382e2008-02-12 20:46:26 +0000777 pndacl->num_aces = cpu_to_le32(3);
Steve French97837582007-12-31 07:47:21 +0000778
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000779 return 0;
Steve French97837582007-12-31 07:47:21 +0000780}
781
782
Steve Frenchbcb02032007-09-25 16:17:24 +0000783static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
784{
785 /* BB need to add parm so we can store the SID BB */
786
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000787 /* validate that we do not go past end of ACL - sid must be at least 8
788 bytes long (assuming no sub-auths - e.g. the null SID */
789 if (end_of_acl < (char *)psid + 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000790 cERROR(1, "ACL too small to parse SID %p", psid);
Steve Frenchbcb02032007-09-25 16:17:24 +0000791 return -EINVAL;
792 }
Steve Frenchbcb02032007-09-25 16:17:24 +0000793
Steve Frenchaf6f4612007-10-16 18:40:37 +0000794 if (psid->num_subauth) {
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000795#ifdef CONFIG_CIFS_DEBUG2
Steve French8f18c132007-10-12 18:54:12 +0000796 int i;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000797 cFYI(1, "SID revision %d num_auth %d",
798 psid->revision, psid->num_subauth);
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000799
Steve Frenchaf6f4612007-10-16 18:40:37 +0000800 for (i = 0; i < psid->num_subauth; i++) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000801 cFYI(1, "SID sub_auth[%d]: 0x%x ", i,
802 le32_to_cpu(psid->sub_auth[i]));
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000803 }
804
Steve Frenchd12fd122007-10-03 19:43:19 +0000805 /* BB add length check to make sure that we do not have huge
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000806 num auths and therefore go off the end */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000807 cFYI(1, "RID 0x%x",
808 le32_to_cpu(psid->sub_auth[psid->num_subauth-1]));
Steve Frenchbcb02032007-09-25 16:17:24 +0000809#endif
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000810 }
811
Steve Frenchbcb02032007-09-25 16:17:24 +0000812 return 0;
813}
814
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000815
Steve Frenchbcb02032007-09-25 16:17:24 +0000816/* Convert CIFS ACL to POSIX form */
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500817static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
818 struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr)
Steve Frenchbcb02032007-09-25 16:17:24 +0000819{
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500820 int rc = 0;
Steve Frenchbcb02032007-09-25 16:17:24 +0000821 struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
822 struct cifs_acl *dacl_ptr; /* no need for SACL ptr */
Steve Frenchbcb02032007-09-25 16:17:24 +0000823 char *end_of_acl = ((char *)pntsd) + acl_len;
Steve French7505e052007-11-01 18:03:01 +0000824 __u32 dacloffset;
Steve Frenchbcb02032007-09-25 16:17:24 +0000825
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400826 if (pntsd == NULL)
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000827 return -EIO;
828
Steve Frenchbcb02032007-09-25 16:17:24 +0000829 owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
Steve Frenchaf6f4612007-10-16 18:40:37 +0000830 le32_to_cpu(pntsd->osidoffset));
Steve Frenchbcb02032007-09-25 16:17:24 +0000831 group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
Steve Frenchaf6f4612007-10-16 18:40:37 +0000832 le32_to_cpu(pntsd->gsidoffset));
Steve French7505e052007-11-01 18:03:01 +0000833 dacloffset = le32_to_cpu(pntsd->dacloffset);
Steve French63d25832007-11-05 21:46:10 +0000834 dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000835 cFYI(DBG2, "revision %d type 0x%x ooffset 0x%x goffset 0x%x "
Steve Frenchbcb02032007-09-25 16:17:24 +0000836 "sacloffset 0x%x dacloffset 0x%x",
Steve Frenchaf6f4612007-10-16 18:40:37 +0000837 pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
838 le32_to_cpu(pntsd->gsidoffset),
Joe Perchesb6b38f72010-04-21 03:50:45 +0000839 le32_to_cpu(pntsd->sacloffset), dacloffset);
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000840/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */
Steve Frenchbcb02032007-09-25 16:17:24 +0000841 rc = parse_sid(owner_sid_ptr, end_of_acl);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500842 if (rc) {
843 cFYI(1, "%s: Error %d parsing Owner SID", __func__, rc);
Steve Frenchbcb02032007-09-25 16:17:24 +0000844 return rc;
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500845 }
846 rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER);
847 if (rc) {
848 cFYI(1, "%s: Error %d mapping Owner SID to uid", __func__, rc);
849 return rc;
850 }
Steve Frenchbcb02032007-09-25 16:17:24 +0000851
852 rc = parse_sid(group_sid_ptr, end_of_acl);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500853 if (rc) {
854 cFYI(1, "%s: Error %d mapping Owner SID to gid", __func__, rc);
Steve Frenchbcb02032007-09-25 16:17:24 +0000855 return rc;
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500856 }
857 rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP);
858 if (rc) {
859 cFYI(1, "%s: Error %d mapping Group SID to gid", __func__, rc);
860 return rc;
861 }
Steve Frenchbcb02032007-09-25 16:17:24 +0000862
Steve French7505e052007-11-01 18:03:01 +0000863 if (dacloffset)
864 parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400865 group_sid_ptr, fattr);
Steve French7505e052007-11-01 18:03:01 +0000866 else
Joe Perchesb6b38f72010-04-21 03:50:45 +0000867 cFYI(1, "no ACL"); /* BB grant all or default perms? */
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000868
Steve Frenchbcb02032007-09-25 16:17:24 +0000869/* cifscred->uid = owner_sid_ptr->rid;
870 cifscred->gid = group_sid_ptr->rid;
871 memcpy((void *)(&(cifscred->osid)), (void *)owner_sid_ptr,
Steve French630f3f0c2007-10-25 21:17:17 +0000872 sizeof(struct cifs_sid));
Steve Frenchbcb02032007-09-25 16:17:24 +0000873 memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr,
Steve French630f3f0c2007-10-25 21:17:17 +0000874 sizeof(struct cifs_sid)); */
Steve Frenchbcb02032007-09-25 16:17:24 +0000875
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500876 return rc;
Steve Frenchbcb02032007-09-25 16:17:24 +0000877}
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000878
879
Steve French97837582007-12-31 07:47:21 +0000880/* Convert permission bits from mode to equivalent CIFS ACL */
881static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
Steve Frenchcce246e2008-04-09 20:55:31 +0000882 struct inode *inode, __u64 nmode)
Steve French97837582007-12-31 07:47:21 +0000883{
884 int rc = 0;
885 __u32 dacloffset;
886 __u32 ndacloffset;
887 __u32 sidsoffset;
888 struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
889 struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */
890 struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
891
892 if ((inode == NULL) || (pntsd == NULL) || (pnntsd == NULL))
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000893 return -EIO;
Steve French97837582007-12-31 07:47:21 +0000894
895 owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
896 le32_to_cpu(pntsd->osidoffset));
897 group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
898 le32_to_cpu(pntsd->gsidoffset));
899
900 dacloffset = le32_to_cpu(pntsd->dacloffset);
901 dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
902
903 ndacloffset = sizeof(struct cifs_ntsd);
904 ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
905 ndacl_ptr->revision = dacl_ptr->revision;
906 ndacl_ptr->size = 0;
907 ndacl_ptr->num_aces = 0;
908
909 rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, nmode);
910
911 sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
912
913 /* copy security descriptor control portion and owner and group sid */
914 copy_sec_desc(pntsd, pnntsd, sidsoffset);
915
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000916 return rc;
Steve French97837582007-12-31 07:47:21 +0000917}
918
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400919static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
920 __u16 fid, u32 *pacllen)
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000921{
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000922 struct cifs_ntsd *pntsd = NULL;
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400923 int xid, rc;
Jeff Layton7ffec372010-09-29 19:51:11 -0400924 struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
925
926 if (IS_ERR(tlink))
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -0600927 return ERR_CAST(tlink);
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000928
929 xid = GetXid();
Jeff Layton7ffec372010-09-29 19:51:11 -0400930 rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), fid, &pntsd, pacllen);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400931 FreeXid(xid);
Steve French8b1327f2008-03-14 22:37:16 +0000932
Jeff Layton7ffec372010-09-29 19:51:11 -0400933 cifs_put_tlink(tlink);
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000934
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -0600935 cFYI(1, "%s: rc = %d ACL len %d", __func__, rc, *pacllen);
936 if (rc)
937 return ERR_PTR(rc);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400938 return pntsd;
939}
940
941static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
942 const char *path, u32 *pacllen)
943{
944 struct cifs_ntsd *pntsd = NULL;
945 int oplock = 0;
946 int xid, rc;
947 __u16 fid;
Steve French96daf2b2011-05-27 04:34:02 +0000948 struct cifs_tcon *tcon;
Jeff Layton7ffec372010-09-29 19:51:11 -0400949 struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400950
Jeff Layton7ffec372010-09-29 19:51:11 -0400951 if (IS_ERR(tlink))
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -0600952 return ERR_CAST(tlink);
Jeff Layton7ffec372010-09-29 19:51:11 -0400953
954 tcon = tlink_tcon(tlink);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400955 xid = GetXid();
956
Jeff Layton7ffec372010-09-29 19:51:11 -0400957 rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL, 0,
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400958 &fid, &oplock, NULL, cifs_sb->local_nls,
959 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -0600960 if (!rc) {
961 rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen);
962 CIFSSMBClose(xid, tcon, fid);
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000963 }
964
Jeff Layton7ffec372010-09-29 19:51:11 -0400965 cifs_put_tlink(tlink);
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000966 FreeXid(xid);
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -0600967
968 cFYI(1, "%s: rc = %d ACL len %d", __func__, rc, *pacllen);
969 if (rc)
970 return ERR_PTR(rc);
Steve French7505e052007-11-01 18:03:01 +0000971 return pntsd;
972}
973
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400974/* Retrieve an ACL from the server */
Shirish Pargaonkarfbeba8b2010-11-27 11:37:54 -0600975struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400976 struct inode *inode, const char *path,
977 u32 *pacllen)
978{
979 struct cifs_ntsd *pntsd = NULL;
980 struct cifsFileInfo *open_file = NULL;
981
982 if (inode)
Jeff Layton6508d902010-09-29 19:51:11 -0400983 open_file = find_readable_file(CIFS_I(inode), true);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400984 if (!open_file)
985 return get_cifs_acl_by_path(cifs_sb, path, pacllen);
986
987 pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->netfid, pacllen);
Dave Kleikamp6ab409b2009-08-31 11:07:12 -0400988 cifsFileInfo_put(open_file);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400989 return pntsd;
990}
991
Christoph Hellwigb96d31a2009-05-27 09:37:33 -0400992static int set_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, __u16 fid,
993 struct cifs_ntsd *pnntsd, u32 acllen)
Steve French97837582007-12-31 07:47:21 +0000994{
Christoph Hellwigb96d31a2009-05-27 09:37:33 -0400995 int xid, rc;
Jeff Layton7ffec372010-09-29 19:51:11 -0400996 struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
997
998 if (IS_ERR(tlink))
999 return PTR_ERR(tlink);
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001000
1001 xid = GetXid();
Jeff Layton7ffec372010-09-29 19:51:11 -04001002 rc = CIFSSMBSetCIFSACL(xid, tlink_tcon(tlink), fid, pnntsd, acllen);
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001003 FreeXid(xid);
Jeff Layton7ffec372010-09-29 19:51:11 -04001004 cifs_put_tlink(tlink);
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001005
Joe Perchesb6b38f72010-04-21 03:50:45 +00001006 cFYI(DBG2, "SetCIFSACL rc = %d", rc);
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001007 return rc;
1008}
1009
1010static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
1011 struct cifs_ntsd *pnntsd, u32 acllen)
1012{
1013 int oplock = 0;
1014 int xid, rc;
Steve French97837582007-12-31 07:47:21 +00001015 __u16 fid;
Steve French96daf2b2011-05-27 04:34:02 +00001016 struct cifs_tcon *tcon;
Jeff Layton7ffec372010-09-29 19:51:11 -04001017 struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
Steve French97837582007-12-31 07:47:21 +00001018
Jeff Layton7ffec372010-09-29 19:51:11 -04001019 if (IS_ERR(tlink))
1020 return PTR_ERR(tlink);
1021
1022 tcon = tlink_tcon(tlink);
Steve French97837582007-12-31 07:47:21 +00001023 xid = GetXid();
1024
Jeff Layton7ffec372010-09-29 19:51:11 -04001025 rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, WRITE_DAC, 0,
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001026 &fid, &oplock, NULL, cifs_sb->local_nls,
1027 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
1028 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001029 cERROR(1, "Unable to open file to set ACL");
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001030 goto out;
Steve French97837582007-12-31 07:47:21 +00001031 }
1032
Jeff Layton7ffec372010-09-29 19:51:11 -04001033 rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen);
Joe Perchesb6b38f72010-04-21 03:50:45 +00001034 cFYI(DBG2, "SetCIFSACL rc = %d", rc);
Steve French97837582007-12-31 07:47:21 +00001035
Jeff Layton7ffec372010-09-29 19:51:11 -04001036 CIFSSMBClose(xid, tcon, fid);
1037out:
Steve French97837582007-12-31 07:47:21 +00001038 FreeXid(xid);
Jeff Layton7ffec372010-09-29 19:51:11 -04001039 cifs_put_tlink(tlink);
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001040 return rc;
1041}
Steve French97837582007-12-31 07:47:21 +00001042
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001043/* Set an ACL on the server */
Steve Frenchb73b9a42011-04-19 18:27:10 +00001044int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001045 struct inode *inode, const char *path)
1046{
1047 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
1048 struct cifsFileInfo *open_file;
1049 int rc;
1050
Joe Perchesb6b38f72010-04-21 03:50:45 +00001051 cFYI(DBG2, "set ACL for %s from mode 0x%x", path, inode->i_mode);
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001052
Jeff Layton6508d902010-09-29 19:51:11 -04001053 open_file = find_readable_file(CIFS_I(inode), true);
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001054 if (!open_file)
1055 return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen);
1056
1057 rc = set_cifs_acl_by_fid(cifs_sb, open_file->netfid, pnntsd, acllen);
Dave Kleikamp6ab409b2009-08-31 11:07:12 -04001058 cifsFileInfo_put(open_file);
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +00001059 return rc;
Steve French97837582007-12-31 07:47:21 +00001060}
1061
Steve French7505e052007-11-01 18:03:01 +00001062/* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -06001063int
Jeff Layton0b8f18e2009-07-09 01:46:37 -04001064cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
1065 struct inode *inode, const char *path, const __u16 *pfid)
Steve French7505e052007-11-01 18:03:01 +00001066{
1067 struct cifs_ntsd *pntsd = NULL;
1068 u32 acllen = 0;
1069 int rc = 0;
1070
Joe Perchesb6b38f72010-04-21 03:50:45 +00001071 cFYI(DBG2, "converting ACL to mode for %s", path);
Christoph Hellwig1bf40722009-05-27 09:37:33 -04001072
1073 if (pfid)
1074 pntsd = get_cifs_acl_by_fid(cifs_sb, *pfid, &acllen);
1075 else
1076 pntsd = get_cifs_acl(cifs_sb, inode, path, &acllen);
Steve French7505e052007-11-01 18:03:01 +00001077
1078 /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -06001079 if (IS_ERR(pntsd)) {
1080 rc = PTR_ERR(pntsd);
1081 cERROR(1, "%s: error %d getting sec desc", __func__, rc);
1082 } else {
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -05001083 rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr);
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -06001084 kfree(pntsd);
1085 if (rc)
1086 cERROR(1, "parse sec desc failed rc = %d", rc);
1087 }
Steve French7505e052007-11-01 18:03:01 +00001088
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -06001089 return rc;
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00001090}
Steve French953f8682007-10-31 04:54:42 +00001091
Steve French7505e052007-11-01 18:03:01 +00001092/* Convert mode bits to an ACL so we can update the ACL on the server */
Shirish Pargaonkar78415d22010-11-27 11:37:26 -06001093int mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode)
Steve French953f8682007-10-31 04:54:42 +00001094{
1095 int rc = 0;
Steve Frenchcce246e2008-04-09 20:55:31 +00001096 __u32 secdesclen = 0;
Steve French97837582007-12-31 07:47:21 +00001097 struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
1098 struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
Steve French953f8682007-10-31 04:54:42 +00001099
Joe Perchesb6b38f72010-04-21 03:50:45 +00001100 cFYI(DBG2, "set ACL from mode for %s", path);
Steve French953f8682007-10-31 04:54:42 +00001101
1102 /* Get the security descriptor */
Christoph Hellwig1bf40722009-05-27 09:37:33 -04001103 pntsd = get_cifs_acl(CIFS_SB(inode->i_sb), inode, path, &secdesclen);
Steve French953f8682007-10-31 04:54:42 +00001104
Steve French97837582007-12-31 07:47:21 +00001105 /* Add three ACEs for owner, group, everyone getting rid of
1106 other ACEs as chmod disables ACEs and set the security descriptor */
Steve French953f8682007-10-31 04:54:42 +00001107
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -06001108 if (IS_ERR(pntsd)) {
1109 rc = PTR_ERR(pntsd);
1110 cERROR(1, "%s: error %d getting sec desc", __func__, rc);
1111 } else {
Steve French97837582007-12-31 07:47:21 +00001112 /* allocate memory for the smb header,
1113 set security descriptor request security descriptor
1114 parameters, and secuirty descriptor itself */
Steve French953f8682007-10-31 04:54:42 +00001115
Steve Frenchcce246e2008-04-09 20:55:31 +00001116 secdesclen = secdesclen < DEFSECDESCLEN ?
1117 DEFSECDESCLEN : secdesclen;
1118 pnntsd = kmalloc(secdesclen, GFP_KERNEL);
Steve French97837582007-12-31 07:47:21 +00001119 if (!pnntsd) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001120 cERROR(1, "Unable to allocate security descriptor");
Steve French97837582007-12-31 07:47:21 +00001121 kfree(pntsd);
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +00001122 return -ENOMEM;
Steve French97837582007-12-31 07:47:21 +00001123 }
Steve French7505e052007-11-01 18:03:01 +00001124
Steve Frenchcce246e2008-04-09 20:55:31 +00001125 rc = build_sec_desc(pntsd, pnntsd, inode, nmode);
Steve French97837582007-12-31 07:47:21 +00001126
Joe Perchesb6b38f72010-04-21 03:50:45 +00001127 cFYI(DBG2, "build_sec_desc rc: %d", rc);
Steve French97837582007-12-31 07:47:21 +00001128
1129 if (!rc) {
1130 /* Set the security descriptor */
Steve Frenchcce246e2008-04-09 20:55:31 +00001131 rc = set_cifs_acl(pnntsd, secdesclen, inode, path);
Joe Perchesb6b38f72010-04-21 03:50:45 +00001132 cFYI(DBG2, "set_cifs_acl rc: %d", rc);
Steve French97837582007-12-31 07:47:21 +00001133 }
1134
1135 kfree(pnntsd);
1136 kfree(pntsd);
1137 }
1138
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +00001139 return rc;
Steve French953f8682007-10-31 04:54:42 +00001140}