[PATCH 16/17] KEYS: Add a function by which the contents of a keyringcan be enumerated

From: David Howells
Date: Tue Jun 16 2009 - 16:44:08 EST


Add a function by which the contents of a keyring can be enumerated.

This allows AFS's VIOCGETTOK/PGetTokens pioctl to list the AFS RxRPC keys on
behalf of userspace.

The following text is added to Documentation/keys.txt:

(*) The contents of a keyring may be enumerated by the following function:

typedef bool (*keyring_enum_filter_t)(const struct key *key,
void *filter_data);
key_ref_t keyring_enum(key_ref_t keyring_ref,
unsigned skip,
keyring_enum_filter_t filter,
void *filter_data,
key_perm_t perm)

This scans the keyring in question for keys for which the caller has the
specified permissions and that match the filter provided. It returns a
reference to the first of those keys, after the specified quantity of them
have been skipped. If no key is found error ENOKEY will be returned.

If the keyring is invalid or unsearchable, error ENOTDIR or EACCES will be
returned.

The filter function should return true if the key it is passed is a match,
and false if it is not. The filter_data passed to keyring_enum() will be
passed on to the filter function.

Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
---

Documentation/keys.txt | 23 +++++++++++++++++
include/linux/key.h | 5 ++++
security/keys/keyring.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 93 insertions(+), 0 deletions(-)


diff --git a/Documentation/keys.txt b/Documentation/keys.txt
index 35618d1..77bbe07 100644
--- a/Documentation/keys.txt
+++ b/Documentation/keys.txt
@@ -947,6 +947,29 @@ payload contents" for more information.
reference pointer if successful.


+(*) The contents of a keyring may be enumerated by the following function:
+
+ typedef bool (*keyring_enum_filter_t)(const struct key *key,
+ void *filter_data);
+ key_ref_t keyring_enum(key_ref_t keyring_ref,
+ unsigned skip,
+ keyring_enum_filter_t filter,
+ void *filter_data,
+ key_perm_t perm)
+
+ This scans the keyring in question for keys for which the caller has the
+ specified permissions and that match the filter provided. It returns a
+ reference to the first of those keys, after the specified quantity of them
+ have been skipped. If no key is found error ENOKEY will be returned.
+
+ If the keyring is invalid or unsearchable, error ENOTDIR or EACCES will be
+ returned.
+
+ The filter function should return true if the key it is passed is a match,
+ and false if it is not. The filter_data passed to keyring_enum() will be
+ passed on to the filter function.
+
+
(*) To check the validity of a key, this function can be called:

int validate_key(struct key *key);
diff --git a/include/linux/key.h b/include/linux/key.h
index 4d8cc1e..6d41a4e 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -273,6 +273,11 @@ extern key_ref_t keyring_search(key_ref_t keyring,
extern int keyring_add_key(struct key *keyring,
struct key *key);

+typedef bool (*keyring_enum_filter_t)(const struct key *key, void *data);
+extern key_ref_t keyring_enum(key_ref_t keyring_r, unsigned skip,
+ keyring_enum_filter_t filter, void *filter_data,
+ key_perm_t perm);
+
extern struct key *key_lookup(key_serial_t id);

static inline key_serial_t key_serial(struct key *key)
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 97529ab..c83ab26 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -1000,3 +1000,68 @@ static void keyring_revoke(struct key *keyring)
}

} /* end keyring_revoke() */
+
+/**
+ * keyring_enum - Allow enumeration of a keyring
+ * @keyring_ref: The keyring to search
+ * @skip: Number of matching keys to skip
+ * @filter: A function to filter out unwanted matches
+ * @filter_data: Data to be passed to filter()
+ * @perm: The permissions desired on the key
+ *
+ * Allow the caller to enumerate a keyring by getting the (skip+1)'th
+ * permissible key that matched a particular filter.
+ *
+ * The caller must lock the keyring if they don't want the contents to change
+ * between calls.
+ */
+key_ref_t keyring_enum(key_ref_t keyring_ref, unsigned skip,
+ keyring_enum_filter_t filter, void *filter_data,
+ key_perm_t perm)
+{
+ struct keyring_list *klist;
+ unsigned long possessed;
+ struct key *keyring, *key;
+ long ret;
+ int loop;
+
+ key_check(keyring);
+
+ /* top keyring must have search permission to begin the search */
+ ret = key_permission(keyring_ref, WANT_KEY_SEARCH);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ keyring = key_ref_to_ptr(keyring_ref);
+ if (keyring->type != &key_type_keyring)
+ return ERR_PTR(-ENOTDIR);
+
+ possessed = is_key_possessed(keyring_ref);
+
+ rcu_read_lock();
+
+ klist = rcu_dereference(keyring->payload.subscriptions);
+ if (klist) {
+ for (loop = 0; loop < klist->nkeys; loop++) {
+ key = klist->keys[loop];
+
+ if (!filter(key, filter_data) ||
+ key_permission(make_key_ref(key, possessed),
+ perm) != 0 ||
+ test_bit(KEY_FLAG_REVOKED, &key->flags))
+ continue;
+ if (skip == 0)
+ goto found;
+ skip--;
+ }
+ }
+
+ rcu_read_unlock();
+ return ERR_PTR(-ENOKEY);
+
+ found:
+ atomic_inc(&key->usage);
+ rcu_read_unlock();
+ return make_key_ref(key, possessed);
+}
+EXPORT_SYMBOL(keyring_enum);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/