|
@@ -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"
|
|
@@ -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)
|
|
|
{
|