aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/cifsacl.c
diff options
context:
space:
mode:
authorShirish Pargaonkar <shirishpargaonkar@gmail.com>2011-08-09 15:30:48 -0400
committerSteve French <smfrench@gmail.com>2011-10-13 00:45:39 -0400
commit21fed0d5b763b94a7d1568c27d0cce892ab8d43e (patch)
tree1f621fe114ac43ad536e89dc583ac24d1557cd9a /fs/cifs/cifsacl.c
parent20c3a200c418ea1b02037800830ba8a7cdd1b275 (diff)
cifs: Add data structures and functions for uid/gid to SID mapping (try #4)
Add data structures and functions necessary to map a uid and gid to SID. These functions are very similar to the ones used to map a SID to uid and gid. This time, instead of storing sid to id mapping sorted on a sid value, id to sid is stored, sorted on an id. A cifs upcall sends an id (uid or gid) and expects a SID structure in return, if mapping was done successfully. A failed id to sid mapping to EINVAL. This patchset aims to enable chown and chgrp commands when cifsacl mount option is specified, especially to Windows SMB servers. Currently we can't do that. So now along with chmod command, chown and chgrp work. Winbind is used to map id to a SID. chown and chgrp use an upcall to provide an id to winbind and upcall returns with corrosponding SID if any exists. That SID is used to build security descriptor. The DACL part of a security descriptor is not changed by either chown or chgrp functionality. cifs client maintains a separate caches for uid to SID and gid to SID mapping. This is similar to the one used earlier to map SID to id (as part of ID mapping code). I tested it by mounting shares from a Windows (2003) server by authenticating as two users, one at a time, as Administrator and as a ordinary user. And then attempting to change owner of a file on the share. Depending on the permissions/privileges at the server for that file, chown request fails to either open a file (to change the ownership) or to set security descriptor. So it all depends on privileges on the file at the server and what user you are authenticated as at the server, cifs client is just a conduit. I compared the security descriptor during chown command to that what smbcacls sends when it is used with -M OWNNER: option and they are similar. This patchset aim to enable chown and chgrp commands when cifsacl mount option is specified, especially to Windows SMB servers. Currently we can't do that. So now along with chmod command, chown and chgrp work. I tested it by mounting shares from a Windows (2003) server by authenticating as two users, one at a time, as Administrator and as a ordinary user. And then attempting to change owner of a file on the share. Depending on the permissions/privileges at the server for that file, chown request fails to either open a file (to change the ownership) or to set security descriptor. So it all depends on privileges on the file at the server and what user you are authenticated as at the server, cifs client is just a conduit. Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com> Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs/cifsacl.c')
-rw-r--r--fs/cifs/cifsacl.c198
1 files changed, 198 insertions, 0 deletions
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index b244e07c3048..992f1fb299d1 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -91,9 +91,76 @@ cifs_idmap_shrinker(struct shrinker *shrink, struct shrink_control *sc)
91 shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del); 91 shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
92 spin_unlock(&sidgidlock); 92 spin_unlock(&sidgidlock);
93 93
94 root = &siduidtree;
95 spin_lock(&uidsidlock);
96 shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
97 spin_unlock(&uidsidlock);
98
99 root = &sidgidtree;
100 spin_lock(&gidsidlock);
101 shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
102 spin_unlock(&gidsidlock);
103
94 return nr_rem; 104 return nr_rem;
95} 105}
96 106
107static void
108sid_rb_insert(struct rb_root *root, unsigned long cid,
109 struct cifs_sid_id **psidid, char *typestr)
110{
111 char *strptr;
112 struct rb_node *node = root->rb_node;
113 struct rb_node *parent = NULL;
114 struct rb_node **linkto = &(root->rb_node);
115 struct cifs_sid_id *lsidid;
116
117 while (node) {
118 lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
119 parent = node;
120 if (cid > lsidid->id) {
121 linkto = &(node->rb_left);
122 node = node->rb_left;
123 }
124 if (cid < lsidid->id) {
125 linkto = &(node->rb_right);
126 node = node->rb_right;
127 }
128 }
129
130 (*psidid)->id = cid;
131 (*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
132 (*psidid)->refcount = 0;
133
134 sprintf((*psidid)->sidstr, "%s", typestr);
135 strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
136 sprintf(strptr, "%ld", cid);
137
138 clear_bit(SID_ID_PENDING, &(*psidid)->state);
139 clear_bit(SID_ID_MAPPED, &(*psidid)->state);
140
141 rb_link_node(&(*psidid)->rbnode, parent, linkto);
142 rb_insert_color(&(*psidid)->rbnode, root);
143}
144
145static struct cifs_sid_id *
146sid_rb_search(struct rb_root *root, unsigned long cid)
147{
148 struct rb_node *node = root->rb_node;
149 struct cifs_sid_id *lsidid;
150
151 while (node) {
152 lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
153 if (cid > lsidid->id)
154 node = node->rb_left;
155 else if (cid < lsidid->id)
156 node = node->rb_right;
157 else /* node found */
158 return lsidid;
159 }
160
161 return NULL;
162}
163
97static struct shrinker cifs_shrinker = { 164static struct shrinker cifs_shrinker = {
98 .shrink = cifs_idmap_shrinker, 165 .shrink = cifs_idmap_shrinker,
99 .seeks = DEFAULT_SEEKS, 166 .seeks = DEFAULT_SEEKS,
@@ -110,6 +177,7 @@ cifs_idmap_key_instantiate(struct key *key, const void *data, size_t datalen)
110 177
111 memcpy(payload, data, datalen); 178 memcpy(payload, data, datalen);
112 key->payload.data = payload; 179 key->payload.data = payload;
180 key->datalen = datalen;
113 return 0; 181 return 0;
114} 182}
115 183
@@ -224,6 +292,120 @@ sidid_pending_wait(void *unused)
224} 292}
225 293
226static int 294static int
295id_to_sid(unsigned long cid, uint sidtype, struct cifs_sid *ssid)
296{
297 int rc = 0;
298 struct key *sidkey;
299 const struct cred *saved_cred;
300 struct cifs_sid *lsid;
301 struct cifs_sid_id *psidid, *npsidid;
302 struct rb_root *cidtree;
303 spinlock_t *cidlock;
304
305 if (sidtype == SIDOWNER) {
306 cidlock = &siduidlock;
307 cidtree = &uidtree;
308 } else if (sidtype == SIDGROUP) {
309 cidlock = &sidgidlock;
310 cidtree = &gidtree;
311 } else
312 return -EINVAL;
313
314 spin_lock(cidlock);
315 psidid = sid_rb_search(cidtree, cid);
316
317 if (!psidid) { /* node does not exist, allocate one & attempt adding */
318 spin_unlock(cidlock);
319 npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
320 if (!npsidid)
321 return -ENOMEM;
322
323 npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
324 if (!npsidid->sidstr) {
325 kfree(npsidid);
326 return -ENOMEM;
327 }
328
329 spin_lock(cidlock);
330 psidid = sid_rb_search(cidtree, cid);
331 if (psidid) { /* node happened to get inserted meanwhile */
332 ++psidid->refcount;
333 spin_unlock(cidlock);
334 kfree(npsidid->sidstr);
335 kfree(npsidid);
336 } else {
337 psidid = npsidid;
338 sid_rb_insert(cidtree, cid, &psidid,
339 sidtype == SIDOWNER ? "oi:" : "gi:");
340 ++psidid->refcount;
341 spin_unlock(cidlock);
342 }
343 } else {
344 ++psidid->refcount;
345 spin_unlock(cidlock);
346 }
347
348 /*
349 * If we are here, it is safe to access psidid and its fields
350 * since a reference was taken earlier while holding the spinlock.
351 * A reference on the node is put without holding the spinlock
352 * and it is OK to do so in this case, shrinker will not erase
353 * this node until all references are put and we do not access
354 * any fields of the node after a reference is put .
355 */
356 if (test_bit(SID_ID_MAPPED, &psidid->state)) {
357 memcpy(ssid, &psidid->sid, sizeof(struct cifs_sid));
358 psidid->time = jiffies; /* update ts for accessing */
359 goto id_sid_out;
360 }
361
362 if (time_after(psidid->time + SID_MAP_RETRY, jiffies)) {
363 rc = -EINVAL;
364 goto id_sid_out;
365 }
366
367 if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
368 saved_cred = override_creds(root_cred);
369 sidkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
370 if (IS_ERR(sidkey)) {
371 rc = -EINVAL;
372 cFYI(1, "%s: Can't map and id to a SID", __func__);
373 } else {
374 lsid = (struct cifs_sid *)sidkey->payload.data;
375 memcpy(&psidid->sid, lsid,
376 sidkey->datalen < sizeof(struct cifs_sid) ?
377 sidkey->datalen : sizeof(struct cifs_sid));
378 memcpy(ssid, &psidid->sid,
379 sidkey->datalen < sizeof(struct cifs_sid) ?
380 sidkey->datalen : sizeof(struct cifs_sid));
381 set_bit(SID_ID_MAPPED, &psidid->state);
382 key_put(sidkey);
383 kfree(psidid->sidstr);
384 }
385 psidid->time = jiffies; /* update ts for accessing */
386 revert_creds(saved_cred);
387 clear_bit(SID_ID_PENDING, &psidid->state);
388 wake_up_bit(&psidid->state, SID_ID_PENDING);
389 } else {
390 rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
391 sidid_pending_wait, TASK_INTERRUPTIBLE);
392 if (rc) {
393 cFYI(1, "%s: sidid_pending_wait interrupted %d",
394 __func__, rc);
395 --psidid->refcount;
396 return rc;
397 }
398 if (test_bit(SID_ID_MAPPED, &psidid->state))
399 memcpy(ssid, &psidid->sid, sizeof(struct cifs_sid));
400 else
401 rc = -EINVAL;
402 }
403id_sid_out:
404 --psidid->refcount;
405 return rc;
406}
407
408static int
227sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, 409sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
228 struct cifs_fattr *fattr, uint sidtype) 410 struct cifs_fattr *fattr, uint sidtype)
229{ 411{
@@ -383,6 +565,10 @@ init_cifs_idmap(void)
383 spin_lock_init(&sidgidlock); 565 spin_lock_init(&sidgidlock);
384 gidtree = RB_ROOT; 566 gidtree = RB_ROOT;
385 567
568 spin_lock_init(&uidsidlock);
569 siduidtree = RB_ROOT;
570 spin_lock_init(&gidsidlock);
571 sidgidtree = RB_ROOT;
386 register_shrinker(&cifs_shrinker); 572 register_shrinker(&cifs_shrinker);
387 573
388 cFYI(1, "cifs idmap keyring: %d\n", key_serial(keyring)); 574 cFYI(1, "cifs idmap keyring: %d\n", key_serial(keyring));
@@ -422,6 +608,18 @@ cifs_destroy_idmaptrees(void)
422 while ((node = rb_first(root))) 608 while ((node = rb_first(root)))
423 rb_erase(node, root); 609 rb_erase(node, root);
424 spin_unlock(&sidgidlock); 610 spin_unlock(&sidgidlock);
611
612 root = &siduidtree;
613 spin_lock(&uidsidlock);
614 while ((node = rb_first(root)))
615 rb_erase(node, root);
616 spin_unlock(&uidsidlock);
617
618 root = &sidgidtree;
619 spin_lock(&gidsidlock);
620 while ((node = rb_first(root)))
621 rb_erase(node, root);
622 spin_unlock(&gidsidlock);
425} 623}
426 624
427/* if the two SIDs (roughly equivalent to a UUID for a user or group) are 625/* if the two SIDs (roughly equivalent to a UUID for a user or group) are