[PATCH] cifs: Support for an upcall to map SID to an uid and a gid
Shirish Pargaonkar
shirishpargaonkar at gmail.com
Sat Dec 11 14:47:12 MST 2010
On Sat, Dec 11, 2010 at 10:17 AM, Jeff Layton <jlayton at samba.org> wrote:
> On Tue, 7 Dec 2010 11:11:12 -0600
> shirishpargaonkar at gmail.com wrote:
>
>> From: Shirish Pargaonkar <shirishpargaonkar at gmail.com>
>>
>>
>> Use a cifs upcall to map a SID to either an uid or a gid using
>> winbind.
>>
>> There is a corrosponding patch for the cifs.upcall binary in cifs-utils rpm
>> that is being posted also.
>>
>> A new type of key, cifs_acl_key_type, is used.
>> To map a SID, which can be either a Onwer 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.
>>
>> Once a key is instantiated, get rid of it since cifs does not need to
>> use in any way.
>> winbind does the id mapping and looks up name for the newly mapped
>> SID/uid or SID/gid combination.
>>
>> 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 (string) for an id.
>>
>> An entry such as this
>>
>> create cifs.cifs_acl * * /usr/sbin/cifs.upcall %k
>>
>> is needed in the file /etc/request-key.conf.
>>
>>
>> Signed-off-by: Shirish Pargaonkar <shirishpargaonkar at gmail.com>
>> ---
>> fs/cifs/cifsacl.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++-----
>> fs/cifs/cifsacl.h | 8 ++++
>> fs/cifs/cifsfs.c | 7 +++
>> 3 files changed, 122 insertions(+), 10 deletions(-)
>>
>> diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
>> index a520091..d3ac6c8 100644
>> --- a/fs/cifs/cifsacl.c
>> +++ b/fs/cifs/cifsacl.c
>> @@ -23,6 +23,9 @@
>>
>> #include <linux/fs.h>
>> #include <linux/slab.h>
>> +#include <linux/string.h>
>> +#include <keys/user-type.h>
>> +#include <linux/key-type.h>
>> #include "cifspdu.h"
>> #include "cifsglob.h"
>> #include "cifsacl.h"
>> @@ -52,6 +55,102 @@ static const struct cifs_sid sid_authusers = {
>> /* group users */
>> static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
>>
>> +static int
>> +cifs_acl_key_instantiate(struct key *key, const void *data, size_t datalen)
>> +{
>> + char *payload;
>> +
>> + payload = kmalloc(datalen, GFP_KERNEL);
>> + if (!payload)
>> + return -ENOMEM;
>> +
>> + memcpy(payload, data, datalen);
>> + key->payload.data = payload;
>> + return 0;
>> +}
>> +
>> +static void
>> +cifs_acl_key_destroy(struct key *key)
>> +{
>> + kfree(key->payload.data);
>> +}
>> +
>> +struct key_type cifs_acl_key_type = {
>> + .name = "cifs.cifs_acl",
>> + .instantiate = cifs_acl_key_instantiate,
>> + .destroy = cifs_acl_key_destroy,
>> + .describe = user_describe,
>> + .match = user_match,
>> +};
>> +
>
> Nit: these don't have so much to do with ACL's per-se, as much as
> idmapping. Maybe these functions and the key type should be called
> "cifs_idmap_*" and "cifs.idmap" ? It might be clearer to admins what
> this is for.
Yes, will change the name as you suggested, that sounds right, rather
that acl.
>
>> +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 int
>> +sid_to_id(struct cifs_sid *psid, struct cifs_fattr *fattr, uint sidtype)
>> +{
>> + int rc = 0;
>> + char *sidstr, *strptr;
>> + struct key *idkey;
>> +
>> + sidstr = kzalloc(SIDLEN, GFP_KERNEL);
>> + if (!sidstr)
>> + return -ENOMEM;
>> + strptr = sidstr;
>> +
>> + if (sidtype == SIDOWNER)
>> + sprintf(strptr, "%s", "os:");
>> + else if (sidtype == SIDGROUP)
>> + sprintf(strptr, "%s", "gs:");
>> + else {
>> + rc = -EINVAL;
>> + goto idresolve_err;
>> + }
>> + strptr = sidstr + strlen(sidstr);
>> +
>> + sid_to_str(psid, strptr);
>> +
>> + idkey = request_key(&cifs_acl_key_type, sidstr, "");
>> + if (IS_ERR(idkey))
>> + cFYI(1, "%s: idkey error: %d\n", __func__, -ENOKEY);
>> + else {
>> + if (sidtype == SIDOWNER)
>> + fattr->cf_uid = *(unsigned long *)idkey->payload.value;
>> + else if (sidtype == SIDGROUP)
>> + fattr->cf_gid = *(unsigned long *)idkey->payload.value;
>> + key_put(idkey);
>> + }
>> +
>> +idresolve_err:
>> + kfree(sidstr);
>> + return rc;
>> +}
>>
>
> What about security? With this code, it'll be possible for a user to
> "stuff" the cache with idmapping. In the situation where the kernel is
> trying to enforce permissions on the client, it'll be possible to
> trick it into mapping a sid to a uid of your choosing. I think you need
> to guard against that.
That means cifs module will have to know what was the range of ids for
uids and gids to be allocated by winbind as dictated by entries in
smb.conf and if a returned id happens not to be in that range, discard it
and assign default id of 0 (root).
winbind know the range from smb.conf, it could be any smb.conf file,
not necessarily under /etc/samba/smb.conf.
How would cifs module know that range.
Not sure if winbind has an API to query the ranges. And even if it had,
it is possible that winbind deamon itself is not running.
>
> You'll also be doing an upcall every time a different user needs to map
> a SID to a UID/GID.
>
> Finally, calling into the keys API every time you want to map an ID
> sounds rather inefficient. This might take quite some time if you were
> doing "ls -l" on a large directory.
>
> Would it be better to consider taking the info in the key and
> populating a different cache? You could register it with a shrinker and
> prune off LRU entries if memory gets tight.
>
Will look into this. One thing that concerns me is if a cached etnry
for a SID with its name and an id (either an uid or a gid), if that SID
now represents a different object and has differernt name, would
not cached info be incorrect? Not sure if this can ever happen
or how would it happen and if it does, what would be a trigger
for a cache revalidation and purges!
> --
> Jeff Layton <jlayton at samba.org>
>
More information about the samba-technical
mailing list