Skip to content

Commit

Permalink
cifs: Add idmap key and related data structures and functions (try #1…
Browse files Browse the repository at this point in the history
…7 repost)

Define (global) data structures to store ids, uids and gids, to which a
SID maps.  There are two separate trees, one for SID/uid and another one
for SID/gid.

A new type of key, cifs_idmap_key_type, is used.

Keys are instantiated and searched using credential of the root by
overriding and restoring the credentials of the caller requesting the key.

Id mapping functions are invoked under config option of cifs acl.

Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
  • Loading branch information
Shirish Pargaonkar authored and Steve French committed May 19, 2011
1 parent 9ad1506 commit 4d79dba
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 8 deletions.
138 changes: 138 additions & 0 deletions fs/cifs/cifsacl.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@

#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/keyctl.h>
#include <linux/key-type.h>
#include <keys/user-type.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsacl.h"
Expand Down Expand Up @@ -50,6 +54,140 @@ 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 const struct cred *root_cred;

/*
* Run idmap cache shrinker.
*/
static int
cifs_idmap_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask)
{
/* Use a pruning scheme in a subsequent patch instead */
cifs_destroy_idmaptrees();
return 0;
}

static struct shrinker cifs_shrinker = {
.shrink = cifs_idmap_shrinker,
.seeks = DEFAULT_SEEKS,
};

static int
cifs_idmap_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 inline void
cifs_idmap_key_destroy(struct key *key)
{
kfree(key->payload.data);
}

static
struct key_type cifs_idmap_key_type = {
.name = "cifs.cifs_idmap",
.instantiate = cifs_idmap_key_instantiate,
.destroy = cifs_idmap_key_destroy,
.describe = user_describe,
.match = user_match,
};

int
init_cifs_idmap(void)
{
struct cred *cred;
struct key *keyring;
int ret;

cFYI(1, "Registering the %s key type\n", cifs_idmap_key_type.name);

/* 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 */
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;

register_shrinker(&cifs_shrinker);

cFYI(1, "cifs idmap keyring: %d\n", key_serial(keyring));
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);
cFYI(1, "Unregistered %s key type\n", cifs_idmap_key_type.name);
}

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);
}

int match_sid(struct cifs_sid *ctsid)
{
Expand Down
29 changes: 21 additions & 8 deletions fs/cifs/cifsfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1033,22 +1033,31 @@ init_cifs(void)
if (rc)
goto out_destroy_mids;

rc = register_filesystem(&cifs_fs_type);
if (rc)
goto out_destroy_request_bufs;
#ifdef CONFIG_CIFS_UPCALL
rc = register_key_type(&cifs_spnego_key_type);
if (rc)
goto out_unregister_filesystem;
#endif
goto out_destroy_request_bufs;
#endif /* CONFIG_CIFS_UPCALL */

#ifdef CONFIG_CIFS_ACL
rc = init_cifs_idmap();
if (rc)
goto out_destroy_request_bufs;
#endif /* CONFIG_CIFS_ACL */

rc = register_filesystem(&cifs_fs_type);
if (rc)
goto out_destroy_request_bufs;

return 0;

out_destroy_request_bufs:
#ifdef CONFIG_CIFS_ACL
exit_cifs_idmap();
#endif
#ifdef CONFIG_CIFS_UPCALL
out_unregister_filesystem:
unregister_filesystem(&cifs_fs_type);
unregister_key_type(&cifs_spnego_key_type);
#endif
out_destroy_request_bufs:
cifs_destroy_request_bufs();
out_destroy_mids:
cifs_destroy_mids();
Expand All @@ -1070,6 +1079,10 @@ exit_cifs(void)
#ifdef CONFIG_CIFS_DFS_UPCALL
cifs_dfs_release_automount_timer();
#endif
#ifdef CONFIG_CIFS_ACL
cifs_destroy_idmaptrees();
exit_cifs_idmap();
#endif
#ifdef CONFIG_CIFS_UPCALL
unregister_key_type(&cifs_spnego_key_type);
#endif
Expand Down
5 changes: 5 additions & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,11 @@ GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/
/* reconnect after this many failed echo attempts */
GLOBAL_EXTERN unsigned short echo_retries;

GLOBAL_EXTERN struct rb_root uidtree;
GLOBAL_EXTERN struct rb_root gidtree;
GLOBAL_EXTERN spinlock_t siduidlock;
GLOBAL_EXTERN spinlock_t sidgidlock;

void cifs_oplock_break(struct work_struct *work);
void cifs_oplock_break_get(struct cifsFileInfo *cfile);
void cifs_oplock_break_put(struct cifsFileInfo *cfile);
Expand Down
3 changes: 3 additions & 0 deletions fs/cifs/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ do { \
cFYI(1, "CIFS VFS: leaving %s (xid = %d) rc = %d", \
__func__, curr_xid, (int)rc); \
} while (0)
extern int init_cifs_idmap(void);
extern void exit_cifs_idmap(void);
extern void cifs_destroy_idmaptrees(void);
extern char *build_path_from_dentry(struct dentry *);
extern char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb,
struct cifsTconInfo *tcon);
Expand Down

0 comments on commit 4d79dba

Please sign in to comment.