summary refs log tree commit diff
path: root/security/keys
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2011-03-07 15:06:09 +0000
committerJames Morris <jmorris@namei.org>2011-03-08 11:17:18 +1100
commitfdd1b94581782a2ddf9124414e5b7a5f48ce2f9c (patch)
treece83bfd1f0b1a7d4b9521bdb3d6afef1bff1d4f2 /security/keys
parentb9fffa3877a3ebbe0a5ad5a247358e2f7df15b24 (diff)
downloadlinux-fdd1b94581782a2ddf9124414e5b7a5f48ce2f9c.tar.gz
KEYS: Add a new keyctl op to reject a key with a specified error code
Add a new keyctl op to reject a key with a specified error code.  This works
much the same as negating a key, and so keyctl_negate_key() is made a special
case of keyctl_reject_key().  The difference is that keyctl_negate_key()
selects ENOKEY as the error to be reported.

Typically the key would be rejected with EKEYEXPIRED, EKEYREVOKED or
EKEYREJECTED, but this is not mandatory.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/keys')
-rw-r--r--security/keys/compat.c3
-rw-r--r--security/keys/internal.h1
-rw-r--r--security/keys/key.c19
-rw-r--r--security/keys/keyctl.c40
-rw-r--r--security/keys/keyring.c4
-rw-r--r--security/keys/request_key.c2
6 files changed, 56 insertions, 13 deletions
diff --git a/security/keys/compat.c b/security/keys/compat.c
index 07a5f35e3970..17c99d0149ec 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -85,6 +85,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
 	case KEYCTL_SESSION_TO_PARENT:
 		return keyctl_session_to_parent();
 
+	case KEYCTL_REJECT:
+		return keyctl_reject_key(arg2, arg3, arg4, arg5);
+
 	default:
 		return -EOPNOTSUPP;
 	}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index a52aa7c88b41..286c0959ee51 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -214,6 +214,7 @@ extern long keyctl_assume_authority(key_serial_t);
 extern long keyctl_get_security(key_serial_t keyid, char __user *buffer,
 				size_t buflen);
 extern long keyctl_session_to_parent(void);
+extern long keyctl_reject_key(key_serial_t, unsigned, unsigned, key_serial_t);
 
 /*
  * Debugging key validation
diff --git a/security/keys/key.c b/security/keys/key.c
index 8e315ef2e88e..f7f9d93f08d9 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -511,26 +511,29 @@ int key_instantiate_and_link(struct key *key,
 EXPORT_SYMBOL(key_instantiate_and_link);
 
 /**
- * key_negate_and_link - Negatively instantiate a key and link it into the keyring.
+ * key_reject_and_link - Negatively instantiate a key and link it into the keyring.
  * @key: The key to instantiate.
  * @timeout: The timeout on the negative key.
+ * @error: The error to return when the key is hit.
  * @keyring: Keyring to create a link in on success (or NULL).
  * @authkey: The authorisation token permitting instantiation.
  *
  * Negatively instantiate a key that's in the uninstantiated state and, if
- * successful, set its timeout and link it in to the destination keyring if one
- * is supplied.  The key and any links to the key will be automatically garbage
- * collected after the timeout expires.
+ * successful, set its timeout and stored error and link it in to the
+ * destination keyring if one is supplied.  The key and any links to the key
+ * will be automatically garbage collected after the timeout expires.
  *
  * Negative keys are used to rate limit repeated request_key() calls by causing
- * them to return -ENOKEY until the negative key expires.
+ * them to return the stored error code (typically ENOKEY) until the negative
+ * key expires.
  *
  * If successful, 0 is returned, the authorisation token is revoked and anyone
  * waiting for the key is woken up.  If the key was already instantiated,
  * -EBUSY will be returned.
  */
