mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
100
security/keys/Kconfig
Normal file
100
security/keys/Kconfig
Normal file
|
@ -0,0 +1,100 @@
|
|||
#
|
||||
# Key management configuration
|
||||
#
|
||||
|
||||
config KEYS
|
||||
bool "Enable access key retention support"
|
||||
select ASSOCIATIVE_ARRAY
|
||||
help
|
||||
This option provides support for retaining authentication tokens and
|
||||
access keys in the kernel.
|
||||
|
||||
It also includes provision of methods by which such keys might be
|
||||
associated with a process so that network filesystems, encryption
|
||||
support and the like can find them.
|
||||
|
||||
Furthermore, a special type of key is available that acts as keyring:
|
||||
a searchable sequence of keys. Each process is equipped with access
|
||||
to five standard keyrings: UID-specific, GID-specific, session,
|
||||
process and thread.
|
||||
|
||||
If you are unsure as to whether this is required, answer N.
|
||||
|
||||
config PERSISTENT_KEYRINGS
|
||||
bool "Enable register of persistent per-UID keyrings"
|
||||
depends on KEYS
|
||||
help
|
||||
This option provides a register of persistent per-UID keyrings,
|
||||
primarily aimed at Kerberos key storage. The keyrings are persistent
|
||||
in the sense that they stay around after all processes of that UID
|
||||
have exited, not that they survive the machine being rebooted.
|
||||
|
||||
A particular keyring may be accessed by either the user whose keyring
|
||||
it is or by a process with administrative privileges. The active
|
||||
LSMs gets to rule on which admin-level processes get to access the
|
||||
cache.
|
||||
|
||||
Keyrings are created and added into the register upon demand and get
|
||||
removed if they expire (a default timeout is set upon creation).
|
||||
|
||||
config BIG_KEYS
|
||||
bool "Large payload keys"
|
||||
depends on KEYS
|
||||
depends on TMPFS
|
||||
help
|
||||
This option provides support for holding large keys within the kernel
|
||||
(for example Kerberos ticket caches). The data may be stored out to
|
||||
swapspace by tmpfs.
|
||||
|
||||
If you are unsure as to whether this is required, answer N.
|
||||
|
||||
config TRUSTED_KEYS
|
||||
tristate "TRUSTED KEYS"
|
||||
depends on KEYS && TCG_TPM
|
||||
select CRYPTO
|
||||
select CRYPTO_HMAC
|
||||
select CRYPTO_SHA1
|
||||
help
|
||||
This option provides support for creating, sealing, and unsealing
|
||||
keys in the kernel. Trusted keys are random number symmetric keys,
|
||||
generated and RSA-sealed by the TPM. The TPM only unseals the keys,
|
||||
if the boot PCRs and other criteria match. Userspace will only ever
|
||||
see encrypted blobs.
|
||||
|
||||
If you are unsure as to whether this is required, answer N.
|
||||
|
||||
config ENCRYPTED_KEYS
|
||||
tristate "ENCRYPTED KEYS"
|
||||
depends on KEYS
|
||||
select CRYPTO
|
||||
select CRYPTO_HMAC
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_SHA256
|
||||
select CRYPTO_RNG
|
||||
help
|
||||
This option provides support for create/encrypting/decrypting keys
|
||||
in the kernel. Encrypted keys are kernel generated random numbers,
|
||||
which are encrypted/decrypted with a 'master' symmetric key. The
|
||||
'master' key can be either a trusted-key or user-key type.
|
||||
Userspace only ever sees/stores encrypted blobs.
|
||||
|
||||
If you are unsure as to whether this is required, answer N.
|
||||
|
||||
config KEYS_DEBUG_PROC_KEYS
|
||||
bool "Enable the /proc/keys file by which keys may be viewed"
|
||||
depends on KEYS
|
||||
help
|
||||
This option turns on support for the /proc/keys file - through which
|
||||
can be listed all the keys on the system that are viewable by the
|
||||
reading process.
|
||||
|
||||
The only keys included in the list are those that grant View
|
||||
permission to the reading process whether or not it possesses them.
|
||||
Note that LSM security checks are still performed, and may further
|
||||
filter out keys that the current process is not authorised to view.
|
||||
|
||||
Only key attributes are listed here; key payloads are not included in
|
||||
the resulting table.
|
||||
|
||||
If you are unsure as to whether this is required, answer N.
|
28
security/keys/Makefile
Normal file
28
security/keys/Makefile
Normal file
|
@ -0,0 +1,28 @@
|
|||
#
|
||||
# Makefile for key management
|
||||
#
|
||||
|
||||
#
|
||||
# Core
|
||||
#
|
||||
obj-y := \
|
||||
gc.o \
|
||||
key.o \
|
||||
keyring.o \
|
||||
keyctl.o \
|
||||
permission.o \
|
||||
process_keys.o \
|
||||
request_key.o \
|
||||
request_key_auth.o \
|
||||
user_defined.o
|
||||
obj-$(CONFIG_KEYS_COMPAT) += compat.o
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_SYSCTL) += sysctl.o
|
||||
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
|
||||
|
||||
#
|
||||
# Key types
|
||||
#
|
||||
obj-$(CONFIG_BIG_KEYS) += big_key.o
|
||||
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
|
||||
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
|
214
security/keys/big_key.c
Normal file
214
security/keys/big_key.c
Normal file
|
@ -0,0 +1,214 @@
|
|||
/* Large capacity key type
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/err.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/big_key-type.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* If the data is under this limit, there's no point creating a shm file to
|
||||
* hold it as the permanently resident metadata for the shmem fs will be at
|
||||
* least as large as the data.
|
||||
*/
|
||||
#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
|
||||
|
||||
/*
|
||||
* big_key defined keys take an arbitrary string as the description and an
|
||||
* arbitrary blob of data as the payload
|
||||
*/
|
||||
struct key_type key_type_big_key = {
|
||||
.name = "big_key",
|
||||
.preparse = big_key_preparse,
|
||||
.free_preparse = big_key_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.revoke = big_key_revoke,
|
||||
.destroy = big_key_destroy,
|
||||
.describe = big_key_describe,
|
||||
.read = big_key_read,
|
||||
};
|
||||
|
||||
/*
|
||||
* Preparse a big key
|
||||
*/
|
||||
int big_key_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct path *path = (struct path *)&prep->payload;
|
||||
struct file *file;
|
||||
ssize_t written;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
|
||||
goto error;
|
||||
|
||||
/* Set an arbitrary quota */
|
||||
prep->quotalen = 16;
|
||||
|
||||
prep->type_data[1] = (void *)(unsigned long)datalen;
|
||||
|
||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
/* Create a shmem file to store the data in. This will permit the data
|
||||
* to be swapped out if needed.
|
||||
*
|
||||
* TODO: Encrypt the stored data with a temporary key.
|
||||
*/
|
||||
file = shmem_kernel_file_setup("", datalen, 0);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto error;
|
||||
}
|
||||
|
||||
written = kernel_write(file, prep->data, prep->datalen, 0);
|
||||
if (written != datalen) {
|
||||
ret = written;
|
||||
if (written >= 0)
|
||||
ret = -ENOMEM;
|
||||
goto err_fput;
|
||||
}
|
||||
|
||||
/* Pin the mount and dentry to the key so that we can open it again
|
||||
* later
|
||||
*/
|
||||
*path = file->f_path;
|
||||
path_get(path);
|
||||
fput(file);
|
||||
} else {
|
||||
/* Just store the data in a buffer */
|
||||
void *data = kmalloc(datalen, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
prep->payload[0] = memcpy(data, prep->data, prep->datalen);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_fput:
|
||||
fput(file);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear preparsement.
|
||||
*/
|
||||
void big_key_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&prep->payload;
|
||||
path_put(path);
|
||||
} else {
|
||||
kfree(prep->payload[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the links from a revoked keyring
|
||||
* - called with the key sem write-locked
|
||||
*/
|
||||
void big_key_revoke(struct key *key)
|
||||
{
|
||||
struct path *path = (struct path *)&key->payload.data2;
|
||||
|
||||
/* clear the quota */
|
||||
key_payload_reserve(key, 0);
|
||||
if (key_is_instantiated(key) && key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD)
|
||||
vfs_truncate(path, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a big_key key
|
||||
*/
|
||||
void big_key_destroy(struct key *key)
|
||||
{
|
||||
if (key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&key->payload.data2;
|
||||
path_put(path);
|
||||
path->mnt = NULL;
|
||||
path->dentry = NULL;
|
||||
} else {
|
||||
kfree(key->payload.data);
|
||||
key->payload.data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* describe the big_key key
|
||||
*/
|
||||
void big_key_describe(const struct key *key, struct seq_file *m)
|
||||
{
|
||||
unsigned long datalen = key->type_data.x[1];
|
||||
|
||||
seq_puts(m, key->description);
|
||||
|
||||
if (key_is_instantiated(key))
|
||||
seq_printf(m, ": %lu [%s]",
|
||||
datalen,
|
||||
datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
|
||||
}
|
||||
|
||||
/*
|
||||
* read the key data
|
||||
* - the key's semaphore is read-locked
|
||||
*/
|
||||
long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
|
||||
{
|
||||
unsigned long datalen = key->type_data.x[1];
|
||||
long ret;
|
||||
|
||||
if (!buffer || buflen < datalen)
|
||||
return datalen;
|
||||
|
||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&key->payload.data2;
|
||||
struct file *file;
|
||||
loff_t pos;
|
||||
|
||||
file = dentry_open(path, O_RDONLY, current_cred());
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
||||
pos = 0;
|
||||
ret = vfs_read(file, buffer, datalen, &pos);
|
||||
fput(file);
|
||||
if (ret >= 0 && ret != datalen)
|
||||
ret = -EIO;
|
||||
} else {
|
||||
ret = datalen;
|
||||
if (copy_to_user(buffer, key->payload.data, datalen) != 0)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
static int __init big_key_init(void)
|
||||
{
|
||||
return register_key_type(&key_type_big_key);
|
||||
}
|
||||
|
||||
static void __exit big_key_cleanup(void)
|
||||
{
|
||||
unregister_key_type(&key_type_big_key);
|
||||
}
|
||||
|
||||
module_init(big_key_init);
|
||||
module_exit(big_key_cleanup);
|
147
security/keys/compat.c
Normal file
147
security/keys/compat.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
/* 32-bit compatibility syscall for 64-bit systems
|
||||
*
|
||||
* Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/keyctl.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/slab.h>
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* Instantiate a key with the specified compatibility multipart payload 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.
|
||||
*
|
||||
* If successful, 0 will be returned.
|
||||
*/
|
||||
static long compat_keyctl_instantiate_key_iov(
|
||||
key_serial_t id,
|
||||
const struct compat_iovec __user *_payload_iov,
|
||||
unsigned ioc,
|
||||
key_serial_t ringid)
|
||||
{
|
||||
struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
|
||||
long ret;
|
||||
|
||||
if (!_payload_iov || !ioc)
|
||||
goto no_payload;
|
||||
|
||||
ret = compat_rw_copy_check_uvector(WRITE, _payload_iov, ioc,
|
||||
ARRAY_SIZE(iovstack),
|
||||
iovstack, &iov);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
if (ret == 0)
|
||||
goto no_payload_free;
|
||||
|
||||
ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid);
|
||||
err:
|
||||
if (iov != iovstack)
|
||||
kfree(iov);
|
||||
return ret;
|
||||
|
||||
no_payload_free:
|
||||
if (iov != iovstack)
|
||||
kfree(iov);
|
||||
no_payload:
|
||||
return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid);
|
||||
}
|
||||
|
||||
/*
|
||||
* The key control system call, 32-bit compatibility version for 64-bit archs
|
||||
*
|
||||
* This should only be called if the 64-bit arch uses weird pointers in 32-bit
|
||||
* mode or doesn't guarantee that the top 32-bits of the argument registers on
|
||||
* taking a 32-bit syscall are zero. If you can, you should call sys_keyctl()
|
||||
* directly.
|
||||
*/
|
||||
COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
|
||||
u32, arg2, u32, arg3, u32, arg4, u32, arg5)
|
||||
{
|
||||
switch (option) {
|
||||
case KEYCTL_GET_KEYRING_ID:
|
||||
return keyctl_get_keyring_ID(arg2, arg3);
|
||||
|
||||
case KEYCTL_JOIN_SESSION_KEYRING:
|
||||
return keyctl_join_session_keyring(compat_ptr(arg2));
|
||||
|
||||
case KEYCTL_UPDATE:
|
||||
return keyctl_update_key(arg2, compat_ptr(arg3), arg4);
|
||||
|
||||
case KEYCTL_REVOKE:
|
||||
return keyctl_revoke_key(arg2);
|
||||
|
||||
case KEYCTL_DESCRIBE:
|
||||
return keyctl_describe_key(arg2, compat_ptr(arg3), arg4);
|
||||
|
||||
case KEYCTL_CLEAR:
|
||||
return keyctl_keyring_clear(arg2);
|
||||
|
||||
case KEYCTL_LINK:
|
||||
return keyctl_keyring_link(arg2, arg3);
|
||||
|
||||
case KEYCTL_UNLINK:
|
||||
return keyctl_keyring_unlink(arg2, arg3);
|
||||
|
||||
case KEYCTL_SEARCH:
|
||||
return keyctl_keyring_search(arg2, compat_ptr(arg3),
|
||||
compat_ptr(arg4), arg5);
|
||||
|
||||
case KEYCTL_READ:
|
||||
return keyctl_read_key(arg2, compat_ptr(arg3), arg4);
|
||||
|
||||
case KEYCTL_CHOWN:
|
||||
return keyctl_chown_key(arg2, arg3, arg4);
|
||||
|
||||
case KEYCTL_SETPERM:
|
||||
return keyctl_setperm_key(arg2, arg3);
|
||||
|
||||
case KEYCTL_INSTANTIATE:
|
||||
return keyctl_instantiate_key(arg2, compat_ptr(arg3), arg4,
|
||||
arg5);
|
||||
|
||||
case KEYCTL_NEGATE:
|
||||
return keyctl_negate_key(arg2, arg3, arg4);
|
||||
|
||||
case KEYCTL_SET_REQKEY_KEYRING:
|
||||
return keyctl_set_reqkey_keyring(arg2);
|
||||
|
||||
case KEYCTL_SET_TIMEOUT:
|
||||
return keyctl_set_timeout(arg2, arg3);
|
||||
|
||||
case KEYCTL_ASSUME_AUTHORITY:
|
||||
return keyctl_assume_authority(arg2);
|
||||
|
||||
case KEYCTL_GET_SECURITY:
|
||||
return keyctl_get_security(arg2, compat_ptr(arg3), arg4);
|
||||
|
||||
case KEYCTL_SESSION_TO_PARENT:
|
||||
return keyctl_session_to_parent();
|
||||
|
||||
case KEYCTL_REJECT:
|
||||
return keyctl_reject_key(arg2, arg3, arg4, arg5);
|
||||
|
||||
case KEYCTL_INSTANTIATE_IOV:
|
||||
return compat_keyctl_instantiate_key_iov(
|
||||
arg2, compat_ptr(arg3), arg4, arg5);
|
||||
|
||||
case KEYCTL_INVALIDATE:
|
||||
return keyctl_invalidate_key(arg2);
|
||||
|
||||
case KEYCTL_GET_PERSISTENT:
|
||||
return keyctl_get_persistent(arg2, arg3);
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
10
security/keys/encrypted-keys/Makefile
Normal file
10
security/keys/encrypted-keys/Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# Makefile for encrypted keys
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys.o
|
||||
|
||||
encrypted-keys-y := encrypted.o ecryptfs_format.o
|
||||
masterkey-$(CONFIG_TRUSTED_KEYS) := masterkey_trusted.o
|
||||
masterkey-$(CONFIG_TRUSTED_KEYS)-$(CONFIG_ENCRYPTED_KEYS) := masterkey_trusted.o
|
||||
encrypted-keys-y += $(masterkey-y) $(masterkey-m-m)
|
81
security/keys/encrypted-keys/ecryptfs_format.c
Normal file
81
security/keys/encrypted-keys/ecryptfs_format.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* ecryptfs_format.c: helper functions for the encrypted key type
|
||||
*
|
||||
* Copyright (C) 2006 International Business Machines Corp.
|
||||
* Copyright (C) 2010 Politecnico di Torino, Italy
|
||||
* TORSEC group -- http://security.polito.it
|
||||
*
|
||||
* Authors:
|
||||
* Michael A. Halcrow <mahalcro@us.ibm.com>
|
||||
* Tyler Hicks <tyhicks@ou.edu>
|
||||
* Roberto Sassu <roberto.sassu@polito.it>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include "ecryptfs_format.h"
|
||||
|
||||
u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok)
|
||||
{
|
||||
return auth_tok->token.password.session_key_encryption_key;
|
||||
}
|
||||
EXPORT_SYMBOL(ecryptfs_get_auth_tok_key);
|
||||
|
||||
/*
|
||||
* ecryptfs_get_versions()
|
||||
*
|
||||
* Source code taken from the software 'ecryptfs-utils' version 83.
|
||||
*
|
||||
*/
|
||||
void ecryptfs_get_versions(int *major, int *minor, int *file_version)
|
||||
{
|
||||
*major = ECRYPTFS_VERSION_MAJOR;
|
||||
*minor = ECRYPTFS_VERSION_MINOR;
|
||||
if (file_version)
|
||||
*file_version = ECRYPTFS_SUPPORTED_FILE_VERSION;
|
||||
}
|
||||
EXPORT_SYMBOL(ecryptfs_get_versions);
|
||||
|
||||
/*
|
||||
* ecryptfs_fill_auth_tok - fill the ecryptfs_auth_tok structure
|
||||
*
|
||||
* Fill the ecryptfs_auth_tok structure with required ecryptfs data.
|
||||
* The source code is inspired to the original function generate_payload()
|
||||
* shipped with the software 'ecryptfs-utils' version 83.
|
||||
*
|
||||
*/
|
||||
int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
|
||||
const char *key_desc)
|
||||
{
|
||||
int major, minor;
|
||||
|
||||
ecryptfs_get_versions(&major, &minor, NULL);
|
||||
auth_tok->version = (((uint16_t)(major << 8) & 0xFF00)
|
||||
| ((uint16_t)minor & 0x00FF));
|
||||
auth_tok->token_type = ECRYPTFS_PASSWORD;
|
||||
strncpy((char *)auth_tok->token.password.signature, key_desc,
|
||||
ECRYPTFS_PASSWORD_SIG_SIZE);
|
||||
auth_tok->token.password.session_key_encryption_key_bytes =
|
||||
ECRYPTFS_MAX_KEY_BYTES;
|
||||
/*
|
||||
* Removed auth_tok->token.password.salt and
|
||||
* auth_tok->token.password.session_key_encryption_key
|
||||
* initialization from the original code
|
||||
*/
|
||||
/* TODO: Make the hash parameterizable via policy */
|
||||
auth_tok->token.password.flags |=
|
||||
ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET;
|
||||
/* The kernel code will encrypt the session key. */
|
||||
auth_tok->session_key.encrypted_key[0] = 0;
|
||||
auth_tok->session_key.encrypted_key_size = 0;
|
||||
/* Default; subject to change by kernel eCryptfs */
|
||||
auth_tok->token.password.hash_algo = PGP_DIGEST_ALGO_SHA512;
|
||||
auth_tok->token.password.flags &= ~(ECRYPTFS_PERSISTENT_PASSWORD);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ecryptfs_fill_auth_tok);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
30
security/keys/encrypted-keys/ecryptfs_format.h
Normal file
30
security/keys/encrypted-keys/ecryptfs_format.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* ecryptfs_format.h: helper functions for the encrypted key type
|
||||
*
|
||||
* Copyright (C) 2006 International Business Machines Corp.
|
||||
* Copyright (C) 2010 Politecnico di Torino, Italy
|
||||
* TORSEC group -- http://security.polito.it
|
||||
*
|
||||
* Authors:
|
||||
* Michael A. Halcrow <mahalcro@us.ibm.com>
|
||||
* Tyler Hicks <tyhicks@ou.edu>
|
||||
* Roberto Sassu <roberto.sassu@polito.it>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2 of the License.
|
||||
*/
|
||||
|
||||
#ifndef __KEYS_ECRYPTFS_H
|
||||
#define __KEYS_ECRYPTFS_H
|
||||
|
||||
#include <linux/ecryptfs.h>
|
||||
|
||||
#define PGP_DIGEST_ALGO_SHA512 10
|
||||
|
||||
u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok);
|
||||
void ecryptfs_get_versions(int *major, int *minor, int *file_version);
|
||||
int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
|
||||
const char *key_desc);
|
||||
|
||||
#endif /* __KEYS_ECRYPTFS_H */
|
1042
security/keys/encrypted-keys/encrypted.c
Normal file
1042
security/keys/encrypted-keys/encrypted.c
Normal file
File diff suppressed because it is too large
Load diff
66
security/keys/encrypted-keys/encrypted.h
Normal file
66
security/keys/encrypted-keys/encrypted.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
#ifndef __ENCRYPTED_KEY_H
|
||||
#define __ENCRYPTED_KEY_H
|
||||
|
||||
#define ENCRYPTED_DEBUG 0
|
||||
#if defined(CONFIG_TRUSTED_KEYS) || \
|
||||
(defined(CONFIG_TRUSTED_KEYS_MODULE) && defined(CONFIG_ENCRYPTED_KEYS_MODULE))
|
||||
extern struct key *request_trusted_key(const char *trusted_desc,
|
||||
u8 **master_key, size_t *master_keylen);
|
||||
#else
|
||||
static inline struct key *request_trusted_key(const char *trusted_desc,
|
||||
u8 **master_key,
|
||||
size_t *master_keylen)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENCRYPTED_DEBUG
|
||||
static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
|
||||
{
|
||||
print_hex_dump(KERN_ERR, "master key: ", DUMP_PREFIX_NONE, 32, 1,
|
||||
master_key, master_keylen, 0);
|
||||
}
|
||||
|
||||
static inline void dump_decrypted_data(struct encrypted_key_payload *epayload)
|
||||
{
|
||||
print_hex_dump(KERN_ERR, "decrypted data: ", DUMP_PREFIX_NONE, 32, 1,
|
||||
epayload->decrypted_data,
|
||||
epayload->decrypted_datalen, 0);
|
||||
}
|
||||
|
||||
static inline void dump_encrypted_data(struct encrypted_key_payload *epayload,
|
||||
unsigned int encrypted_datalen)
|
||||
{
|
||||
print_hex_dump(KERN_ERR, "encrypted data: ", DUMP_PREFIX_NONE, 32, 1,
|
||||
epayload->encrypted_data, encrypted_datalen, 0);
|
||||
}
|
||||
|
||||
static inline void dump_hmac(const char *str, const u8 *digest,
|
||||
unsigned int hmac_size)
|
||||
{
|
||||
if (str)
|
||||
pr_info("encrypted_key: %s", str);
|
||||
print_hex_dump(KERN_ERR, "hmac: ", DUMP_PREFIX_NONE, 32, 1, digest,
|
||||
hmac_size, 0);
|
||||
}
|
||||
#else
|
||||
static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dump_decrypted_data(struct encrypted_key_payload *epayload)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dump_encrypted_data(struct encrypted_key_payload *epayload,
|
||||
unsigned int encrypted_datalen)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dump_hmac(const char *str, const u8 *digest,
|
||||
unsigned int hmac_size)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
#endif
|
47
security/keys/encrypted-keys/masterkey_trusted.c
Normal file
47
security/keys/encrypted-keys/masterkey_trusted.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2010 IBM Corporation
|
||||
* Copyright (C) 2010 Politecnico di Torino, Italy
|
||||
* TORSEC group -- http://security.polito.it
|
||||
*
|
||||
* Authors:
|
||||
* Mimi Zohar <zohar@us.ibm.com>
|
||||
* Roberto Sassu <roberto.sassu@polito.it>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2 of the License.
|
||||
*
|
||||
* See Documentation/security/keys-trusted-encrypted.txt
|
||||
*/
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <keys/trusted-type.h>
|
||||
#include <keys/encrypted-type.h>
|
||||
#include "encrypted.h"
|
||||
|
||||
/*
|
||||
* request_trusted_key - request the trusted key
|
||||
*
|
||||
* Trusted keys are sealed to PCRs and other metadata. Although userspace
|
||||
* manages both trusted/encrypted key-types, like the encrypted key type
|
||||
* data, trusted key type data is not visible decrypted from userspace.
|
||||
*/
|
||||
struct key *request_trusted_key(const char *trusted_desc,
|
||||
u8 **master_key, size_t *master_keylen)
|
||||
{
|
||||
struct trusted_key_payload *tpayload;
|
||||
struct key *tkey;
|
||||
|
||||
tkey = request_key(&key_type_trusted, trusted_desc, NULL);
|
||||
if (IS_ERR(tkey))
|
||||
goto error;
|
||||
|
||||
down_read(&tkey->sem);
|
||||
tpayload = tkey->payload.data;
|
||||
*master_key = tpayload->key;
|
||||
*master_keylen = tpayload->key_len;
|
||||
error:
|
||||
return tkey;
|
||||
}
|
358
security/keys/gc.c
Normal file
358
security/keys/gc.c
Normal file
|
@ -0,0 +1,358 @@
|
|||
/* Key garbage collector
|
||||
*
|
||||
* Copyright (C) 2009-2011 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/security.h>
|
||||
#include <keys/keyring-type.h>
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* Delay between key revocation/expiry in seconds
|
||||
*/
|
||||
unsigned key_gc_delay = 5 * 60;
|
||||
|
||||
/*
|
||||
* Reaper for unused keys.
|
||||
*/
|
||||
static void key_garbage_collector(struct work_struct *work);
|
||||
DECLARE_WORK(key_gc_work, key_garbage_collector);
|
||||
|
||||
/*
|
||||
* Reaper for links from keyrings to dead keys.
|
||||
*/
|
||||
static void key_gc_timer_func(unsigned long);
|
||||
static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
|
||||
|
||||
static time_t key_gc_next_run = LONG_MAX;
|
||||
static struct key_type *key_gc_dead_keytype;
|
||||
|
||||
static unsigned long key_gc_flags;
|
||||
#define KEY_GC_KEY_EXPIRED 0 /* A key expired and needs unlinking */
|
||||
#define KEY_GC_REAP_KEYTYPE 1 /* A keytype is being unregistered */
|
||||
#define KEY_GC_REAPING_KEYTYPE 2 /* Cleared when keytype reaped */
|
||||
|
||||
|
||||
/*
|
||||
* Any key whose type gets unregistered will be re-typed to this if it can't be
|
||||
* immediately unlinked.
|
||||
*/
|
||||
struct key_type key_type_dead = {
|
||||
.name = "dead",
|
||||
};
|
||||
|
||||
/*
|
||||
* Schedule a garbage collection run.
|
||||
* - time precision isn't particularly important
|
||||
*/
|
||||
void key_schedule_gc(time_t gc_at)
|
||||
{
|
||||
unsigned long expires;
|
||||
time_t now = current_kernel_time().tv_sec;
|
||||
|
||||
kenter("%ld", gc_at - now);
|
||||
|
||||
if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) {
|
||||
kdebug("IMMEDIATE");
|
||||
schedule_work(&key_gc_work);
|
||||
} else if (gc_at < key_gc_next_run) {
|
||||
kdebug("DEFERRED");
|
||||
key_gc_next_run = gc_at;
|
||||
expires = jiffies + (gc_at - now) * HZ;
|
||||
mod_timer(&key_gc_timer, expires);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Schedule a dead links collection run.
|
||||
*/
|
||||
void key_schedule_gc_links(void)
|
||||
{
|
||||
set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags);
|
||||
schedule_work(&key_gc_work);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some key's cleanup time was met after it expired, so we need to get the
|
||||
* reaper to go through a cycle finding expired keys.
|
||||
*/
|
||||
static void key_gc_timer_func(unsigned long data)
|
||||
{
|
||||
kenter("");
|
||||
key_gc_next_run = LONG_MAX;
|
||||
key_schedule_gc_links();
|
||||
}
|
||||
|
||||
/*
|
||||
* Reap keys of dead type.
|
||||
*
|
||||
* We use three flags to make sure we see three complete cycles of the garbage
|
||||
* collector: the first to mark keys of that type as being dead, the second to
|
||||
* collect dead links and the third to clean up the dead keys. We have to be
|
||||
* careful as there may already be a cycle in progress.
|
||||
*
|
||||
* The caller must be holding key_types_sem.
|
||||
*/
|
||||
void key_gc_keytype(struct key_type *ktype)
|
||||
{
|
||||
kenter("%s", ktype->name);
|
||||
|
||||
key_gc_dead_keytype = ktype;
|
||||
set_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags);
|
||||
smp_mb();
|
||||
set_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags);
|
||||
|
||||
kdebug("schedule");
|
||||
schedule_work(&key_gc_work);
|
||||
|
||||
kdebug("sleep");
|
||||
wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
|
||||
key_gc_dead_keytype = NULL;
|
||||
kleave("");
|
||||
}
|
||||
|
||||
/*
|
||||
* Garbage collect a list of unreferenced, detached keys
|
||||
*/
|
||||
static noinline void key_gc_unused_keys(struct list_head *keys)
|
||||
{
|
||||
while (!list_empty(keys)) {
|
||||
struct key *key =
|
||||
list_entry(keys->next, struct key, graveyard_link);
|
||||
list_del(&key->graveyard_link);
|
||||
|
||||
kdebug("- %u", key->serial);
|
||||
key_check(key);
|
||||
|
||||
security_key_free(key);
|
||||
|
||||
/* deal with the user's key tracking and quota */
|
||||
if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
|
||||
spin_lock(&key->user->lock);
|
||||
key->user->qnkeys--;
|
||||
key->user->qnbytes -= key->quotalen;
|
||||
spin_unlock(&key->user->lock);
|
||||
}
|
||||
|
||||
atomic_dec(&key->user->nkeys);
|
||||
if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
|
||||
atomic_dec(&key->user->nikeys);
|
||||
|
||||
/* now throw away the key memory */
|
||||
if (key->type->destroy)
|
||||
key->type->destroy(key);
|
||||
|
||||
key_user_put(key->user);
|
||||
|
||||
kfree(key->description);
|
||||
|
||||
#ifdef KEY_DEBUGGING
|
||||
key->magic = KEY_DEBUG_MAGIC_X;
|
||||
#endif
|
||||
kmem_cache_free(key_jar, key);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Garbage collector for unused keys.
|
||||
*
|
||||
* This is done in process context so that we don't have to disable interrupts
|
||||
* all over the place. key_put() schedules this rather than trying to do the
|
||||
* cleanup itself, which means key_put() doesn't have to sleep.
|
||||
*/
|
||||
static void key_garbage_collector(struct work_struct *work)
|
||||
{
|
||||
static LIST_HEAD(graveyard);
|
||||
static u8 gc_state; /* Internal persistent state */
|
||||
#define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */
|
||||
#define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */
|
||||
#define KEY_GC_SET_TIMER 0x04 /* - We need to restart the timer */
|
||||
#define KEY_GC_REAPING_DEAD_1 0x10 /* - We need to mark dead keys */
|
||||
#define KEY_GC_REAPING_DEAD_2 0x20 /* - We need to reap dead key links */
|
||||
#define KEY_GC_REAPING_DEAD_3 0x40 /* - We need to reap dead keys */
|
||||
#define KEY_GC_FOUND_DEAD_KEY 0x80 /* - We found at least one dead key */
|
||||
|
||||
struct rb_node *cursor;
|
||||
struct key *key;
|
||||
time_t new_timer, limit;
|
||||
|
||||
kenter("[%lx,%x]", key_gc_flags, gc_state);
|
||||
|
||||
limit = current_kernel_time().tv_sec;
|
||||
if (limit > key_gc_delay)
|
||||
limit -= key_gc_delay;
|
||||
else
|
||||
limit = key_gc_delay;
|
||||
|
||||
/* Work out what we're going to be doing in this pass */
|
||||
gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2;
|
||||
gc_state <<= 1;
|
||||
if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags))
|
||||
gc_state |= KEY_GC_REAPING_LINKS | KEY_GC_SET_TIMER;
|
||||
|
||||
if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags))
|
||||
gc_state |= KEY_GC_REAPING_DEAD_1;
|
||||
kdebug("new pass %x", gc_state);
|
||||
|
||||
new_timer = LONG_MAX;
|
||||
|
||||
/* As only this function is permitted to remove things from the key
|
||||
* serial tree, if cursor is non-NULL then it will always point to a
|
||||
* valid node in the tree - even if lock got dropped.
|
||||
*/
|
||||
spin_lock(&key_serial_lock);
|
||||
cursor = rb_first(&key_serial_tree);
|
||||
|
||||
continue_scanning:
|
||||
while (cursor) {
|
||||
key = rb_entry(cursor, struct key, serial_node);
|
||||
cursor = rb_next(cursor);
|
||||
|
||||
if (atomic_read(&key->usage) == 0)
|
||||
goto found_unreferenced_key;
|
||||
|
||||
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
|
||||
if (key->type == key_gc_dead_keytype) {
|
||||
gc_state |= KEY_GC_FOUND_DEAD_KEY;
|
||||
set_bit(KEY_FLAG_DEAD, &key->flags);
|
||||
key->perm = 0;
|
||||
goto skip_dead_key;
|
||||
}
|
||||
}
|
||||
|
||||
if (gc_state & KEY_GC_SET_TIMER) {
|
||||
if (key->expiry > limit && key->expiry < new_timer) {
|
||||
kdebug("will expire %x in %ld",
|
||||
key_serial(key), key->expiry - limit);
|
||||
new_timer = key->expiry;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2))
|
||||
if (key->type == key_gc_dead_keytype)
|
||||
gc_state |= KEY_GC_FOUND_DEAD_KEY;
|
||||
|
||||
if ((gc_state & KEY_GC_REAPING_LINKS) ||
|
||||
unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) {
|
||||
if (key->type == &key_type_keyring)
|
||||
goto found_keyring;
|
||||
}
|
||||
|
||||
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3))
|
||||
if (key->type == key_gc_dead_keytype)
|
||||
goto destroy_dead_key;
|
||||
|
||||
skip_dead_key:
|
||||
if (spin_is_contended(&key_serial_lock) || need_resched())
|
||||
goto contended;
|
||||
}
|
||||
|
||||
contended:
|
||||
spin_unlock(&key_serial_lock);
|
||||
|
||||
maybe_resched:
|
||||
if (cursor) {
|
||||
cond_resched();
|
||||
spin_lock(&key_serial_lock);
|
||||
goto continue_scanning;
|
||||
}
|
||||
|
||||
/* We've completed the pass. Set the timer if we need to and queue a
|
||||
* new cycle if necessary. We keep executing cycles until we find one
|
||||
* where we didn't reap any keys.
|
||||
*/
|
||||
kdebug("pass complete");
|
||||
|
||||
if (gc_state & KEY_GC_SET_TIMER && new_timer != (time_t)LONG_MAX) {
|
||||
new_timer += key_gc_delay;
|
||||
key_schedule_gc(new_timer);
|
||||
}
|
||||
|
||||
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2) ||
|
||||
!list_empty(&graveyard)) {
|
||||
/* Make sure that all pending keyring payload destructions are
|
||||
* fulfilled and that people aren't now looking at dead or
|
||||
* dying keys that they don't have a reference upon or a link
|
||||
* to.
|
||||
*/
|
||||
kdebug("gc sync");
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
if (!list_empty(&graveyard)) {
|
||||
kdebug("gc keys");
|
||||
key_gc_unused_keys(&graveyard);
|
||||
}
|
||||
|
||||
if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 |
|
||||
KEY_GC_REAPING_DEAD_2))) {
|
||||
if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) {
|
||||
/* No remaining dead keys: short circuit the remaining
|
||||
* keytype reap cycles.
|
||||
*/
|
||||
kdebug("dead short");
|
||||
gc_state &= ~(KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2);
|
||||
gc_state |= KEY_GC_REAPING_DEAD_3;
|
||||
} else {
|
||||
gc_state |= KEY_GC_REAP_AGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3)) {
|
||||
kdebug("dead wake");
|
||||
smp_mb();
|
||||
clear_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags);
|
||||
wake_up_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE);
|
||||
}
|
||||
|
||||
if (gc_state & KEY_GC_REAP_AGAIN)
|
||||
schedule_work(&key_gc_work);
|
||||
kleave(" [end %x]", gc_state);
|
||||
return;
|
||||
|
||||
/* We found an unreferenced key - once we've removed it from the tree,
|
||||
* we can safely drop the lock.
|
||||
*/
|
||||
found_unreferenced_key:
|
||||
kdebug("unrefd key %d", key->serial);
|
||||
rb_erase(&key->serial_node, &key_serial_tree);
|
||||
spin_unlock(&key_serial_lock);
|
||||
|
||||
list_add_tail(&key->graveyard_link, &graveyard);
|
||||
gc_state |= KEY_GC_REAP_AGAIN;
|
||||
goto maybe_resched;
|
||||
|
||||
/* We found a keyring and we need to check the payload for links to
|
||||
* dead or expired keys. We don't flag another reap immediately as we
|
||||
* have to wait for the old payload to be destroyed by RCU before we
|
||||
* can reap the keys to which it refers.
|
||||
*/
|
||||
found_keyring:
|
||||
spin_unlock(&key_serial_lock);
|
||||
keyring_gc(key, limit);
|
||||
goto maybe_resched;
|
||||
|
||||
/* We found a dead key that is still referenced. Reset its type and
|
||||
* destroy its payload with its semaphore held.
|
||||
*/
|
||||
destroy_dead_key:
|
||||
spin_unlock(&key_serial_lock);
|
||||
kdebug("destroy key %d", key->serial);
|
||||
down_write(&key->sem);
|
||||
key->type = &key_type_dead;
|
||||
if (key_gc_dead_keytype->destroy)
|
||||
key_gc_dead_keytype->destroy(key);
|
||||
memset(&key->payload, KEY_DESTROY, sizeof(key->payload));
|
||||
up_write(&key->sem);
|
||||
goto maybe_resched;
|
||||
}
|
277
security/keys/internal.h
Normal file
277
security/keys/internal.h
Normal file
|
@ -0,0 +1,277 @@
|
|||
/* Authentication token and access key management internal defs
|
||||
*
|
||||
* Copyright (C) 2003-5, 2007 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _INTERNAL_H
|
||||
#define _INTERNAL_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/task_work.h>
|
||||
|
||||
struct iovec;
|
||||
|
||||
#ifdef __KDEBUG
|
||||
#define kenter(FMT, ...) \
|
||||
printk(KERN_DEBUG "==> %s("FMT")\n", __func__, ##__VA_ARGS__)
|
||||
#define kleave(FMT, ...) \
|
||||
printk(KERN_DEBUG "<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
|
||||
#define kdebug(FMT, ...) \
|
||||
printk(KERN_DEBUG " "FMT"\n", ##__VA_ARGS__)
|
||||
#else
|
||||
#define kenter(FMT, ...) \
|
||||
no_printk(KERN_DEBUG "==> %s("FMT")\n", __func__, ##__VA_ARGS__)
|
||||
#define kleave(FMT, ...) \
|
||||
no_printk(KERN_DEBUG "<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
|
||||
#define kdebug(FMT, ...) \
|
||||
no_printk(KERN_DEBUG FMT"\n", ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
extern struct key_type key_type_dead;
|
||||
extern struct key_type key_type_user;
|
||||
extern struct key_type key_type_logon;
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* Keep track of keys for a user.
|
||||
*
|
||||
* This needs to be separate to user_struct to avoid a refcount-loop
|
||||
* (user_struct pins some keyrings which pin this struct).
|
||||
*
|
||||
* We also keep track of keys under request from userspace for this UID here.
|
||||
*/
|
||||
struct key_user {
|
||||
struct rb_node node;
|
||||
struct mutex cons_lock; /* construction initiation lock */
|
||||
spinlock_t lock;
|
||||
atomic_t usage; /* for accessing qnkeys & qnbytes */
|
||||
atomic_t nkeys; /* number of keys */
|
||||
atomic_t nikeys; /* number of instantiated keys */
|
||||
kuid_t uid;
|
||||
int qnkeys; /* number of keys allocated to this user */
|
||||
int qnbytes; /* number of bytes allocated to this user */
|
||||
};
|
||||
|
||||
extern struct rb_root key_user_tree;
|
||||
extern spinlock_t key_user_lock;
|
||||
extern struct key_user root_key_user;
|
||||
|
||||
extern struct key_user *key_user_lookup(kuid_t uid);
|
||||
extern void key_user_put(struct key_user *user);
|
||||
|
||||
/*
|
||||
* Key quota limits.
|
||||
* - root has its own separate limits to everyone else
|
||||
*/
|
||||
extern unsigned key_quota_root_maxkeys;
|
||||
extern unsigned key_quota_root_maxbytes;
|
||||
extern unsigned key_quota_maxkeys;
|
||||
extern unsigned key_quota_maxbytes;
|
||||
|
||||
#define KEYQUOTA_LINK_BYTES 4 /* a link in a keyring is worth 4 bytes */
|
||||
|
||||
|
||||
extern struct kmem_cache *key_jar;
|
||||
extern struct rb_root key_serial_tree;
|
||||
extern spinlock_t key_serial_lock;
|
||||
extern struct mutex key_construction_mutex;
|
||||
extern wait_queue_head_t request_key_conswq;
|
||||
|
||||
|
||||
extern struct key_type *key_type_lookup(const char *type);
|
||||
extern void key_type_put(struct key_type *ktype);
|
||||
|
||||
extern int __key_link_begin(struct key *keyring,
|
||||
const struct keyring_index_key *index_key,
|
||||
struct assoc_array_edit **_edit);
|
||||
extern int __key_link_check_live_key(struct key *keyring, struct key *key);
|
||||
extern void __key_link(struct key *key, struct assoc_array_edit **_edit);
|
||||
extern void __key_link_end(struct key *keyring,
|
||||
const struct keyring_index_key *index_key,
|
||||
struct assoc_array_edit *edit);
|
||||
|
||||
extern key_ref_t find_key_to_update(key_ref_t keyring_ref,
|
||||
const struct keyring_index_key *index_key);
|
||||
|
||||
extern struct key *keyring_search_instkey(struct key *keyring,
|
||||
key_serial_t target_id);
|
||||
|
||||
extern int iterate_over_keyring(const struct key *keyring,
|
||||
int (*func)(const struct key *key, void *data),
|
||||
void *data);
|
||||
|
||||
struct keyring_search_context {
|
||||
struct keyring_index_key index_key;
|
||||
const struct cred *cred;
|
||||
struct key_match_data match_data;
|
||||
unsigned flags;
|
||||
#define KEYRING_SEARCH_NO_STATE_CHECK 0x0001 /* Skip state checks */
|
||||
#define KEYRING_SEARCH_DO_STATE_CHECK 0x0002 /* Override NO_STATE_CHECK */
|
||||
#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0004 /* Don't update times */
|
||||
#define KEYRING_SEARCH_NO_CHECK_PERM 0x0008 /* Don't check permissions */
|
||||
#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010 /* Give an error on excessive depth */
|
||||
#define KEYRING_SEARCH_SKIP_EXPIRED 0x0020 /* Ignore expired keys (intention to replace) */
|
||||
|
||||
int (*iterator)(const void *object, void *iterator_data);
|
||||
|
||||
/* Internal stuff */
|
||||
int skipped_ret;
|
||||
bool possessed;
|
||||
key_ref_t result;
|
||||
struct timespec now;
|
||||
};
|
||||
|
||||
extern bool key_default_cmp(const struct key *key,
|
||||
const struct key_match_data *match_data);
|
||||
extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
|
||||
struct keyring_search_context *ctx);
|
||||
|
||||
extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
|
||||
extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);
|
||||
|
||||
extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
|
||||
|
||||
extern int install_user_keyrings(void);
|
||||
extern int install_thread_keyring_to_cred(struct cred *);
|
||||
extern int install_process_keyring_to_cred(struct cred *);
|
||||
extern int install_session_keyring_to_cred(struct cred *, struct key *);
|
||||
|
||||
extern struct key *request_key_and_link(struct key_type *type,
|
||||
const char *description,
|
||||
const void *callout_info,
|
||||
size_t callout_len,
|
||||
void *aux,
|
||||
struct key *dest_keyring,
|
||||
unsigned long flags);
|
||||
|
||||
extern bool lookup_user_key_possessed(const struct key *key,
|
||||
const struct key_match_data *match_data);
|
||||
extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
|
||||
key_perm_t perm);
|
||||
#define KEY_LOOKUP_CREATE 0x01
|
||||
#define KEY_LOOKUP_PARTIAL 0x02
|
||||
#define KEY_LOOKUP_FOR_UNLINK 0x04
|
||||
|
||||
extern long join_session_keyring(const char *name);
|
||||
extern void key_change_session_keyring(struct callback_head *twork);
|
||||
|
||||
extern struct work_struct key_gc_work;
|
||||
extern unsigned key_gc_delay;
|
||||
extern void keyring_gc(struct key *keyring, time_t limit);
|
||||
extern void key_schedule_gc(time_t gc_at);
|
||||
extern void key_schedule_gc_links(void);
|
||||
extern void key_gc_keytype(struct key_type *ktype);
|
||||
|
||||
extern int key_task_permission(const key_ref_t key_ref,
|
||||
const struct cred *cred,
|
||||
key_perm_t perm);
|
||||
|
||||
/*
|
||||
* Check to see whether permission is granted to use a key in the desired way.
|
||||
*/
|
||||
static inline int key_permission(const key_ref_t key_ref, unsigned perm)
|
||||
{
|
||||
return key_task_permission(key_ref, current_cred(), perm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Authorisation record for request_key().
|
||||
*/
|
||||
struct request_key_auth {
|
||||
struct key *target_key;
|
||||
struct key *dest_keyring;
|
||||
const struct cred *cred;
|
||||
void *callout_info;
|
||||
size_t callout_len;
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
extern struct key_type key_type_request_key_auth;
|
||||
extern struct key *request_key_auth_new(struct key *target,
|
||||
const void *callout_info,
|
||||
size_t callout_len,
|
||||
struct key *dest_keyring);
|
||||
|
||||
extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
|
||||
|
||||
/*
|
||||
* Determine whether a key is dead.
|
||||
*/
|
||||
static inline bool key_is_dead(const struct key *key, time_t limit)
|
||||
{
|
||||
return
|
||||
key->flags & ((1 << KEY_FLAG_DEAD) |
|
||||
(1 << KEY_FLAG_INVALIDATED)) ||
|
||||
(key->expiry > 0 && key->expiry <= limit);
|
||||
}
|
||||
|
||||
/*
|
||||
* keyctl() functions
|
||||
*/
|
||||
extern long keyctl_get_keyring_ID(key_serial_t, int);
|
||||
extern long keyctl_join_session_keyring(const char __user *);
|
||||
extern long keyctl_update_key(key_serial_t, const void __user *, size_t);
|
||||
extern long keyctl_revoke_key(key_serial_t);
|
||||
extern long keyctl_keyring_clear(key_serial_t);
|
||||
extern long keyctl_keyring_link(key_serial_t, key_serial_t);
|
||||
extern long keyctl_keyring_unlink(key_serial_t, key_serial_t);
|
||||
extern long keyctl_describe_key(key_serial_t, char __user *, size_t);
|
||||
extern long keyctl_keyring_search(key_serial_t, const char __user *,
|
||||
const char __user *, key_serial_t);
|
||||
extern long keyctl_read_key(key_serial_t, char __user *, size_t);
|
||||
extern long keyctl_chown_key(key_serial_t, uid_t, gid_t);
|
||||
extern long keyctl_setperm_key(key_serial_t, key_perm_t);
|
||||
extern long keyctl_instantiate_key(key_serial_t, const void __user *,
|
||||
size_t, key_serial_t);
|
||||
extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t);
|
||||
extern long keyctl_set_reqkey_keyring(int);
|
||||
extern long keyctl_set_timeout(key_serial_t, unsigned);
|
||||
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);
|
||||
extern long keyctl_instantiate_key_iov(key_serial_t,
|
||||
const struct iovec __user *,
|
||||
unsigned, key_serial_t);
|
||||
extern long keyctl_invalidate_key(key_serial_t);
|
||||
|
||||
extern long keyctl_instantiate_key_common(key_serial_t,
|
||||
const struct iovec *,
|
||||
unsigned, size_t, key_serial_t);
|
||||
#ifdef CONFIG_PERSISTENT_KEYRINGS
|
||||
extern long keyctl_get_persistent(uid_t, key_serial_t);
|
||||
extern unsigned persistent_keyring_expiry;
|
||||
#else
|
||||
static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Debugging key validation
|
||||
*/
|
||||
#ifdef KEY_DEBUGGING
|
||||
extern void __key_check(const struct key *);
|
||||
|
||||
static inline void key_check(const struct key *key)
|
||||
{
|
||||
if (key && (IS_ERR(key) || key->magic != KEY_DEBUG_MAGIC))
|
||||
__key_check(key);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define key_check(key) do {} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _INTERNAL_H */
|
1142
security/keys/key.c
Normal file
1142
security/keys/key.c
Normal file
File diff suppressed because it is too large
Load diff
1689
security/keys/keyctl.c
Normal file
1689
security/keys/keyctl.c
Normal file
File diff suppressed because it is too large
Load diff
1394
security/keys/keyring.c
Normal file
1394
security/keys/keyring.c
Normal file
File diff suppressed because it is too large
Load diff
110
security/keys/permission.c
Normal file
110
security/keys/permission.c
Normal file
|
@ -0,0 +1,110 @@
|
|||
/* Key permission checking
|
||||
*
|
||||
* Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/security.h>
|
||||
#include "internal.h"
|
||||
|
||||
/**
|
||||
* key_task_permission - Check a key can be used
|
||||
* @key_ref: The key to check.
|
||||
* @cred: The credentials to use.
|
||||
* @perm: The permissions to check for.
|
||||
*
|
||||
* Check to see whether permission is granted to use a key in the desired way,
|
||||
* but permit the security modules to override.
|
||||
*
|
||||
* The caller must hold either a ref on cred or must hold the RCU readlock.
|
||||
*
|
||||
* Returns 0 if successful, -EACCES if access is denied based on the
|
||||
* permissions bits or the LSM check.
|
||||
*/
|
||||
int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
|
||||
unsigned perm)
|
||||
{
|
||||
struct key *key;
|
||||
key_perm_t kperm;
|
||||
int ret;
|
||||
|
||||
key = key_ref_to_ptr(key_ref);
|
||||
|
||||
/* use the second 8-bits of permissions for keys the caller owns */
|
||||
if (uid_eq(key->uid, cred->fsuid)) {
|
||||
kperm = key->perm >> 16;
|
||||
goto use_these_perms;
|
||||
}
|
||||
|
||||
/* use the third 8-bits of permissions for keys the caller has a group
|
||||
* membership in common with */
|
||||
if (gid_valid(key->gid) && key->perm & KEY_GRP_ALL) {
|
||||
if (gid_eq(key->gid, cred->fsgid)) {
|
||||
kperm = key->perm >> 8;
|
||||
goto use_these_perms;
|
||||
}
|
||||
|
||||
ret = groups_search(cred->group_info, key->gid);
|
||||
if (ret) {
|
||||
kperm = key->perm >> 8;
|
||||
goto use_these_perms;
|
||||
}
|
||||
}
|
||||
|
||||
/* otherwise use the least-significant 8-bits */
|
||||
kperm = key->perm;
|
||||
|
||||
use_these_perms:
|
||||
|
||||
/* use the top 8-bits of permissions for keys the caller possesses
|
||||
* - possessor permissions are additive with other permissions
|
||||
*/
|
||||
if (is_key_possessed(key_ref))
|
||||
kperm |= key->perm >> 24;
|
||||
|
||||
kperm = kperm & perm & KEY_NEED_ALL;
|
||||
|
||||
if (kperm != perm)
|
||||
return -EACCES;
|
||||
|
||||
/* let LSM be the final arbiter */
|
||||
return security_key_permission(key_ref, cred, perm);
|
||||
}
|
||||
EXPORT_SYMBOL(key_task_permission);
|
||||
|
||||
/**
|
||||
* key_validate - Validate a key.
|
||||
* @key: The key to be validated.
|
||||
*
|
||||
* Check that a key is valid, returning 0 if the key is okay, -ENOKEY if the
|
||||
* key is invalidated, -EKEYREVOKED if the key's type has been removed or if
|
||||
* the key has been revoked or -EKEYEXPIRED if the key has expired.
|
||||
*/
|
||||
int key_validate(const struct key *key)
|
||||
{
|
||||
unsigned long flags = key->flags;
|
||||
|
||||
if (flags & (1 << KEY_FLAG_INVALIDATED))
|
||||
return -ENOKEY;
|
||||
|
||||
/* check it's still accessible */
|
||||
if (flags & ((1 << KEY_FLAG_REVOKED) |
|
||||
(1 << KEY_FLAG_DEAD)))
|
||||
return -EKEYREVOKED;
|
||||
|
||||
/* check it hasn't expired */
|
||||
if (key->expiry) {
|
||||
struct timespec now = current_kernel_time();
|
||||
if (now.tv_sec >= key->expiry)
|
||||
return -EKEYEXPIRED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(key_validate);
|
167
security/keys/persistent.c
Normal file
167
security/keys/persistent.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
/* General persistent per-UID keyrings register
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/user_namespace.h>
|
||||
#include "internal.h"
|
||||
|
||||
unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */
|
||||
|
||||
/*
|
||||
* Create the persistent keyring register for the current user namespace.
|
||||
*
|
||||
* Called with the namespace's sem locked for writing.
|
||||
*/
|
||||
static int key_create_persistent_register(struct user_namespace *ns)
|
||||
{
|
||||
struct key *reg = keyring_alloc(".persistent_register",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0),
|
||||
current_cred(),
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
if (IS_ERR(reg))
|
||||
return PTR_ERR(reg);
|
||||
|
||||
ns->persistent_keyring_register = reg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the persistent keyring for the specified user.
|
||||
*
|
||||
* Called with the namespace's sem locked for writing.
|
||||
*/
|
||||
static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
|
||||
struct keyring_index_key *index_key)
|
||||
{
|
||||
struct key *persistent;
|
||||
key_ref_t reg_ref, persistent_ref;
|
||||
|
||||
if (!ns->persistent_keyring_register) {
|
||||
long err = key_create_persistent_register(ns);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
} else {
|
||||
reg_ref = make_key_ref(ns->persistent_keyring_register, true);
|
||||
persistent_ref = find_key_to_update(reg_ref, index_key);
|
||||
if (persistent_ref)
|
||||
return persistent_ref;
|
||||
}
|
||||
|
||||
persistent = keyring_alloc(index_key->description,
|
||||
uid, INVALID_GID, current_cred(),
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
ns->persistent_keyring_register);
|
||||
if (IS_ERR(persistent))
|
||||
return ERR_CAST(persistent);
|
||||
|
||||
return make_key_ref(persistent, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the persistent keyring for a specific UID and link it to the nominated
|
||||
* keyring.
|
||||
*/
|
||||
static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
|
||||
key_ref_t dest_ref)
|
||||
{
|
||||
struct keyring_index_key index_key;
|
||||
struct key *persistent;
|
||||
key_ref_t reg_ref, persistent_ref;
|
||||
char buf[32];
|
||||
long ret;
|
||||
|
||||
/* Look in the register if it exists */
|
||||
index_key.type = &key_type_keyring;
|
||||
index_key.description = buf;
|
||||
index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));
|
||||
|
||||
if (ns->persistent_keyring_register) {
|
||||
reg_ref = make_key_ref(ns->persistent_keyring_register, true);
|
||||
down_read(&ns->persistent_keyring_register_sem);
|
||||
persistent_ref = find_key_to_update(reg_ref, &index_key);
|
||||
up_read(&ns->persistent_keyring_register_sem);
|
||||
|
||||
if (persistent_ref)
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* It wasn't in the register, so we'll need to create it. We might
|
||||
* also need to create the register.
|
||||
*/
|
||||
down_write(&ns->persistent_keyring_register_sem);
|
||||
persistent_ref = key_create_persistent(ns, uid, &index_key);
|
||||
up_write(&ns->persistent_keyring_register_sem);
|
||||
if (!IS_ERR(persistent_ref))
|
||||
goto found;
|
||||
|
||||
return PTR_ERR(persistent_ref);
|
||||
|
||||
found:
|
||||
ret = key_task_permission(persistent_ref, current_cred(), KEY_NEED_LINK);
|
||||
if (ret == 0) {
|
||||
persistent = key_ref_to_ptr(persistent_ref);
|
||||
ret = key_link(key_ref_to_ptr(dest_ref), persistent);
|
||||
if (ret == 0) {
|
||||
key_set_timeout(persistent, persistent_keyring_expiry);
|
||||
ret = persistent->serial;
|
||||
}
|
||||
}
|
||||
|
||||
key_ref_put(persistent_ref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the persistent keyring for a specific UID and link it to the nominated
|
||||
* keyring.
|
||||
*/
|
||||
long keyctl_get_persistent(uid_t _uid, key_serial_t destid)
|
||||
{
|
||||
struct user_namespace *ns = current_user_ns();
|
||||
key_ref_t dest_ref;
|
||||
kuid_t uid;
|
||||
long ret;
|
||||
|
||||
/* -1 indicates the current user */
|
||||
if (_uid == (uid_t)-1) {
|
||||
uid = current_uid();
|
||||
} else {
|
||||
uid = make_kuid(ns, _uid);
|
||||
if (!uid_valid(uid))
|
||||
return -EINVAL;
|
||||
|
||||
/* You can only see your own persistent cache if you're not
|
||||
* sufficiently privileged.
|
||||
*/
|
||||
if (!uid_eq(uid, current_uid()) &&
|
||||
!uid_eq(uid, current_euid()) &&
|
||||
!ns_capable(ns, CAP_SETUID))
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/* There must be a destination keyring */
|
||||
dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
|
||||
if (IS_ERR(dest_ref))
|
||||
return PTR_ERR(dest_ref);
|
||||
if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) {
|
||||
ret = -ENOTDIR;
|
||||
goto out_put_dest;
|
||||
}
|
||||
|
||||
ret = key_get_persistent(ns, uid, dest_ref);
|
||||
|
||||
out_put_dest:
|
||||
key_ref_put(dest_ref);
|
||||
return ret;
|
||||
}
|
360
security/keys/proc.c
Normal file
360
security/keys/proc.c
Normal file
|
@ -0,0 +1,360 @@
|
|||
/* procfs files for key database enumeration
|
||||
*
|
||||
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <asm/errno.h>
|
||||
#include "internal.h"
|
||||
|
||||
#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
|
||||
static int proc_keys_open(struct inode *inode, struct file *file);
|
||||
static void *proc_keys_start(struct seq_file *p, loff_t *_pos);
|
||||
static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos);
|
||||
static void proc_keys_stop(struct seq_file *p, void *v);
|
||||
static int proc_keys_show(struct seq_file *m, void *v);
|
||||
|
||||
static const struct seq_operations proc_keys_ops = {
|
||||
.start = proc_keys_start,
|
||||
.next = proc_keys_next,
|
||||
.stop = proc_keys_stop,
|
||||
.show = proc_keys_show,
|
||||
};
|
||||
|
||||
static const struct file_operations proc_keys_fops = {
|
||||
.open = proc_keys_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int proc_key_users_open(struct inode *inode, struct file *file);
|
||||
static void *proc_key_users_start(struct seq_file *p, loff_t *_pos);
|
||||
static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos);
|
||||
static void proc_key_users_stop(struct seq_file *p, void *v);
|
||||
static int proc_key_users_show(struct seq_file *m, void *v);
|
||||
|
||||
static const struct seq_operations proc_key_users_ops = {
|
||||
.start = proc_key_users_start,
|
||||
.next = proc_key_users_next,
|
||||
.stop = proc_key_users_stop,
|
||||
.show = proc_key_users_show,
|
||||
};
|
||||
|
||||
static const struct file_operations proc_key_users_fops = {
|
||||
.open = proc_key_users_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* Declare the /proc files.
|
||||
*/
|
||||
static int __init key_proc_init(void)
|
||||
{
|
||||
struct proc_dir_entry *p;
|
||||
|
||||
#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
|
||||
p = proc_create("keys", 0, NULL, &proc_keys_fops);
|
||||
if (!p)
|
||||
panic("Cannot create /proc/keys\n");
|
||||
#endif
|
||||
|
||||
p = proc_create("key-users", 0, NULL, &proc_key_users_fops);
|
||||
if (!p)
|
||||
panic("Cannot create /proc/key-users\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(key_proc_init);
|
||||
|
||||
/*
|
||||
* Implement "/proc/keys" to provide a list of the keys on the system that
|
||||
* grant View permission to the caller.
|
||||
*/
|
||||
#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
|
||||
|
||||
static struct rb_node *key_serial_next(struct seq_file *p, struct rb_node *n)
|
||||
{
|
||||
struct user_namespace *user_ns = seq_user_ns(p);
|
||||
|
||||
n = rb_next(n);
|
||||
while (n) {
|
||||
struct key *key = rb_entry(n, struct key, serial_node);
|
||||
if (kuid_has_mapping(user_ns, key->user->uid))
|
||||
break;
|
||||
n = rb_next(n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int proc_keys_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &proc_keys_ops);
|
||||
}
|
||||
|
||||
static struct key *find_ge_key(struct seq_file *p, key_serial_t id)
|
||||
{
|
||||
struct user_namespace *user_ns = seq_user_ns(p);
|
||||
struct rb_node *n = key_serial_tree.rb_node;
|
||||
struct key *minkey = NULL;
|
||||
|
||||
while (n) {
|
||||
struct key *key = rb_entry(n, struct key, serial_node);
|
||||
if (id < key->serial) {
|
||||
if (!minkey || minkey->serial > key->serial)
|
||||
minkey = key;
|
||||
n = n->rb_left;
|
||||
} else if (id > key->serial) {
|
||||
n = n->rb_right;
|
||||
} else {
|
||||
minkey = key;
|
||||
break;
|
||||
}
|
||||
key = NULL;
|
||||
}
|
||||
|
||||
if (!minkey)
|
||||
return NULL;
|
||||
|
||||
for (;;) {
|
||||
if (kuid_has_mapping(user_ns, minkey->user->uid))
|
||||
return minkey;
|
||||
n = rb_next(&minkey->serial_node);
|
||||
if (!n)
|
||||
return NULL;
|
||||
minkey = rb_entry(n, struct key, serial_node);
|
||||
}
|
||||
}
|
||||
|
||||
static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
|
||||
__acquires(key_serial_lock)
|
||||
{
|
||||
key_serial_t pos = *_pos;
|
||||
struct key *key;
|
||||
|
||||
spin_lock(&key_serial_lock);
|
||||
|
||||
if (*_pos > INT_MAX)
|
||||
return NULL;
|
||||
key = find_ge_key(p, pos);
|
||||
if (!key)
|
||||
return NULL;
|
||||
*_pos = key->serial;
|
||||
return &key->serial_node;
|
||||
}
|
||||
|
||||
static inline key_serial_t key_node_serial(struct rb_node *n)
|
||||
{
|
||||
struct key *key = rb_entry(n, struct key, serial_node);
|
||||
return key->serial;
|
||||
}
|
||||
|
||||
static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
|
||||
{
|
||||
struct rb_node *n;
|
||||
|
||||
n = key_serial_next(p, v);
|
||||
if (n)
|
||||
*_pos = key_node_serial(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void proc_keys_stop(struct seq_file *p, void *v)
|
||||
__releases(key_serial_lock)
|
||||
{
|
||||
spin_unlock(&key_serial_lock);
|
||||
}
|
||||
|
||||
static int proc_keys_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct rb_node *_p = v;
|
||||
struct key *key = rb_entry(_p, struct key, serial_node);
|
||||
struct timespec now;
|
||||
unsigned long timo;
|
||||
key_ref_t key_ref, skey_ref;
|
||||
char xbuf[12];
|
||||
int rc;
|
||||
|
||||
struct keyring_search_context ctx = {
|
||||
.index_key.type = key->type,
|
||||
.index_key.description = key->description,
|
||||
.cred = current_cred(),
|
||||
.match_data.cmp = lookup_user_key_possessed,
|
||||
.match_data.raw_data = key,
|
||||
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.flags = KEYRING_SEARCH_NO_STATE_CHECK,
|
||||
};
|
||||
|
||||
key_ref = make_key_ref(key, 0);
|
||||
|
||||
/* determine if the key is possessed by this process (a test we can
|
||||
* skip if the key does not indicate the possessor can view it
|
||||
*/
|
||||
if (key->perm & KEY_POS_VIEW) {
|
||||
skey_ref = search_my_process_keyrings(&ctx);
|
||||
if (!IS_ERR(skey_ref)) {
|
||||
key_ref_put(skey_ref);
|
||||
key_ref = make_key_ref(key, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* check whether the current task is allowed to view the key (assuming
|
||||
* non-possession)
|
||||
* - the caller holds a spinlock, and thus the RCU read lock, making our
|
||||
* access to __current_cred() safe
|
||||
*/
|
||||
rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW);
|
||||
if (rc < 0)
|
||||
return 0;
|
||||
|
||||
now = current_kernel_time();
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* come up with a suitable timeout value */
|
||||
if (key->expiry == 0) {
|
||||
memcpy(xbuf, "perm", 5);
|
||||
} else if (now.tv_sec >= key->expiry) {
|
||||
memcpy(xbuf, "expd", 5);
|
||||
} else {
|
||||
timo = key->expiry - now.tv_sec;
|
||||
|
||||
if (timo < 60)
|
||||
sprintf(xbuf, "%lus", timo);
|
||||
else if (timo < 60*60)
|
||||
sprintf(xbuf, "%lum", timo / 60);
|
||||
else if (timo < 60*60*24)
|
||||
sprintf(xbuf, "%luh", timo / (60*60));
|
||||
else if (timo < 60*60*24*7)
|
||||
sprintf(xbuf, "%lud", timo / (60*60*24));
|
||||
else
|
||||
sprintf(xbuf, "%luw", timo / (60*60*24*7));
|
||||
}
|
||||
|
||||
#define showflag(KEY, LETTER, FLAG) \
|
||||
(test_bit(FLAG, &(KEY)->flags) ? LETTER : '-')
|
||||
|
||||
seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
|
||||
key->serial,
|
||||
showflag(key, 'I', KEY_FLAG_INSTANTIATED),
|
||||
showflag(key, 'R', KEY_FLAG_REVOKED),
|
||||
showflag(key, 'D', KEY_FLAG_DEAD),
|
||||
showflag(key, 'Q', KEY_FLAG_IN_QUOTA),
|
||||
showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
|
||||
showflag(key, 'N', KEY_FLAG_NEGATIVE),
|
||||
showflag(key, 'i', KEY_FLAG_INVALIDATED),
|
||||
atomic_read(&key->usage),
|
||||
xbuf,
|
||||
key->perm,
|
||||
from_kuid_munged(seq_user_ns(m), key->uid),
|
||||
from_kgid_munged(seq_user_ns(m), key->gid),
|
||||
key->type->name);
|
||||
|
||||
#undef showflag
|
||||
|
||||
if (key->type->describe)
|
||||
key->type->describe(key, m);
|
||||
seq_putc(m, '\n');
|
||||
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
|
||||
|
||||
static struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n)
|
||||
{
|
||||
while (n) {
|
||||
struct key_user *user = rb_entry(n, struct key_user, node);
|
||||
if (kuid_has_mapping(user_ns, user->uid))
|
||||
break;
|
||||
n = rb_next(n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n)
|
||||
{
|
||||
return __key_user_next(user_ns, rb_next(n));
|
||||
}
|
||||
|
||||
static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r)
|
||||
{
|
||||
struct rb_node *n = rb_first(r);
|
||||
return __key_user_next(user_ns, n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implement "/proc/key-users" to provides a list of the key users and their
|
||||
* quotas.
|
||||
*/
|
||||
static int proc_key_users_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &proc_key_users_ops);
|
||||
}
|
||||
|
||||
static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
|
||||
__acquires(key_user_lock)
|
||||
{
|
||||
struct rb_node *_p;
|
||||
loff_t pos = *_pos;
|
||||
|
||||
spin_lock(&key_user_lock);
|
||||
|
||||
_p = key_user_first(seq_user_ns(p), &key_user_tree);
|
||||
while (pos > 0 && _p) {
|
||||
pos--;
|
||||
_p = key_user_next(seq_user_ns(p), _p);
|
||||
}
|
||||
|
||||
return _p;
|
||||
}
|
||||
|
||||
static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
|
||||
{
|
||||
(*_pos)++;
|
||||
return key_user_next(seq_user_ns(p), (struct rb_node *)v);
|
||||
}
|
||||
|
||||
static void proc_key_users_stop(struct seq_file *p, void *v)
|
||||
__releases(key_user_lock)
|
||||
{
|
||||
spin_unlock(&key_user_lock);
|
||||
}
|
||||
|
||||
static int proc_key_users_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct rb_node *_p = v;
|
||||
struct key_user *user = rb_entry(_p, struct key_user, node);
|
||||
unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ?
|
||||
key_quota_root_maxkeys : key_quota_maxkeys;
|
||||
unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ?
|
||||
key_quota_root_maxbytes : key_quota_maxbytes;
|
||||
|
||||
seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
|
||||
from_kuid_munged(seq_user_ns(m), user->uid),
|
||||
atomic_read(&user->usage),
|
||||
atomic_read(&user->nkeys),
|
||||
atomic_read(&user->nikeys),
|
||||
user->qnkeys,
|
||||
maxkeys,
|
||||
user->qnbytes,
|
||||
maxbytes);
|
||||
|
||||
return 0;
|
||||
}
|
870
security/keys/process_keys.c
Normal file
870
security/keys/process_keys.c
Normal file
|
@ -0,0 +1,870 @@
|
|||
/* Manage a process's keyrings
|
||||
*
|
||||
* Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/keyctl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "internal.h"
|
||||
|
||||
/* Session keyring create vs join semaphore */
|
||||
static DEFINE_MUTEX(key_session_mutex);
|
||||
|
||||
/* User keyring creation semaphore */
|
||||
static DEFINE_MUTEX(key_user_keyring_mutex);
|
||||
|
||||
/* The root user's tracking struct */
|
||||
struct key_user root_key_user = {
|
||||
.usage = ATOMIC_INIT(3),
|
||||
.cons_lock = __MUTEX_INITIALIZER(root_key_user.cons_lock),
|
||||
.lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock),
|
||||
.nkeys = ATOMIC_INIT(2),
|
||||
.nikeys = ATOMIC_INIT(2),
|
||||
.uid = GLOBAL_ROOT_UID,
|
||||
};
|
||||
|
||||
/*
|
||||
* Install the user and user session keyrings for the current process's UID.
|
||||
*/
|
||||
int install_user_keyrings(void)
|
||||
{
|
||||
struct user_struct *user;
|
||||
const struct cred *cred;
|
||||
struct key *uid_keyring, *session_keyring;
|
||||
key_perm_t user_keyring_perm;
|
||||
char buf[20];
|
||||
int ret;
|
||||
uid_t uid;
|
||||
|
||||
user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL;
|
||||
cred = current_cred();
|
||||
user = cred->user;
|
||||
uid = from_kuid(cred->user_ns, user->uid);
|
||||
|
||||
kenter("%p{%u}", user, uid);
|
||||
|
||||
if (user->uid_keyring && user->session_keyring) {
|
||||
kleave(" = 0 [exist]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_lock(&key_user_keyring_mutex);
|
||||
ret = 0;
|
||||
|
||||
if (!user->uid_keyring) {
|
||||
/* get the UID-specific keyring
|
||||
* - there may be one in existence already as it may have been
|
||||
* pinned by a session, but the user_struct pointing to it
|
||||
* may have been destroyed by setuid */
|
||||
sprintf(buf, "_uid.%u", uid);
|
||||
|
||||
uid_keyring = find_keyring_by_name(buf, true);
|
||||
if (IS_ERR(uid_keyring)) {
|
||||
uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
|
||||
cred, user_keyring_perm,
|
||||
KEY_ALLOC_IN_QUOTA, NULL);
|
||||
if (IS_ERR(uid_keyring)) {
|
||||
ret = PTR_ERR(uid_keyring);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* get a default session keyring (which might also exist
|
||||
* already) */
|
||||
sprintf(buf, "_uid_ses.%u", uid);
|
||||
|
||||
session_keyring = find_keyring_by_name(buf, true);
|
||||
if (IS_ERR(session_keyring)) {
|
||||
session_keyring =
|
||||
keyring_alloc(buf, user->uid, INVALID_GID,
|
||||
cred, user_keyring_perm,
|
||||
KEY_ALLOC_IN_QUOTA, NULL);
|
||||
if (IS_ERR(session_keyring)) {
|
||||
ret = PTR_ERR(session_keyring);
|
||||
goto error_release;
|
||||
}
|
||||
|
||||
/* we install a link from the user session keyring to
|
||||
* the user keyring */
|
||||
ret = key_link(session_keyring, uid_keyring);
|
||||
if (ret < 0)
|
||||
goto error_release_both;
|
||||
}
|
||||
|
||||
/* install the keyrings */
|
||||
user->uid_keyring = uid_keyring;
|
||||
user->session_keyring = session_keyring;
|
||||
}
|
||||
|
||||
mutex_unlock(&key_user_keyring_mutex);
|
||||
kleave(" = 0");
|
||||
return 0;
|
||||
|
||||
error_release_both:
|
||||
key_put(session_keyring);
|
||||
error_release:
|
||||
key_put(uid_keyring);
|
||||
error:
|
||||
mutex_unlock(&key_user_keyring_mutex);
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Install a fresh thread keyring directly to new credentials. This keyring is
|
||||
* allowed to overrun the quota.
|
||||
*/
|
||||
int install_thread_keyring_to_cred(struct cred *new)
|
||||
{
|
||||
struct key *keyring;
|
||||
|
||||
keyring = keyring_alloc("_tid", new->uid, new->gid, new,
|
||||
KEY_POS_ALL | KEY_USR_VIEW,
|
||||
KEY_ALLOC_QUOTA_OVERRUN, NULL);
|
||||
if (IS_ERR(keyring))
|
||||
return PTR_ERR(keyring);
|
||||
|
||||
new->thread_keyring = keyring;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Install a fresh thread keyring, discarding the old one.
|
||||
*/
|
||||
static int install_thread_keyring(void)
|
||||
{
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
BUG_ON(new->thread_keyring);
|
||||
|
||||
ret = install_thread_keyring_to_cred(new);
|
||||
if (ret < 0) {
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return commit_creds(new);
|
||||
}
|
||||
|
||||
/*
|
||||
* Install a process keyring directly to a credentials struct.
|
||||
*
|
||||
* Returns -EEXIST if there was already a process keyring, 0 if one installed,
|
||||
* and other value on any other error
|
||||
*/
|
||||
int install_process_keyring_to_cred(struct cred *new)
|
||||
{
|
||||
struct key *keyring;
|
||||
|
||||
if (new->process_keyring)
|
||||
return -EEXIST;
|
||||
|
||||
keyring = keyring_alloc("_pid", new->uid, new->gid, new,
|
||||
KEY_POS_ALL | KEY_USR_VIEW,
|
||||
KEY_ALLOC_QUOTA_OVERRUN, NULL);
|
||||
if (IS_ERR(keyring))
|
||||
return PTR_ERR(keyring);
|
||||
|
||||
new->process_keyring = keyring;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure a process keyring is installed for the current process. The
|
||||
* existing process keyring is not replaced.
|
||||
*
|
||||
* Returns 0 if there is a process keyring by the end of this function, some
|
||||
* error otherwise.
|
||||
*/
|
||||
static int install_process_keyring(void)
|
||||
{
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = install_process_keyring_to_cred(new);
|
||||
if (ret < 0) {
|
||||
abort_creds(new);
|
||||
return ret != -EEXIST ? ret : 0;
|
||||
}
|
||||
|
||||
return commit_creds(new);
|
||||
}
|
||||
|
||||
/*
|
||||
* Install a session keyring directly to a credentials struct.
|
||||
*/
|
||||
int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct key *old;
|
||||
|
||||
might_sleep();
|
||||
|
||||
/* create an empty session keyring */
|
||||
if (!keyring) {
|
||||
flags = KEY_ALLOC_QUOTA_OVERRUN;
|
||||
if (cred->session_keyring)
|
||||
flags = KEY_ALLOC_IN_QUOTA;
|
||||
|
||||
keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred,
|
||||
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
|
||||
flags, NULL);
|
||||
if (IS_ERR(keyring))
|
||||
return PTR_ERR(keyring);
|
||||
} else {
|
||||
__key_get(keyring);
|
||||
}
|
||||
|
||||
/* install the keyring */
|
||||
old = cred->session_keyring;
|
||||
rcu_assign_pointer(cred->session_keyring, keyring);
|
||||
|
||||
if (old)
|
||||
key_put(old);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Install a session keyring, discarding the old one. If a keyring is not
|
||||
* supplied, an empty one is invented.
|
||||
*/
|
||||
static int install_session_keyring(struct key *keyring)
|
||||
{
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = install_session_keyring_to_cred(new, keyring);
|
||||
if (ret < 0) {
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return commit_creds(new);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the fsuid changing.
|
||||
*/
|
||||
void key_fsuid_changed(struct task_struct *tsk)
|
||||
{
|
||||
/* update the ownership of the thread keyring */
|
||||
BUG_ON(!tsk->cred);
|
||||
if (tsk->cred->thread_keyring) {
|
||||
down_write(&tsk->cred->thread_keyring->sem);
|
||||
tsk->cred->thread_keyring->uid = tsk->cred->fsuid;
|
||||
up_write(&tsk->cred->thread_keyring->sem);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the fsgid changing.
|
||||
*/
|
||||
void key_fsgid_changed(struct task_struct *tsk)
|
||||
{
|
||||
/* update the ownership of the thread keyring */
|
||||
BUG_ON(!tsk->cred);
|
||||
if (tsk->cred->thread_keyring) {
|
||||
down_write(&tsk->cred->thread_keyring->sem);
|
||||
tsk->cred->thread_keyring->gid = tsk->cred->fsgid;
|
||||
up_write(&tsk->cred->thread_keyring->sem);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Search the process keyrings attached to the supplied cred for the first
|
||||
* matching key.
|
||||
*
|
||||
* The search criteria are the type and the match function. The description is
|
||||
* given to the match function as a parameter, but doesn't otherwise influence
|
||||
* the search. Typically the match function will compare the description
|
||||
* parameter to the key's description.
|
||||
*
|
||||
* This can only search keyrings that grant Search permission to the supplied
|
||||
* credentials. Keyrings linked to searched keyrings will also be searched if
|
||||
* they grant Search permission too. Keys can only be found if they grant
|
||||
* Search permission to the credentials.
|
||||
*
|
||||
* Returns a pointer to the key with the key usage count incremented if
|
||||
* successful, -EAGAIN if we didn't find any matching key or -ENOKEY if we only
|
||||
* matched negative keys.
|
||||
*
|
||||
* In the case of a successful return, the possession attribute is set on the
|
||||
* returned key reference.
|
||||
*/
|
||||
key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
|
||||
{
|
||||
key_ref_t key_ref, ret, err;
|
||||
|
||||
/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
|
||||
* searchable, but we failed to find a key or we found a negative key;
|
||||
* otherwise we want to return a sample error (probably -EACCES) if
|
||||
* none of the keyrings were searchable
|
||||
*
|
||||
* in terms of priority: success > -ENOKEY > -EAGAIN > other error
|
||||
*/
|
||||
key_ref = NULL;
|
||||
ret = NULL;
|
||||
err = ERR_PTR(-EAGAIN);
|
||||
|
||||
/* search the thread keyring first */
|
||||
if (ctx->cred->thread_keyring) {
|
||||
key_ref = keyring_search_aux(
|
||||
make_key_ref(ctx->cred->thread_keyring, 1), ctx);
|
||||
if (!IS_ERR(key_ref))
|
||||
goto found;
|
||||
|
||||
switch (PTR_ERR(key_ref)) {
|
||||
case -EAGAIN: /* no key */
|
||||
case -ENOKEY: /* negative key */
|
||||
ret = key_ref;
|
||||
break;
|
||||
default:
|
||||
err = key_ref;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* search the process keyring second */
|
||||
if (ctx->cred->process_keyring) {
|
||||
key_ref = keyring_search_aux(
|
||||
make_key_ref(ctx->cred->process_keyring, 1), ctx);
|
||||
if (!IS_ERR(key_ref))
|
||||
goto found;
|
||||
|
||||
switch (PTR_ERR(key_ref)) {
|
||||
case -EAGAIN: /* no key */
|
||||
if (ret)
|
||||
break;
|
||||
case -ENOKEY: /* negative key */
|
||||
ret = key_ref;
|
||||
break;
|
||||
default:
|
||||
err = key_ref;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* search the session keyring */
|
||||
if (ctx->cred->session_keyring) {
|
||||
rcu_read_lock();
|
||||
key_ref = keyring_search_aux(
|
||||
make_key_ref(rcu_dereference(ctx->cred->session_keyring), 1),
|
||||
ctx);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!IS_ERR(key_ref))
|
||||
goto found;
|
||||
|
||||
switch (PTR_ERR(key_ref)) {
|
||||
case -EAGAIN: /* no key */
|
||||
if (ret)
|
||||
break;
|
||||
case -ENOKEY: /* negative key */
|
||||
ret = key_ref;
|
||||
break;
|
||||
default:
|
||||
err = key_ref;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* or search the user-session keyring */
|
||||
else if (ctx->cred->user->session_keyring) {
|
||||
key_ref = keyring_search_aux(
|
||||
make_key_ref(ctx->cred->user->session_keyring, 1),
|
||||
ctx);
|
||||
if (!IS_ERR(key_ref))
|
||||
goto found;
|
||||
|
||||
switch (PTR_ERR(key_ref)) {
|
||||
case -EAGAIN: /* no key */
|
||||
if (ret)
|
||||
break;
|
||||
case -ENOKEY: /* negative key */
|
||||
ret = key_ref;
|
||||
break;
|
||||
default:
|
||||
err = key_ref;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* no key - decide on the error we're going to go for */
|
||||
key_ref = ret ? ret : err;
|
||||
|
||||
found:
|
||||
return key_ref;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search the process keyrings attached to the supplied cred for the first
|
||||
* matching key in the manner of search_my_process_keyrings(), but also search
|
||||
* the keys attached to the assumed authorisation key using its credentials if
|
||||
* one is available.
|
||||
*
|
||||
* Return same as search_my_process_keyrings().
|
||||
*/
|
||||
key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
|
||||
{
|
||||
struct request_key_auth *rka;
|
||||
key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;
|
||||
|
||||
might_sleep();
|
||||
|
||||
key_ref = search_my_process_keyrings(ctx);
|
||||
if (!IS_ERR(key_ref))
|
||||
goto found;
|
||||
err = key_ref;
|
||||
|
||||
/* if this process has an instantiation authorisation key, then we also
|
||||
* search the keyrings of the process mentioned there
|
||||
* - we don't permit access to request_key auth keys via this method
|
||||
*/
|
||||
if (ctx->cred->request_key_auth &&
|
||||
ctx->cred == current_cred() &&
|
||||
ctx->index_key.type != &key_type_request_key_auth
|
||||
) {
|
||||
const struct cred *cred = ctx->cred;
|
||||
|
||||
/* defend against the auth key being revoked */
|
||||
down_read(&cred->request_key_auth->sem);
|
||||
|
||||
if (key_validate(ctx->cred->request_key_auth) == 0) {
|
||||
rka = ctx->cred->request_key_auth->payload.data;
|
||||
|
||||
ctx->cred = rka->cred;
|
||||
key_ref = search_process_keyrings(ctx);
|
||||
ctx->cred = cred;
|
||||
|
||||
up_read(&cred->request_key_auth->sem);
|
||||
|
||||
if (!IS_ERR(key_ref))
|
||||
goto found;
|
||||
|
||||
ret = key_ref;
|
||||
} else {
|
||||
up_read(&cred->request_key_auth->sem);
|
||||
}
|
||||
}
|
||||
|
||||
/* no key - decide on the error we're going to go for */
|
||||
if (err == ERR_PTR(-ENOKEY) || ret == ERR_PTR(-ENOKEY))
|
||||
key_ref = ERR_PTR(-ENOKEY);
|
||||
else if (err == ERR_PTR(-EACCES))
|
||||
key_ref = ret;
|
||||
else
|
||||
key_ref = err;
|
||||
|
||||
found:
|
||||
return key_ref;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if the key we're looking at is the target key.
|
||||
*/
|
||||
bool lookup_user_key_possessed(const struct key *key,
|
||||
const struct key_match_data *match_data)
|
||||
{
|
||||
return key == match_data->raw_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up a key ID given us by userspace with a given permissions mask to get
|
||||
* the key it refers to.
|
||||
*
|
||||
* Flags can be passed to request that special keyrings be created if referred
|
||||
* to directly, to permit partially constructed keys to be found and to skip
|
||||
* validity and permission checks on the found key.
|
||||
*
|
||||
* Returns a pointer to the key with an incremented usage count if successful;
|
||||
* -EINVAL if the key ID is invalid; -ENOKEY if the key ID does not correspond
|
||||
* to a key or the best found key was a negative key; -EKEYREVOKED or
|
||||
* -EKEYEXPIRED if the best found key was revoked or expired; -EACCES if the
|
||||
* found key doesn't grant the requested permit or the LSM denied access to it;
|
||||
* or -ENOMEM if a special keyring couldn't be created.
|
||||
*
|
||||
* In the case of a successful return, the possession attribute is set on the
|
||||
* returned key reference.
|
||||
*/
|
||||
key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
|
||||
key_perm_t perm)
|
||||
{
|
||||
struct keyring_search_context ctx = {
|
||||
.match_data.cmp = lookup_user_key_possessed,
|
||||
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.flags = KEYRING_SEARCH_NO_STATE_CHECK,
|
||||
};
|
||||
struct request_key_auth *rka;
|
||||
struct key *key;
|
||||
key_ref_t key_ref, skey_ref;
|
||||
int ret;
|
||||
|
||||
try_again:
|
||||
ctx.cred = get_current_cred();
|
||||
key_ref = ERR_PTR(-ENOKEY);
|
||||
|
||||
switch (id) {
|
||||
case KEY_SPEC_THREAD_KEYRING:
|
||||
if (!ctx.cred->thread_keyring) {
|
||||
if (!(lflags & KEY_LOOKUP_CREATE))
|
||||
goto error;
|
||||
|
||||
ret = install_thread_keyring();
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error;
|
||||
}
|
||||
goto reget_creds;
|
||||
}
|
||||
|
||||
key = ctx.cred->thread_keyring;
|
||||
__key_get(key);
|
||||
key_ref = make_key_ref(key, 1);
|
||||
break;
|
||||
|
||||
case KEY_SPEC_PROCESS_KEYRING:
|
||||
if (!ctx.cred->process_keyring) {
|
||||
if (!(lflags & KEY_LOOKUP_CREATE))
|
||||
goto error;
|
||||
|
||||
ret = install_process_keyring();
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error;
|
||||
}
|
||||
goto reget_creds;
|
||||
}
|
||||
|
||||
key = ctx.cred->process_keyring;
|
||||
__key_get(key);
|
||||
key_ref = make_key_ref(key, 1);
|
||||
break;
|
||||
|
||||
case KEY_SPEC_SESSION_KEYRING:
|
||||
if (!ctx.cred->session_keyring) {
|
||||
/* always install a session keyring upon access if one
|
||||
* doesn't exist yet */
|
||||
ret = install_user_keyrings();
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
if (lflags & KEY_LOOKUP_CREATE)
|
||||
ret = join_session_keyring(NULL);
|
||||
else
|
||||
ret = install_session_keyring(
|
||||
ctx.cred->user->session_keyring);
|
||||
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
goto reget_creds;
|
||||
} else if (ctx.cred->session_keyring ==
|
||||
ctx.cred->user->session_keyring &&
|
||||
lflags & KEY_LOOKUP_CREATE) {
|
||||
ret = join_session_keyring(NULL);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
goto reget_creds;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
key = rcu_dereference(ctx.cred->session_keyring);
|
||||
__key_get(key);
|
||||
rcu_read_unlock();
|
||||
key_ref = make_key_ref(key, 1);
|
||||
break;
|
||||
|
||||
case KEY_SPEC_USER_KEYRING:
|
||||
if (!ctx.cred->user->uid_keyring) {
|
||||
ret = install_user_keyrings();
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = ctx.cred->user->uid_keyring;
|
||||
__key_get(key);
|
||||
key_ref = make_key_ref(key, 1);
|
||||
break;
|
||||
|
||||
case KEY_SPEC_USER_SESSION_KEYRING:
|
||||
if (!ctx.cred->user->session_keyring) {
|
||||
ret = install_user_keyrings();
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = ctx.cred->user->session_keyring;
|
||||
__key_get(key);
|
||||
key_ref = make_key_ref(key, 1);
|
||||
break;
|
||||
|
||||
case KEY_SPEC_GROUP_KEYRING:
|
||||
/* group keyrings are not yet supported */
|
||||
key_ref = ERR_PTR(-EINVAL);
|
||||
goto error;
|
||||
|
||||
case KEY_SPEC_REQKEY_AUTH_KEY:
|
||||
key = ctx.cred->request_key_auth;
|
||||
if (!key)
|
||||
goto error;
|
||||
|
||||
__key_get(key);
|
||||
key_ref = make_key_ref(key, 1);
|
||||
break;
|
||||
|
||||
case KEY_SPEC_REQUESTOR_KEYRING:
|
||||
if (!ctx.cred->request_key_auth)
|
||||
goto error;
|
||||
|
||||
down_read(&ctx.cred->request_key_auth->sem);
|
||||
if (test_bit(KEY_FLAG_REVOKED,
|
||||
&ctx.cred->request_key_auth->flags)) {
|
||||
key_ref = ERR_PTR(-EKEYREVOKED);
|
||||
key = NULL;
|
||||
} else {
|
||||
rka = ctx.cred->request_key_auth->payload.data;
|
||||
key = rka->dest_keyring;
|
||||
__key_get(key);
|
||||
}
|
||||
up_read(&ctx.cred->request_key_auth->sem);
|
||||
if (!key)
|
||||
goto error;
|
||||
key_ref = make_key_ref(key, 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
key_ref = ERR_PTR(-EINVAL);
|
||||
if (id < 1)
|
||||
goto error;
|
||||
|
||||
key = key_lookup(id);
|
||||
if (IS_ERR(key)) {
|
||||
key_ref = ERR_CAST(key);
|
||||
goto error;
|
||||
}
|
||||
|
||||
key_ref = make_key_ref(key, 0);
|
||||
|
||||
/* check to see if we possess the key */
|
||||
ctx.index_key.type = key->type;
|
||||
ctx.index_key.description = key->description;
|
||||
ctx.index_key.desc_len = strlen(key->description);
|
||||
ctx.match_data.raw_data = key;
|
||||
kdebug("check possessed");
|
||||
skey_ref = search_process_keyrings(&ctx);
|
||||
kdebug("possessed=%p", skey_ref);
|
||||
|
||||
if (!IS_ERR(skey_ref)) {
|
||||
key_put(key);
|
||||
key_ref = skey_ref;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* unlink does not use the nominated key in any way, so can skip all
|
||||
* the permission checks as it is only concerned with the keyring */
|
||||
if (lflags & KEY_LOOKUP_FOR_UNLINK) {
|
||||
ret = 0;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(lflags & KEY_LOOKUP_PARTIAL)) {
|
||||
ret = wait_for_key_construction(key, true);
|
||||
switch (ret) {
|
||||
case -ERESTARTSYS:
|
||||
goto invalid_key;
|
||||
default:
|
||||
if (perm)
|
||||
goto invalid_key;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
} else if (perm) {
|
||||
ret = key_validate(key);
|
||||
if (ret < 0)
|
||||
goto invalid_key;
|
||||
}
|
||||
|
||||
ret = -EIO;
|
||||
if (!(lflags & KEY_LOOKUP_PARTIAL) &&
|
||||
!test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
|
||||
goto invalid_key;
|
||||
|
||||
/* check the permissions */
|
||||
ret = key_task_permission(key_ref, ctx.cred, perm);
|
||||
if (ret < 0)
|
||||
goto invalid_key;
|
||||
|
||||
key->last_used_at = current_kernel_time().tv_sec;
|
||||
|
||||
error:
|
||||
put_cred(ctx.cred);
|
||||
return key_ref;
|
||||
|
||||
invalid_key:
|
||||
key_ref_put(key_ref);
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error;
|
||||
|
||||
/* if we attempted to install a keyring, then it may have caused new
|
||||
* creds to be installed */
|
||||
reget_creds:
|
||||
put_cred(ctx.cred);
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
/*
|
||||
* Join the named keyring as the session keyring if possible else attempt to
|
||||
* create a new one of that name and join that.
|
||||
*
|
||||
* If the name is NULL, an empty anonymous keyring will be installed as the
|
||||
* session keyring.
|
||||
*
|
||||
* Named session keyrings are joined with a semaphore held to prevent the
|
||||
* keyrings from going away whilst the attempt is made to going them and also
|
||||
* to prevent a race in creating compatible session keyrings.
|
||||
*/
|
||||
long join_session_keyring(const char *name)
|
||||
{
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
struct key *keyring;
|
||||
long ret, serial;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
old = current_cred();
|
||||
|
||||
/* if no name is provided, install an anonymous keyring */
|
||||
if (!name) {
|
||||
ret = install_session_keyring_to_cred(new, NULL);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
serial = new->session_keyring->serial;
|
||||
ret = commit_creds(new);
|
||||
if (ret == 0)
|
||||
ret = serial;
|
||||
goto okay;
|
||||
}
|
||||
|
||||
/* allow the user to join or create a named keyring */
|
||||
mutex_lock(&key_session_mutex);
|
||||
|
||||
/* look for an existing keyring of this name */
|
||||
keyring = find_keyring_by_name(name, false);
|
||||
if (PTR_ERR(keyring) == -ENOKEY) {
|
||||
/* not found - try and create a new one */
|
||||
keyring = keyring_alloc(
|
||||
name, old->uid, old->gid, old,
|
||||
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK,
|
||||
KEY_ALLOC_IN_QUOTA, NULL);
|
||||
if (IS_ERR(keyring)) {
|
||||
ret = PTR_ERR(keyring);
|
||||
goto error2;
|
||||
}
|
||||
} else if (IS_ERR(keyring)) {
|
||||
ret = PTR_ERR(keyring);
|
||||
goto error2;
|
||||
} else if (keyring == new->session_keyring) {
|
||||
ret = 0;
|
||||
goto error2;
|
||||
}
|
||||
|
||||
/* we've got a keyring - now to install it */
|
||||
ret = install_session_keyring_to_cred(new, keyring);
|
||||
if (ret < 0)
|
||||
goto error2;
|
||||
|
||||
commit_creds(new);
|
||||
mutex_unlock(&key_session_mutex);
|
||||
|
||||
ret = keyring->serial;
|
||||
key_put(keyring);
|
||||
okay:
|
||||
return ret;
|
||||
|
||||
error2:
|
||||
mutex_unlock(&key_session_mutex);
|
||||
error:
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace a process's session keyring on behalf of one of its children when
|
||||
* the target process is about to resume userspace execution.
|
||||
*/
|
||||
void key_change_session_keyring(struct callback_head *twork)
|
||||
{
|
||||
const struct cred *old = current_cred();
|
||||
struct cred *new = container_of(twork, struct cred, rcu);
|
||||
|
||||
if (unlikely(current->flags & PF_EXITING)) {
|
||||
put_cred(new);
|
||||
return;
|
||||
}
|
||||
|
||||
new-> uid = old-> uid;
|
||||
new-> euid = old-> euid;
|
||||
new-> suid = old-> suid;
|
||||
new->fsuid = old->fsuid;
|
||||
new-> gid = old-> gid;
|
||||
new-> egid = old-> egid;
|
||||
new-> sgid = old-> sgid;
|
||||
new->fsgid = old->fsgid;
|
||||
new->user = get_uid(old->user);
|
||||
new->user_ns = get_user_ns(old->user_ns);
|
||||
new->group_info = get_group_info(old->group_info);
|
||||
|
||||
new->securebits = old->securebits;
|
||||
new->cap_inheritable = old->cap_inheritable;
|
||||
new->cap_permitted = old->cap_permitted;
|
||||
new->cap_effective = old->cap_effective;
|
||||
new->cap_bset = old->cap_bset;
|
||||
|
||||
new->jit_keyring = old->jit_keyring;
|
||||
new->thread_keyring = key_get(old->thread_keyring);
|
||||
new->process_keyring = key_get(old->process_keyring);
|
||||
|
||||
security_transfer_creds(new, old);
|
||||
|
||||
commit_creds(new);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure that root's user and user-session keyrings exist.
|
||||
*/
|
||||
static int __init init_root_keyring(void)
|
||||
{
|
||||
return install_user_keyrings();
|
||||
}
|
||||
|
||||
late_initcall(init_root_keyring);
|
722
security/keys/request_key.c
Normal file
722
security/keys/request_key.c
Normal file
|
@ -0,0 +1,722 @@
|
|||
/* Request a key from userspace
|
||||
*
|
||||
* Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* See Documentation/security/keys-request-key.txt
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/keyctl.h>
|
||||
#include <linux/slab.h>
|
||||
#include "internal.h"
|
||||
|
||||
#define key_negative_timeout 60 /* default timeout on a negative key's existence */
|
||||
|
||||
/**
|
||||
* complete_request_key - Complete the construction of a key.
|
||||
* @cons: The key construction record.
|
||||
* @error: The success or failute of the construction.
|
||||
*
|
||||
* Complete the attempt to construct a key. The key will be negated
|
||||
* if an error is indicated. The authorisation key will be revoked
|
||||
* unconditionally.
|
||||
*/
|
||||
void complete_request_key(struct key_construction *cons, int error)
|
||||
{
|
||||
kenter("{%d,%d},%d", cons->key->serial, cons->authkey->serial, error);
|
||||
|
||||
if (error < 0)
|
||||
key_negate_and_link(cons->key, key_negative_timeout, NULL,
|
||||
cons->authkey);
|
||||
else
|
||||
key_revoke(cons->authkey);
|
||||
|
||||
key_put(cons->key);
|
||||
key_put(cons->authkey);
|
||||
kfree(cons);
|
||||
}
|
||||
EXPORT_SYMBOL(complete_request_key);
|
||||
|
||||
/*
|
||||
* Initialise a usermode helper that is going to have a specific session
|
||||
* keyring.
|
||||
*
|
||||
* This is called in context of freshly forked kthread before kernel_execve(),
|
||||
* so we can simply install the desired session_keyring at this point.
|
||||
*/
|
||||
static int umh_keys_init(struct subprocess_info *info, struct cred *cred)
|
||||
{
|
||||
struct key *keyring = info->data;
|
||||
|
||||
return install_session_keyring_to_cred(cred, keyring);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up a usermode helper with session keyring.
|
||||
*/
|
||||
static void umh_keys_cleanup(struct subprocess_info *info)
|
||||
{
|
||||
struct key *keyring = info->data;
|
||||
key_put(keyring);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call a usermode helper with a specific session keyring.
|
||||
*/
|
||||
static int call_usermodehelper_keys(char *path, char **argv, char **envp,
|
||||
struct key *session_keyring, int wait)
|
||||
{
|
||||
struct subprocess_info *info;
|
||||
|
||||
info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL,
|
||||
umh_keys_init, umh_keys_cleanup,
|
||||
session_keyring);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
key_get(session_keyring);
|
||||
return call_usermodehelper_exec(info, wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* Request userspace finish the construction of a key
|
||||
* - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"
|
||||
*/
|
||||
static int call_sbin_request_key(struct key_construction *cons,
|
||||
const char *op,
|
||||
void *aux)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
key_serial_t prkey, sskey;
|
||||
struct key *key = cons->key, *authkey = cons->authkey, *keyring,
|
||||
*session;
|
||||
char *argv[9], *envp[3], uid_str[12], gid_str[12];
|
||||
char key_str[12], keyring_str[3][12];
|
||||
char desc[20];
|
||||
int ret, i;
|
||||
|
||||
kenter("{%d},{%d},%s", key->serial, authkey->serial, op);
|
||||
|
||||
ret = install_user_keyrings();
|
||||
if (ret < 0)
|
||||
goto error_alloc;
|
||||
|
||||
/* allocate a new session keyring */
|
||||
sprintf(desc, "_req.%u", key->serial);
|
||||
|
||||
cred = get_current_cred();
|
||||
keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred,
|
||||
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
|
||||
KEY_ALLOC_QUOTA_OVERRUN, NULL);
|
||||
put_cred(cred);
|
||||
if (IS_ERR(keyring)) {
|
||||
ret = PTR_ERR(keyring);
|
||||
goto error_alloc;
|
||||
}
|
||||
|
||||
/* attach the auth key to the session keyring */
|
||||
ret = key_link(keyring, authkey);
|
||||
if (ret < 0)
|
||||
goto error_link;
|
||||
|
||||
/* record the UID and GID */
|
||||
sprintf(uid_str, "%d", from_kuid(&init_user_ns, cred->fsuid));
|
||||
sprintf(gid_str, "%d", from_kgid(&init_user_ns, cred->fsgid));
|
||||
|
||||
/* we say which key is under construction */
|
||||
sprintf(key_str, "%d", key->serial);
|
||||
|
||||
/* we specify the process's default keyrings */
|
||||
sprintf(keyring_str[0], "%d",
|
||||
cred->thread_keyring ? cred->thread_keyring->serial : 0);
|
||||
|
||||
prkey = 0;
|
||||
if (cred->process_keyring)
|
||||
prkey = cred->process_keyring->serial;
|
||||
sprintf(keyring_str[1], "%d", prkey);
|
||||
|
||||
rcu_read_lock();
|
||||
session = rcu_dereference(cred->session_keyring);
|
||||
if (!session)
|
||||
session = cred->user->session_keyring;
|
||||
sskey = session->serial;
|
||||
rcu_read_unlock();
|
||||
|
||||
sprintf(keyring_str[2], "%d", sskey);
|
||||
|
||||
/* set up a minimal environment */
|
||||
i = 0;
|
||||
envp[i++] = "HOME=/";
|
||||
envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
|
||||
envp[i] = NULL;
|
||||
|
||||
/* set up the argument list */
|
||||
i = 0;
|
||||
argv[i++] = "/sbin/request-key";
|
||||
argv[i++] = (char *) op;
|
||||
argv[i++] = key_str;
|
||||
argv[i++] = uid_str;
|
||||
argv[i++] = gid_str;
|
||||
argv[i++] = keyring_str[0];
|
||||
argv[i++] = keyring_str[1];
|
||||
argv[i++] = keyring_str[2];
|
||||
argv[i] = NULL;
|
||||
|
||||
/* do it */
|
||||
ret = call_usermodehelper_keys(argv[0], argv, envp, keyring,
|
||||
UMH_WAIT_PROC);
|
||||
kdebug("usermode -> 0x%x", ret);
|
||||
if (ret >= 0) {
|
||||
/* ret is the exit/wait code */
|
||||
if (test_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags) ||
|
||||
key_validate(key) < 0)
|
||||
ret = -ENOKEY;
|
||||
else
|
||||
/* ignore any errors from userspace if the key was
|
||||
* instantiated */
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
error_link:
|
||||
key_put(keyring);
|
||||
|
||||
error_alloc:
|
||||
complete_request_key(cons, ret);
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call out to userspace for key construction.
|
||||
*
|
||||
* Program failure is ignored in favour of key status.
|
||||
*/
|
||||
static int construct_key(struct key *key, const void *callout_info,
|
||||
size_t callout_len, void *aux,
|
||||
struct key *dest_keyring)
|
||||
{
|
||||
struct key_construction *cons;
|
||||
request_key_actor_t actor;
|
||||
struct key *authkey;
|
||||
int ret;
|
||||
|
||||
kenter("%d,%p,%zu,%p", key->serial, callout_info, callout_len, aux);
|
||||
|
||||
cons = kmalloc(sizeof(*cons), GFP_KERNEL);
|
||||
if (!cons)
|
||||
return -ENOMEM;
|
||||
|
||||
/* allocate an authorisation key */
|
||||
authkey = request_key_auth_new(key, callout_info, callout_len,
|
||||
dest_keyring);
|
||||
if (IS_ERR(authkey)) {
|
||||
kfree(cons);
|
||||
ret = PTR_ERR(authkey);
|
||||
authkey = NULL;
|
||||
} else {
|
||||
cons->authkey = key_get(authkey);
|
||||
cons->key = key_get(key);
|
||||
|
||||
/* make the call */
|
||||
actor = call_sbin_request_key;
|
||||
if (key->type->request_key)
|
||||
actor = key->type->request_key;
|
||||
|
||||
ret = actor(cons, "create", aux);
|
||||
|
||||
/* check that the actor called complete_request_key() prior to
|
||||
* returning an error */
|
||||
WARN_ON(ret < 0 &&
|
||||
!test_bit(KEY_FLAG_REVOKED, &authkey->flags));
|
||||
key_put(authkey);
|
||||
}
|
||||
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the appropriate destination keyring for the request.
|
||||
*
|
||||
* The keyring selected is returned with an extra reference upon it which the
|
||||
* caller must release.
|
||||
*/
|
||||
static void construct_get_dest_keyring(struct key **_dest_keyring)
|
||||
{
|
||||
struct request_key_auth *rka;
|
||||
const struct cred *cred = current_cred();
|
||||
struct key *dest_keyring = *_dest_keyring, *authkey;
|
||||
|
||||
kenter("%p", dest_keyring);
|
||||
|
||||
/* find the appropriate keyring */
|
||||
if (dest_keyring) {
|
||||
/* the caller supplied one */
|
||||
key_get(dest_keyring);
|
||||
} else {
|
||||
/* use a default keyring; falling through the cases until we
|
||||
* find one that we actually have */
|
||||
switch (cred->jit_keyring) {
|
||||
case KEY_REQKEY_DEFL_DEFAULT:
|
||||
case KEY_REQKEY_DEFL_REQUESTOR_KEYRING:
|
||||
if (cred->request_key_auth) {
|
||||
authkey = cred->request_key_auth;
|
||||
down_read(&authkey->sem);
|
||||
rka = authkey->payload.data;
|
||||
if (!test_bit(KEY_FLAG_REVOKED,
|
||||
&authkey->flags))
|
||||
dest_keyring =
|
||||
key_get(rka->dest_keyring);
|
||||
up_read(&authkey->sem);
|
||||
if (dest_keyring)
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_REQKEY_DEFL_THREAD_KEYRING:
|
||||
dest_keyring = key_get(cred->thread_keyring);
|
||||
if (dest_keyring)
|
||||
break;
|
||||
|
||||
case KEY_REQKEY_DEFL_PROCESS_KEYRING:
|
||||
dest_keyring = key_get(cred->process_keyring);
|
||||
if (dest_keyring)
|
||||
break;
|
||||
|
||||
case KEY_REQKEY_DEFL_SESSION_KEYRING:
|
||||
rcu_read_lock();
|
||||
dest_keyring = key_get(
|
||||
rcu_dereference(cred->session_keyring));
|
||||
rcu_read_unlock();
|
||||
|
||||
if (dest_keyring)
|
||||
break;
|
||||
|
||||
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
|
||||
dest_keyring =
|
||||
key_get(cred->user->session_keyring);
|
||||
break;
|
||||
|
||||
case KEY_REQKEY_DEFL_USER_KEYRING:
|
||||
dest_keyring = key_get(cred->user->uid_keyring);
|
||||
break;
|
||||
|
||||
case KEY_REQKEY_DEFL_GROUP_KEYRING:
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
*_dest_keyring = dest_keyring;
|
||||
kleave(" [dk %d]", key_serial(dest_keyring));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a new key in under-construction state and attempt to link it in to
|
||||
* the requested keyring.
|
||||
*
|
||||
* May return a key that's already under construction instead if there was a
|
||||
* race between two thread calling request_key().
|
||||
*/
|
||||
static int construct_alloc_key(struct keyring_search_context *ctx,
|
||||
struct key *dest_keyring,
|
||||
unsigned long flags,
|
||||
struct key_user *user,
|
||||
struct key **_key)
|
||||
{
|
||||
struct assoc_array_edit *edit;
|
||||
struct key *key;
|
||||
key_perm_t perm;
|
||||
key_ref_t key_ref;
|
||||
int ret;
|
||||
|
||||
kenter("%s,%s,,,",
|
||||
ctx->index_key.type->name, ctx->index_key.description);
|
||||
|
||||
*_key = NULL;
|
||||
mutex_lock(&user->cons_lock);
|
||||
|
||||
perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
|
||||
perm |= KEY_USR_VIEW;
|
||||
if (ctx->index_key.type->read)
|
||||
perm |= KEY_POS_READ;
|
||||
if (ctx->index_key.type == &key_type_keyring ||
|
||||
ctx->index_key.type->update)
|
||||
perm |= KEY_POS_WRITE;
|
||||
|
||||
key = key_alloc(ctx->index_key.type, ctx->index_key.description,
|
||||
ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred,
|
||||
perm, flags);
|
||||
if (IS_ERR(key))
|
||||
goto alloc_failed;
|
||||
|
||||
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
|
||||
|
||||
if (dest_keyring) {
|
||||
ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit);
|
||||
if (ret < 0)
|
||||
goto link_prealloc_failed;
|
||||
}
|
||||
|
||||
/* attach the key to the destination keyring under lock, but we do need
|
||||
* to do another check just in case someone beat us to it whilst we
|
||||
* waited for locks */
|
||||
mutex_lock(&key_construction_mutex);
|
||||
|
||||
key_ref = search_process_keyrings(ctx);
|
||||
if (!IS_ERR(key_ref))
|
||||
goto key_already_present;
|
||||
|
||||
if (dest_keyring)
|
||||
__key_link(key, &edit);
|
||||
|
||||
mutex_unlock(&key_construction_mutex);
|
||||
if (dest_keyring)
|
||||
__key_link_end(dest_keyring, &ctx->index_key, edit);
|
||||
mutex_unlock(&user->cons_lock);
|
||||
*_key = key;
|
||||
kleave(" = 0 [%d]", key_serial(key));
|
||||
return 0;
|
||||
|
||||
/* the key is now present - we tell the caller that we found it by
|
||||
* returning -EINPROGRESS */
|
||||
key_already_present:
|
||||
key_put(key);
|
||||
mutex_unlock(&key_construction_mutex);
|
||||
key = key_ref_to_ptr(key_ref);
|
||||
if (dest_keyring) {
|
||||
ret = __key_link_check_live_key(dest_keyring, key);
|
||||
if (ret == 0)
|
||||
__key_link(key, &edit);
|
||||
__key_link_end(dest_keyring, &ctx->index_key, edit);
|
||||
if (ret < 0)
|
||||
goto link_check_failed;
|
||||
}
|
||||
mutex_unlock(&user->cons_lock);
|
||||
*_key = key;
|
||||
kleave(" = -EINPROGRESS [%d]", key_serial(key));
|
||||
return -EINPROGRESS;
|
||||
|
||||
link_check_failed:
|
||||
mutex_unlock(&user->cons_lock);
|
||||
key_put(key);
|
||||
kleave(" = %d [linkcheck]", ret);
|
||||
return ret;
|
||||
|
||||
link_prealloc_failed:
|
||||
mutex_unlock(&user->cons_lock);
|
||||
kleave(" = %d [prelink]", ret);
|
||||
return ret;
|
||||
|
||||
alloc_failed:
|
||||
mutex_unlock(&user->cons_lock);
|
||||
kleave(" = %ld", PTR_ERR(key));
|
||||
return PTR_ERR(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* Commence key construction.
|
||||
*/
|
||||
static struct key *construct_key_and_link(struct keyring_search_context *ctx,
|
||||
const char *callout_info,
|
||||
size_t callout_len,
|
||||
void *aux,
|
||||
struct key *dest_keyring,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct key_user *user;
|
||||
struct key *key;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
|
||||
user = key_user_lookup(current_fsuid());
|
||||
if (!user)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
construct_get_dest_keyring(&dest_keyring);
|
||||
|
||||
ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key);
|
||||
key_user_put(user);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = construct_key(key, callout_info, callout_len, aux,
|
||||
dest_keyring);
|
||||
if (ret < 0) {
|
||||
kdebug("cons failed");
|
||||
goto construction_failed;
|
||||
}
|
||||
} else if (ret == -EINPROGRESS) {
|
||||
ret = 0;
|
||||
} else {
|
||||
goto couldnt_alloc_key;
|
||||
}
|
||||
|
||||
key_put(dest_keyring);
|
||||
kleave(" = key %d", key_serial(key));
|
||||
return key;
|
||||
|
||||
construction_failed:
|
||||
key_negate_and_link(key, key_negative_timeout, NULL, NULL);
|
||||
key_put(key);
|
||||
couldnt_alloc_key:
|
||||
key_put(dest_keyring);
|
||||
kleave(" = %d", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* request_key_and_link - Request a key and cache it in a keyring.
|
||||
* @type: The type of key we want.
|
||||
* @description: The searchable description of the key.
|
||||
* @callout_info: The data to pass to the instantiation upcall (or NULL).
|
||||
* @callout_len: The length of callout_info.
|
||||
* @aux: Auxiliary data for the upcall.
|
||||
* @dest_keyring: Where to cache the key.
|
||||
* @flags: Flags to key_alloc().
|
||||
*
|
||||
* A key matching the specified criteria is searched for in the process's
|
||||
* keyrings and returned with its usage count incremented if found. Otherwise,
|
||||
* if callout_info is not NULL, a key will be allocated and some service
|
||||
* (probably in userspace) will be asked to instantiate it.
|
||||
*
|
||||
* If successfully found or created, the key will be linked to the destination
|
||||
* keyring if one is provided.
|
||||
*
|
||||
* Returns a pointer to the key if successful; -EACCES, -ENOKEY, -EKEYREVOKED
|
||||
* or -EKEYEXPIRED if an inaccessible, negative, revoked or expired key was
|
||||
* found; -ENOKEY if no key was found and no @callout_info was given; -EDQUOT
|
||||
* if insufficient key quota was available to create a new key; or -ENOMEM if
|
||||
* insufficient memory was available.
|
||||
*
|
||||
* If the returned key was created, then it may still be under construction,
|
||||
* and wait_for_key_construction() should be used to wait for that to complete.
|
||||
*/
|
||||
struct key *request_key_and_link(struct key_type *type,
|
||||
const char *description,
|
||||
const void *callout_info,
|
||||
size_t callout_len,
|
||||
void *aux,
|
||||
struct key *dest_keyring,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct keyring_search_context ctx = {
|
||||
.index_key.type = type,
|
||||
.index_key.description = description,
|
||||
.cred = current_cred(),
|
||||
.match_data.cmp = key_default_cmp,
|
||||
.match_data.raw_data = description,
|
||||
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.flags = (KEYRING_SEARCH_DO_STATE_CHECK |
|
||||
KEYRING_SEARCH_SKIP_EXPIRED),
|
||||
};
|
||||
struct key *key;
|
||||
key_ref_t key_ref;
|
||||
int ret;
|
||||
|
||||
kenter("%s,%s,%p,%zu,%p,%p,%lx",
|
||||
ctx.index_key.type->name, ctx.index_key.description,
|
||||
callout_info, callout_len, aux, dest_keyring, flags);
|
||||
|
||||
if (type->match_preparse) {
|
||||
ret = type->match_preparse(&ctx.match_data);
|
||||
if (ret < 0) {
|
||||
key = ERR_PTR(ret);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* search all the process keyrings for a key */
|
||||
key_ref = search_process_keyrings(&ctx);
|
||||
|
||||
if (!IS_ERR(key_ref)) {
|
||||
key = key_ref_to_ptr(key_ref);
|
||||
if (dest_keyring) {
|
||||
construct_get_dest_keyring(&dest_keyring);
|
||||
ret = key_link(dest_keyring, key);
|
||||
key_put(dest_keyring);
|
||||
if (ret < 0) {
|
||||
key_put(key);
|
||||
key = ERR_PTR(ret);
|
||||
goto error_free;
|
||||
}
|
||||
}
|
||||
} else if (PTR_ERR(key_ref) != -EAGAIN) {
|
||||
key = ERR_CAST(key_ref);
|
||||
} else {
|
||||
/* the search failed, but the keyrings were searchable, so we
|
||||
* should consult userspace if we can */
|
||||
key = ERR_PTR(-ENOKEY);
|
||||
if (!callout_info)
|
||||
goto error_free;
|
||||
|
||||
key = construct_key_and_link(&ctx, callout_info, callout_len,
|
||||
aux, dest_keyring, flags);
|
||||
}
|
||||
|
||||
error_free:
|
||||
if (type->match_free)
|
||||
type->match_free(&ctx.match_data);
|
||||
error:
|
||||
kleave(" = %p", key);
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait_for_key_construction - Wait for construction of a key to complete
|
||||
* @key: The key being waited for.
|
||||
* @intr: Whether to wait interruptibly.
|
||||
*
|
||||
* Wait for a key to finish being constructed.
|
||||
*
|
||||
* Returns 0 if successful; -ERESTARTSYS if the wait was interrupted; -ENOKEY
|
||||
* if the key was negated; or -EKEYREVOKED or -EKEYEXPIRED if the key was
|
||||
* revoked or expired.
|
||||
*/
|
||||
int wait_for_key_construction(struct key *key, bool intr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = wait_on_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT,
|
||||
intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
|
||||
if (ret)
|
||||
return -ERESTARTSYS;
|
||||
if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
|
||||
smp_rmb();
|
||||
return key->type_data.reject_error;
|
||||
}
|
||||
return key_validate(key);
|
||||
}
|
||||
EXPORT_SYMBOL(wait_for_key_construction);
|
||||
|
||||
/**
|
||||
* request_key - Request a key and wait for construction
|
||||
* @type: Type of key.
|
||||
* @description: The searchable description of the key.
|
||||
* @callout_info: The data to pass to the instantiation upcall (or NULL).
|
||||
*
|
||||
* As for request_key_and_link() except that it does not add the returned key
|
||||
* to a keyring if found, new keys are always allocated in the user's quota,
|
||||
* the callout_info must be a NUL-terminated string and no auxiliary data can
|
||||
* be passed.
|
||||
*
|
||||
* Furthermore, it then works as wait_for_key_construction() to wait for the
|
||||
* completion of keys undergoing construction with a non-interruptible wait.
|
||||
*/
|
||||
struct key *request_key(struct key_type *type,
|
||||
const char *description,
|
||||
const char *callout_info)
|
||||
{
|
||||
struct key *key;
|
||||
size_t callout_len = 0;
|
||||
int ret;
|
||||
|
||||
if (callout_info)
|
||||
callout_len = strlen(callout_info);
|
||||
key = request_key_and_link(type, description, callout_info, callout_len,
|
||||
NULL, NULL, KEY_ALLOC_IN_QUOTA);
|
||||
if (!IS_ERR(key)) {
|
||||
ret = wait_for_key_construction(key, false);
|
||||
if (ret < 0) {
|
||||
key_put(key);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
EXPORT_SYMBOL(request_key);
|
||||
|
||||
/**
|
||||
* request_key_with_auxdata - Request a key with auxiliary data for the upcaller
|
||||
* @type: The type of key we want.
|
||||
* @description: The searchable description of the key.
|
||||
* @callout_info: The data to pass to the instantiation upcall (or NULL).
|
||||
* @callout_len: The length of callout_info.
|
||||
* @aux: Auxiliary data for the upcall.
|
||||
*
|
||||
* As for request_key_and_link() except that it does not add the returned key
|
||||
* to a keyring if found and new keys are always allocated in the user's quota.
|
||||
*
|
||||
* Furthermore, it then works as wait_for_key_construction() to wait for the
|
||||
* completion of keys undergoing construction with a non-interruptible wait.
|
||||
*/
|
||||
struct key *request_key_with_auxdata(struct key_type *type,
|
||||
const char *description,
|
||||
const void *callout_info,
|
||||
size_t callout_len,
|
||||
void *aux)
|
||||
{
|
||||
struct key *key;
|
||||
int ret;
|
||||
|
||||
key = request_key_and_link(type, description, callout_info, callout_len,
|
||||
aux, NULL, KEY_ALLOC_IN_QUOTA);
|
||||
if (!IS_ERR(key)) {
|
||||
ret = wait_for_key_construction(key, false);
|
||||
if (ret < 0) {
|
||||
key_put(key);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
EXPORT_SYMBOL(request_key_with_auxdata);
|
||||
|
||||
/*
|
||||
* request_key_async - Request a key (allow async construction)
|
||||
* @type: Type of key.
|
||||
* @description: The searchable description of the key.
|
||||
* @callout_info: The data to pass to the instantiation upcall (or NULL).
|
||||
* @callout_len: The length of callout_info.
|
||||
*
|
||||
* As for request_key_and_link() except that it does not add the returned key
|
||||
* to a keyring if found, new keys are always allocated in the user's quota and
|
||||
* no auxiliary data can be passed.
|
||||
*
|
||||
* The caller should call wait_for_key_construction() to wait for the
|
||||
* completion of the returned key if it is still undergoing construction.
|
||||
*/
|
||||
struct key *request_key_async(struct key_type *type,
|
||||
const char *description,
|
||||
const void *callout_info,
|
||||
size_t callout_len)
|
||||
{
|
||||
return request_key_and_link(type, description, callout_info,
|
||||
callout_len, NULL, NULL,
|
||||
KEY_ALLOC_IN_QUOTA);
|
||||
}
|
||||
EXPORT_SYMBOL(request_key_async);
|
||||
|
||||
/*
|
||||
* request a key with auxiliary data for the upcaller (allow async construction)
|
||||
* @type: Type of key.
|
||||
* @description: The searchable description of the key.
|
||||
* @callout_info: The data to pass to the instantiation upcall (or NULL).
|
||||
* @callout_len: The length of callout_info.
|
||||
* @aux: Auxiliary data for the upcall.
|
||||
*
|
||||
* As for request_key_and_link() except that it does not add the returned key
|
||||
* to a keyring if found and new keys are always allocated in the user's quota.
|
||||
*
|
||||
* The caller should call wait_for_key_construction() to wait for the
|
||||
* completion of the returned key if it is still undergoing construction.
|
||||
*/
|
||||
struct key *request_key_async_with_auxdata(struct key_type *type,
|
||||
const char *description,
|
||||
const void *callout_info,
|
||||
size_t callout_len,
|
||||
void *aux)
|
||||
{
|
||||
return request_key_and_link(type, description, callout_info,
|
||||
callout_len, aux, NULL, KEY_ALLOC_IN_QUOTA);
|
||||
}
|
||||
EXPORT_SYMBOL(request_key_async_with_auxdata);
|
276
security/keys/request_key_auth.c
Normal file
276
security/keys/request_key_auth.c
Normal file
|
@ -0,0 +1,276 @@
|
|||
/* Request key authorisation token key definition.
|
||||
*
|
||||
* Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* See Documentation/security/keys-request-key.txt
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "internal.h"
|
||||
#include <keys/user-type.h>
|
||||
|
||||
static int request_key_auth_preparse(struct key_preparsed_payload *);
|
||||
static void request_key_auth_free_preparse(struct key_preparsed_payload *);
|
||||
static int request_key_auth_instantiate(struct key *,
|
||||
struct key_preparsed_payload *);
|
||||
static void request_key_auth_describe(const struct key *, struct seq_file *);
|
||||
static void request_key_auth_revoke(struct key *);
|
||||
static void request_key_auth_destroy(struct key *);
|
||||
static long request_key_auth_read(const struct key *, char __user *, size_t);
|
||||
|
||||
/*
|
||||
* The request-key authorisation key type definition.
|
||||
*/
|
||||
struct key_type key_type_request_key_auth = {
|
||||
.name = ".request_key_auth",
|
||||
.def_datalen = sizeof(struct request_key_auth),
|
||||
.preparse = request_key_auth_preparse,
|
||||
.free_preparse = request_key_auth_free_preparse,
|
||||
.instantiate = request_key_auth_instantiate,
|
||||
.describe = request_key_auth_describe,
|
||||
.revoke = request_key_auth_revoke,
|
||||
.destroy = request_key_auth_destroy,
|
||||
.read = request_key_auth_read,
|
||||
};
|
||||
|
||||
static int request_key_auth_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void request_key_auth_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Instantiate a request-key authorisation key.
|
||||
*/
|
||||
static int request_key_auth_instantiate(struct key *key,
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
key->payload.data = (struct request_key_auth *)prep->data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Describe an authorisation token.
|
||||
*/
|
||||
static void request_key_auth_describe(const struct key *key,
|
||||
struct seq_file *m)
|
||||
{
|
||||
struct request_key_auth *rka = key->payload.data;
|
||||
|
||||
seq_puts(m, "key:");
|
||||
seq_puts(m, key->description);
|
||||
if (key_is_instantiated(key))
|
||||
seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the callout_info data (retrieves the callout information).
|
||||
* - the key's semaphore is read-locked
|
||||
*/
|
||||
static long request_key_auth_read(const struct key *key,
|
||||
char __user *buffer, size_t buflen)
|
||||
{
|
||||
struct request_key_auth *rka = key->payload.data;
|
||||
size_t datalen;
|
||||
long ret;
|
||||
|
||||
datalen = rka->callout_len;
|
||||
ret = datalen;
|
||||
|
||||
/* we can return the data as is */
|
||||
if (buffer && buflen > 0) {
|
||||
if (buflen > datalen)
|
||||
buflen = datalen;
|
||||
|
||||
if (copy_to_user(buffer, rka->callout_info, buflen) != 0)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle revocation of an authorisation token key.
|
||||
*
|
||||
* Called with the key sem write-locked.
|
||||
*/
|
||||
static void request_key_auth_revoke(struct key *key)
|
||||
{
|
||||
struct request_key_auth *rka = key->payload.data;
|
||||
|
||||
kenter("{%d}", key->serial);
|
||||
|
||||
if (rka->cred) {
|
||||
put_cred(rka->cred);
|
||||
rka->cred = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy an instantiation authorisation token key.
|
||||
*/
|
||||
static void request_key_auth_destroy(struct key *key)
|
||||
{
|
||||
struct request_key_auth *rka = key->payload.data;
|
||||
|
||||
kenter("{%d}", key->serial);
|
||||
|
||||
if (rka->cred) {
|
||||
put_cred(rka->cred);
|
||||
rka->cred = NULL;
|
||||
}
|
||||
|
||||
key_put(rka->target_key);
|
||||
key_put(rka->dest_keyring);
|
||||
kfree(rka->callout_info);
|
||||
kfree(rka);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an authorisation token for /sbin/request-key or whoever to gain
|
||||
* access to the caller's security data.
|
||||
*/
|
||||
struct key *request_key_auth_new(struct key *target, const void *callout_info,
|
||||
size_t callout_len, struct key *dest_keyring)
|
||||
{
|
||||
struct request_key_auth *rka, *irka;
|
||||
const struct cred *cred = current->cred;
|
||||
struct key *authkey = NULL;
|
||||
char desc[20];
|
||||
int ret;
|
||||
|
||||
kenter("%d,", target->serial);
|
||||
|
||||
/* allocate a auth record */
|
||||
rka = kmalloc(sizeof(*rka), GFP_KERNEL);
|
||||
if (!rka) {
|
||||
kleave(" = -ENOMEM");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
rka->callout_info = kmalloc(callout_len, GFP_KERNEL);
|
||||
if (!rka->callout_info) {
|
||||
kleave(" = -ENOMEM");
|
||||
kfree(rka);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/* see if the calling process is already servicing the key request of
|
||||
* another process */
|
||||
if (cred->request_key_auth) {
|
||||
/* it is - use that instantiation context here too */
|
||||
down_read(&cred->request_key_auth->sem);
|
||||
|
||||
/* if the auth key has been revoked, then the key we're
|
||||
* servicing is already instantiated */
|
||||
if (test_bit(KEY_FLAG_REVOKED, &cred->request_key_auth->flags))
|
||||
goto auth_key_revoked;
|
||||
|
||||
irka = cred->request_key_auth->payload.data;
|
||||
rka->cred = get_cred(irka->cred);
|
||||
rka->pid = irka->pid;
|
||||
|
||||
up_read(&cred->request_key_auth->sem);
|
||||
}
|
||||
else {
|
||||
/* it isn't - use this process as the context */
|
||||
rka->cred = get_cred(cred);
|
||||
rka->pid = current->pid;
|
||||
}
|
||||
|
||||
rka->target_key = key_get(target);
|
||||
rka->dest_keyring = key_get(dest_keyring);
|
||||
memcpy(rka->callout_info, callout_info, callout_len);
|
||||
rka->callout_len = callout_len;
|
||||
|
||||
/* allocate the auth key */
|
||||
sprintf(desc, "%x", target->serial);
|
||||
|
||||
authkey = key_alloc(&key_type_request_key_auth, desc,
|
||||
cred->fsuid, cred->fsgid, cred,
|
||||
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
|
||||
KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
|
||||
if (IS_ERR(authkey)) {
|
||||
ret = PTR_ERR(authkey);
|
||||
goto error_alloc;
|
||||
}
|
||||
|
||||
/* construct the auth key */
|
||||
ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
|
||||
if (ret < 0)
|
||||
goto error_inst;
|
||||
|
||||
kleave(" = {%d,%d}", authkey->serial, atomic_read(&authkey->usage));
|
||||
return authkey;
|
||||
|
||||
auth_key_revoked:
|
||||
up_read(&cred->request_key_auth->sem);
|
||||
kfree(rka->callout_info);
|
||||
kfree(rka);
|
||||
kleave("= -EKEYREVOKED");
|
||||
return ERR_PTR(-EKEYREVOKED);
|
||||
|
||||
error_inst:
|
||||
key_revoke(authkey);
|
||||
key_put(authkey);
|
||||
error_alloc:
|
||||
key_put(rka->target_key);
|
||||
key_put(rka->dest_keyring);
|
||||
kfree(rka->callout_info);
|
||||
kfree(rka);
|
||||
kleave("= %d", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Search the current process's keyrings for the authorisation key for
|
||||
* instantiation of a key.
|
||||
*/
|
||||
struct key *key_get_instantiation_authkey(key_serial_t target_id)
|
||||
{
|
||||
char description[16];
|
||||
struct keyring_search_context ctx = {
|
||||
.index_key.type = &key_type_request_key_auth,
|
||||
.index_key.description = description,
|
||||
.cred = current_cred(),
|
||||
.match_data.cmp = key_default_cmp,
|
||||
.match_data.raw_data = description,
|
||||
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.flags = KEYRING_SEARCH_DO_STATE_CHECK,
|
||||
};
|
||||
struct key *authkey;
|
||||
key_ref_t authkey_ref;
|
||||
|
||||
sprintf(description, "%x", target_id);
|
||||
|
||||
authkey_ref = search_process_keyrings(&ctx);
|
||||
|
||||
if (IS_ERR(authkey_ref)) {
|
||||
authkey = ERR_CAST(authkey_ref);
|
||||
if (authkey == ERR_PTR(-EAGAIN))
|
||||
authkey = ERR_PTR(-ENOKEY);
|
||||
goto error;
|
||||
}
|
||||
|
||||
authkey = key_ref_to_ptr(authkey_ref);
|
||||
if (test_bit(KEY_FLAG_REVOKED, &authkey->flags)) {
|
||||
key_put(authkey);
|
||||
authkey = ERR_PTR(-EKEYREVOKED);
|
||||
}
|
||||
|
||||
error:
|
||||
return authkey;
|
||||
}
|
76
security/keys/sysctl.c
Normal file
76
security/keys/sysctl.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* Key management controls
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/key.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include "internal.h"
|
||||
|
||||
static const int zero, one = 1, max = INT_MAX;
|
||||
|
||||
struct ctl_table key_sysctls[] = {
|
||||
{
|
||||
.procname = "maxkeys",
|
||||
.data = &key_quota_maxkeys,
|
||||
.maxlen = sizeof(unsigned),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = (void *) &one,
|
||||
.extra2 = (void *) &max,
|
||||
},
|
||||
{
|
||||
.procname = "maxbytes",
|
||||
.data = &key_quota_maxbytes,
|
||||
.maxlen = sizeof(unsigned),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = (void *) &one,
|
||||
.extra2 = (void *) &max,
|
||||
},
|
||||
{
|
||||
.procname = "root_maxkeys",
|
||||
.data = &key_quota_root_maxkeys,
|
||||
.maxlen = sizeof(unsigned),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = (void *) &one,
|
||||
.extra2 = (void *) &max,
|
||||
},
|
||||
{
|
||||
.procname = "root_maxbytes",
|
||||
.data = &key_quota_root_maxbytes,
|
||||
.maxlen = sizeof(unsigned),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = (void *) &one,
|
||||
.extra2 = (void *) &max,
|
||||
},
|
||||
{
|
||||
.procname = "gc_delay",
|
||||
.data = &key_gc_delay,
|
||||
.maxlen = sizeof(unsigned),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = (void *) &zero,
|
||||
.extra2 = (void *) &max,
|
||||
},
|
||||
#ifdef CONFIG_PERSISTENT_KEYRINGS
|
||||
{
|
||||
.procname = "persistent_keyring_expiry",
|
||||
.data = &persistent_keyring_expiry,
|
||||
.maxlen = sizeof(unsigned),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = (void *) &zero,
|
||||
.extra2 = (void *) &max,
|
||||
},
|
||||
#endif
|
||||
{ }
|
||||
};
|
1162
security/keys/trusted.c
Normal file
1162
security/keys/trusted.c
Normal file
File diff suppressed because it is too large
Load diff
134
security/keys/trusted.h
Normal file
134
security/keys/trusted.h
Normal file
|
@ -0,0 +1,134 @@
|
|||
#ifndef __TRUSTED_KEY_H
|
||||
#define __TRUSTED_KEY_H
|
||||
|
||||
/* implementation specific TPM constants */
|
||||
#define MAX_PCRINFO_SIZE 64
|
||||
#define MAX_BUF_SIZE 512
|
||||
#define TPM_GETRANDOM_SIZE 14
|
||||
#define TPM_OSAP_SIZE 36
|
||||
#define TPM_OIAP_SIZE 10
|
||||
#define TPM_SEAL_SIZE 87
|
||||
#define TPM_UNSEAL_SIZE 104
|
||||
#define TPM_SIZE_OFFSET 2
|
||||
#define TPM_RETURN_OFFSET 6
|
||||
#define TPM_DATA_OFFSET 10
|
||||
|
||||
#define LOAD32(buffer, offset) (ntohl(*(uint32_t *)&buffer[offset]))
|
||||
#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset])
|
||||
#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset]))
|
||||
|
||||
struct tpm_buf {
|
||||
int len;
|
||||
unsigned char data[MAX_BUF_SIZE];
|
||||
};
|
||||
|
||||
#define INIT_BUF(tb) (tb->len = 0)
|
||||
|
||||
struct osapsess {
|
||||
uint32_t handle;
|
||||
unsigned char secret[SHA1_DIGEST_SIZE];
|
||||
unsigned char enonce[TPM_NONCE_SIZE];
|
||||
};
|
||||
|
||||
/* discrete values, but have to store in uint16_t for TPM use */
|
||||
enum {
|
||||
SEAL_keytype = 1,
|
||||
SRK_keytype = 4
|
||||
};
|
||||
|
||||
struct trusted_key_options {
|
||||
uint16_t keytype;
|
||||
uint32_t keyhandle;
|
||||
unsigned char keyauth[SHA1_DIGEST_SIZE];
|
||||
unsigned char blobauth[SHA1_DIGEST_SIZE];
|
||||
uint32_t pcrinfo_len;
|
||||
unsigned char pcrinfo[MAX_PCRINFO_SIZE];
|
||||
int pcrlock;
|
||||
};
|
||||
|
||||
#define TPM_DEBUG 0
|
||||
|
||||
#if TPM_DEBUG
|
||||
static inline void dump_options(struct trusted_key_options *o)
|
||||
{
|
||||
pr_info("trusted_key: sealing key type %d\n", o->keytype);
|
||||
pr_info("trusted_key: sealing key handle %0X\n", o->keyhandle);
|
||||
pr_info("trusted_key: pcrlock %d\n", o->pcrlock);
|
||||
pr_info("trusted_key: pcrinfo %d\n", o->pcrinfo_len);
|
||||
print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE,
|
||||
16, 1, o->pcrinfo, o->pcrinfo_len, 0);
|
||||
}
|
||||
|
||||
static inline void dump_payload(struct trusted_key_payload *p)
|
||||
{
|
||||
pr_info("trusted_key: key_len %d\n", p->key_len);
|
||||
print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
|
||||
16, 1, p->key, p->key_len, 0);
|
||||
pr_info("trusted_key: bloblen %d\n", p->blob_len);
|
||||
print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
|
||||
16, 1, p->blob, p->blob_len, 0);
|
||||
pr_info("trusted_key: migratable %d\n", p->migratable);
|
||||
}
|
||||
|
||||
static inline void dump_sess(struct osapsess *s)
|
||||
{
|
||||
print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE,
|
||||
16, 1, &s->handle, 4, 0);
|
||||
pr_info("trusted-key: secret:\n");
|
||||
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
|
||||
16, 1, &s->secret, SHA1_DIGEST_SIZE, 0);
|
||||
pr_info("trusted-key: enonce:\n");
|
||||
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
|
||||
16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0);
|
||||
}
|
||||
|
||||
static inline void dump_tpm_buf(unsigned char *buf)
|
||||
{
|
||||
int len;
|
||||
|
||||
pr_info("\ntrusted-key: tpm buffer\n");
|
||||
len = LOAD32(buf, TPM_SIZE_OFFSET);
|
||||
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
|
||||
}
|
||||
#else
|
||||
static inline void dump_options(struct trusted_key_options *o)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dump_payload(struct trusted_key_payload *p)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dump_sess(struct osapsess *s)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dump_tpm_buf(unsigned char *buf)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void store8(struct tpm_buf *buf, const unsigned char value)
|
||||
{
|
||||
buf->data[buf->len++] = value;
|
||||
}
|
||||
|
||||
static inline void store16(struct tpm_buf *buf, const uint16_t value)
|
||||
{
|
||||
*(uint16_t *) & buf->data[buf->len] = htons(value);
|
||||
buf->len += sizeof value;
|
||||
}
|
||||
|
||||
static inline void store32(struct tpm_buf *buf, const uint32_t value)
|
||||
{
|
||||
*(uint32_t *) & buf->data[buf->len] = htonl(value);
|
||||
buf->len += sizeof value;
|
||||
}
|
||||
|
||||
static inline void storebytes(struct tpm_buf *buf, const unsigned char *in,
|
||||
const int len)
|
||||
{
|
||||
memcpy(buf->data + buf->len, in, len);
|
||||
buf->len += len;
|
||||
}
|
||||
#endif
|
227
security/keys/user_defined.c
Normal file
227
security/keys/user_defined.c
Normal file
|
@ -0,0 +1,227 @@
|
|||
/* user_defined.c: user defined key type
|
||||
*
|
||||
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/err.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "internal.h"
|
||||
|
||||
static int logon_vet_description(const char *desc);
|
||||
|
||||
/*
|
||||
* user defined keys take an arbitrary string as the description and an
|
||||
* arbitrary blob of data as the payload
|
||||
*/
|
||||
struct key_type key_type_user = {
|
||||
.name = "user",
|
||||
.preparse = user_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.update = user_update,
|
||||
.revoke = user_revoke,
|
||||
.destroy = user_destroy,
|
||||
.describe = user_describe,
|
||||
.read = user_read,
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL_GPL(key_type_user);
|
||||
|
||||
/*
|
||||
* This key type is essentially the same as key_type_user, but it does
|
||||
* not define a .read op. This is suitable for storing username and
|
||||
* password pairs in the keyring that you do not want to be readable
|
||||
* from userspace.
|
||||
*/
|
||||
struct key_type key_type_logon = {
|
||||
.name = "logon",
|
||||
.preparse = user_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.update = user_update,
|
||||
.revoke = user_revoke,
|
||||
.destroy = user_destroy,
|
||||
.describe = user_describe,
|
||||
.vet_description = logon_vet_description,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(key_type_logon);
|
||||
|
||||
/*
|
||||
* Preparse a user defined key payload
|
||||
*/
|
||||
int user_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct user_key_payload *upayload;
|
||||
size_t datalen = prep->datalen;
|
||||
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
return -EINVAL;
|
||||
|
||||
upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
|
||||
if (!upayload)
|
||||
return -ENOMEM;
|
||||
|
||||
/* attach the data */
|
||||
prep->quotalen = datalen;
|
||||
prep->payload[0] = upayload;
|
||||
upayload->datalen = datalen;
|
||||
memcpy(upayload->data, prep->data, datalen);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(user_preparse);
|
||||
|
||||
/*
|
||||
* Free a preparse of a user defined key payload
|
||||
*/
|
||||
void user_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
kfree(prep->payload[0]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(user_free_preparse);
|
||||
|
||||
/*
|
||||
* update a user defined key
|
||||
* - the key's semaphore is write-locked
|
||||
*/
|
||||
int user_update(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct user_key_payload *upayload, *zap;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
goto error;
|
||||
|
||||
/* construct a replacement payload */
|
||||
ret = -ENOMEM;
|
||||
upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
|
||||
if (!upayload)
|
||||
goto error;
|
||||
|
||||
upayload->datalen = datalen;
|
||||
memcpy(upayload->data, prep->data, datalen);
|
||||
|
||||
/* check the quota and attach the new data */
|
||||
zap = upayload;
|
||||
|
||||
ret = key_payload_reserve(key, datalen);
|
||||
|
||||
if (ret == 0) {
|
||||
/* attach the new data, displacing the old */
|
||||
zap = key->payload.data;
|
||||
rcu_assign_keypointer(key, upayload);
|
||||
key->expiry = 0;
|
||||
}
|
||||
|
||||
if (zap)
|
||||
kfree_rcu(zap, rcu);
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(user_update);
|
||||
|
||||
/*
|
||||
* dispose of the links from a revoked keyring
|
||||
* - called with the key sem write-locked
|
||||
*/
|
||||
void user_revoke(struct key *key)
|
||||
{
|
||||
struct user_key_payload *upayload = key->payload.data;
|
||||
|
||||
/* clear the quota */
|
||||
key_payload_reserve(key, 0);
|
||||
|
||||
if (upayload) {
|
||||
rcu_assign_keypointer(key, NULL);
|
||||
kfree_rcu(upayload, rcu);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(user_revoke);
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a user key
|
||||
*/
|
||||
void user_destroy(struct key *key)
|
||||
{
|
||||
struct user_key_payload *upayload = key->payload.data;
|
||||
|
||||
#ifdef CONFIG_CRYPTO_FIPS
|
||||
if(upayload)
|
||||
{
|
||||
memset(upayload->data, 0, upayload->datalen);
|
||||
}
|
||||
#endif
|
||||
kfree(upayload);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(user_destroy);
|
||||
|
||||
/*
|
||||
* describe the user key
|
||||
*/
|
||||
void user_describe(const struct key *key, struct seq_file *m)
|
||||
{
|
||||
seq_puts(m, key->description);
|
||||
if (key_is_instantiated(key))
|
||||
seq_printf(m, ": %u", key->datalen);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(user_describe);
|
||||
|
||||
/*
|
||||
* read the key data
|
||||
* - the key's semaphore is read-locked
|
||||
*/
|
||||
long user_read(const struct key *key, char __user *buffer, size_t buflen)
|
||||
{
|
||||
struct user_key_payload *upayload;
|
||||
long ret;
|
||||
|
||||
upayload = rcu_dereference_key(key);
|
||||
ret = upayload->datalen;
|
||||
|
||||
/* we can return the data as is */
|
||||
if (buffer && buflen > 0) {
|
||||
if (buflen > upayload->datalen)
|
||||
buflen = upayload->datalen;
|
||||
|
||||
if (copy_to_user(buffer, upayload->data, buflen) != 0)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(user_read);
|
||||
|
||||
/* Vet the description for a "logon" key */
|
||||
static int logon_vet_description(const char *desc)
|
||||
{
|
||||
char *p;
|
||||
|
||||
/* require a "qualified" description string */
|
||||
p = strchr(desc, ':');
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
|
||||
/* also reject description with ':' as first char */
|
||||
if (p == desc)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue