blob: b244e07c3048e37795f8ef2286e51ea889aec7ff [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 = {
Steve French4f612582011-05-27 20:40:18 +000041 1, 1, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(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
Al Viroef1d5752011-05-29 13:46:08 +010077cifs_idmap_shrinker(struct shrinker *shrink, struct shrink_control *sc)
Shirish Pargaonkar4d79dba2011-04-27 23:34:35 -050078{
Al Viroef1d5752011-05-29 13:46:08 +010079 int nr_to_scan = sc->nr_to_scan;
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -050080 int nr_del = 0;
81 int nr_rem = 0;
82 struct rb_root *root;
83
84 root = &uidtree;
85 spin_lock(&siduidlock);
86 shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
87 spin_unlock(&siduidlock);
88
89 root = &gidtree;
90 spin_lock(&sidgidlock);
91 shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
92 spin_unlock(&sidgidlock);
93
94 return nr_rem;
Shirish Pargaonkar4d79dba2011-04-27 23:34:35 -050095}
96
97static struct shrinker cifs_shrinker = {
98 .shrink = cifs_idmap_shrinker,
99 .seeks = DEFAULT_SEEKS,
100};
101
102static int
103cifs_idmap_key_instantiate(struct key *key, const void *data, size_t datalen)
104{
105 char *payload;
106
107 payload = kmalloc(datalen, GFP_KERNEL);
108 if (!payload)
109 return -ENOMEM;
110
111 memcpy(payload, data, datalen);
112 key->payload.data = payload;
113 return 0;
114}
115
116static inline void
117cifs_idmap_key_destroy(struct key *key)
118{
119 kfree(key->payload.data);
120}
121
Shirish Pargaonkar4d79dba2011-04-27 23:34:35 -0500122struct key_type cifs_idmap_key_type = {
Shirish Pargaonkarc4aca0c2011-05-06 02:35:00 -0500123 .name = "cifs.idmap",
Shirish Pargaonkar4d79dba2011-04-27 23:34:35 -0500124 .instantiate = cifs_idmap_key_instantiate,
125 .destroy = cifs_idmap_key_destroy,
126 .describe = user_describe,
127 .match = user_match,
128};
129
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500130static void
131sid_to_str(struct cifs_sid *sidptr, char *sidstr)
132{
133 int i;
134 unsigned long saval;
135 char *strptr;
136
137 strptr = sidstr;
138
139 sprintf(strptr, "%s", "S");
140 strptr = sidstr + strlen(sidstr);
141
142 sprintf(strptr, "-%d", sidptr->revision);
143 strptr = sidstr + strlen(sidstr);
144
145 for (i = 0; i < 6; ++i) {
146 if (sidptr->authority[i]) {
147 sprintf(strptr, "-%d", sidptr->authority[i]);
148 strptr = sidstr + strlen(sidstr);
149 }
150 }
151
152 for (i = 0; i < sidptr->num_subauth; ++i) {
153 saval = le32_to_cpu(sidptr->sub_auth[i]);
154 sprintf(strptr, "-%ld", saval);
155 strptr = sidstr + strlen(sidstr);
156 }
157}
158
159static void
160id_rb_insert(struct rb_root *root, struct cifs_sid *sidptr,
161 struct cifs_sid_id **psidid, char *typestr)
162{
163 int rc;
164 char *strptr;
165 struct rb_node *node = root->rb_node;
166 struct rb_node *parent = NULL;
167 struct rb_node **linkto = &(root->rb_node);
168 struct cifs_sid_id *lsidid;
169
170 while (node) {
171 lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
172 parent = node;
173 rc = compare_sids(sidptr, &((lsidid)->sid));
174 if (rc > 0) {
175 linkto = &(node->rb_left);
176 node = node->rb_left;
177 } else if (rc < 0) {
178 linkto = &(node->rb_right);
179 node = node->rb_right;
180 }
181 }
182
183 memcpy(&(*psidid)->sid, sidptr, sizeof(struct cifs_sid));
184 (*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
185 (*psidid)->refcount = 0;
186
187 sprintf((*psidid)->sidstr, "%s", typestr);
188 strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
189 sid_to_str(&(*psidid)->sid, strptr);
190
191 clear_bit(SID_ID_PENDING, &(*psidid)->state);
192 clear_bit(SID_ID_MAPPED, &(*psidid)->state);
193
194 rb_link_node(&(*psidid)->rbnode, parent, linkto);
195 rb_insert_color(&(*psidid)->rbnode, root);
196}
197
198static struct cifs_sid_id *
199id_rb_search(struct rb_root *root, struct cifs_sid *sidptr)
200{
201 int rc;
202 struct rb_node *node = root->rb_node;
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500203 struct cifs_sid_id *lsidid;
204
205 while (node) {
206 lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500207 rc = compare_sids(sidptr, &((lsidid)->sid));
208 if (rc > 0) {
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500209 node = node->rb_left;
210 } else if (rc < 0) {
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500211 node = node->rb_right;
212 } else /* node found */
213 return lsidid;
214 }
215
216 return NULL;
217}
218
219static int
220sidid_pending_wait(void *unused)
221{
222 schedule();
223 return signal_pending(current) ? -ERESTARTSYS : 0;
224}
225
226static int
227sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
228 struct cifs_fattr *fattr, uint sidtype)
229{
230 int rc;
231 unsigned long cid;
232 struct key *idkey;
233 const struct cred *saved_cred;
234 struct cifs_sid_id *psidid, *npsidid;
235 struct rb_root *cidtree;
236 spinlock_t *cidlock;
237
238 if (sidtype == SIDOWNER) {
239 cid = cifs_sb->mnt_uid; /* default uid, in case upcall fails */
240 cidlock = &siduidlock;
241 cidtree = &uidtree;
242 } else if (sidtype == SIDGROUP) {
243 cid = cifs_sb->mnt_gid; /* default gid, in case upcall fails */
244 cidlock = &sidgidlock;
245 cidtree = &gidtree;
246 } else
247 return -ENOENT;
248
249 spin_lock(cidlock);
250 psidid = id_rb_search(cidtree, psid);
251
252 if (!psidid) { /* node does not exist, allocate one & attempt adding */
253 spin_unlock(cidlock);
254 npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
255 if (!npsidid)
256 return -ENOMEM;
257
258 npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
259 if (!npsidid->sidstr) {
260 kfree(npsidid);
261 return -ENOMEM;
262 }
263
264 spin_lock(cidlock);
265 psidid = id_rb_search(cidtree, psid);
266 if (psidid) { /* node happened to get inserted meanwhile */
267 ++psidid->refcount;
268 spin_unlock(cidlock);
269 kfree(npsidid->sidstr);
270 kfree(npsidid);
271 } else {
272 psidid = npsidid;
273 id_rb_insert(cidtree, psid, &psidid,
274 sidtype == SIDOWNER ? "os:" : "gs:");
275 ++psidid->refcount;
276 spin_unlock(cidlock);
277 }
278 } else {
279 ++psidid->refcount;
280 spin_unlock(cidlock);
281 }
282
283 /*
284 * If we are here, it is safe to access psidid and its fields
285 * since a reference was taken earlier while holding the spinlock.
286 * A reference on the node is put without holding the spinlock
287 * and it is OK to do so in this case, shrinker will not erase
288 * this node until all references are put and we do not access
289 * any fields of the node after a reference is put .
290 */
291 if (test_bit(SID_ID_MAPPED, &psidid->state)) {
292 cid = psidid->id;
293 psidid->time = jiffies; /* update ts for accessing */
294 goto sid_to_id_out;
295 }
296
297 if (time_after(psidid->time + SID_MAP_RETRY, jiffies))
298 goto sid_to_id_out;
299
300 if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
301 saved_cred = override_creds(root_cred);
302 idkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
303 if (IS_ERR(idkey))
304 cFYI(1, "%s: Can't map SID to an id", __func__);
305 else {
306 cid = *(unsigned long *)idkey->payload.value;
307 psidid->id = cid;
308 set_bit(SID_ID_MAPPED, &psidid->state);
309 key_put(idkey);
310 kfree(psidid->sidstr);
311 }
312 revert_creds(saved_cred);
313 psidid->time = jiffies; /* update ts for accessing */
314 clear_bit(SID_ID_PENDING, &psidid->state);
315 wake_up_bit(&psidid->state, SID_ID_PENDING);
316 } else {
317 rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
318 sidid_pending_wait, TASK_INTERRUPTIBLE);
319 if (rc) {
320 cFYI(1, "%s: sidid_pending_wait interrupted %d",
321 __func__, rc);
322 --psidid->refcount; /* decremented without spinlock */
323 return rc;
324 }
325 if (test_bit(SID_ID_MAPPED, &psidid->state))
326 cid = psidid->id;
327 }
328
329sid_to_id_out:
330 --psidid->refcount; /* decremented without spinlock */
331 if (sidtype == SIDOWNER)
332 fattr->cf_uid = cid;
333 else
334 fattr->cf_gid = cid;
335
336 return 0;
337}
338
Shirish Pargaonkar4d79dba2011-04-27 23:34:35 -0500339int
340init_cifs_idmap(void)
341{
342 struct cred *cred;
343 struct key *keyring;
344 int ret;
345
346 cFYI(1, "Registering the %s key type\n", cifs_idmap_key_type.name);
347
348 /* create an override credential set with a special thread keyring in
349 * which requests are cached
350 *
351 * this is used to prevent malicious redirections from being installed
352 * with add_key().
353 */
354 cred = prepare_kernel_cred(NULL);
355 if (!cred)
356 return -ENOMEM;
357
358 keyring = key_alloc(&key_type_keyring, ".cifs_idmap", 0, 0, cred,
359 (KEY_POS_ALL & ~KEY_POS_SETATTR) |
360 KEY_USR_VIEW | KEY_USR_READ,
361 KEY_ALLOC_NOT_IN_QUOTA);
362 if (IS_ERR(keyring)) {
363 ret = PTR_ERR(keyring);
364 goto failed_put_cred;
365 }
366
367 ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
368 if (ret < 0)
369 goto failed_put_key;
370
371 ret = register_key_type(&cifs_idmap_key_type);
372 if (ret < 0)
373 goto failed_put_key;
374
375 /* instruct request_key() to use this special keyring as a cache for
376 * the results it looks up */
377 cred->thread_keyring = keyring;
378 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
379 root_cred = cred;
380
381 spin_lock_init(&siduidlock);
382 uidtree = RB_ROOT;
383 spin_lock_init(&sidgidlock);
384 gidtree = RB_ROOT;
385
386 register_shrinker(&cifs_shrinker);
387
388 cFYI(1, "cifs idmap keyring: %d\n", key_serial(keyring));
389 return 0;
390
391failed_put_key:
392 key_put(keyring);
393failed_put_cred:
394 put_cred(cred);
395 return ret;
396}
397
398void
399exit_cifs_idmap(void)
400{
401 key_revoke(root_cred->thread_keyring);
402 unregister_key_type(&cifs_idmap_key_type);
403 put_cred(root_cred);
404 unregister_shrinker(&cifs_shrinker);
405 cFYI(1, "Unregistered %s key type\n", cifs_idmap_key_type.name);
406}
407
408void
409cifs_destroy_idmaptrees(void)
410{
411 struct rb_root *root;
412 struct rb_node *node;
413
414 root = &uidtree;
415 spin_lock(&siduidlock);
416 while ((node = rb_first(root)))
417 rb_erase(node, root);
418 spin_unlock(&siduidlock);
419
420 root = &gidtree;
421 spin_lock(&sidgidlock);
422 while ((node = rb_first(root)))
423 rb_erase(node, root);
424 spin_unlock(&sidgidlock);
425}
Steve French297647c2007-10-12 04:11:59 +0000426
Steve Frencha750e772007-10-17 22:50:39 +0000427/* if the two SIDs (roughly equivalent to a UUID for a user or group) are
428 the same returns 1, if they do not match returns 0 */
Steve French630f3f0c2007-10-25 21:17:17 +0000429int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
Steve French297647c2007-10-12 04:11:59 +0000430{
431 int i;
432 int num_subauth, num_sat, num_saw;
433
434 if ((!ctsid) || (!cwsid))
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500435 return 1;
Steve French297647c2007-10-12 04:11:59 +0000436
437 /* compare the revision */
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500438 if (ctsid->revision != cwsid->revision) {
439 if (ctsid->revision > cwsid->revision)
440 return 1;
441 else
442 return -1;
443 }
Steve French297647c2007-10-12 04:11:59 +0000444
445 /* compare all of the six auth values */
446 for (i = 0; i < 6; ++i) {
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500447 if (ctsid->authority[i] != cwsid->authority[i]) {
448 if (ctsid->authority[i] > cwsid->authority[i])
449 return 1;
450 else
451 return -1;
452 }
Steve French297647c2007-10-12 04:11:59 +0000453 }
454
455 /* compare all of the subauth values if any */
Steve Frenchadbc0352007-10-17 02:12:46 +0000456 num_sat = ctsid->num_subauth;
Steve Frenchadddd492007-10-17 02:48:17 +0000457 num_saw = cwsid->num_subauth;
Steve French297647c2007-10-12 04:11:59 +0000458 num_subauth = num_sat < num_saw ? num_sat : num_saw;
459 if (num_subauth) {
460 for (i = 0; i < num_subauth; ++i) {
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500461 if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
Steve French383c5532011-05-27 15:19:12 +0000462 if (le32_to_cpu(ctsid->sub_auth[i]) >
463 le32_to_cpu(cwsid->sub_auth[i]))
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500464 return 1;
465 else
466 return -1;
467 }
Steve French297647c2007-10-12 04:11:59 +0000468 }
469 }
470
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500471 return 0; /* sids compare/match */
Steve French297647c2007-10-12 04:11:59 +0000472}
473
Steve French97837582007-12-31 07:47:21 +0000474
475/* copy ntsd, owner sid, and group sid from a security descriptor to another */
476static void copy_sec_desc(const struct cifs_ntsd *pntsd,
477 struct cifs_ntsd *pnntsd, __u32 sidsoffset)
478{
479 int i;
480
481 struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
482 struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
483
484 /* copy security descriptor control portion */
485 pnntsd->revision = pntsd->revision;
486 pnntsd->type = pntsd->type;
487 pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd));
488 pnntsd->sacloffset = 0;
489 pnntsd->osidoffset = cpu_to_le32(sidsoffset);
490 pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid));
491
492 /* copy owner sid */
493 owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
494 le32_to_cpu(pntsd->osidoffset));
495 nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
496
497 nowner_sid_ptr->revision = owner_sid_ptr->revision;
498 nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
499 for (i = 0; i < 6; i++)
500 nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
501 for (i = 0; i < 5; i++)
502 nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
503
504 /* copy group sid */
505 group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
506 le32_to_cpu(pntsd->gsidoffset));
507 ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
508 sizeof(struct cifs_sid));
509
510 ngroup_sid_ptr->revision = group_sid_ptr->revision;
511 ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
512 for (i = 0; i < 6; i++)
513 ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
514 for (i = 0; i < 5; i++)
Shirish Pargaonkarb1910ad2008-07-24 14:53:20 +0000515 ngroup_sid_ptr->sub_auth[i] = group_sid_ptr->sub_auth[i];
Steve French97837582007-12-31 07:47:21 +0000516
517 return;
518}
519
520
Steve French630f3f0c2007-10-25 21:17:17 +0000521/*
522 change posix mode to reflect permissions
523 pmode is the existing mode (we only want to overwrite part of this
524 bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007
525*/
Al Viro9b5e6852007-12-05 08:24:38 +0000526static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode,
Steve French15b03952007-11-08 17:57:40 +0000527 umode_t *pbits_to_set)
Steve French4879b442007-10-19 21:57:39 +0000528{
Al Viro9b5e6852007-12-05 08:24:38 +0000529 __u32 flags = le32_to_cpu(ace_flags);
Steve French15b03952007-11-08 17:57:40 +0000530 /* the order of ACEs is important. The canonical order is to begin with
Steve Frenchce06c9f2007-11-08 21:12:01 +0000531 DENY entries followed by ALLOW, otherwise an allow entry could be
Steve French15b03952007-11-08 17:57:40 +0000532 encountered first, making the subsequent deny entry like "dead code"
Steve Frenchce06c9f2007-11-08 21:12:01 +0000533 which would be superflous since Windows stops when a match is made
Steve French15b03952007-11-08 17:57:40 +0000534 for the operation you are trying to perform for your user */
535
536 /* For deny ACEs we change the mask so that subsequent allow access
537 control entries do not turn on the bits we are denying */
538 if (type == ACCESS_DENIED) {
Steve Frenchad7a2922008-02-07 23:25:02 +0000539 if (flags & GENERIC_ALL)
Steve French15b03952007-11-08 17:57:40 +0000540 *pbits_to_set &= ~S_IRWXUGO;
Steve Frenchad7a2922008-02-07 23:25:02 +0000541
Al Viro9b5e6852007-12-05 08:24:38 +0000542 if ((flags & GENERIC_WRITE) ||
543 ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000544 *pbits_to_set &= ~S_IWUGO;
Al Viro9b5e6852007-12-05 08:24:38 +0000545 if ((flags & GENERIC_READ) ||
546 ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000547 *pbits_to_set &= ~S_IRUGO;
Al Viro9b5e6852007-12-05 08:24:38 +0000548 if ((flags & GENERIC_EXECUTE) ||
549 ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000550 *pbits_to_set &= ~S_IXUGO;
551 return;
552 } else if (type != ACCESS_ALLOWED) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000553 cERROR(1, "unknown access control type %d", type);
Steve French15b03952007-11-08 17:57:40 +0000554 return;
555 }
556 /* else ACCESS_ALLOWED type */
Steve French44093ca2007-10-23 21:22:55 +0000557
Al Viro9b5e6852007-12-05 08:24:38 +0000558 if (flags & GENERIC_ALL) {
Steve French15b03952007-11-08 17:57:40 +0000559 *pmode |= (S_IRWXUGO & (*pbits_to_set));
Joe Perchesb6b38f72010-04-21 03:50:45 +0000560 cFYI(DBG2, "all perms");
Steve Frenchd61e5802007-10-26 04:32:43 +0000561 return;
562 }
Al Viro9b5e6852007-12-05 08:24:38 +0000563 if ((flags & GENERIC_WRITE) ||
564 ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000565 *pmode |= (S_IWUGO & (*pbits_to_set));
Al Viro9b5e6852007-12-05 08:24:38 +0000566 if ((flags & GENERIC_READ) ||
567 ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000568 *pmode |= (S_IRUGO & (*pbits_to_set));
Al Viro9b5e6852007-12-05 08:24:38 +0000569 if ((flags & GENERIC_EXECUTE) ||
570 ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
Steve French15b03952007-11-08 17:57:40 +0000571 *pmode |= (S_IXUGO & (*pbits_to_set));
Steve Frenchd61e5802007-10-26 04:32:43 +0000572
Joe Perchesb6b38f72010-04-21 03:50:45 +0000573 cFYI(DBG2, "access flags 0x%x mode now 0x%x", flags, *pmode);
Steve French630f3f0c2007-10-25 21:17:17 +0000574 return;
575}
576
Steve Frenchce06c9f2007-11-08 21:12:01 +0000577/*
578 Generate access flags to reflect permissions mode is the existing mode.
579 This function is called for every ACE in the DACL whose SID matches
580 with either owner or group or everyone.
581*/
582
583static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
584 __u32 *pace_flags)
585{
586 /* reset access mask */
587 *pace_flags = 0x0;
588
589 /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */
590 mode &= bits_to_use;
591
592 /* check for R/W/X UGO since we do not know whose flags
593 is this but we have cleared all the bits sans RWX for
594 either user or group or other as per bits_to_use */
595 if (mode & S_IRUGO)
596 *pace_flags |= SET_FILE_READ_RIGHTS;
597 if (mode & S_IWUGO)
598 *pace_flags |= SET_FILE_WRITE_RIGHTS;
599 if (mode & S_IXUGO)
600 *pace_flags |= SET_FILE_EXEC_RIGHTS;
601
Joe Perchesb6b38f72010-04-21 03:50:45 +0000602 cFYI(DBG2, "mode: 0x%x, access flags now 0x%x", mode, *pace_flags);
Steve Frenchce06c9f2007-11-08 21:12:01 +0000603 return;
604}
605
Al Viro2b210ad2008-03-29 03:09:18 +0000606static __u16 fill_ace_for_sid(struct cifs_ace *pntace,
Steve French97837582007-12-31 07:47:21 +0000607 const struct cifs_sid *psid, __u64 nmode, umode_t bits)
608{
609 int i;
610 __u16 size = 0;
611 __u32 access_req = 0;
612
613 pntace->type = ACCESS_ALLOWED;
614 pntace->flags = 0x0;
615 mode_to_access_flags(nmode, bits, &access_req);
616 if (!access_req)
617 access_req = SET_MINIMUM_RIGHTS;
618 pntace->access_req = cpu_to_le32(access_req);
619
620 pntace->sid.revision = psid->revision;
621 pntace->sid.num_subauth = psid->num_subauth;
622 for (i = 0; i < 6; i++)
623 pntace->sid.authority[i] = psid->authority[i];
624 for (i = 0; i < psid->num_subauth; i++)
625 pntace->sid.sub_auth[i] = psid->sub_auth[i];
626
627 size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4);
628 pntace->size = cpu_to_le16(size);
629
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000630 return size;
Steve French97837582007-12-31 07:47:21 +0000631}
632
Steve French297647c2007-10-12 04:11:59 +0000633
Steve French953f8682007-10-31 04:54:42 +0000634#ifdef CONFIG_CIFS_DEBUG2
635static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000636{
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000637 int num_subauth;
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000638
639 /* validate that we do not go past end of acl */
Steve French297647c2007-10-12 04:11:59 +0000640
Steve French44093ca2007-10-23 21:22:55 +0000641 if (le16_to_cpu(pace->size) < 16) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000642 cERROR(1, "ACE too small %d", le16_to_cpu(pace->size));
Steve French44093ca2007-10-23 21:22:55 +0000643 return;
644 }
645
646 if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000647 cERROR(1, "ACL too small to parse ACE");
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000648 return;
Steve French44093ca2007-10-23 21:22:55 +0000649 }
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000650
Steve French44093ca2007-10-23 21:22:55 +0000651 num_subauth = pace->sid.num_subauth;
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000652 if (num_subauth) {
Steve French8f18c132007-10-12 18:54:12 +0000653 int i;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000654 cFYI(1, "ACE revision %d num_auth %d type %d flags %d size %d",
Steve French44093ca2007-10-23 21:22:55 +0000655 pace->sid.revision, pace->sid.num_subauth, pace->type,
Joe Perchesb6b38f72010-04-21 03:50:45 +0000656 pace->flags, le16_to_cpu(pace->size));
Steve Frenchd12fd122007-10-03 19:43:19 +0000657 for (i = 0; i < num_subauth; ++i) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000658 cFYI(1, "ACE sub_auth[%d]: 0x%x", i,
659 le32_to_cpu(pace->sid.sub_auth[i]));
Steve Frenchd12fd122007-10-03 19:43:19 +0000660 }
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000661
Steve Frenchd12fd122007-10-03 19:43:19 +0000662 /* BB add length check to make sure that we do not have huge
663 num auths and therefore go off the end */
Steve Frenchd12fd122007-10-03 19:43:19 +0000664 }
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000665
Steve Frenchd12fd122007-10-03 19:43:19 +0000666 return;
667}
Steve French953f8682007-10-31 04:54:42 +0000668#endif
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000669
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000670
Steve Frencha750e772007-10-17 22:50:39 +0000671static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
Steve Frenchd61e5802007-10-26 04:32:43 +0000672 struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400673 struct cifs_fattr *fattr)
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000674{
675 int i;
676 int num_aces = 0;
677 int acl_size;
678 char *acl_base;
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000679 struct cifs_ace **ppace;
680
681 /* BB need to add parm so we can store the SID BB */
682
Steve French2b834572007-11-25 10:01:00 +0000683 if (!pdacl) {
684 /* no DACL in the security descriptor, set
685 all the permissions for user/group/other */
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400686 fattr->cf_mode |= S_IRWXUGO;
Steve French2b834572007-11-25 10:01:00 +0000687 return;
688 }
689
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000690 /* validate that we do not go past end of acl */
Steve Frenchaf6f4612007-10-16 18:40:37 +0000691 if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000692 cERROR(1, "ACL too small to parse DACL");
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000693 return;
694 }
695
Joe Perchesb6b38f72010-04-21 03:50:45 +0000696 cFYI(DBG2, "DACL revision %d size %d num aces %d",
Steve Frenchaf6f4612007-10-16 18:40:37 +0000697 le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
Joe Perchesb6b38f72010-04-21 03:50:45 +0000698 le32_to_cpu(pdacl->num_aces));
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000699
Steve French7505e052007-11-01 18:03:01 +0000700 /* reset rwx permissions for user/group/other.
701 Also, if num_aces is 0 i.e. DACL has no ACEs,
702 user/group/other have no permissions */
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400703 fattr->cf_mode &= ~(S_IRWXUGO);
Steve French7505e052007-11-01 18:03:01 +0000704
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000705 acl_base = (char *)pdacl;
706 acl_size = sizeof(struct cifs_acl);
707
Steve Frenchadbc0352007-10-17 02:12:46 +0000708 num_aces = le32_to_cpu(pdacl->num_aces);
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000709 if (num_aces > 0) {
Steve French15b03952007-11-08 17:57:40 +0000710 umode_t user_mask = S_IRWXU;
711 umode_t group_mask = S_IRWXG;
Shirish Pargaonkar2fbc2f12010-12-06 14:56:46 -0600712 umode_t other_mask = S_IRWXU | S_IRWXG | S_IRWXO;
Steve French15b03952007-11-08 17:57:40 +0000713
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000714 ppace = kmalloc(num_aces * sizeof(struct cifs_ace *),
715 GFP_KERNEL);
Stanislav Fomichev8132b652011-02-06 02:05:28 +0300716 if (!ppace) {
717 cERROR(1, "DACL memory allocation error");
718 return;
719 }
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000720
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000721 for (i = 0; i < num_aces; ++i) {
Steve French44093ca2007-10-23 21:22:55 +0000722 ppace[i] = (struct cifs_ace *) (acl_base + acl_size);
Steve French953f8682007-10-31 04:54:42 +0000723#ifdef CONFIG_CIFS_DEBUG2
724 dump_ace(ppace[i], end_of_acl);
725#endif
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500726 if (compare_sids(&(ppace[i]->sid), pownersid) == 0)
Shirish Pargaonkare01b6402007-10-30 04:45:14 +0000727 access_flags_to_mode(ppace[i]->access_req,
Steve French15b03952007-11-08 17:57:40 +0000728 ppace[i]->type,
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400729 &fattr->cf_mode,
Steve French15b03952007-11-08 17:57:40 +0000730 &user_mask);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500731 if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0)
Shirish Pargaonkare01b6402007-10-30 04:45:14 +0000732 access_flags_to_mode(ppace[i]->access_req,
Steve French15b03952007-11-08 17:57:40 +0000733 ppace[i]->type,
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400734 &fattr->cf_mode,
Steve French15b03952007-11-08 17:57:40 +0000735 &group_mask);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500736 if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0)
Shirish Pargaonkare01b6402007-10-30 04:45:14 +0000737 access_flags_to_mode(ppace[i]->access_req,
Steve French15b03952007-11-08 17:57:40 +0000738 ppace[i]->type,
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400739 &fattr->cf_mode,
Steve French15b03952007-11-08 17:57:40 +0000740 &other_mask);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500741 if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)
Shirish Pargaonkar2fbc2f12010-12-06 14:56:46 -0600742 access_flags_to_mode(ppace[i]->access_req,
743 ppace[i]->type,
744 &fattr->cf_mode,
745 &other_mask);
746
Shirish Pargaonkare01b6402007-10-30 04:45:14 +0000747
Steve French44093ca2007-10-23 21:22:55 +0000748/* memcpy((void *)(&(cifscred->aces[i])),
Steve Frenchd12fd122007-10-03 19:43:19 +0000749 (void *)ppace[i],
750 sizeof(struct cifs_ace)); */
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000751
Steve French44093ca2007-10-23 21:22:55 +0000752 acl_base = (char *)ppace[i];
753 acl_size = le16_to_cpu(ppace[i]->size);
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000754 }
755
756 kfree(ppace);
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000757 }
758
759 return;
760}
761
Steve Frenchbcb02032007-09-25 16:17:24 +0000762
Steve French97837582007-12-31 07:47:21 +0000763static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
764 struct cifs_sid *pgrpsid, __u64 nmode)
765{
Al Viro2b210ad2008-03-29 03:09:18 +0000766 u16 size = 0;
Steve French97837582007-12-31 07:47:21 +0000767 struct cifs_acl *pnndacl;
768
769 pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
770
771 size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
772 pownersid, nmode, S_IRWXU);
773 size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
774 pgrpsid, nmode, S_IRWXG);
775 size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
776 &sid_everyone, nmode, S_IRWXO);
777
778 pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
Shirish Pargaonkard9f382e2008-02-12 20:46:26 +0000779 pndacl->num_aces = cpu_to_le32(3);
Steve French97837582007-12-31 07:47:21 +0000780
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000781 return 0;
Steve French97837582007-12-31 07:47:21 +0000782}
783
784
Steve Frenchbcb02032007-09-25 16:17:24 +0000785static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
786{
787 /* BB need to add parm so we can store the SID BB */
788
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000789 /* validate that we do not go past end of ACL - sid must be at least 8
790 bytes long (assuming no sub-auths - e.g. the null SID */
791 if (end_of_acl < (char *)psid + 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000792 cERROR(1, "ACL too small to parse SID %p", psid);
Steve Frenchbcb02032007-09-25 16:17:24 +0000793 return -EINVAL;
794 }
Steve Frenchbcb02032007-09-25 16:17:24 +0000795
Steve Frenchaf6f4612007-10-16 18:40:37 +0000796 if (psid->num_subauth) {
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000797#ifdef CONFIG_CIFS_DEBUG2
Steve French8f18c132007-10-12 18:54:12 +0000798 int i;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000799 cFYI(1, "SID revision %d num_auth %d",
800 psid->revision, psid->num_subauth);
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000801
Steve Frenchaf6f4612007-10-16 18:40:37 +0000802 for (i = 0; i < psid->num_subauth; i++) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000803 cFYI(1, "SID sub_auth[%d]: 0x%x ", i,
804 le32_to_cpu(psid->sub_auth[i]));
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000805 }
806
Steve Frenchd12fd122007-10-03 19:43:19 +0000807 /* BB add length check to make sure that we do not have huge
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000808 num auths and therefore go off the end */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000809 cFYI(1, "RID 0x%x",
810 le32_to_cpu(psid->sub_auth[psid->num_subauth-1]));
Steve Frenchbcb02032007-09-25 16:17:24 +0000811#endif
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000812 }
813
Steve Frenchbcb02032007-09-25 16:17:24 +0000814 return 0;
815}
816
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000817
Steve Frenchbcb02032007-09-25 16:17:24 +0000818/* Convert CIFS ACL to POSIX form */
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500819static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
820 struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr)
Steve Frenchbcb02032007-09-25 16:17:24 +0000821{
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500822 int rc = 0;
Steve Frenchbcb02032007-09-25 16:17:24 +0000823 struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
824 struct cifs_acl *dacl_ptr; /* no need for SACL ptr */
Steve Frenchbcb02032007-09-25 16:17:24 +0000825 char *end_of_acl = ((char *)pntsd) + acl_len;
Steve French7505e052007-11-01 18:03:01 +0000826 __u32 dacloffset;
Steve Frenchbcb02032007-09-25 16:17:24 +0000827
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400828 if (pntsd == NULL)
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000829 return -EIO;
830
Steve Frenchbcb02032007-09-25 16:17:24 +0000831 owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
Steve Frenchaf6f4612007-10-16 18:40:37 +0000832 le32_to_cpu(pntsd->osidoffset));
Steve Frenchbcb02032007-09-25 16:17:24 +0000833 group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
Steve Frenchaf6f4612007-10-16 18:40:37 +0000834 le32_to_cpu(pntsd->gsidoffset));
Steve French7505e052007-11-01 18:03:01 +0000835 dacloffset = le32_to_cpu(pntsd->dacloffset);
Steve French63d25832007-11-05 21:46:10 +0000836 dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000837 cFYI(DBG2, "revision %d type 0x%x ooffset 0x%x goffset 0x%x "
Steve Frenchbcb02032007-09-25 16:17:24 +0000838 "sacloffset 0x%x dacloffset 0x%x",
Steve Frenchaf6f4612007-10-16 18:40:37 +0000839 pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
840 le32_to_cpu(pntsd->gsidoffset),
Joe Perchesb6b38f72010-04-21 03:50:45 +0000841 le32_to_cpu(pntsd->sacloffset), dacloffset);
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000842/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */
Steve Frenchbcb02032007-09-25 16:17:24 +0000843 rc = parse_sid(owner_sid_ptr, end_of_acl);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500844 if (rc) {
845 cFYI(1, "%s: Error %d parsing Owner SID", __func__, rc);
Steve Frenchbcb02032007-09-25 16:17:24 +0000846 return rc;
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500847 }
848 rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER);
849 if (rc) {
850 cFYI(1, "%s: Error %d mapping Owner SID to uid", __func__, rc);
851 return rc;
852 }
Steve Frenchbcb02032007-09-25 16:17:24 +0000853
854 rc = parse_sid(group_sid_ptr, end_of_acl);
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500855 if (rc) {
856 cFYI(1, "%s: Error %d mapping Owner SID to gid", __func__, rc);
Steve Frenchbcb02032007-09-25 16:17:24 +0000857 return rc;
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500858 }
859 rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP);
860 if (rc) {
861 cFYI(1, "%s: Error %d mapping Group SID to gid", __func__, rc);
862 return rc;
863 }
Steve Frenchbcb02032007-09-25 16:17:24 +0000864
Steve French7505e052007-11-01 18:03:01 +0000865 if (dacloffset)
866 parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400867 group_sid_ptr, fattr);
Steve French7505e052007-11-01 18:03:01 +0000868 else
Joe Perchesb6b38f72010-04-21 03:50:45 +0000869 cFYI(1, "no ACL"); /* BB grant all or default perms? */
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +0000870
Steve Frenchbcb02032007-09-25 16:17:24 +0000871/* cifscred->uid = owner_sid_ptr->rid;
872 cifscred->gid = group_sid_ptr->rid;
873 memcpy((void *)(&(cifscred->osid)), (void *)owner_sid_ptr,
Steve French630f3f0c2007-10-25 21:17:17 +0000874 sizeof(struct cifs_sid));
Steve Frenchbcb02032007-09-25 16:17:24 +0000875 memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr,
Steve French630f3f0c2007-10-25 21:17:17 +0000876 sizeof(struct cifs_sid)); */
Steve Frenchbcb02032007-09-25 16:17:24 +0000877
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -0500878 return rc;
Steve Frenchbcb02032007-09-25 16:17:24 +0000879}
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000880
881
Steve French97837582007-12-31 07:47:21 +0000882/* Convert permission bits from mode to equivalent CIFS ACL */
883static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
Steve Frenchcce246e2008-04-09 20:55:31 +0000884 struct inode *inode, __u64 nmode)
Steve French97837582007-12-31 07:47:21 +0000885{
886 int rc = 0;
887 __u32 dacloffset;
888 __u32 ndacloffset;
889 __u32 sidsoffset;
890 struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
891 struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */
892 struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
893
894 if ((inode == NULL) || (pntsd == NULL) || (pnntsd == NULL))
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000895 return -EIO;
Steve French97837582007-12-31 07:47:21 +0000896
897 owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
898 le32_to_cpu(pntsd->osidoffset));
899 group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
900 le32_to_cpu(pntsd->gsidoffset));
901
902 dacloffset = le32_to_cpu(pntsd->dacloffset);
903 dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
904
905 ndacloffset = sizeof(struct cifs_ntsd);
906 ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
907 ndacl_ptr->revision = dacl_ptr->revision;
908 ndacl_ptr->size = 0;
909 ndacl_ptr->num_aces = 0;
910
911 rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, nmode);
912
913 sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
914
915 /* copy security descriptor control portion and owner and group sid */
916 copy_sec_desc(pntsd, pnntsd, sidsoffset);
917
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000918 return rc;
Steve French97837582007-12-31 07:47:21 +0000919}
920
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400921static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
922 __u16 fid, u32 *pacllen)
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000923{
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000924 struct cifs_ntsd *pntsd = NULL;
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400925 int xid, rc;
Jeff Layton7ffec372010-09-29 19:51:11 -0400926 struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
927
928 if (IS_ERR(tlink))
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -0600929 return ERR_CAST(tlink);
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000930
931 xid = GetXid();
Jeff Layton7ffec372010-09-29 19:51:11 -0400932 rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), fid, &pntsd, pacllen);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400933 FreeXid(xid);
Steve French8b1327f2008-03-14 22:37:16 +0000934
Jeff Layton7ffec372010-09-29 19:51:11 -0400935 cifs_put_tlink(tlink);
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000936
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -0600937 cFYI(1, "%s: rc = %d ACL len %d", __func__, rc, *pacllen);
938 if (rc)
939 return ERR_PTR(rc);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400940 return pntsd;
941}
942
943static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
944 const char *path, u32 *pacllen)
945{
946 struct cifs_ntsd *pntsd = NULL;
947 int oplock = 0;
Shirish Pargaonkar3d3ea8e2011-09-26 09:56:44 -0500948 int xid, rc, create_options = 0;
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400949 __u16 fid;
Steve French96daf2b2011-05-27 04:34:02 +0000950 struct cifs_tcon *tcon;
Jeff Layton7ffec372010-09-29 19:51:11 -0400951 struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400952
Jeff Layton7ffec372010-09-29 19:51:11 -0400953 if (IS_ERR(tlink))
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -0600954 return ERR_CAST(tlink);
Jeff Layton7ffec372010-09-29 19:51:11 -0400955
956 tcon = tlink_tcon(tlink);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400957 xid = GetXid();
958
Shirish Pargaonkar3d3ea8e2011-09-26 09:56:44 -0500959 if (backup_cred(cifs_sb))
960 create_options |= CREATE_OPEN_BACKUP_INTENT;
961
962 rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL,
963 create_options, &fid, &oplock, NULL, cifs_sb->local_nls,
964 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -0600965 if (!rc) {
966 rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen);
967 CIFSSMBClose(xid, tcon, fid);
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000968 }
969
Jeff Layton7ffec372010-09-29 19:51:11 -0400970 cifs_put_tlink(tlink);
Steve Frenchb9c7a2b2007-10-26 23:40:20 +0000971 FreeXid(xid);
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -0600972
973 cFYI(1, "%s: rc = %d ACL len %d", __func__, rc, *pacllen);
974 if (rc)
975 return ERR_PTR(rc);
Steve French7505e052007-11-01 18:03:01 +0000976 return pntsd;
977}
978
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400979/* Retrieve an ACL from the server */
Shirish Pargaonkarfbeba8b2010-11-27 11:37:54 -0600980struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400981 struct inode *inode, const char *path,
982 u32 *pacllen)
983{
984 struct cifs_ntsd *pntsd = NULL;
985 struct cifsFileInfo *open_file = NULL;
986
987 if (inode)
Jeff Layton6508d902010-09-29 19:51:11 -0400988 open_file = find_readable_file(CIFS_I(inode), true);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400989 if (!open_file)
990 return get_cifs_acl_by_path(cifs_sb, path, pacllen);
991
992 pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->netfid, pacllen);
Dave Kleikamp6ab409b2009-08-31 11:07:12 -0400993 cifsFileInfo_put(open_file);
Christoph Hellwig1bf40722009-05-27 09:37:33 -0400994 return pntsd;
995}
996
Christoph Hellwigb96d31a2009-05-27 09:37:33 -0400997static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
998 struct cifs_ntsd *pnntsd, u32 acllen)
999{
1000 int oplock = 0;
Shirish Pargaonkar3d3ea8e2011-09-26 09:56:44 -05001001 int xid, rc, create_options = 0;
Steve French97837582007-12-31 07:47:21 +00001002 __u16 fid;
Steve French96daf2b2011-05-27 04:34:02 +00001003 struct cifs_tcon *tcon;
Jeff Layton7ffec372010-09-29 19:51:11 -04001004 struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
Steve French97837582007-12-31 07:47:21 +00001005
Jeff Layton7ffec372010-09-29 19:51:11 -04001006 if (IS_ERR(tlink))
1007 return PTR_ERR(tlink);
1008
1009 tcon = tlink_tcon(tlink);
Steve French97837582007-12-31 07:47:21 +00001010 xid = GetXid();
1011
Shirish Pargaonkar3d3ea8e2011-09-26 09:56:44 -05001012 if (backup_cred(cifs_sb))
1013 create_options |= CREATE_OPEN_BACKUP_INTENT;
1014
1015 rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, WRITE_DAC, create_options,
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001016 &fid, &oplock, NULL, cifs_sb->local_nls,
1017 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
1018 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001019 cERROR(1, "Unable to open file to set ACL");
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001020 goto out;
Steve French97837582007-12-31 07:47:21 +00001021 }
1022
Jeff Layton7ffec372010-09-29 19:51:11 -04001023 rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen);
Joe Perchesb6b38f72010-04-21 03:50:45 +00001024 cFYI(DBG2, "SetCIFSACL rc = %d", rc);
Steve French97837582007-12-31 07:47:21 +00001025
Jeff Layton7ffec372010-09-29 19:51:11 -04001026 CIFSSMBClose(xid, tcon, fid);
1027out:
Steve French97837582007-12-31 07:47:21 +00001028 FreeXid(xid);
Jeff Layton7ffec372010-09-29 19:51:11 -04001029 cifs_put_tlink(tlink);
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001030 return rc;
1031}
Steve French97837582007-12-31 07:47:21 +00001032
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001033/* Set an ACL on the server */
Steve Frenchb73b9a42011-04-19 18:27:10 +00001034int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001035 struct inode *inode, const char *path)
1036{
1037 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001038
Joe Perchesb6b38f72010-04-21 03:50:45 +00001039 cFYI(DBG2, "set ACL for %s from mode 0x%x", path, inode->i_mode);
Christoph Hellwigb96d31a2009-05-27 09:37:33 -04001040
Shirish Pargaonkare22906c2011-08-09 14:30:39 -05001041 return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen);
Steve French97837582007-12-31 07:47:21 +00001042}
1043
Steve French7505e052007-11-01 18:03:01 +00001044/* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -06001045int
Jeff Layton0b8f18e2009-07-09 01:46:37 -04001046cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
1047 struct inode *inode, const char *path, const __u16 *pfid)
Steve French7505e052007-11-01 18:03:01 +00001048{
1049 struct cifs_ntsd *pntsd = NULL;
1050 u32 acllen = 0;
1051 int rc = 0;
1052
Joe Perchesb6b38f72010-04-21 03:50:45 +00001053 cFYI(DBG2, "converting ACL to mode for %s", path);
Christoph Hellwig1bf40722009-05-27 09:37:33 -04001054
1055 if (pfid)
1056 pntsd = get_cifs_acl_by_fid(cifs_sb, *pfid, &acllen);
1057 else
1058 pntsd = get_cifs_acl(cifs_sb, inode, path, &acllen);
Steve French7505e052007-11-01 18:03:01 +00001059
1060 /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -06001061 if (IS_ERR(pntsd)) {
1062 rc = PTR_ERR(pntsd);
1063 cERROR(1, "%s: error %d getting sec desc", __func__, rc);
1064 } else {
Shirish Pargaonkar9409ae52011-04-22 12:09:36 -05001065 rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr);
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -06001066 kfree(pntsd);
1067 if (rc)
1068 cERROR(1, "parse sec desc failed rc = %d", rc);
1069 }
Steve French7505e052007-11-01 18:03:01 +00001070
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -06001071 return rc;
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00001072}
Steve French953f8682007-10-31 04:54:42 +00001073
Steve French7505e052007-11-01 18:03:01 +00001074/* Convert mode bits to an ACL so we can update the ACL on the server */
Shirish Pargaonkar78415d22010-11-27 11:37:26 -06001075int mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode)
Steve French953f8682007-10-31 04:54:42 +00001076{
1077 int rc = 0;
Steve Frenchcce246e2008-04-09 20:55:31 +00001078 __u32 secdesclen = 0;
Steve French97837582007-12-31 07:47:21 +00001079 struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
1080 struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
Steve French953f8682007-10-31 04:54:42 +00001081
Joe Perchesb6b38f72010-04-21 03:50:45 +00001082 cFYI(DBG2, "set ACL from mode for %s", path);
Steve French953f8682007-10-31 04:54:42 +00001083
1084 /* Get the security descriptor */
Christoph Hellwig1bf40722009-05-27 09:37:33 -04001085 pntsd = get_cifs_acl(CIFS_SB(inode->i_sb), inode, path, &secdesclen);
Steve French953f8682007-10-31 04:54:42 +00001086
Steve French97837582007-12-31 07:47:21 +00001087 /* Add three ACEs for owner, group, everyone getting rid of
1088 other ACEs as chmod disables ACEs and set the security descriptor */
Steve French953f8682007-10-31 04:54:42 +00001089
Shirish Pargaonkar987b21d2010-11-10 07:50:35 -06001090 if (IS_ERR(pntsd)) {
1091 rc = PTR_ERR(pntsd);
1092 cERROR(1, "%s: error %d getting sec desc", __func__, rc);
1093 } else {
Steve French97837582007-12-31 07:47:21 +00001094 /* allocate memory for the smb header,
1095 set security descriptor request security descriptor
1096 parameters, and secuirty descriptor itself */
Steve French953f8682007-10-31 04:54:42 +00001097
Steve Frenchcce246e2008-04-09 20:55:31 +00001098 secdesclen = secdesclen < DEFSECDESCLEN ?
1099 DEFSECDESCLEN : secdesclen;
1100 pnntsd = kmalloc(secdesclen, GFP_KERNEL);
Steve French97837582007-12-31 07:47:21 +00001101 if (!pnntsd) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001102 cERROR(1, "Unable to allocate security descriptor");
Steve French97837582007-12-31 07:47:21 +00001103 kfree(pntsd);
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +00001104 return -ENOMEM;
Steve French97837582007-12-31 07:47:21 +00001105 }
Steve French7505e052007-11-01 18:03:01 +00001106
Steve Frenchcce246e2008-04-09 20:55:31 +00001107 rc = build_sec_desc(pntsd, pnntsd, inode, nmode);
Steve French97837582007-12-31 07:47:21 +00001108
Joe Perchesb6b38f72010-04-21 03:50:45 +00001109 cFYI(DBG2, "build_sec_desc rc: %d", rc);
Steve French97837582007-12-31 07:47:21 +00001110
1111 if (!rc) {
1112 /* Set the security descriptor */
Steve Frenchcce246e2008-04-09 20:55:31 +00001113 rc = set_cifs_acl(pnntsd, secdesclen, inode, path);
Joe Perchesb6b38f72010-04-21 03:50:45 +00001114 cFYI(DBG2, "set_cifs_acl rc: %d", rc);
Steve French97837582007-12-31 07:47:21 +00001115 }
1116
1117 kfree(pnntsd);
1118 kfree(pntsd);
1119 }
1120
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +00001121 return rc;
Steve French953f8682007-10-31 04:54:42 +00001122}