-int key_negate_and_link(struct key *key,
+int key_reject_and_link(struct key *key,
 			unsigned timeout,
+			unsigned error,
 			struct key *keyring,
 			struct key *authkey)
 {
@@ -556,6 +559,7 @@ int key_negate_and_link(struct key *key,
 		atomic_inc(&key->user->nikeys);
 		set_bit(KEY_FLAG_NEGATIVE, &key->flags);
 		set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
+		key->type_data.reject_error = -error;
 		now = current_kernel_time();
 		key->expiry = now.tv_sec + timeout;
 		key_schedule_gc(key->expiry + key_gc_delay);
@@ -585,8 +589,7 @@ int key_negate_and_link(struct key *key,
 
 	return ret == 0 ? link_ret : ret;
 }
-
-EXPORT_SYMBOL(key_negate_and_link);
+EXPORT_SYMBOL(key_reject_and_link);
 
 /*
  * Garbage collect keys in process context so that we don't have to disable
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 31a0fd8189f1..0d7b1946ff94 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1013,12 +1013,42 @@ error:
  */
 long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
 {
+	return keyctl_reject_key(id, timeout, ENOKEY, ringid);
+}
+
+/*
+ * Negatively instantiate the key with the given timeout (in seconds) and error
+ * code and link the key into the destination keyring if one is given.
+ *
+ * The caller must have the appropriate instantiation permit set for this to
+ * work (see keyctl_assume_authority).  No other permissions are required.
+ *
+ * The key and any links to the key will be automatically garbage collected
+ * after the timeout expires.
+ *
+ * Negative keys are used to rate limit repeated request_key() calls by causing
+ * them to return the specified error code until the negative key expires.
+ *
+ * If successful, 0 will be returned.
+ */
+long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error,
+		       key_serial_t ringid)
+{
 	const struct cred *cred = current_cred();
 	struct request_key_auth *rka;
 	struct key *instkey, *dest_keyring;
 	long ret;
 
-	kenter("%d,%u,%d", id, timeout, ringid);
+	kenter("%d,%u,%u,%d", id, timeout, error, ringid);
+
+	/* must be a valid error code and mustn't be a kernel special */
+	if (error <= 0 ||
+	    error >= MAX_ERRNO ||
+	    error == ERESTARTSYS ||
+	    error == ERESTARTNOINTR ||
+	    error == ERESTARTNOHAND ||
+	    error == ERESTART_RESTARTBLOCK)
+		return -EINVAL;
 
 	/* the appropriate instantiation authorisation key must have been
 	 * assumed before calling this */
@@ -1038,7 +1068,7 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
 		goto error;
 
 	/* instantiate the key and link it into a keyring */
-	ret = key_negate_and_link(rka->target_key, timeout,
+	ret = key_reject_and_link(rka->target_key, timeout, error,
 				  dest_keyring, instkey);
 
 	key_put(dest_keyring);
@@ -1492,6 +1522,12 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
 	case KEYCTL_SESSION_TO_PARENT:
 		return keyctl_session_to_parent();
 
+	case KEYCTL_REJECT:
+		return keyctl_reject_key((key_serial_t) arg2,
+					 (unsigned) arg3,
+					 (unsigned) arg4,
+					 (key_serial_t) arg5);
+
 	default:
 		return -EOPNOTSUPP;
 	}
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 5620f084dede..cdd2f3f88c88 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -352,7 +352,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
 			goto error_2;
 		if (key->expiry && now.tv_sec >= key->expiry)
 			goto error_2;
-		key_ref = ERR_PTR(-ENOKEY);
+		key_ref = ERR_PTR(key->type_data.reject_error);
 		if (kflags & (1 << KEY_FLAG_NEGATIVE))
 			goto error_2;
 		goto found;
@@ -401,7 +401,7 @@ descend:
 
 		/* we set a different error code if we pass a negative key */
 		if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
-			err = -ENOKEY;
+			err = key->type_data.reject_error;
 			continue;
 		}
 
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index a3dc0d460def..df3c0417ee40 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -585,7 +585,7 @@ int wait_for_key_construction(struct key *key, bool intr)
 	if (ret < 0)
 		return ret;
 	if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
-		return -ENOKEY;
+		return key->type_data.reject_error;
 	return key_validate(key);
 }
 EXPORT_SYMBOL(wait_for_key_construction);