2007-09-26 00:17:24 +08:00
|
|
|
/*
|
|
|
|
* fs/cifs/cifsacl.c
|
|
|
|
*
|
2008-03-15 06:37:16 +08:00
|
|
|
* Copyright (C) International Business Machines Corp., 2007,2008
|
2007-09-26 00:17:24 +08:00
|
|
|
* Author(s): Steve French (sfrench@us.ibm.com)
|
|
|
|
*
|
|
|
|
* Contains the routines for mapping CIFS/NTFS ACLs
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Lesser General Public License as published
|
|
|
|
* by the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
|
|
|
* the GNU Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
* along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
2007-09-26 03:53:44 +08:00
|
|
|
#include <linux/fs.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2011-04-28 12:34:35 +08:00
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/keyctl.h>
|
|
|
|
#include <linux/key-type.h>
|
|
|
|
#include <keys/user-type.h>
|
2007-09-26 03:53:44 +08:00
|
|
|
#include "cifspdu.h"
|
|
|
|
#include "cifsglob.h"
|
2007-10-04 02:22:19 +08:00
|
|
|
#include "cifsacl.h"
|
2007-09-26 03:53:44 +08:00
|
|
|
#include "cifsproto.h"
|
|
|
|
#include "cifs_debug.h"
|
|
|
|
|
2010-12-07 04:56:46 +08:00
|
|
|
/* security id for everyone/world system group */
|
2007-10-30 12:45:14 +08:00
|
|
|
static const struct cifs_sid sid_everyone = {
|
|
|
|
1, 1, {0, 0, 0, 0, 0, 1}, {0} };
|
2010-12-07 04:56:46 +08:00
|
|
|
/* security id for Authenticated Users system group */
|
|
|
|
static const struct cifs_sid sid_authusers = {
|
2011-05-28 04:40:18 +08:00
|
|
|
1, 1, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(11)} };
|
2007-09-26 00:17:24 +08:00
|
|
|
/* group users */
|
2008-02-08 07:25:02 +08:00
|
|
|
static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
|
2007-10-04 02:22:19 +08:00
|
|
|
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
const struct cred *root_cred;
|
|
|
|
|
|
|
|
static void
|
|
|
|
shrink_idmap_tree(struct rb_root *root, int nr_to_scan, int *nr_rem,
|
|
|
|
int *nr_del)
|
|
|
|
{
|
|
|
|
struct rb_node *node;
|
|
|
|
struct rb_node *tmp;
|
|
|
|
struct cifs_sid_id *psidid;
|
|
|
|
|
|
|
|
node = rb_first(root);
|
|
|
|
while (node) {
|
|
|
|
tmp = node;
|
|
|
|
node = rb_next(tmp);
|
|
|
|
psidid = rb_entry(tmp, struct cifs_sid_id, rbnode);
|
|
|
|
if (nr_to_scan == 0 || *nr_del == nr_to_scan)
|
|
|
|
++(*nr_rem);
|
|
|
|
else {
|
|
|
|
if (time_after(jiffies, psidid->time + SID_MAP_EXPIRE)
|
|
|
|
&& psidid->refcount == 0) {
|
|
|
|
rb_erase(tmp, root);
|
|
|
|
++(*nr_del);
|
|
|
|
} else
|
|
|
|
++(*nr_rem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-04-28 12:34:35 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Run idmap cache shrinker.
|
|
|
|
*/
|
|
|
|
static int
|
2011-05-29 20:46:08 +08:00
|
|
|
cifs_idmap_shrinker(struct shrinker *shrink, struct shrink_control *sc)
|
2011-04-28 12:34:35 +08:00
|
|
|
{
|
2011-05-29 20:46:08 +08:00
|
|
|
int nr_to_scan = sc->nr_to_scan;
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
int nr_del = 0;
|
|
|
|
int nr_rem = 0;
|
|
|
|
struct rb_root *root;
|
|
|
|
|
|
|
|
root = &uidtree;
|
|
|
|
spin_lock(&siduidlock);
|
|
|
|
shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
|
|
|
|
spin_unlock(&siduidlock);
|
|
|
|
|
|
|
|
root = &gidtree;
|
|
|
|
spin_lock(&sidgidlock);
|
|
|
|
shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
|
|
|
|
spin_unlock(&sidgidlock);
|
|
|
|
|
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>
2011-08-10 03:30:48 +08:00
|
|
|
root = &siduidtree;
|
|
|
|
spin_lock(&uidsidlock);
|
|
|
|
shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
|
|
|
|
spin_unlock(&uidsidlock);
|
|
|
|
|
|
|
|
root = &sidgidtree;
|
|
|
|
spin_lock(&gidsidlock);
|
|
|
|
shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
|
|
|
|
spin_unlock(&gidsidlock);
|
|
|
|
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
return nr_rem;
|
2011-04-28 12:34:35 +08:00
|
|
|
}
|
|
|
|
|
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>
2011-08-10 03:30:48 +08:00
|
|
|
static void
|
|
|
|
sid_rb_insert(struct rb_root *root, unsigned long cid,
|
|
|
|
struct cifs_sid_id **psidid, char *typestr)
|
|
|
|
{
|
|
|
|
char *strptr;
|
|
|
|
struct rb_node *node = root->rb_node;
|
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct rb_node **linkto = &(root->rb_node);
|
|
|
|
struct cifs_sid_id *lsidid;
|
|
|
|
|
|
|
|
while (node) {
|
|
|
|
lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
|
|
|
|
parent = node;
|
|
|
|
if (cid > lsidid->id) {
|
|
|
|
linkto = &(node->rb_left);
|
|
|
|
node = node->rb_left;
|
|
|
|
}
|
|
|
|
if (cid < lsidid->id) {
|
|
|
|
linkto = &(node->rb_right);
|
|
|
|
node = node->rb_right;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(*psidid)->id = cid;
|
|
|
|
(*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
|
|
|
|
(*psidid)->refcount = 0;
|
|
|
|
|
|
|
|
sprintf((*psidid)->sidstr, "%s", typestr);
|
|
|
|
strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
|
|
|
|
sprintf(strptr, "%ld", cid);
|
|
|
|
|
|
|
|
clear_bit(SID_ID_PENDING, &(*psidid)->state);
|
|
|
|
clear_bit(SID_ID_MAPPED, &(*psidid)->state);
|
|
|
|
|
|
|
|
rb_link_node(&(*psidid)->rbnode, parent, linkto);
|
|
|
|
rb_insert_color(&(*psidid)->rbnode, root);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cifs_sid_id *
|
|
|
|
sid_rb_search(struct rb_root *root, unsigned long cid)
|
|
|
|
{
|
|
|
|
struct rb_node *node = root->rb_node;
|
|
|
|
struct cifs_sid_id *lsidid;
|
|
|
|
|
|
|
|
while (node) {
|
|
|
|
lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
|
|
|
|
if (cid > lsidid->id)
|
|
|
|
node = node->rb_left;
|
|
|
|
else if (cid < lsidid->id)
|
|
|
|
node = node->rb_right;
|
|
|
|
else /* node found */
|
|
|
|
return lsidid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-04-28 12:34:35 +08:00
|
|
|
static struct shrinker cifs_shrinker = {
|
|
|
|
.shrink = cifs_idmap_shrinker,
|
|
|
|
.seeks = DEFAULT_SEEKS,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
2012-09-13 20:06:29 +08:00
|
|
|
cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
2011-04-28 12:34:35 +08:00
|
|
|
{
|
|
|
|
char *payload;
|
|
|
|
|
2012-09-13 20:06:29 +08:00
|
|
|
payload = kmalloc(prep->datalen, GFP_KERNEL);
|
2011-04-28 12:34:35 +08:00
|
|
|
if (!payload)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2012-09-13 20:06:29 +08:00
|
|
|
memcpy(payload, prep->data, prep->datalen);
|
2011-04-28 12:34:35 +08:00
|
|
|
key->payload.data = payload;
|
2012-09-13 20:06:29 +08:00
|
|
|
key->datalen = prep->datalen;
|
2011-04-28 12:34:35 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
cifs_idmap_key_destroy(struct key *key)
|
|
|
|
{
|
|
|
|
kfree(key->payload.data);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct key_type cifs_idmap_key_type = {
|
2011-05-06 15:35:00 +08:00
|
|
|
.name = "cifs.idmap",
|
2011-04-28 12:34:35 +08:00
|
|
|
.instantiate = cifs_idmap_key_instantiate,
|
|
|
|
.destroy = cifs_idmap_key_destroy,
|
|
|
|
.describe = user_describe,
|
|
|
|
.match = user_match,
|
|
|
|
};
|
|
|
|
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
static void
|
|
|
|
sid_to_str(struct cifs_sid *sidptr, char *sidstr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
unsigned long saval;
|
|
|
|
char *strptr;
|
|
|
|
|
|
|
|
strptr = sidstr;
|
|
|
|
|
|
|
|
sprintf(strptr, "%s", "S");
|
|
|
|
strptr = sidstr + strlen(sidstr);
|
|
|
|
|
|
|
|
sprintf(strptr, "-%d", sidptr->revision);
|
|
|
|
strptr = sidstr + strlen(sidstr);
|
|
|
|
|
|
|
|
for (i = 0; i < 6; ++i) {
|
|
|
|
if (sidptr->authority[i]) {
|
|
|
|
sprintf(strptr, "-%d", sidptr->authority[i]);
|
|
|
|
strptr = sidstr + strlen(sidstr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < sidptr->num_subauth; ++i) {
|
|
|
|
saval = le32_to_cpu(sidptr->sub_auth[i]);
|
|
|
|
sprintf(strptr, "-%ld", saval);
|
|
|
|
strptr = sidstr + strlen(sidstr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
id_rb_insert(struct rb_root *root, struct cifs_sid *sidptr,
|
|
|
|
struct cifs_sid_id **psidid, char *typestr)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
char *strptr;
|
|
|
|
struct rb_node *node = root->rb_node;
|
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct rb_node **linkto = &(root->rb_node);
|
|
|
|
struct cifs_sid_id *lsidid;
|
|
|
|
|
|
|
|
while (node) {
|
|
|
|
lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
|
|
|
|
parent = node;
|
|
|
|
rc = compare_sids(sidptr, &((lsidid)->sid));
|
|
|
|
if (rc > 0) {
|
|
|
|
linkto = &(node->rb_left);
|
|
|
|
node = node->rb_left;
|
|
|
|
} else if (rc < 0) {
|
|
|
|
linkto = &(node->rb_right);
|
|
|
|
node = node->rb_right;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&(*psidid)->sid, sidptr, sizeof(struct cifs_sid));
|
|
|
|
(*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
|
|
|
|
(*psidid)->refcount = 0;
|
|
|
|
|
|
|
|
sprintf((*psidid)->sidstr, "%s", typestr);
|
|
|
|
strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
|
|
|
|
sid_to_str(&(*psidid)->sid, strptr);
|
|
|
|
|
|
|
|
clear_bit(SID_ID_PENDING, &(*psidid)->state);
|
|
|
|
clear_bit(SID_ID_MAPPED, &(*psidid)->state);
|
|
|
|
|
|
|
|
rb_link_node(&(*psidid)->rbnode, parent, linkto);
|
|
|
|
rb_insert_color(&(*psidid)->rbnode, root);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cifs_sid_id *
|
|
|
|
id_rb_search(struct rb_root *root, struct cifs_sid *sidptr)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct rb_node *node = root->rb_node;
|
|
|
|
struct cifs_sid_id *lsidid;
|
|
|
|
|
|
|
|
while (node) {
|
|
|
|
lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
|
|
|
|
rc = compare_sids(sidptr, &((lsidid)->sid));
|
|
|
|
if (rc > 0) {
|
|
|
|
node = node->rb_left;
|
|
|
|
} else if (rc < 0) {
|
|
|
|
node = node->rb_right;
|
|
|
|
} else /* node found */
|
|
|
|
return lsidid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sidid_pending_wait(void *unused)
|
|
|
|
{
|
|
|
|
schedule();
|
|
|
|
return signal_pending(current) ? -ERESTARTSYS : 0;
|
|
|
|
}
|
|
|
|
|
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>
2011-08-10 03:30:48 +08:00
|
|
|
static int
|
|
|
|
id_to_sid(unsigned long cid, uint sidtype, struct cifs_sid *ssid)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
struct key *sidkey;
|
|
|
|
const struct cred *saved_cred;
|
|
|
|
struct cifs_sid *lsid;
|
|
|
|
struct cifs_sid_id *psidid, *npsidid;
|
|
|
|
struct rb_root *cidtree;
|
|
|
|
spinlock_t *cidlock;
|
|
|
|
|
|
|
|
if (sidtype == SIDOWNER) {
|
|
|
|
cidlock = &siduidlock;
|
|
|
|
cidtree = &uidtree;
|
|
|
|
} else if (sidtype == SIDGROUP) {
|
|
|
|
cidlock = &sidgidlock;
|
|
|
|
cidtree = &gidtree;
|
|
|
|
} else
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
spin_lock(cidlock);
|
|
|
|
psidid = sid_rb_search(cidtree, cid);
|
|
|
|
|
|
|
|
if (!psidid) { /* node does not exist, allocate one & attempt adding */
|
|
|
|
spin_unlock(cidlock);
|
|
|
|
npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
|
|
|
|
if (!npsidid)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
|
|
|
|
if (!npsidid->sidstr) {
|
|
|
|
kfree(npsidid);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock(cidlock);
|
|
|
|
psidid = sid_rb_search(cidtree, cid);
|
|
|
|
if (psidid) { /* node happened to get inserted meanwhile */
|
|
|
|
++psidid->refcount;
|
|
|
|
spin_unlock(cidlock);
|
|
|
|
kfree(npsidid->sidstr);
|
|
|
|
kfree(npsidid);
|
|
|
|
} else {
|
|
|
|
psidid = npsidid;
|
|
|
|
sid_rb_insert(cidtree, cid, &psidid,
|
|
|
|
sidtype == SIDOWNER ? "oi:" : "gi:");
|
|
|
|
++psidid->refcount;
|
|
|
|
spin_unlock(cidlock);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
++psidid->refcount;
|
|
|
|
spin_unlock(cidlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are here, it is safe to access psidid and its fields
|
|
|
|
* since a reference was taken earlier while holding the spinlock.
|
|
|
|
* A reference on the node is put without holding the spinlock
|
|
|
|
* and it is OK to do so in this case, shrinker will not erase
|
|
|
|
* this node until all references are put and we do not access
|
|
|
|
* any fields of the node after a reference is put .
|
|
|
|
*/
|
|
|
|
if (test_bit(SID_ID_MAPPED, &psidid->state)) {
|
|
|
|
memcpy(ssid, &psidid->sid, sizeof(struct cifs_sid));
|
|
|
|
psidid->time = jiffies; /* update ts for accessing */
|
|
|
|
goto id_sid_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (time_after(psidid->time + SID_MAP_RETRY, jiffies)) {
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto id_sid_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
|
|
|
|
saved_cred = override_creds(root_cred);
|
|
|
|
sidkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
|
|
|
|
if (IS_ERR(sidkey)) {
|
|
|
|
rc = -EINVAL;
|
|
|
|
cFYI(1, "%s: Can't map and id to a SID", __func__);
|
|
|
|
} else {
|
|
|
|
lsid = (struct cifs_sid *)sidkey->payload.data;
|
|
|
|
memcpy(&psidid->sid, lsid,
|
|
|
|
sidkey->datalen < sizeof(struct cifs_sid) ?
|
|
|
|
sidkey->datalen : sizeof(struct cifs_sid));
|
|
|
|
memcpy(ssid, &psidid->sid,
|
|
|
|
sidkey->datalen < sizeof(struct cifs_sid) ?
|
|
|
|
sidkey->datalen : sizeof(struct cifs_sid));
|
|
|
|
set_bit(SID_ID_MAPPED, &psidid->state);
|
|
|
|
key_put(sidkey);
|
|
|
|
kfree(psidid->sidstr);
|
|
|
|
}
|
|
|
|
psidid->time = jiffies; /* update ts for accessing */
|
|
|
|
revert_creds(saved_cred);
|
|
|
|
clear_bit(SID_ID_PENDING, &psidid->state);
|
|
|
|
wake_up_bit(&psidid->state, SID_ID_PENDING);
|
|
|
|
} else {
|
|
|
|
rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
|
|
|
|
sidid_pending_wait, TASK_INTERRUPTIBLE);
|
|
|
|
if (rc) {
|
|
|
|
cFYI(1, "%s: sidid_pending_wait interrupted %d",
|
|
|
|
__func__, rc);
|
|
|
|
--psidid->refcount;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
if (test_bit(SID_ID_MAPPED, &psidid->state))
|
|
|
|
memcpy(ssid, &psidid->sid, sizeof(struct cifs_sid));
|
|
|
|
else
|
|
|
|
rc = -EINVAL;
|
|
|
|
}
|
|
|
|
id_sid_out:
|
|
|
|
--psidid->refcount;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
static int
|
|
|
|
sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
|
|
|
|
struct cifs_fattr *fattr, uint sidtype)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
unsigned long cid;
|
|
|
|
struct key *idkey;
|
|
|
|
const struct cred *saved_cred;
|
|
|
|
struct cifs_sid_id *psidid, *npsidid;
|
|
|
|
struct rb_root *cidtree;
|
|
|
|
spinlock_t *cidlock;
|
|
|
|
|
|
|
|
if (sidtype == SIDOWNER) {
|
|
|
|
cid = cifs_sb->mnt_uid; /* default uid, in case upcall fails */
|
|
|
|
cidlock = &siduidlock;
|
|
|
|
cidtree = &uidtree;
|
|
|
|
} else if (sidtype == SIDGROUP) {
|
|
|
|
cid = cifs_sb->mnt_gid; /* default gid, in case upcall fails */
|
|
|
|
cidlock = &sidgidlock;
|
|
|
|
cidtree = &gidtree;
|
|
|
|
} else
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
spin_lock(cidlock);
|
|
|
|
psidid = id_rb_search(cidtree, psid);
|
|
|
|
|
|
|
|
if (!psidid) { /* node does not exist, allocate one & attempt adding */
|
|
|
|
spin_unlock(cidlock);
|
|
|
|
npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
|
|
|
|
if (!npsidid)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
|
|
|
|
if (!npsidid->sidstr) {
|
|
|
|
kfree(npsidid);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock(cidlock);
|
|
|
|
psidid = id_rb_search(cidtree, psid);
|
|
|
|
if (psidid) { /* node happened to get inserted meanwhile */
|
|
|
|
++psidid->refcount;
|
|
|
|
spin_unlock(cidlock);
|
|
|
|
kfree(npsidid->sidstr);
|
|
|
|
kfree(npsidid);
|
|
|
|
} else {
|
|
|
|
psidid = npsidid;
|
|
|
|
id_rb_insert(cidtree, psid, &psidid,
|
|
|
|
sidtype == SIDOWNER ? "os:" : "gs:");
|
|
|
|
++psidid->refcount;
|
|
|
|
spin_unlock(cidlock);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
++psidid->refcount;
|
|
|
|
spin_unlock(cidlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are here, it is safe to access psidid and its fields
|
|
|
|
* since a reference was taken earlier while holding the spinlock.
|
|
|
|
* A reference on the node is put without holding the spinlock
|
|
|
|
* and it is OK to do so in this case, shrinker will not erase
|
|
|
|
* this node until all references are put and we do not access
|
|
|
|
* any fields of the node after a reference is put .
|
|
|
|
*/
|
|
|
|
if (test_bit(SID_ID_MAPPED, &psidid->state)) {
|
|
|
|
cid = psidid->id;
|
|
|
|
psidid->time = jiffies; /* update ts for accessing */
|
|
|
|
goto sid_to_id_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (time_after(psidid->time + SID_MAP_RETRY, jiffies))
|
|
|
|
goto sid_to_id_out;
|
|
|
|
|
|
|
|
if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
|
|
|
|
saved_cred = override_creds(root_cred);
|
|
|
|
idkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
|
|
|
|
if (IS_ERR(idkey))
|
|
|
|
cFYI(1, "%s: Can't map SID to an id", __func__);
|
|
|
|
else {
|
|
|
|
cid = *(unsigned long *)idkey->payload.value;
|
|
|
|
psidid->id = cid;
|
|
|
|
set_bit(SID_ID_MAPPED, &psidid->state);
|
|
|
|
key_put(idkey);
|
|
|
|
kfree(psidid->sidstr);
|
|
|
|
}
|
|
|
|
revert_creds(saved_cred);
|
|
|
|
psidid->time = jiffies; /* update ts for accessing */
|
|
|
|
clear_bit(SID_ID_PENDING, &psidid->state);
|
|
|
|
wake_up_bit(&psidid->state, SID_ID_PENDING);
|
|
|
|
} else {
|
|
|
|
rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
|
|
|
|
sidid_pending_wait, TASK_INTERRUPTIBLE);
|
|
|
|
if (rc) {
|
|
|
|
cFYI(1, "%s: sidid_pending_wait interrupted %d",
|
|
|
|
__func__, rc);
|
|
|
|
--psidid->refcount; /* decremented without spinlock */
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
if (test_bit(SID_ID_MAPPED, &psidid->state))
|
|
|
|
cid = psidid->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
sid_to_id_out:
|
|
|
|
--psidid->refcount; /* decremented without spinlock */
|
|
|
|
if (sidtype == SIDOWNER)
|
|
|
|
fattr->cf_uid = cid;
|
|
|
|
else
|
|
|
|
fattr->cf_gid = cid;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-04-28 12:34:35 +08:00
|
|
|
int
|
|
|
|
init_cifs_idmap(void)
|
|
|
|
{
|
|
|
|
struct cred *cred;
|
|
|
|
struct key *keyring;
|
|
|
|
int ret;
|
|
|
|
|
2012-07-24 01:14:28 +08:00
|
|
|
cFYI(1, "Registering the %s key type", cifs_idmap_key_type.name);
|
2011-04-28 12:34:35 +08:00
|
|
|
|
|
|
|
/* create an override credential set with a special thread keyring in
|
|
|
|
* which requests are cached
|
|
|
|
*
|
|
|
|
* this is used to prevent malicious redirections from being installed
|
|
|
|
* with add_key().
|
|
|
|
*/
|
|
|
|
cred = prepare_kernel_cred(NULL);
|
|
|
|
if (!cred)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
keyring = key_alloc(&key_type_keyring, ".cifs_idmap", 0, 0, cred,
|
|
|
|
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
|
|
|
KEY_USR_VIEW | KEY_USR_READ,
|
|
|
|
KEY_ALLOC_NOT_IN_QUOTA);
|
|
|
|
if (IS_ERR(keyring)) {
|
|
|
|
ret = PTR_ERR(keyring);
|
|
|
|
goto failed_put_cred;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
|
|
|
|
if (ret < 0)
|
|
|
|
goto failed_put_key;
|
|
|
|
|
|
|
|
ret = register_key_type(&cifs_idmap_key_type);
|
|
|
|
if (ret < 0)
|
|
|
|
goto failed_put_key;
|
|
|
|
|
|
|
|
/* instruct request_key() to use this special keyring as a cache for
|
|
|
|
* the results it looks up */
|
2012-01-18 23:31:45 +08:00
|
|
|
set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
|
2011-04-28 12:34:35 +08:00
|
|
|
cred->thread_keyring = keyring;
|
|
|
|
cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
|
|
|
|
root_cred = cred;
|
|
|
|
|
|
|
|
spin_lock_init(&siduidlock);
|
|
|
|
uidtree = RB_ROOT;
|
|
|
|
spin_lock_init(&sidgidlock);
|
|
|
|
gidtree = RB_ROOT;
|
|
|
|
|
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>
2011-08-10 03:30:48 +08:00
|
|
|
spin_lock_init(&uidsidlock);
|
|
|
|
siduidtree = RB_ROOT;
|
|
|
|
spin_lock_init(&gidsidlock);
|
|
|
|
sidgidtree = RB_ROOT;
|
2011-04-28 12:34:35 +08:00
|
|
|
register_shrinker(&cifs_shrinker);
|
|
|
|
|
2012-07-24 01:14:28 +08:00
|
|
|
cFYI(1, "cifs idmap keyring: %d", key_serial(keyring));
|
2011-04-28 12:34:35 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
failed_put_key:
|
|
|
|
key_put(keyring);
|
|
|
|
failed_put_cred:
|
|
|
|
put_cred(cred);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
exit_cifs_idmap(void)
|
|
|
|
{
|
|
|
|
key_revoke(root_cred->thread_keyring);
|
|
|
|
unregister_key_type(&cifs_idmap_key_type);
|
|
|
|
put_cred(root_cred);
|
|
|
|
unregister_shrinker(&cifs_shrinker);
|
2012-07-24 01:14:28 +08:00
|
|
|
cFYI(1, "Unregistered %s key type", cifs_idmap_key_type.name);
|
2011-04-28 12:34:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cifs_destroy_idmaptrees(void)
|
|
|
|
{
|
|
|
|
struct rb_root *root;
|
|
|
|
struct rb_node *node;
|
|
|
|
|
|
|
|
root = &uidtree;
|
|
|
|
spin_lock(&siduidlock);
|
|
|
|
while ((node = rb_first(root)))
|
|
|
|
rb_erase(node, root);
|
|
|
|
spin_unlock(&siduidlock);
|
|
|
|
|
|
|
|
root = &gidtree;
|
|
|
|
spin_lock(&sidgidlock);
|
|
|
|
while ((node = rb_first(root)))
|
|
|
|
rb_erase(node, root);
|
|
|
|
spin_unlock(&sidgidlock);
|
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>
2011-08-10 03:30:48 +08:00
|
|
|
|
|
|
|
root = &siduidtree;
|
|
|
|
spin_lock(&uidsidlock);
|
|
|
|
while ((node = rb_first(root)))
|
|
|
|
rb_erase(node, root);
|
|
|
|
spin_unlock(&uidsidlock);
|
|
|
|
|
|
|
|
root = &sidgidtree;
|
|
|
|
spin_lock(&gidsidlock);
|
|
|
|
while ((node = rb_first(root)))
|
|
|
|
rb_erase(node, root);
|
|
|
|
spin_unlock(&gidsidlock);
|
2011-04-28 12:34:35 +08:00
|
|
|
}
|
2007-10-12 12:11:59 +08:00
|
|
|
|
2007-10-18 06:50:39 +08:00
|
|
|
/* if the two SIDs (roughly equivalent to a UUID for a user or group) are
|
|
|
|
the same returns 1, if they do not match returns 0 */
|
2007-10-26 05:17:17 +08:00
|
|
|
int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
|
2007-10-12 12:11:59 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int num_subauth, num_sat, num_saw;
|
|
|
|
|
|
|
|
if ((!ctsid) || (!cwsid))
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
return 1;
|
2007-10-12 12:11:59 +08:00
|
|
|
|
|
|
|
/* compare the revision */
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
if (ctsid->revision != cwsid->revision) {
|
|
|
|
if (ctsid->revision > cwsid->revision)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
2007-10-12 12:11:59 +08:00
|
|
|
|
|
|
|
/* compare all of the six auth values */
|
|
|
|
for (i = 0; i < 6; ++i) {
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
if (ctsid->authority[i] != cwsid->authority[i]) {
|
|
|
|
if (ctsid->authority[i] > cwsid->authority[i])
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
2007-10-12 12:11:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* compare all of the subauth values if any */
|
2007-10-17 10:12:46 +08:00
|
|
|
num_sat = ctsid->num_subauth;
|
2007-10-17 10:48:17 +08:00
|
|
|
num_saw = cwsid->num_subauth;
|
2007-10-12 12:11:59 +08:00
|
|
|
num_subauth = num_sat < num_saw ? num_sat : num_saw;
|
|
|
|
if (num_subauth) {
|
|
|
|
for (i = 0; i < num_subauth; ++i) {
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
|
2011-05-27 23:19:12 +08:00
|
|
|
if (le32_to_cpu(ctsid->sub_auth[i]) >
|
|
|
|
le32_to_cpu(cwsid->sub_auth[i]))
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
2007-10-12 12:11:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
return 0; /* sids compare/match */
|
2007-10-12 12:11:59 +08:00
|
|
|
}
|
|
|
|
|
2007-12-31 15:47:21 +08:00
|
|
|
|
|
|
|
/* copy ntsd, owner sid, and group sid from a security descriptor to another */
|
|
|
|
static void copy_sec_desc(const struct cifs_ntsd *pntsd,
|
|
|
|
struct cifs_ntsd *pnntsd, __u32 sidsoffset)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
|
|
|
|
struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
|
|
|
|
|
|
|
|
/* copy security descriptor control portion */
|
|
|
|
pnntsd->revision = pntsd->revision;
|
|
|
|
pnntsd->type = pntsd->type;
|
|
|
|
pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd));
|
|
|
|
pnntsd->sacloffset = 0;
|
|
|
|
pnntsd->osidoffset = cpu_to_le32(sidsoffset);
|
|
|
|
pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid));
|
|
|
|
|
|
|
|
/* copy owner sid */
|
|
|
|
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
|
|
|
le32_to_cpu(pntsd->osidoffset));
|
|
|
|
nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
|
|
|
|
|
|
|
|
nowner_sid_ptr->revision = owner_sid_ptr->revision;
|
|
|
|
nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
|
|
nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
|
|
|
|
for (i = 0; i < 5; i++)
|
|
|
|
nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
|
|
|
|
|
|
|
|
/* copy group sid */
|
|
|
|
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
|
|
|
le32_to_cpu(pntsd->gsidoffset));
|
|
|
|
ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
|
|
|
|
sizeof(struct cifs_sid));
|
|
|
|
|
|
|
|
ngroup_sid_ptr->revision = group_sid_ptr->revision;
|
|
|
|
ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
|
|
ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
|
|
|
|
for (i = 0; i < 5; i++)
|
2008-07-24 22:53:20 +08:00
|
|
|
ngroup_sid_ptr->sub_auth[i] = group_sid_ptr->sub_auth[i];
|
2007-12-31 15:47:21 +08:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-26 05:17:17 +08:00
|
|
|
/*
|
|
|
|
change posix mode to reflect permissions
|
|
|
|
pmode is the existing mode (we only want to overwrite part of this
|
|
|
|
bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007
|
|
|
|
*/
|
2007-12-05 16:24:38 +08:00
|
|
|
static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode,
|
2007-11-09 01:57:40 +08:00
|
|
|
umode_t *pbits_to_set)
|
2007-10-26 05:17:17 +08:00
|
|
|
{
|
2007-12-05 16:24:38 +08:00
|
|
|
__u32 flags = le32_to_cpu(ace_flags);
|
2007-11-09 01:57:40 +08:00
|
|
|
/* the order of ACEs is important. The canonical order is to begin with
|
2007-11-09 05:12:01 +08:00
|
|
|
DENY entries followed by ALLOW, otherwise an allow entry could be
|
2007-11-09 01:57:40 +08:00
|
|
|
encountered first, making the subsequent deny entry like "dead code"
|
2007-11-09 05:12:01 +08:00
|
|
|
which would be superflous since Windows stops when a match is made
|
2007-11-09 01:57:40 +08:00
|
|
|
for the operation you are trying to perform for your user */
|
|
|
|
|
|
|
|
/* For deny ACEs we change the mask so that subsequent allow access
|
|
|
|
control entries do not turn on the bits we are denying */
|
|
|
|
if (type == ACCESS_DENIED) {
|
2008-02-08 07:25:02 +08:00
|
|
|
if (flags & GENERIC_ALL)
|
2007-11-09 01:57:40 +08:00
|
|
|
*pbits_to_set &= ~S_IRWXUGO;
|
2008-02-08 07:25:02 +08:00
|
|
|
|
2007-12-05 16:24:38 +08:00
|
|
|
if ((flags & GENERIC_WRITE) ||
|
|
|
|
((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
|
2007-11-09 01:57:40 +08:00
|
|
|
*pbits_to_set &= ~S_IWUGO;
|
2007-12-05 16:24:38 +08:00
|
|
|
if ((flags & GENERIC_READ) ||
|
|
|
|
((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
|
2007-11-09 01:57:40 +08:00
|
|
|
*pbits_to_set &= ~S_IRUGO;
|
2007-12-05 16:24:38 +08:00
|
|
|
if ((flags & GENERIC_EXECUTE) ||
|
|
|
|
((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
|
2007-11-09 01:57:40 +08:00
|
|
|
*pbits_to_set &= ~S_IXUGO;
|
|
|
|
return;
|
|
|
|
} else if (type != ACCESS_ALLOWED) {
|
2010-04-21 11:50:45 +08:00
|
|
|
cERROR(1, "unknown access control type %d", type);
|
2007-11-09 01:57:40 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* else ACCESS_ALLOWED type */
|
2007-10-26 05:17:17 +08:00
|
|
|
|
2007-12-05 16:24:38 +08:00
|
|
|
if (flags & GENERIC_ALL) {
|
2007-11-09 01:57:40 +08:00
|
|
|
*pmode |= (S_IRWXUGO & (*pbits_to_set));
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(DBG2, "all perms");
|
2007-10-26 12:32:43 +08:00
|
|
|
return;
|
|
|
|
}
|
2007-12-05 16:24:38 +08:00
|
|
|
if ((flags & GENERIC_WRITE) ||
|
|
|
|
((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
|
2007-11-09 01:57:40 +08:00
|
|
|
*pmode |= (S_IWUGO & (*pbits_to_set));
|
2007-12-05 16:24:38 +08:00
|
|
|
if ((flags & GENERIC_READ) ||
|
|
|
|
((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
|
2007-11-09 01:57:40 +08:00
|
|
|
*pmode |= (S_IRUGO & (*pbits_to_set));
|
2007-12-05 16:24:38 +08:00
|
|
|
if ((flags & GENERIC_EXECUTE) ||
|
|
|
|
((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
|
2007-11-09 01:57:40 +08:00
|
|
|
*pmode |= (S_IXUGO & (*pbits_to_set));
|
2007-10-26 05:17:17 +08:00
|
|
|
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(DBG2, "access flags 0x%x mode now 0x%x", flags, *pmode);
|
2007-10-26 05:17:17 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-11-09 05:12:01 +08:00
|
|
|
/*
|
|
|
|
Generate access flags to reflect permissions mode is the existing mode.
|
|
|
|
This function is called for every ACE in the DACL whose SID matches
|
|
|
|
with either owner or group or everyone.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
|
|
|
|
__u32 *pace_flags)
|
|
|
|
{
|
|
|
|
/* reset access mask */
|
|
|
|
*pace_flags = 0x0;
|
|
|
|
|
|
|
|
/* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */
|
|
|
|
mode &= bits_to_use;
|
|
|
|
|
|
|
|
/* check for R/W/X UGO since we do not know whose flags
|
|
|
|
is this but we have cleared all the bits sans RWX for
|
|
|
|
either user or group or other as per bits_to_use */
|
|
|
|
if (mode & S_IRUGO)
|
|
|
|
*pace_flags |= SET_FILE_READ_RIGHTS;
|
|
|
|
if (mode & S_IWUGO)
|
|
|
|
*pace_flags |= SET_FILE_WRITE_RIGHTS;
|
|
|
|
if (mode & S_IXUGO)
|
|
|
|
*pace_flags |= SET_FILE_EXEC_RIGHTS;
|
|
|
|
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(DBG2, "mode: 0x%x, access flags now 0x%x", mode, *pace_flags);
|
2007-11-09 05:12:01 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-03-29 11:09:18 +08:00
|
|
|
static __u16 fill_ace_for_sid(struct cifs_ace *pntace,
|
2007-12-31 15:47:21 +08:00
|
|
|
const struct cifs_sid *psid, __u64 nmode, umode_t bits)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
__u16 size = 0;
|
|
|
|
__u32 access_req = 0;
|
|
|
|
|
|
|
|
pntace->type = ACCESS_ALLOWED;
|
|
|
|
pntace->flags = 0x0;
|
|
|
|
mode_to_access_flags(nmode, bits, &access_req);
|
|
|
|
if (!access_req)
|
|
|
|
access_req = SET_MINIMUM_RIGHTS;
|
|
|
|
pntace->access_req = cpu_to_le32(access_req);
|
|
|
|
|
|
|
|
pntace->sid.revision = psid->revision;
|
|
|
|
pntace->sid.num_subauth = psid->num_subauth;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
|
|
pntace->sid.authority[i] = psid->authority[i];
|
|
|
|
for (i = 0; i < psid->num_subauth; i++)
|
|
|
|
pntace->sid.sub_auth[i] = psid->sub_auth[i];
|
|
|
|
|
|
|
|
size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4);
|
|
|
|
pntace->size = cpu_to_le16(size);
|
|
|
|
|
2008-07-24 23:56:05 +08:00
|
|
|
return size;
|
2007-12-31 15:47:21 +08:00
|
|
|
}
|
|
|
|
|
2007-10-12 12:11:59 +08:00
|
|
|
|
2007-10-31 12:54:42 +08:00
|
|
|
#ifdef CONFIG_CIFS_DEBUG2
|
|
|
|
static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
|
2007-10-04 02:22:19 +08:00
|
|
|
{
|
|
|
|
int num_subauth;
|
|
|
|
|
|
|
|
/* validate that we do not go past end of acl */
|
2007-10-12 12:11:59 +08:00
|
|
|
|
2007-10-24 05:22:55 +08:00
|
|
|
if (le16_to_cpu(pace->size) < 16) {
|
2010-04-21 11:50:45 +08:00
|
|
|
cERROR(1, "ACE too small %d", le16_to_cpu(pace->size));
|
2007-10-24 05:22:55 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) {
|
2010-04-21 11:50:45 +08:00
|
|
|
cERROR(1, "ACL too small to parse ACE");
|
2007-10-04 02:22:19 +08:00
|
|
|
return;
|
2007-10-24 05:22:55 +08:00
|
|
|
}
|
2007-10-04 02:22:19 +08:00
|
|
|
|
2007-10-24 05:22:55 +08:00
|
|
|
num_subauth = pace->sid.num_subauth;
|
2007-10-04 02:22:19 +08:00
|
|
|
if (num_subauth) {
|
2007-10-13 02:54:12 +08:00
|
|
|
int i;
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(1, "ACE revision %d num_auth %d type %d flags %d size %d",
|
2007-10-24 05:22:55 +08:00
|
|
|
pace->sid.revision, pace->sid.num_subauth, pace->type,
|
2010-04-21 11:50:45 +08:00
|
|
|
pace->flags, le16_to_cpu(pace->size));
|
2007-10-04 03:43:19 +08:00
|
|
|
for (i = 0; i < num_subauth; ++i) {
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(1, "ACE sub_auth[%d]: 0x%x", i,
|
|
|
|
le32_to_cpu(pace->sid.sub_auth[i]));
|
2007-10-04 03:43:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* BB add length check to make sure that we do not have huge
|
|
|
|
num auths and therefore go off the end */
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2007-10-31 12:54:42 +08:00
|
|
|
#endif
|
2007-10-04 03:43:19 +08:00
|
|
|
|
2007-10-04 02:22:19 +08:00
|
|
|
|
2007-10-18 06:50:39 +08:00
|
|
|
static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
|
2007-10-26 12:32:43 +08:00
|
|
|
struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
|
2009-07-09 13:46:37 +08:00
|
|
|
struct cifs_fattr *fattr)
|
2007-10-04 02:22:19 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int num_aces = 0;
|
|
|
|
int acl_size;
|
|
|
|
char *acl_base;
|
|
|
|
struct cifs_ace **ppace;
|
|
|
|
|
|
|
|
/* BB need to add parm so we can store the SID BB */
|
|
|
|
|
2007-11-25 18:01:00 +08:00
|
|
|
if (!pdacl) {
|
|
|
|
/* no DACL in the security descriptor, set
|
|
|
|
all the permissions for user/group/other */
|
2009-07-09 13:46:37 +08:00
|
|
|
fattr->cf_mode |= S_IRWXUGO;
|
2007-11-25 18:01:00 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-10-04 02:22:19 +08:00
|
|
|
/* validate that we do not go past end of acl */
|
2007-10-17 02:40:37 +08:00
|
|
|
if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
|
2010-04-21 11:50:45 +08:00
|
|
|
cERROR(1, "ACL too small to parse DACL");
|
2007-10-04 02:22:19 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(DBG2, "DACL revision %d size %d num aces %d",
|
2007-10-17 02:40:37 +08:00
|
|
|
le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
|
2010-04-21 11:50:45 +08:00
|
|
|
le32_to_cpu(pdacl->num_aces));
|
2007-10-04 02:22:19 +08:00
|
|
|
|
2007-11-02 02:03:01 +08:00
|
|
|
/* reset rwx permissions for user/group/other.
|
|
|
|
Also, if num_aces is 0 i.e. DACL has no ACEs,
|
|
|
|
user/group/other have no permissions */
|
2009-07-09 13:46:37 +08:00
|
|
|
fattr->cf_mode &= ~(S_IRWXUGO);
|
2007-11-02 02:03:01 +08:00
|
|
|
|
2007-10-04 02:22:19 +08:00
|
|
|
acl_base = (char *)pdacl;
|
|
|
|
acl_size = sizeof(struct cifs_acl);
|
|
|
|
|
2007-10-17 10:12:46 +08:00
|
|
|
num_aces = le32_to_cpu(pdacl->num_aces);
|
2011-10-13 23:26:03 +08:00
|
|
|
if (num_aces > 0) {
|
2007-11-09 01:57:40 +08:00
|
|
|
umode_t user_mask = S_IRWXU;
|
|
|
|
umode_t group_mask = S_IRWXG;
|
2010-12-07 04:56:46 +08:00
|
|
|
umode_t other_mask = S_IRWXU | S_IRWXG | S_IRWXO;
|
2007-11-09 01:57:40 +08:00
|
|
|
|
2012-01-11 15:46:27 +08:00
|
|
|
if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *))
|
|
|
|
return;
|
2007-10-04 02:22:19 +08:00
|
|
|
ppace = kmalloc(num_aces * sizeof(struct cifs_ace *),
|
|
|
|
GFP_KERNEL);
|
2011-02-06 07:05:28 +08:00
|
|
|
if (!ppace) {
|
|
|
|
cERROR(1, "DACL memory allocation error");
|
|
|
|
return;
|
|
|
|
}
|
2007-10-04 02:22:19 +08:00
|
|
|
|
|
|
|
for (i = 0; i < num_aces; ++i) {
|
2007-10-24 05:22:55 +08:00
|
|
|
ppace[i] = (struct cifs_ace *) (acl_base + acl_size);
|
2007-10-31 12:54:42 +08:00
|
|
|
#ifdef CONFIG_CIFS_DEBUG2
|
|
|
|
dump_ace(ppace[i], end_of_acl);
|
|
|
|
#endif
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
if (compare_sids(&(ppace[i]->sid), pownersid) == 0)
|
2007-10-30 12:45:14 +08:00
|
|
|
access_flags_to_mode(ppace[i]->access_req,
|
2007-11-09 01:57:40 +08:00
|
|
|
ppace[i]->type,
|
2009-07-09 13:46:37 +08:00
|
|
|
&fattr->cf_mode,
|
2007-11-09 01:57:40 +08:00
|
|
|
&user_mask);
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0)
|
2007-10-30 12:45:14 +08:00
|
|
|
access_flags_to_mode(ppace[i]->access_req,
|
2007-11-09 01:57:40 +08:00
|
|
|
ppace[i]->type,
|
2009-07-09 13:46:37 +08:00
|
|
|
&fattr->cf_mode,
|
2007-11-09 01:57:40 +08:00
|
|
|
&group_mask);
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0)
|
2007-10-30 12:45:14 +08:00
|
|
|
access_flags_to_mode(ppace[i]->access_req,
|
2007-11-09 01:57:40 +08:00
|
|
|
ppace[i]->type,
|
2009-07-09 13:46:37 +08:00
|
|
|
&fattr->cf_mode,
|
2007-11-09 01:57:40 +08:00
|
|
|
&other_mask);
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)
|
2010-12-07 04:56:46 +08:00
|
|
|
access_flags_to_mode(ppace[i]->access_req,
|
|
|
|
ppace[i]->type,
|
|
|
|
&fattr->cf_mode,
|
|
|
|
&other_mask);
|
|
|
|
|
2007-10-30 12:45:14 +08:00
|
|
|
|
2007-10-24 05:22:55 +08:00
|
|
|
/* memcpy((void *)(&(cifscred->aces[i])),
|
2007-10-04 03:43:19 +08:00
|
|
|
(void *)ppace[i],
|
|
|
|
sizeof(struct cifs_ace)); */
|
2007-10-04 02:22:19 +08:00
|
|
|
|
2007-10-24 05:22:55 +08:00
|
|
|
acl_base = (char *)ppace[i];
|
|
|
|
acl_size = le16_to_cpu(ppace[i]->size);
|
2007-10-04 02:22:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
kfree(ppace);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-09-26 00:17:24 +08:00
|
|
|
|
2007-12-31 15:47:21 +08:00
|
|
|
static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
|
|
|
|
struct cifs_sid *pgrpsid, __u64 nmode)
|
|
|
|
{
|
2008-03-29 11:09:18 +08:00
|
|
|
u16 size = 0;
|
2007-12-31 15:47:21 +08:00
|
|
|
struct cifs_acl *pnndacl;
|
|
|
|
|
|
|
|
pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
|
|
|
|
|
|
|
|
size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
|
|
|
|
pownersid, nmode, S_IRWXU);
|
|
|
|
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
|
|
|
|
pgrpsid, nmode, S_IRWXG);
|
|
|
|
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
|
|
|
|
&sid_everyone, nmode, S_IRWXO);
|
|
|
|
|
|
|
|
pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
|
2008-02-13 04:46:26 +08:00
|
|
|
pndacl->num_aces = cpu_to_le32(3);
|
2007-12-31 15:47:21 +08:00
|
|
|
|
2008-07-24 23:56:05 +08:00
|
|
|
return 0;
|
2007-12-31 15:47:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-26 00:17:24 +08:00
|
|
|
static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
|
|
|
|
{
|
|
|
|
/* BB need to add parm so we can store the SID BB */
|
|
|
|
|
2007-10-27 07:40:20 +08:00
|
|
|
/* validate that we do not go past end of ACL - sid must be at least 8
|
|
|
|
bytes long (assuming no sub-auths - e.g. the null SID */
|
|
|
|
if (end_of_acl < (char *)psid + 8) {
|
2010-04-21 11:50:45 +08:00
|
|
|
cERROR(1, "ACL too small to parse SID %p", psid);
|
2007-09-26 00:17:24 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2007-10-04 02:22:19 +08:00
|
|
|
|
2007-10-17 02:40:37 +08:00
|
|
|
if (psid->num_subauth) {
|
2007-09-26 00:17:24 +08:00
|
|
|
#ifdef CONFIG_CIFS_DEBUG2
|
2007-10-13 02:54:12 +08:00
|
|
|
int i;
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(1, "SID revision %d num_auth %d",
|
|
|
|
psid->revision, psid->num_subauth);
|
2007-09-26 00:17:24 +08:00
|
|
|
|
2007-10-17 02:40:37 +08:00
|
|
|
for (i = 0; i < psid->num_subauth; i++) {
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(1, "SID sub_auth[%d]: 0x%x ", i,
|
|
|
|
le32_to_cpu(psid->sub_auth[i]));
|
2007-10-04 02:22:19 +08:00
|
|
|
}
|
|
|
|
|
2007-10-04 03:43:19 +08:00
|
|
|
/* BB add length check to make sure that we do not have huge
|
2007-10-04 02:22:19 +08:00
|
|
|
num auths and therefore go off the end */
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(1, "RID 0x%x",
|
|
|
|
le32_to_cpu(psid->sub_auth[psid->num_subauth-1]));
|
2007-09-26 00:17:24 +08:00
|
|
|
#endif
|
2007-10-04 02:22:19 +08:00
|
|
|
}
|
|
|
|
|
2007-09-26 00:17:24 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-10-04 02:22:19 +08:00
|
|
|
|
2007-09-26 00:17:24 +08:00
|
|
|
/* Convert CIFS ACL to POSIX form */
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
|
|
|
|
struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr)
|
2007-09-26 00:17:24 +08:00
|
|
|
{
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
int rc = 0;
|
2007-09-26 00:17:24 +08:00
|
|
|
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
|
|
|
|
struct cifs_acl *dacl_ptr; /* no need for SACL ptr */
|
|
|
|
char *end_of_acl = ((char *)pntsd) + acl_len;
|
2007-11-02 02:03:01 +08:00
|
|
|
__u32 dacloffset;
|
2007-09-26 00:17:24 +08:00
|
|
|
|
2009-07-09 13:46:37 +08:00
|
|
|
if (pntsd == NULL)
|
2007-10-27 07:40:20 +08:00
|
|
|
return -EIO;
|
|
|
|
|
2007-09-26 00:17:24 +08:00
|
|
|
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
2007-10-17 02:40:37 +08:00
|
|
|
le32_to_cpu(pntsd->osidoffset));
|
2007-09-26 00:17:24 +08:00
|
|
|
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
2007-10-17 02:40:37 +08:00
|
|
|
le32_to_cpu(pntsd->gsidoffset));
|
2007-11-02 02:03:01 +08:00
|
|
|
dacloffset = le32_to_cpu(pntsd->dacloffset);
|
2007-11-06 05:46:10 +08:00
|
|
|
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(DBG2, "revision %d type 0x%x ooffset 0x%x goffset 0x%x "
|
2007-09-26 00:17:24 +08:00
|
|
|
"sacloffset 0x%x dacloffset 0x%x",
|
2007-10-17 02:40:37 +08:00
|
|
|
pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
|
|
|
|
le32_to_cpu(pntsd->gsidoffset),
|
2010-04-21 11:50:45 +08:00
|
|
|
le32_to_cpu(pntsd->sacloffset), dacloffset);
|
2007-10-27 07:40:20 +08:00
|
|
|
/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */
|
2007-09-26 00:17:24 +08:00
|
|
|
rc = parse_sid(owner_sid_ptr, end_of_acl);
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
if (rc) {
|
|
|
|
cFYI(1, "%s: Error %d parsing Owner SID", __func__, rc);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER);
|
|
|
|
if (rc) {
|
|
|
|
cFYI(1, "%s: Error %d mapping Owner SID to uid", __func__, rc);
|
2007-09-26 00:17:24 +08:00
|
|
|
return rc;
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
}
|
2007-09-26 00:17:24 +08:00
|
|
|
|
|
|
|
rc = parse_sid(group_sid_ptr, end_of_acl);
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
if (rc) {
|
|
|
|
cFYI(1, "%s: Error %d mapping Owner SID to gid", __func__, rc);
|
2007-09-26 00:17:24 +08:00
|
|
|
return rc;
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
}
|
|
|
|
rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP);
|
|
|
|
if (rc) {
|
|
|
|
cFYI(1, "%s: Error %d mapping Group SID to gid", __func__, rc);
|
|
|
|
return rc;
|
|
|
|
}
|
2007-09-26 00:17:24 +08:00
|
|
|
|
2007-11-02 02:03:01 +08:00
|
|
|
if (dacloffset)
|
|
|
|
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
|
2009-07-09 13:46:37 +08:00
|
|
|
group_sid_ptr, fattr);
|
2007-11-02 02:03:01 +08:00
|
|
|
else
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(1, "no ACL"); /* BB grant all or default perms? */
|
2007-10-04 02:22:19 +08:00
|
|
|
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
return rc;
|
2007-09-26 00:17:24 +08:00
|
|
|
}
|
2007-10-27 07:40:20 +08:00
|
|
|
|
2007-12-31 15:47:21 +08:00
|
|
|
/* Convert permission bits from mode to equivalent CIFS ACL */
|
|
|
|
static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
|
2011-10-13 23:26:03 +08:00
|
|
|
__u32 secdesclen, __u64 nmode, uid_t uid, gid_t gid, int *aclflag)
|
2007-12-31 15:47:21 +08:00
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
__u32 dacloffset;
|
|
|
|
__u32 ndacloffset;
|
|
|
|
__u32 sidsoffset;
|
|
|
|
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
|
2011-10-13 23:26:03 +08:00
|
|
|
struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
|
2007-12-31 15:47:21 +08:00
|
|
|
struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */
|
|
|
|
struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
|
|
|
|
|
2011-10-13 23:26:03 +08:00
|
|
|
if (nmode != NO_CHANGE_64) { /* chmod */
|
|
|
|
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
2007-12-31 15:47:21 +08:00
|
|
|
le32_to_cpu(pntsd->osidoffset));
|
2011-10-13 23:26:03 +08:00
|
|
|
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
2007-12-31 15:47:21 +08:00
|
|
|
le32_to_cpu(pntsd->gsidoffset));
|
2011-10-13 23:26:03 +08:00
|
|
|
dacloffset = le32_to_cpu(pntsd->dacloffset);
|
|
|
|
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
|
|
|
|
ndacloffset = sizeof(struct cifs_ntsd);
|
|
|
|
ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
|
|
|
|
ndacl_ptr->revision = dacl_ptr->revision;
|
|
|
|
ndacl_ptr->size = 0;
|
|
|
|
ndacl_ptr->num_aces = 0;
|
|
|
|
|
|
|
|
rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr,
|
|
|
|
nmode);
|
|
|
|
sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
|
|
|
|
/* copy sec desc control portion & owner and group sids */
|
|
|
|
copy_sec_desc(pntsd, pnntsd, sidsoffset);
|
|
|
|
*aclflag = CIFS_ACL_DACL;
|
|
|
|
} else {
|
|
|
|
memcpy(pnntsd, pntsd, secdesclen);
|
|
|
|
if (uid != NO_CHANGE_32) { /* chown */
|
|
|
|
owner_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
|
|
|
|
le32_to_cpu(pnntsd->osidoffset));
|
|
|
|
nowner_sid_ptr = kmalloc(sizeof(struct cifs_sid),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!nowner_sid_ptr)
|
|
|
|
return -ENOMEM;
|
|
|
|
rc = id_to_sid(uid, SIDOWNER, nowner_sid_ptr);
|
|
|
|
if (rc) {
|
|
|
|
cFYI(1, "%s: Mapping error %d for owner id %d",
|
|
|
|
__func__, rc, uid);
|
|
|
|
kfree(nowner_sid_ptr);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
memcpy(owner_sid_ptr, nowner_sid_ptr,
|
|
|
|
sizeof(struct cifs_sid));
|
|
|
|
kfree(nowner_sid_ptr);
|
|
|
|
*aclflag = CIFS_ACL_OWNER;
|
|
|
|
}
|
|
|
|
if (gid != NO_CHANGE_32) { /* chgrp */
|
|
|
|
group_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
|
|
|
|
le32_to_cpu(pnntsd->gsidoffset));
|
|
|
|
ngroup_sid_ptr = kmalloc(sizeof(struct cifs_sid),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!ngroup_sid_ptr)
|
|
|
|
return -ENOMEM;
|
|
|
|
rc = id_to_sid(gid, SIDGROUP, ngroup_sid_ptr);
|
|
|
|
if (rc) {
|
|
|
|
cFYI(1, "%s: Mapping error %d for group id %d",
|
|
|
|
__func__, rc, gid);
|
|
|
|
kfree(ngroup_sid_ptr);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
memcpy(group_sid_ptr, ngroup_sid_ptr,
|
|
|
|
sizeof(struct cifs_sid));
|
|
|
|
kfree(ngroup_sid_ptr);
|
|
|
|
*aclflag = CIFS_ACL_GROUP;
|
|
|
|
}
|
|
|
|
}
|
2007-12-31 15:47:21 +08:00
|
|
|
|
2008-07-24 23:56:05 +08:00
|
|
|
return rc;
|
2007-12-31 15:47:21 +08:00
|
|
|
}
|
|
|
|
|
2009-05-27 21:37:33 +08:00
|
|
|
static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
|
|
|
|
__u16 fid, u32 *pacllen)
|
2007-10-27 07:40:20 +08:00
|
|
|
{
|
|
|
|
struct cifs_ntsd *pntsd = NULL;
|
2012-06-20 15:21:16 +08:00
|
|
|
unsigned int xid;
|
|
|
|
int rc;
|
2010-09-30 07:51:11 +08:00
|
|
|
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
|
|
|
|
|
|
|
|
if (IS_ERR(tlink))
|
2010-11-10 21:50:35 +08:00
|
|
|
return ERR_CAST(tlink);
|
2007-10-27 07:40:20 +08:00
|
|
|
|
2012-06-20 15:21:16 +08:00
|
|
|
xid = get_xid();
|
2010-09-30 07:51:11 +08:00
|
|
|
rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), fid, &pntsd, pacllen);
|
2012-06-20 15:21:16 +08:00
|
|
|
free_xid(xid);
|
2007-10-27 07:40:20 +08:00
|
|
|
|
2010-09-30 07:51:11 +08:00
|
|
|
cifs_put_tlink(tlink);
|
2007-10-27 07:40:20 +08:00
|
|
|
|
2010-11-10 21:50:35 +08:00
|
|
|
cFYI(1, "%s: rc = %d ACL len %d", __func__, rc, *pacllen);
|
|
|
|
if (rc)
|
|
|
|
return ERR_PTR(rc);
|
2009-05-27 21:37:33 +08:00
|
|
|
return pntsd;
|
|
|
|
}
|
2008-03-15 06:37:16 +08:00
|
|
|
|
2009-05-27 21:37:33 +08:00
|
|
|
static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
|
|
|
|
const char *path, u32 *pacllen)
|
|
|
|
{
|
|
|
|
struct cifs_ntsd *pntsd = NULL;
|
|
|
|
int oplock = 0;
|
2012-06-20 15:21:16 +08:00
|
|
|
unsigned int xid;
|
|
|
|
int rc, create_options = 0;
|
2009-05-27 21:37:33 +08:00
|
|
|
__u16 fid;
|
2011-05-27 12:34:02 +08:00
|
|
|
struct cifs_tcon *tcon;
|
2010-09-30 07:51:11 +08:00
|
|
|
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
|
|
|
|
|
|
|
|
if (IS_ERR(tlink))
|
2010-11-10 21:50:35 +08:00
|
|
|
return ERR_CAST(tlink);
|
2007-10-27 07:40:20 +08:00
|
|
|
|
2010-09-30 07:51:11 +08:00
|
|
|
tcon = tlink_tcon(tlink);
|
2012-06-20 15:21:16 +08:00
|
|
|
xid = get_xid();
|
2009-05-27 21:37:33 +08:00
|
|
|
|
2011-09-26 22:56:44 +08:00
|
|
|
if (backup_cred(cifs_sb))
|
|
|
|
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
|
|
|
|
|
|
|
rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL,
|
|
|
|
create_options, &fid, &oplock, NULL, cifs_sb->local_nls,
|
|
|
|
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
2010-11-10 21:50:35 +08:00
|
|
|
if (!rc) {
|
|
|
|
rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen);
|
|
|
|
CIFSSMBClose(xid, tcon, fid);
|
2007-10-27 07:40:20 +08:00
|
|
|
}
|
|
|
|
|
2010-09-30 07:51:11 +08:00
|
|
|
cifs_put_tlink(tlink);
|
2012-06-20 15:21:16 +08:00
|
|
|
free_xid(xid);
|
2010-11-10 21:50:35 +08:00
|
|
|
|
|
|
|
cFYI(1, "%s: rc = %d ACL len %d", __func__, rc, *pacllen);
|
|
|
|
if (rc)
|
|
|
|
return ERR_PTR(rc);
|
2007-11-02 02:03:01 +08:00
|
|
|
return pntsd;
|
|
|
|
}
|
|
|
|
|
2009-05-27 21:37:33 +08:00
|
|
|
/* Retrieve an ACL from the server */
|
2010-11-28 01:37:54 +08:00
|
|
|
struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
|
2009-05-27 21:37:33 +08:00
|
|
|
struct inode *inode, const char *path,
|
|
|
|
u32 *pacllen)
|
|
|
|
{
|
|
|
|
struct cifs_ntsd *pntsd = NULL;
|
|
|
|
struct cifsFileInfo *open_file = NULL;
|
|
|
|
|
|
|
|
if (inode)
|
2010-09-30 07:51:11 +08:00
|
|
|
open_file = find_readable_file(CIFS_I(inode), true);
|
2009-05-27 21:37:33 +08:00
|
|
|
if (!open_file)
|
|
|
|
return get_cifs_acl_by_path(cifs_sb, path, pacllen);
|
|
|
|
|
2012-09-19 07:20:26 +08:00
|
|
|
pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->fid.netfid, pacllen);
|
2009-08-31 23:07:12 +08:00
|
|
|
cifsFileInfo_put(open_file);
|
2009-05-27 21:37:33 +08:00
|
|
|
return pntsd;
|
|
|
|
}
|
|
|
|
|
2011-10-13 23:26:03 +08:00
|
|
|
/* Set an ACL on the server */
|
|
|
|
int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
|
|
|
|
struct inode *inode, const char *path, int aclflag)
|
2009-05-27 21:37:33 +08:00
|
|
|
{
|
|
|
|
int oplock = 0;
|
2012-06-20 15:21:16 +08:00
|
|
|
unsigned int xid;
|
|
|
|
int rc, access_flags, create_options = 0;
|
2009-05-27 21:37:33 +08:00
|
|
|
__u16 fid;
|
2011-05-27 12:34:02 +08:00
|
|
|
struct cifs_tcon *tcon;
|
2011-10-13 23:26:03 +08:00
|
|
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
2010-09-30 07:51:11 +08:00
|
|
|
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
|
2007-12-31 15:47:21 +08:00
|
|
|
|
2010-09-30 07:51:11 +08:00
|
|
|
if (IS_ERR(tlink))
|
|
|
|
return PTR_ERR(tlink);
|
|
|
|
|
|
|
|
tcon = tlink_tcon(tlink);
|
2012-06-20 15:21:16 +08:00
|
|
|
xid = get_xid();
|
2007-12-31 15:47:21 +08:00
|
|
|
|
2011-09-26 22:56:44 +08:00
|
|
|
if (backup_cred(cifs_sb))
|
|
|
|
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
|
|
|
|
2011-10-13 23:26:03 +08:00
|
|
|
if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP)
|
|
|
|
access_flags = WRITE_OWNER;
|
|
|
|
else
|
|
|
|
access_flags = WRITE_DAC;
|
|
|
|
|
|
|
|
rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, access_flags,
|
|
|
|
create_options, &fid, &oplock, NULL, cifs_sb->local_nls,
|
|
|
|
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
2009-05-27 21:37:33 +08:00
|
|
|
if (rc) {
|
2010-04-21 11:50:45 +08:00
|
|
|
cERROR(1, "Unable to open file to set ACL");
|
2009-05-27 21:37:33 +08:00
|
|
|
goto out;
|
2007-12-31 15:47:21 +08:00
|
|
|
}
|
|
|
|
|
2011-10-13 23:26:03 +08:00
|
|
|
rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen, aclflag);
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(DBG2, "SetCIFSACL rc = %d", rc);
|
2007-12-31 15:47:21 +08:00
|
|
|
|
2010-09-30 07:51:11 +08:00
|
|
|
CIFSSMBClose(xid, tcon, fid);
|
|
|
|
out:
|
2012-06-20 15:21:16 +08:00
|
|
|
free_xid(xid);
|
2010-09-30 07:51:11 +08:00
|
|
|
cifs_put_tlink(tlink);
|
2009-05-27 21:37:33 +08:00
|
|
|
return rc;
|
|
|
|
}
|
2007-12-31 15:47:21 +08:00
|
|
|
|
2007-11-02 02:03:01 +08:00
|
|
|
/* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
|
2010-11-10 21:50:35 +08:00
|
|
|
int
|
2009-07-09 13:46:37 +08:00
|
|
|
cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
|
|
|
|
struct inode *inode, const char *path, const __u16 *pfid)
|
2007-11-02 02:03:01 +08:00
|
|
|
{
|
|
|
|
struct cifs_ntsd *pntsd = NULL;
|
|
|
|
u32 acllen = 0;
|
|
|
|
int rc = 0;
|
|
|
|
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(DBG2, "converting ACL to mode for %s", path);
|
2009-05-27 21:37:33 +08:00
|
|
|
|
|
|
|
if (pfid)
|
|
|
|
pntsd = get_cifs_acl_by_fid(cifs_sb, *pfid, &acllen);
|
|
|
|
else
|
|
|
|
pntsd = get_cifs_acl(cifs_sb, inode, path, &acllen);
|
2007-11-02 02:03:01 +08:00
|
|
|
|
|
|
|
/* if we can retrieve the ACL, now parse Access Control Entries, ACEs */
|
2010-11-10 21:50:35 +08:00
|
|
|
if (IS_ERR(pntsd)) {
|
|
|
|
rc = PTR_ERR(pntsd);
|
|
|
|
cERROR(1, "%s: error %d getting sec desc", __func__, rc);
|
|
|
|
} else {
|
cifs: Invoke id mapping functions (try #17 repost)
rb tree search and insertion routines.
A SID which needs to be mapped, is looked up in one of the rb trees
depending on whether SID is either owner or group SID.
If found in the tree, a (mapped) id from that node is assigned to
uid or gid as appropriate. If unmapped, an upcall is attempted to
map the SID to an id. If upcall is successful, node is marked as
mapped. If upcall fails, node stays marked as unmapped and a mapping
is attempted again only after an arbitrary time period has passed.
To map a SID, which can be either a Owner SID or a Group SID, key
description starts with the string "os" or "gs" followed by SID converted
to a string. Without "os" or "gs", cifs.upcall does not know whether
SID needs to be mapped to either an uid or a gid.
Nodes in rb tree have fields to prevent multiple upcalls for
a SID. Searching, adding, and removing nodes is done within global locks.
Whenever a node is either found or inserted in a tree, a reference
is taken on that node.
Shrinker routine prunes a node if it has expired but does not prune
an expired node if its refcount is not zero (i.e. sid/id of that node
is_being/will_be accessed).
Thus a node, if its SID needs to be mapped by making an upcall,
can safely stay and its fields accessed without shrinker pruning it.
A reference (refcount) is put on the node without holding the spinlock
but a reference is get on the node by holding the spinlock.
Every time an existing mapped node is accessed or mapping is attempted,
its timestamp is updated to prevent it from getting erased or a
to prevent multiple unnecessary repeat mapping retries respectively.
For now, cifs.upcall is only used to map a SID to an id (uid or gid) but
it would be used to obtain an SID for an id.
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
2011-04-23 01:09:36 +08:00
|
|
|
rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr);
|
2010-11-10 21:50:35 +08:00
|
|
|
kfree(pntsd);
|
|
|
|
if (rc)
|
|
|
|
cERROR(1, "parse sec desc failed rc = %d", rc);
|
|
|
|
}
|
2007-11-02 02:03:01 +08:00
|
|
|
|
2010-11-10 21:50:35 +08:00
|
|
|
return rc;
|
2007-10-27 07:40:20 +08:00
|
|
|
}
|
2007-10-31 12:54:42 +08:00
|
|
|
|
2007-11-02 02:03:01 +08:00
|
|
|
/* Convert mode bits to an ACL so we can update the ACL on the server */
|
2011-10-13 23:26:03 +08:00
|
|
|
int
|
|
|
|
id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,
|
|
|
|
uid_t uid, gid_t gid)
|
2007-10-31 12:54:42 +08:00
|
|
|
{
|
|
|
|
int rc = 0;
|
2011-10-13 23:26:03 +08:00
|
|
|
int aclflag = CIFS_ACL_DACL; /* default flag to set */
|
2008-04-10 04:55:31 +08:00
|
|
|
__u32 secdesclen = 0;
|
2007-12-31 15:47:21 +08:00
|
|
|
struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
|
|
|
|
struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
|
2007-10-31 12:54:42 +08:00
|
|
|
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(DBG2, "set ACL from mode for %s", path);
|
2007-10-31 12:54:42 +08:00
|
|
|
|
|
|
|
/* Get the security descriptor */
|
2009-05-27 21:37:33 +08:00
|
|
|
pntsd = get_cifs_acl(CIFS_SB(inode->i_sb), inode, path, &secdesclen);
|
2007-10-31 12:54:42 +08:00
|
|
|
|
2007-12-31 15:47:21 +08:00
|
|
|
/* Add three ACEs for owner, group, everyone getting rid of
|
|
|
|
other ACEs as chmod disables ACEs and set the security descriptor */
|
2007-10-31 12:54:42 +08:00
|
|
|
|
2010-11-10 21:50:35 +08:00
|
|
|
if (IS_ERR(pntsd)) {
|
|
|
|
rc = PTR_ERR(pntsd);
|
|
|
|
cERROR(1, "%s: error %d getting sec desc", __func__, rc);
|
|
|
|
} else {
|
2007-12-31 15:47:21 +08:00
|
|
|
/* allocate memory for the smb header,
|
|
|
|
set security descriptor request security descriptor
|
|
|
|
parameters, and secuirty descriptor itself */
|
2007-10-31 12:54:42 +08:00
|
|
|
|
2008-04-10 04:55:31 +08:00
|
|
|
secdesclen = secdesclen < DEFSECDESCLEN ?
|
|
|
|
DEFSECDESCLEN : secdesclen;
|
|
|
|
pnntsd = kmalloc(secdesclen, GFP_KERNEL);
|
2007-12-31 15:47:21 +08:00
|
|
|
if (!pnntsd) {
|
2010-04-21 11:50:45 +08:00
|
|
|
cERROR(1, "Unable to allocate security descriptor");
|
2007-12-31 15:47:21 +08:00
|
|
|
kfree(pntsd);
|
2008-07-24 23:56:05 +08:00
|
|
|
return -ENOMEM;
|
2007-12-31 15:47:21 +08:00
|
|
|
}
|
2007-11-02 02:03:01 +08:00
|
|
|
|
2011-10-13 23:26:03 +08:00
|
|
|
rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
|
|
|
|
&aclflag);
|
2007-12-31 15:47:21 +08:00
|
|
|
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(DBG2, "build_sec_desc rc: %d", rc);
|
2007-12-31 15:47:21 +08:00
|
|
|
|
|
|
|
if (!rc) {
|
|
|
|
/* Set the security descriptor */
|
2011-10-13 23:26:03 +08:00
|
|
|
rc = set_cifs_acl(pnntsd, secdesclen, inode,
|
|
|
|
path, aclflag);
|
2010-04-21 11:50:45 +08:00
|
|
|
cFYI(DBG2, "set_cifs_acl rc: %d", rc);
|
2007-12-31 15:47:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
kfree(pnntsd);
|
|
|
|
kfree(pntsd);
|
|
|
|
}
|
|
|
|
|
2008-07-24 23:56:05 +08:00
|
|
|
return rc;
|
2007-10-31 12:54:42 +08:00
|
|
|
}
|