From patchwork Thu Apr 21 14:15:40 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shirish Pargaonkar X-Patchwork-Id: 724921 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p3LECQLm011169 for ; Thu, 21 Apr 2011 14:12:26 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752859Ab1DUOM0 (ORCPT ); Thu, 21 Apr 2011 10:12:26 -0400 Received: from mail-yw0-f46.google.com ([209.85.213.46]:62280 "EHLO mail-yw0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750981Ab1DUOMZ (ORCPT ); Thu, 21 Apr 2011 10:12:25 -0400 Received: by ywj3 with SMTP id 3so478620ywj.19 for ; Thu, 21 Apr 2011 07:12:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:from:to:cc:subject:date:message-id:x-mailer; bh=9Mp+ASTAuHzndK/UTcCJTLQPCM1Bu/bHP0CvueOVv8k=; b=i6pA8d8Cj3i44RQLAImyUKqcjvi9xSWoh4prJK4l1HEcS6heCH8b+f54tmEpOZA8/4 XYX/2hTyHcMPS4d7hKF9gBm3QlToc21xF7+kJwES/16kbd2fRe9GCK4iZ3Ab7tR/UlQX JYjUhnQrg0/5f39xlvsKs9tYFTTQijErZm7kY= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer; b=aXqPAXjyDk0nPSCUfBW1VIkedeBAWKm79u+oQ418SgtdFaGCn5Kz6eGQiVLV6ua9fe rn6OzSal/U9ehypCqzu06fnKetmSAzxeaenAGIGQP8zGC+B8nKZecA4ruBfvDPQ71lk5 aUhTzBwuLAcjF3+XrZXo3+O3Apzd73CLsFx6E= Received: by 10.150.26.17 with SMTP id 17mr486114ybz.437.1303395144333; Thu, 21 Apr 2011 07:12:24 -0700 (PDT) Received: from localhost ([32.97.110.58]) by mx.google.com with ESMTPS id u37sm2109218yba.22.2011.04.21.07.12.21 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 21 Apr 2011 07:12:22 -0700 (PDT) From: shirishpargaonkar@gmail.com To: smfrench@gmail.com Cc: linux-cifs@vger.kernel.org, Shirish Pargaonkar Subject: [PATCH 1/2] cifs: Add idmap key and related data structures and functions (try #17) Date: Thu, 21 Apr 2011 09:15:40 -0500 Message-Id: <1303395340-26664-1-git-send-email-shirishpargaonkar@gmail.com> X-Mailer: git-send-email 1.6.0.2 Sender: linux-cifs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-cifs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Thu, 21 Apr 2011 14:12:26 +0000 (UTC) From: Shirish Pargaonkar 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. Signed-off-by: Shirish Pargaonkar Reviewed-by: Jeff Layton --- fs/cifs/cifsacl.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/cifsfs.c | 7 +++ fs/cifs/cifsglob.h | 5 ++ fs/cifs/cifsproto.h | 3 + 4 files changed, 153 insertions(+), 0 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index a0d11ea..061fc3a 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -23,6 +23,10 @@ #include #include +#include +#include +#include +#include #include "cifspdu.h" #include "cifsglob.h" #include "cifsacl.h" @@ -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) { diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 30fc505..665bf1d 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -1040,11 +1040,16 @@ init_cifs(void) rc = register_key_type(&cifs_spnego_key_type); if (rc) goto out_unregister_filesystem; + rc = init_cifs_idmap(); + if (rc) + goto out_unregister_keytype; #endif return 0; #ifdef CONFIG_CIFS_UPCALL +out_unregister_keytype: + unregister_key_type(&cifs_spnego_key_type); out_unregister_filesystem: unregister_filesystem(&cifs_fs_type); #endif @@ -1071,6 +1076,8 @@ exit_cifs(void) cifs_dfs_release_automount_timer(); #endif #ifdef CONFIG_CIFS_UPCALL + cifs_destroy_idmaptrees(); + exit_cifs_idmap(); unregister_key_type(&cifs_spnego_key_type); #endif unregister_filesystem(&cifs_fs_type); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 108a1e9..76b4517 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -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); diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 0e4e057..7c1ed01 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -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);