mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00:00
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "Highlights: IMA: - provide ">" and "<" operators for fowner/uid/euid rules KEYS: - add a system blacklist keyring - add KEYCTL_RESTRICT_KEYRING, exposes keyring link restriction functionality to userland via keyctl() LSM: - harden LSM API with __ro_after_init - add prlmit security hook, implement for SELinux - revive security_task_alloc hook TPM: - implement contextual TPM command 'spaces'" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (98 commits) tpm: Fix reference count to main device tpm_tis: convert to using locality callbacks tpm: fix handling of the TPM 2.0 event logs tpm_crb: remove a cruft constant keys: select CONFIG_CRYPTO when selecting DH / KDF apparmor: Make path_max parameter readonly apparmor: fix parameters so that the permission test is bypassed at boot apparmor: fix invalid reference to index variable of iterator line 836 apparmor: use SHASH_DESC_ON_STACK security/apparmor/lsm.c: set debug messages apparmor: fix boolreturn.cocci warnings Smack: Use GFP_KERNEL for smk_netlbl_mls(). smack: fix double free in smack_parse_opts_str() KEYS: add SP800-56A KDF support for DH KEYS: Keyring asymmetric key restrict method with chaining KEYS: Restrict asymmetric key linkage using a specific keychain KEYS: Add a lookup_restriction function for the asymmetric key type KEYS: Add KEYCTL_RESTRICT_KEYRING KEYS: Consistent ordering for __key_link_begin and restrict check KEYS: Add an optional lookup_restriction hook to key_type ...
This commit is contained in:
commit
0302e28dee
@ -311,3 +311,54 @@ Functions are provided to register and unregister parsers:
|
||||
|
||||
Parsers may not have the same name. The names are otherwise only used for
|
||||
displaying in debugging messages.
|
||||
|
||||
|
||||
=========================
|
||||
KEYRING LINK RESTRICTIONS
|
||||
=========================
|
||||
|
||||
Keyrings created from userspace using add_key can be configured to check the
|
||||
signature of the key being linked.
|
||||
|
||||
Several restriction methods are available:
|
||||
|
||||
(1) Restrict using the kernel builtin trusted keyring
|
||||
|
||||
- Option string used with KEYCTL_RESTRICT_KEYRING:
|
||||
- "builtin_trusted"
|
||||
|
||||
The kernel builtin trusted keyring will be searched for the signing
|
||||
key. The ca_keys kernel parameter also affects which keys are used for
|
||||
signature verification.
|
||||
|
||||
(2) Restrict using the kernel builtin and secondary trusted keyrings
|
||||
|
||||
- Option string used with KEYCTL_RESTRICT_KEYRING:
|
||||
- "builtin_and_secondary_trusted"
|
||||
|
||||
The kernel builtin and secondary trusted keyrings will be searched for the
|
||||
signing key. The ca_keys kernel parameter also affects which keys are used
|
||||
for signature verification.
|
||||
|
||||
(3) Restrict using a separate key or keyring
|
||||
|
||||
- Option string used with KEYCTL_RESTRICT_KEYRING:
|
||||
- "key_or_keyring:<key or keyring serial number>[:chain]"
|
||||
|
||||
Whenever a key link is requested, the link will only succeed if the key
|
||||
being linked is signed by one of the designated keys. This key may be
|
||||
specified directly by providing a serial number for one asymmetric key, or
|
||||
a group of keys may be searched for the signing key by providing the
|
||||
serial number for a keyring.
|
||||
|
||||
When the "chain" option is provided at the end of the string, the keys
|
||||
within the destination keyring will also be searched for signing keys.
|
||||
This allows for verification of certificate chains by adding each
|
||||
cert in order (starting closest to the root) to one keyring.
|
||||
|
||||
In all of these cases, if the signing key is found the signature of the key to
|
||||
be linked will be verified using the signing key. The requested key is added
|
||||
to the keyring only if the signature is successfully verified. -ENOKEY is
|
||||
returned if the parent certificate could not be found, or -EKEYREJECTED is
|
||||
returned if the signature check fails or the key is blacklisted. Other errors
|
||||
may be returned if the signature check could not be performed.
|
||||
|
@ -827,7 +827,7 @@ The keyctl syscall functions are:
|
||||
|
||||
long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params,
|
||||
char *buffer, size_t buflen,
|
||||
void *reserved);
|
||||
struct keyctl_kdf_params *kdf);
|
||||
|
||||
The params struct contains serial numbers for three keys:
|
||||
|
||||
@ -844,18 +844,61 @@ The keyctl syscall functions are:
|
||||
public key. If the base is the remote public key, the result is
|
||||
the shared secret.
|
||||
|
||||
The reserved argument must be set to NULL.
|
||||
If the parameter kdf is NULL, the following applies:
|
||||
|
||||
The buffer length must be at least the length of the prime, or zero.
|
||||
- The buffer length must be at least the length of the prime, or zero.
|
||||
|
||||
If the buffer length is nonzero, the length of the result is
|
||||
returned when it is successfully calculated and copied in to the
|
||||
buffer. When the buffer length is zero, the minimum required
|
||||
buffer length is returned.
|
||||
- If the buffer length is nonzero, the length of the result is
|
||||
returned when it is successfully calculated and copied in to the
|
||||
buffer. When the buffer length is zero, the minimum required
|
||||
buffer length is returned.
|
||||
|
||||
The kdf parameter allows the caller to apply a key derivation function
|
||||
(KDF) on the Diffie-Hellman computation where only the result
|
||||
of the KDF is returned to the caller. The KDF is characterized with
|
||||
struct keyctl_kdf_params as follows:
|
||||
|
||||
- char *hashname specifies the NUL terminated string identifying
|
||||
the hash used from the kernel crypto API and applied for the KDF
|
||||
operation. The KDF implemenation complies with SP800-56A as well
|
||||
as with SP800-108 (the counter KDF).
|
||||
|
||||
- char *otherinfo specifies the OtherInfo data as documented in
|
||||
SP800-56A section 5.8.1.2. The length of the buffer is given with
|
||||
otherinfolen. The format of OtherInfo is defined by the caller.
|
||||
The otherinfo pointer may be NULL if no OtherInfo shall be used.
|
||||
|
||||
This function will return error EOPNOTSUPP if the key type is not
|
||||
supported, error ENOKEY if the key could not be found, or error
|
||||
EACCES if the key is not readable by the caller.
|
||||
EACCES if the key is not readable by the caller. In addition, the
|
||||
function will return EMSGSIZE when the parameter kdf is non-NULL
|
||||
and either the buffer length or the OtherInfo length exceeds the
|
||||
allowed length.
|
||||
|
||||
(*) Restrict keyring linkage
|
||||
|
||||
long keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring,
|
||||
const char *type, const char *restriction);
|
||||
|
||||
An existing keyring can restrict linkage of additional keys by evaluating
|
||||
the contents of the key according to a restriction scheme.
|
||||
|
||||
"keyring" is the key ID for an existing keyring to apply a restriction
|
||||
to. It may be empty or may already have keys linked. Existing linked keys
|
||||
will remain in the keyring even if the new restriction would reject them.
|
||||
|
||||
"type" is a registered key type.
|
||||
|
||||
"restriction" is a string describing how key linkage is to be restricted.
|
||||
The format varies depending on the key type, and the string is passed to
|
||||
the lookup_restriction() function for the requested type. It may specify
|
||||
a method and relevant data for the restriction such as signature
|
||||
verification or constraints on key payload. If the requested key type is
|
||||
later unregistered, no keys may be added to the keyring after the key type
|
||||
is removed.
|
||||
|
||||
To apply a keyring restriction the process must have Set Attribute
|
||||
permission and the keyring must not be previously restricted.
|
||||
|
||||
===============
|
||||
KERNEL SERVICES
|
||||
@ -1032,10 +1075,7 @@ payload contents" for more information.
|
||||
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
|
||||
const struct cred *cred,
|
||||
key_perm_t perm,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
unsigned long,
|
||||
const union key_payload *),
|
||||
struct key_restriction *restrict_link,
|
||||
unsigned long flags,
|
||||
struct key *dest);
|
||||
|
||||
@ -1047,20 +1087,23 @@ payload contents" for more information.
|
||||
KEY_ALLOC_NOT_IN_QUOTA in flags if the keyring shouldn't be accounted
|
||||
towards the user's quota). Error ENOMEM can also be returned.
|
||||
|
||||
If restrict_link not NULL, it should point to a function that will be
|
||||
called each time an attempt is made to link a key into the new keyring.
|
||||
This function is called to check whether a key may be added into the keying
|
||||
or not. Callers of key_create_or_update() within the kernel can pass
|
||||
KEY_ALLOC_BYPASS_RESTRICTION to suppress the check. An example of using
|
||||
this is to manage rings of cryptographic keys that are set up when the
|
||||
kernel boots where userspace is also permitted to add keys - provided they
|
||||
can be verified by a key the kernel already has.
|
||||
If restrict_link is not NULL, it should point to a structure that contains
|
||||
the function that will be called each time an attempt is made to link a
|
||||
key into the new keyring. The structure may also contain a key pointer
|
||||
and an associated key type. The function is called to check whether a key
|
||||
may be added into the keyring or not. The key type is used by the garbage
|
||||
collector to clean up function or data pointers in this structure if the
|
||||
given key type is unregistered. Callers of key_create_or_update() within
|
||||
the kernel can pass KEY_ALLOC_BYPASS_RESTRICTION to suppress the check.
|
||||
An example of using this is to manage rings of cryptographic keys that are
|
||||
set up when the kernel boots where userspace is also permitted to add keys
|
||||
- provided they can be verified by a key the kernel already has.
|
||||
|
||||
When called, the restriction function will be passed the keyring being
|
||||
added to, the key flags value and the type and payload of the key being
|
||||
added. Note that when a new key is being created, this is called between
|
||||
payload preparsing and actual key creation. The function should return 0
|
||||
to allow the link or an error to reject it.
|
||||
added to, the key type, the payload of the key being added, and data to be
|
||||
used in the restriction check. Note that when a new key is being created,
|
||||
this is called between payload preparsing and actual key creation. The
|
||||
function should return 0 to allow the link or an error to reject it.
|
||||
|
||||
A convenience function, restrict_link_reject, exists to always return
|
||||
-EPERM to in this case.
|
||||
@ -1445,6 +1488,15 @@ The structure has a number of fields, some of which are mandatory:
|
||||
The authorisation key.
|
||||
|
||||
|
||||
(*) struct key_restriction *(*lookup_restriction)(const char *params);
|
||||
|
||||
This optional method is used to enable userspace configuration of keyring
|
||||
restrictions. The restriction parameter string (not including the key type
|
||||
name) is passed in, and this method returns a pointer to a key_restriction
|
||||
structure containing the relevant functions and data to evaluate each
|
||||
attempted key link operation. If there is no match, -EINVAL is returned.
|
||||
|
||||
|
||||
============================
|
||||
REQUEST-KEY CALLBACK SERVICE
|
||||
============================
|
||||
|
@ -64,4 +64,22 @@ config SECONDARY_TRUSTED_KEYRING
|
||||
those keys are not blacklisted and are vouched for by a key built
|
||||
into the kernel or already in the secondary trusted keyring.
|
||||
|
||||
config SYSTEM_BLACKLIST_KEYRING
|
||||
bool "Provide system-wide ring of blacklisted keys"
|
||||
depends on KEYS
|
||||
help
|
||||
Provide a system keyring to which blacklisted keys can be added.
|
||||
Keys in the keyring are considered entirely untrusted. Keys in this
|
||||
keyring are used by the module signature checking to reject loading
|
||||
of modules signed with a blacklisted key.
|
||||
|
||||
config SYSTEM_BLACKLIST_HASH_LIST
|
||||
string "Hashes to be preloaded into the system blacklist keyring"
|
||||
depends on SYSTEM_BLACKLIST_KEYRING
|
||||
help
|
||||
If set, this option should be the filename of a list of hashes in the
|
||||
form "<hash>", "<hash>", ... . This will be included into a C
|
||||
wrapper to incorporate the list into the kernel. Each <hash> should
|
||||
be a string of hex digits.
|
||||
|
||||
endmenu
|
||||
|
@ -3,6 +3,12 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o
|
||||
ifneq ($(CONFIG_SYSTEM_BLACKLIST_HASH_LIST),"")
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o
|
||||
else
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_nohashes.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
|
||||
|
||||
|
174
certs/blacklist.c
Normal file
174
certs/blacklist.c
Normal file
@ -0,0 +1,174 @@
|
||||
/* System hash blacklist.
|
||||
*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "blacklist: "fmt
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "blacklist.h"
|
||||
|
||||
static struct key *blacklist_keyring;
|
||||
|
||||
/*
|
||||
* The description must be a type prefix, a colon and then an even number of
|
||||
* hex digits. The hash is kept in the description.
|
||||
*/
|
||||
static int blacklist_vet_description(const char *desc)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
if (*desc == ':')
|
||||
return -EINVAL;
|
||||
for (; *desc; desc++)
|
||||
if (*desc == ':')
|
||||
goto found_colon;
|
||||
return -EINVAL;
|
||||
|
||||
found_colon:
|
||||
desc++;
|
||||
for (; *desc; desc++) {
|
||||
if (!isxdigit(*desc))
|
||||
return -EINVAL;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n == 0 || n & 1)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The hash to be blacklisted is expected to be in the description. There will
|
||||
* be no payload.
|
||||
*/
|
||||
static int blacklist_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
if (prep->datalen > 0)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void blacklist_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
}
|
||||
|
||||
static void blacklist_describe(const struct key *key, struct seq_file *m)
|
||||
{
|
||||
seq_puts(m, key->description);
|
||||
}
|
||||
|
||||
static struct key_type key_type_blacklist = {
|
||||
.name = "blacklist",
|
||||
.vet_description = blacklist_vet_description,
|
||||
.preparse = blacklist_preparse,
|
||||
.free_preparse = blacklist_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.describe = blacklist_describe,
|
||||
};
|
||||
|
||||
/**
|
||||
* mark_hash_blacklisted - Add a hash to the system blacklist
|
||||
* @hash - The hash as a hex string with a type prefix (eg. "tbs:23aa429783")
|
||||
*/
|
||||
int mark_hash_blacklisted(const char *hash)
|
||||
{
|
||||
key_ref_t key;
|
||||
|
||||
key = key_create_or_update(make_key_ref(blacklist_keyring, true),
|
||||
"blacklist",
|
||||
hash,
|
||||
NULL,
|
||||
0,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW),
|
||||
KEY_ALLOC_NOT_IN_QUOTA |
|
||||
KEY_ALLOC_BUILT_IN);
|
||||
if (IS_ERR(key)) {
|
||||
pr_err("Problem blacklisting hash (%ld)\n", PTR_ERR(key));
|
||||
return PTR_ERR(key);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_hash_blacklisted - Determine if a hash is blacklisted
|
||||
* @hash: The hash to be checked as a binary blob
|
||||
* @hash_len: The length of the binary hash
|
||||
* @type: Type of hash
|
||||
*/
|
||||
int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type)
|
||||
{
|
||||
key_ref_t kref;
|
||||
size_t type_len = strlen(type);
|
||||
char *buffer, *p;
|
||||
int ret = 0;
|
||||
|
||||
buffer = kmalloc(type_len + 1 + hash_len * 2 + 1, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
p = memcpy(buffer, type, type_len);
|
||||
p += type_len;
|
||||
*p++ = ':';
|
||||
bin2hex(p, hash, hash_len);
|
||||
p += hash_len * 2;
|
||||
*p = 0;
|
||||
|
||||
kref = keyring_search(make_key_ref(blacklist_keyring, true),
|
||||
&key_type_blacklist, buffer);
|
||||
if (!IS_ERR(kref)) {
|
||||
key_ref_put(kref);
|
||||
ret = -EKEYREJECTED;
|
||||
}
|
||||
|
||||
kfree(buffer);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(is_hash_blacklisted);
|
||||
|
||||
/*
|
||||
* Intialise the blacklist
|
||||
*/
|
||||
static int __init blacklist_init(void)
|
||||
{
|
||||
const char *const *bl;
|
||||
|
||||
if (register_key_type(&key_type_blacklist) < 0)
|
||||
panic("Can't allocate system blacklist key type\n");
|
||||
|
||||
blacklist_keyring =
|
||||
keyring_alloc(".blacklist",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0),
|
||||
current_cred(),
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_SEARCH,
|
||||
KEY_ALLOC_NOT_IN_QUOTA |
|
||||
KEY_FLAG_KEEP,
|
||||
NULL, NULL);
|
||||
if (IS_ERR(blacklist_keyring))
|
||||
panic("Can't allocate system blacklist keyring\n");
|
||||
|
||||
for (bl = blacklist_hashes; *bl; bl++)
|
||||
if (mark_hash_blacklisted(*bl) < 0)
|
||||
pr_err("- blacklisting failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be initialised before we try and load the keys into the keyring.
|
||||
*/
|
||||
device_initcall(blacklist_init);
|
3
certs/blacklist.h
Normal file
3
certs/blacklist.h
Normal file
@ -0,0 +1,3 @@
|
||||
#include <linux/kernel.h>
|
||||
|
||||
extern const char __initdata *const blacklist_hashes[];
|
6
certs/blacklist_hashes.c
Normal file
6
certs/blacklist_hashes.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include "blacklist.h"
|
||||
|
||||
const char __initdata *const blacklist_hashes[] = {
|
||||
#include CONFIG_SYSTEM_BLACKLIST_HASH_LIST
|
||||
, NULL
|
||||
};
|
5
certs/blacklist_nohashes.c
Normal file
5
certs/blacklist_nohashes.c
Normal file
@ -0,0 +1,5 @@
|
||||
#include "blacklist.h"
|
||||
|
||||
const char __initdata *const blacklist_hashes[] = {
|
||||
NULL
|
||||
};
|
@ -14,6 +14,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
@ -32,11 +33,13 @@ extern __initconst const unsigned long system_certificate_list_size;
|
||||
* Restrict the addition of keys into a keyring based on the key-to-be-added
|
||||
* being vouched for by a key in the built in system keyring.
|
||||
*/
|
||||
int restrict_link_by_builtin_trusted(struct key *keyring,
|
||||
int restrict_link_by_builtin_trusted(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key)
|
||||
{
|
||||
return restrict_link_by_signature(builtin_trusted_keys, type, payload);
|
||||
return restrict_link_by_signature(dest_keyring, type, payload,
|
||||
builtin_trusted_keys);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
|
||||
@ -49,20 +52,40 @@ int restrict_link_by_builtin_trusted(struct key *keyring,
|
||||
* keyrings.
|
||||
*/
|
||||
int restrict_link_by_builtin_and_secondary_trusted(
|
||||
struct key *keyring,
|
||||
struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
const union key_payload *payload,
|
||||
struct key *restrict_key)
|
||||
{
|
||||
/* If we have a secondary trusted keyring, then that contains a link
|
||||
* through to the builtin keyring and the search will follow that link.
|
||||
*/
|
||||
if (type == &key_type_keyring &&
|
||||
keyring == secondary_trusted_keys &&
|
||||
dest_keyring == secondary_trusted_keys &&
|
||||
payload == &builtin_trusted_keys->payload)
|
||||
/* Allow the builtin keyring to be added to the secondary */
|
||||
return 0;
|
||||
|
||||
return restrict_link_by_signature(secondary_trusted_keys, type, payload);
|
||||
return restrict_link_by_signature(dest_keyring, type, payload,
|
||||
secondary_trusted_keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a struct key_restriction for the "builtin and secondary trust"
|
||||
* keyring. Only for use in system_trusted_keyring_init().
|
||||
*/
|
||||
static __init struct key_restriction *get_builtin_and_secondary_restriction(void)
|
||||
{
|
||||
struct key_restriction *restriction;
|
||||
|
||||
restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
|
||||
if (!restriction)
|
||||
panic("Can't allocate secondary trusted keyring restriction\n");
|
||||
|
||||
restriction->check = restrict_link_by_builtin_and_secondary_trusted;
|
||||
|
||||
return restriction;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -91,7 +114,7 @@ static __init int system_trusted_keyring_init(void)
|
||||
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH |
|
||||
KEY_USR_WRITE),
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
restrict_link_by_builtin_and_secondary_trusted,
|
||||
get_builtin_and_secondary_restriction(),
|
||||
NULL);
|
||||
if (IS_ERR(secondary_trusted_keys))
|
||||
panic("Can't allocate secondary trusted keyring\n");
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "asymmetric_keys.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@ -451,15 +452,100 @@ static void asymmetric_key_destroy(struct key *key)
|
||||
asymmetric_key_free_kids(kids);
|
||||
}
|
||||
|
||||
static struct key_restriction *asymmetric_restriction_alloc(
|
||||
key_restrict_link_func_t check,
|
||||
struct key *key)
|
||||
{
|
||||
struct key_restriction *keyres =
|
||||
kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
|
||||
if (!keyres)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
keyres->check = check;
|
||||
keyres->key = key;
|
||||
keyres->keytype = &key_type_asymmetric;
|
||||
|
||||
return keyres;
|
||||
}
|
||||
|
||||
/*
|
||||
* look up keyring restrict functions for asymmetric keys
|
||||
*/
|
||||
static struct key_restriction *asymmetric_lookup_restriction(
|
||||
const char *restriction)
|
||||
{
|
||||
char *restrict_method;
|
||||
char *parse_buf;
|
||||
char *next;
|
||||
struct key_restriction *ret = ERR_PTR(-EINVAL);
|
||||
|
||||
if (strcmp("builtin_trusted", restriction) == 0)
|
||||
return asymmetric_restriction_alloc(
|
||||
restrict_link_by_builtin_trusted, NULL);
|
||||
|
||||
if (strcmp("builtin_and_secondary_trusted", restriction) == 0)
|
||||
return asymmetric_restriction_alloc(
|
||||
restrict_link_by_builtin_and_secondary_trusted, NULL);
|
||||
|
||||
parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL);
|
||||
if (!parse_buf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
next = parse_buf;
|
||||
restrict_method = strsep(&next, ":");
|
||||
|
||||
if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) {
|
||||
char *key_text;
|
||||
key_serial_t serial;
|
||||
struct key *key;
|
||||
key_restrict_link_func_t link_fn =
|
||||
restrict_link_by_key_or_keyring;
|
||||
bool allow_null_key = false;
|
||||
|
||||
key_text = strsep(&next, ":");
|
||||
|
||||
if (next) {
|
||||
if (strcmp(next, "chain") != 0)
|
||||
goto out;
|
||||
|
||||
link_fn = restrict_link_by_key_or_keyring_chain;
|
||||
allow_null_key = true;
|
||||
}
|
||||
|
||||
if (kstrtos32(key_text, 0, &serial) < 0)
|
||||
goto out;
|
||||
|
||||
if ((serial == 0) && allow_null_key) {
|
||||
key = NULL;
|
||||
} else {
|
||||
key = key_lookup(serial);
|
||||
if (IS_ERR(key)) {
|
||||
ret = ERR_CAST(key);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = asymmetric_restriction_alloc(link_fn, key);
|
||||
if (IS_ERR(ret))
|
||||
key_put(key);
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(parse_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct key_type key_type_asymmetric = {
|
||||
.name = "asymmetric",
|
||||
.preparse = asymmetric_key_preparse,
|
||||
.free_preparse = asymmetric_key_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match_preparse = asymmetric_key_match_preparse,
|
||||
.match_free = asymmetric_key_match_free,
|
||||
.destroy = asymmetric_key_destroy,
|
||||
.describe = asymmetric_key_describe,
|
||||
.name = "asymmetric",
|
||||
.preparse = asymmetric_key_preparse,
|
||||
.free_preparse = asymmetric_key_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match_preparse = asymmetric_key_match_preparse,
|
||||
.match_free = asymmetric_key_match_free,
|
||||
.destroy = asymmetric_key_destroy,
|
||||
.describe = asymmetric_key_describe,
|
||||
.lookup_restriction = asymmetric_lookup_restriction,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(key_type_asymmetric);
|
||||
|
||||
|
@ -23,6 +23,7 @@ struct pkcs7_signed_info {
|
||||
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
|
||||
unsigned index;
|
||||
bool unsupported_crypto; /* T if not usable due to missing crypto */
|
||||
bool blacklisted;
|
||||
|
||||
/* Message digest - the digest of the Content Data (or NULL) */
|
||||
const void *msgdigest;
|
||||
|
@ -190,6 +190,18 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
||||
x509->subject,
|
||||
x509->raw_serial_size, x509->raw_serial);
|
||||
x509->seen = true;
|
||||
|
||||
if (x509->blacklisted) {
|
||||
/* If this cert is blacklisted, then mark everything
|
||||
* that depends on this as blacklisted too.
|
||||
*/
|
||||
sinfo->blacklisted = true;
|
||||
for (p = sinfo->signer; p != x509; p = p->signer)
|
||||
p->blacklisted = true;
|
||||
pr_debug("- blacklisted\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (x509->unsupported_key)
|
||||
goto unsupported_crypto_in_x509;
|
||||
|
||||
@ -357,17 +369,19 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
|
||||
*
|
||||
* (*) -EBADMSG if some part of the message was invalid, or:
|
||||
*
|
||||
* (*) -ENOPKG if none of the signature chains are verifiable because suitable
|
||||
* crypto modules couldn't be found, or:
|
||||
* (*) 0 if no signature chains were found to be blacklisted or to contain
|
||||
* unsupported crypto, or:
|
||||
*
|
||||
* (*) 0 if all the signature chains that don't incur -ENOPKG can be verified
|
||||
* (note that a signature chain may be of zero length), or:
|
||||
* (*) -EKEYREJECTED if a blacklisted key was encountered, or:
|
||||
*
|
||||
* (*) -ENOPKG if none of the signature chains are verifiable because suitable
|
||||
* crypto modules couldn't be found.
|
||||
*/
|
||||
int pkcs7_verify(struct pkcs7_message *pkcs7,
|
||||
enum key_being_used_for usage)
|
||||
{
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
int enopkg = -ENOPKG;
|
||||
int actual_ret = -ENOPKG;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
@ -412,6 +426,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
|
||||
|
||||
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
|
||||
ret = pkcs7_verify_one(pkcs7, sinfo);
|
||||
if (sinfo->blacklisted && actual_ret == -ENOPKG)
|
||||
actual_ret = -EKEYREJECTED;
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOPKG) {
|
||||
sinfo->unsupported_crypto = true;
|
||||
@ -420,11 +436,11 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
enopkg = 0;
|
||||
actual_ret = 0;
|
||||
}
|
||||
|
||||
kleave(" = %d", enopkg);
|
||||
return enopkg;
|
||||
kleave(" = %d", actual_ret);
|
||||
return actual_ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_verify);
|
||||
|
||||
|
@ -56,9 +56,10 @@ __setup("ca_keys=", ca_keys_setup);
|
||||
|
||||
/**
|
||||
* restrict_link_by_signature - Restrict additions to a ring of public keys
|
||||
* @trust_keyring: A ring of keys that can be used to vouch for the new cert.
|
||||
* @dest_keyring: Keyring being linked to.
|
||||
* @type: The type of key being added.
|
||||
* @payload: The payload of the new key.
|
||||
* @trust_keyring: A ring of keys that can be used to vouch for the new cert.
|
||||
*
|
||||
* Check the new certificate against the ones in the trust keyring. If one of
|
||||
* those is the signing key and validates the new certificate, then mark the
|
||||
@ -69,9 +70,10 @@ __setup("ca_keys=", ca_keys_setup);
|
||||
* signature check fails or the key is blacklisted and some other error if
|
||||
* there is a matching certificate but the signature check cannot be performed.
|
||||
*/
|
||||
int restrict_link_by_signature(struct key *trust_keyring,
|
||||
int restrict_link_by_signature(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
const union key_payload *payload,
|
||||
struct key *trust_keyring)
|
||||
{
|
||||
const struct public_key_signature *sig;
|
||||
struct key *key;
|
||||
@ -106,3 +108,156 @@ int restrict_link_by_signature(struct key *trust_keyring,
|
||||
key_put(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool match_either_id(const struct asymmetric_key_ids *pair,
|
||||
const struct asymmetric_key_id *single)
|
||||
{
|
||||
return (asymmetric_key_id_same(pair->id[0], single) ||
|
||||
asymmetric_key_id_same(pair->id[1], single));
|
||||
}
|
||||
|
||||
static int key_or_keyring_common(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *trusted, bool check_dest)
|
||||
{
|
||||
const struct public_key_signature *sig;
|
||||
struct key *key = NULL;
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (!dest_keyring)
|
||||
return -ENOKEY;
|
||||
else if (dest_keyring->type != &key_type_keyring)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!trusted && !check_dest)
|
||||
return -ENOKEY;
|
||||
|
||||
if (type != &key_type_asymmetric)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
sig = payload->data[asym_auth];
|
||||
if (!sig->auth_ids[0] && !sig->auth_ids[1])
|
||||
return -ENOKEY;
|
||||
|
||||
if (trusted) {
|
||||
if (trusted->type == &key_type_keyring) {
|
||||
/* See if we have a key that signed this one. */
|
||||
key = find_asymmetric_key(trusted, sig->auth_ids[0],
|
||||
sig->auth_ids[1], false);
|
||||
if (IS_ERR(key))
|
||||
key = NULL;
|
||||
} else if (trusted->type == &key_type_asymmetric) {
|
||||
const struct asymmetric_key_ids *signer_ids;
|
||||
|
||||
signer_ids = asymmetric_key_ids(trusted);
|
||||
|
||||
/*
|
||||
* The auth_ids come from the candidate key (the
|
||||
* one that is being considered for addition to
|
||||
* dest_keyring) and identify the key that was
|
||||
* used to sign.
|
||||
*
|
||||
* The signer_ids are identifiers for the
|
||||
* signing key specified for dest_keyring.
|
||||
*
|
||||
* The first auth_id is the preferred id, and
|
||||
* the second is the fallback. If only one
|
||||
* auth_id is present, it may match against
|
||||
* either signer_id. If two auth_ids are
|
||||
* present, the first auth_id must match one
|
||||
* signer_id and the second auth_id must match
|
||||
* the second signer_id.
|
||||
*/
|
||||
if (!sig->auth_ids[0] || !sig->auth_ids[1]) {
|
||||
const struct asymmetric_key_id *auth_id;
|
||||
|
||||
auth_id = sig->auth_ids[0] ?: sig->auth_ids[1];
|
||||
if (match_either_id(signer_ids, auth_id))
|
||||
key = __key_get(trusted);
|
||||
|
||||
} else if (asymmetric_key_id_same(signer_ids->id[1],
|
||||
sig->auth_ids[1]) &&
|
||||
match_either_id(signer_ids,
|
||||
sig->auth_ids[0])) {
|
||||
key = __key_get(trusted);
|
||||
}
|
||||
} else {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
if (check_dest && !key) {
|
||||
/* See if the destination has a key that signed this one. */
|
||||
key = find_asymmetric_key(dest_keyring, sig->auth_ids[0],
|
||||
sig->auth_ids[1], false);
|
||||
if (IS_ERR(key))
|
||||
key = NULL;
|
||||
}
|
||||
|
||||
if (!key)
|
||||
return -ENOKEY;
|
||||
|
||||
ret = key_validate(key);
|
||||
if (ret == 0)
|
||||
ret = verify_signature(key, sig);
|
||||
|
||||
key_put(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* restrict_link_by_key_or_keyring - Restrict additions to a ring of public
|
||||
* keys using the restrict_key information stored in the ring.
|
||||
* @dest_keyring: Keyring being linked to.
|
||||
* @type: The type of key being added.
|
||||
* @payload: The payload of the new key.
|
||||
* @trusted: A key or ring of keys that can be used to vouch for the new cert.
|
||||
*
|
||||
* Check the new certificate only against the key or keys passed in the data
|
||||
* parameter. If one of those is the signing key and validates the new
|
||||
* certificate, then mark the new certificate as being ok to link.
|
||||
*
|
||||
* Returns 0 if the new certificate was accepted, -ENOKEY if we
|
||||
* couldn't find a matching parent certificate in the trusted list,
|
||||
* -EKEYREJECTED if the signature check fails, and some other error if
|
||||
* there is a matching certificate but the signature check cannot be
|
||||
* performed.
|
||||
*/
|
||||
int restrict_link_by_key_or_keyring(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *trusted)
|
||||
{
|
||||
return key_or_keyring_common(dest_keyring, type, payload, trusted,
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
* restrict_link_by_key_or_keyring_chain - Restrict additions to a ring of
|
||||
* public keys using the restrict_key information stored in the ring.
|
||||
* @dest_keyring: Keyring being linked to.
|
||||
* @type: The type of key being added.
|
||||
* @payload: The payload of the new key.
|
||||
* @trusted: A key or ring of keys that can be used to vouch for the new cert.
|
||||
*
|
||||
* Check the new certificate only against the key or keys passed in the data
|
||||
* parameter. If one of those is the signing key and validates the new
|
||||
* certificate, then mark the new certificate as being ok to link.
|
||||
*
|
||||
* Returns 0 if the new certificate was accepted, -ENOKEY if we
|
||||
* couldn't find a matching parent certificate in the trusted list,
|
||||
* -EKEYREJECTED if the signature check fails, and some other error if
|
||||
* there is a matching certificate but the signature check cannot be
|
||||
* performed.
|
||||
*/
|
||||
int restrict_link_by_key_or_keyring_chain(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *trusted)
|
||||
{
|
||||
return key_or_keyring_common(dest_keyring, type, payload, trusted,
|
||||
true);
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ struct x509_certificate {
|
||||
bool self_signed; /* T if self-signed (check unsupported_sig too) */
|
||||
bool unsupported_key; /* T if key uses unsupported crypto */
|
||||
bool unsupported_sig; /* T if signature uses unsupported crypto */
|
||||
bool blacklisted;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -84,6 +84,16 @@ int x509_get_sig_params(struct x509_certificate *cert)
|
||||
goto error_2;
|
||||
might_sleep();
|
||||
ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest);
|
||||
if (ret < 0)
|
||||
goto error_2;
|
||||
|
||||
ret = is_hash_blacklisted(sig->digest, sig->digest_size, "tbs");
|
||||
if (ret == -EKEYREJECTED) {
|
||||
pr_err("Cert %*phN is blacklisted\n",
|
||||
sig->digest_size, sig->digest);
|
||||
cert->blacklisted = true;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
error_2:
|
||||
kfree(desc);
|
||||
@ -186,6 +196,11 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||
cert->sig->pkey_algo, cert->sig->hash_algo);
|
||||
}
|
||||
|
||||
/* Don't permit addition of blacklisted keys */
|
||||
ret = -EKEYREJECTED;
|
||||
if (cert->blacklisted)
|
||||
goto error_free_cert;
|
||||
|
||||
/* Propose a description */
|
||||
sulen = strlen(cert->subject);
|
||||
if (cert->raw_skid) {
|
||||
|
@ -6,6 +6,7 @@ menuconfig TCG_TPM
|
||||
tristate "TPM Hardware Support"
|
||||
depends on HAS_IOMEM
|
||||
select SECURITYFS
|
||||
select CRYPTO
|
||||
select CRYPTO_HASH_INFO
|
||||
---help---
|
||||
If you have a TPM security chip in your system, which
|
||||
@ -135,7 +136,7 @@ config TCG_XEN
|
||||
|
||||
config TCG_CRB
|
||||
tristate "TPM 2.0 CRB Interface"
|
||||
depends on X86 && ACPI
|
||||
depends on ACPI
|
||||
---help---
|
||||
If you have a TPM security chip that is compliant with the
|
||||
TCG CRB 2.0 TPM specification say Yes and it will be accessible
|
||||
|
@ -3,7 +3,8 @@
|
||||
#
|
||||
obj-$(CONFIG_TCG_TPM) += tpm.o
|
||||
tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o \
|
||||
tpm1_eventlog.o tpm2_eventlog.o
|
||||
tpm-dev-common.o tpmrm-dev.o tpm1_eventlog.o tpm2_eventlog.o \
|
||||
tpm2-space.o
|
||||
tpm-$(CONFIG_ACPI) += tpm_ppi.o tpm_acpi.o
|
||||
tpm-$(CONFIG_OF) += tpm_of.o
|
||||
obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o
|
||||
|
@ -111,6 +111,13 @@ static const struct st33zp24_phy_ops i2c_phy_ops = {
|
||||
.recv = st33zp24_i2c_recv,
|
||||
};
|
||||
|
||||
static const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false };
|
||||
|
||||
static const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = {
|
||||
{ "lpcpd-gpios", &lpcpd_gpios, 1 },
|
||||
{},
|
||||
};
|
||||
|
||||
static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client)
|
||||
{
|
||||
struct tpm_chip *chip = i2c_get_clientdata(client);
|
||||
@ -118,10 +125,14 @@ static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client)
|
||||
struct st33zp24_i2c_phy *phy = tpm_dev->phy_id;
|
||||
struct gpio_desc *gpiod_lpcpd;
|
||||
struct device *dev = &client->dev;
|
||||
int ret;
|
||||
|
||||
ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), acpi_st33zp24_gpios);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Get LPCPD GPIO from ACPI */
|
||||
gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1,
|
||||
GPIOD_OUT_HIGH);
|
||||
gpiod_lpcpd = devm_gpiod_get(dev, "lpcpd", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpiod_lpcpd)) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to retrieve lpcpd-gpios from acpi.\n");
|
||||
@ -268,8 +279,14 @@ static int st33zp24_i2c_probe(struct i2c_client *client,
|
||||
static int st33zp24_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tpm_chip *chip = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
return st33zp24_remove(chip);
|
||||
ret = st33zp24_remove(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&client->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id st33zp24_i2c_id[] = {
|
||||
|
@ -230,6 +230,13 @@ static const struct st33zp24_phy_ops spi_phy_ops = {
|
||||
.recv = st33zp24_spi_recv,
|
||||
};
|
||||
|
||||
static const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false };
|
||||
|
||||
static const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = {
|
||||
{ "lpcpd-gpios", &lpcpd_gpios, 1 },
|
||||
{},
|
||||
};
|
||||
|
||||
static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev)
|
||||
{
|
||||
struct tpm_chip *chip = spi_get_drvdata(spi_dev);
|
||||
@ -237,10 +244,14 @@ static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev)
|
||||
struct st33zp24_spi_phy *phy = tpm_dev->phy_id;
|
||||
struct gpio_desc *gpiod_lpcpd;
|
||||
struct device *dev = &spi_dev->dev;
|
||||
int ret;
|
||||
|
||||
ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), acpi_st33zp24_gpios);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Get LPCPD GPIO from ACPI */
|
||||
gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1,
|
||||
GPIOD_OUT_HIGH);
|
||||
gpiod_lpcpd = devm_gpiod_get(dev, "lpcpd", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpiod_lpcpd)) {
|
||||
dev_err(dev, "Failed to retrieve lpcpd-gpios from acpi.\n");
|
||||
phy->io_lpcpd = -1;
|
||||
@ -385,8 +396,14 @@ static int st33zp24_spi_probe(struct spi_device *dev)
|
||||
static int st33zp24_spi_remove(struct spi_device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = spi_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
return st33zp24_remove(chip);
|
||||
ret = st33zp24_remove(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&dev->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id st33zp24_spi_id[] = {
|
||||
|
@ -117,9 +117,9 @@ static u8 st33zp24_status(struct tpm_chip *chip)
|
||||
/*
|
||||
* check_locality if the locality is active
|
||||
* @param: chip, the tpm chip description
|
||||
* @return: the active locality or -EACCESS.
|
||||
* @return: true if LOCALITY0 is active, otherwise false
|
||||
*/
|
||||
static int check_locality(struct tpm_chip *chip)
|
||||
static bool check_locality(struct tpm_chip *chip)
|
||||
{
|
||||
struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
|
||||
u8 data;
|
||||
@ -129,9 +129,9 @@ static int check_locality(struct tpm_chip *chip)
|
||||
if (status && (data &
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
|
||||
return tpm_dev->locality;
|
||||
return true;
|
||||
|
||||
return -EACCES;
|
||||
return false;
|
||||
} /* check_locality() */
|
||||
|
||||
/*
|
||||
@ -146,7 +146,7 @@ static int request_locality(struct tpm_chip *chip)
|
||||
long ret;
|
||||
u8 data;
|
||||
|
||||
if (check_locality(chip) == tpm_dev->locality)
|
||||
if (check_locality(chip))
|
||||
return tpm_dev->locality;
|
||||
|
||||
data = TPM_ACCESS_REQUEST_USE;
|
||||
@ -158,7 +158,7 @@ static int request_locality(struct tpm_chip *chip)
|
||||
|
||||
/* Request locality is usually effective after the request */
|
||||
do {
|
||||
if (check_locality(chip) >= 0)
|
||||
if (check_locality(chip))
|
||||
return tpm_dev->locality;
|
||||
msleep(TPM_TIMEOUT);
|
||||
} while (time_before(jiffies, stop));
|
||||
|
@ -33,6 +33,7 @@ DEFINE_IDR(dev_nums_idr);
|
||||
static DEFINE_MUTEX(idr_lock);
|
||||
|
||||
struct class *tpm_class;
|
||||
struct class *tpmrm_class;
|
||||
dev_t tpm_devt;
|
||||
|
||||
/**
|
||||
@ -128,9 +129,19 @@ static void tpm_dev_release(struct device *dev)
|
||||
mutex_unlock(&idr_lock);
|
||||
|
||||
kfree(chip->log.bios_event_log);
|
||||
kfree(chip->work_space.context_buf);
|
||||
kfree(chip->work_space.session_buf);
|
||||
kfree(chip);
|
||||
}
|
||||
|
||||
static void tpm_devs_release(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs);
|
||||
|
||||
/* release the master device reference */
|
||||
put_device(&chip->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_chip_alloc() - allocate a new struct tpm_chip instance
|
||||
* @pdev: device to which the chip is associated
|
||||
@ -167,18 +178,36 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
|
||||
chip->dev_num = rc;
|
||||
|
||||
device_initialize(&chip->dev);
|
||||
device_initialize(&chip->devs);
|
||||
|
||||
chip->dev.class = tpm_class;
|
||||
chip->dev.release = tpm_dev_release;
|
||||
chip->dev.parent = pdev;
|
||||
chip->dev.groups = chip->groups;
|
||||
|
||||
chip->devs.parent = pdev;
|
||||
chip->devs.class = tpmrm_class;
|
||||
chip->devs.release = tpm_devs_release;
|
||||
/* get extra reference on main device to hold on
|
||||
* behalf of devs. This holds the chip structure
|
||||
* while cdevs is in use. The corresponding put
|
||||
* is in the tpm_devs_release (TPM2 only)
|
||||
*/
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
get_device(&chip->dev);
|
||||
|
||||
if (chip->dev_num == 0)
|
||||
chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR);
|
||||
else
|
||||
chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num);
|
||||
|
||||
chip->devs.devt =
|
||||
MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES);
|
||||
|
||||
rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num);
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = dev_set_name(&chip->devs, "tpmrm%d", chip->dev_num);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
@ -186,12 +215,28 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
|
||||
chip->flags |= TPM_CHIP_FLAG_VIRTUAL;
|
||||
|
||||
cdev_init(&chip->cdev, &tpm_fops);
|
||||
cdev_init(&chip->cdevs, &tpmrm_fops);
|
||||
chip->cdev.owner = THIS_MODULE;
|
||||
chip->cdevs.owner = THIS_MODULE;
|
||||
chip->cdev.kobj.parent = &chip->dev.kobj;
|
||||
chip->cdevs.kobj.parent = &chip->devs.kobj;
|
||||
|
||||
chip->work_space.context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!chip->work_space.context_buf) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
chip->work_space.session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!chip->work_space.session_buf) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
chip->locality = -1;
|
||||
return chip;
|
||||
|
||||
out:
|
||||
put_device(&chip->devs);
|
||||
put_device(&chip->dev);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
@ -236,7 +281,6 @@ static int tpm_add_char_device(struct tpm_chip *chip)
|
||||
"unable to cdev_add() %s, major %d, minor %d, err=%d\n",
|
||||
dev_name(&chip->dev), MAJOR(chip->dev.devt),
|
||||
MINOR(chip->dev.devt), rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -251,6 +295,27 @@ static int tpm_add_char_device(struct tpm_chip *chip)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
rc = cdev_add(&chip->cdevs, chip->devs.devt, 1);
|
||||
if (rc) {
|
||||
dev_err(&chip->dev,
|
||||
"unable to cdev_add() %s, major %d, minor %d, err=%d\n",
|
||||
dev_name(&chip->devs), MAJOR(chip->devs.devt),
|
||||
MINOR(chip->devs.devt), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
rc = device_add(&chip->devs);
|
||||
if (rc) {
|
||||
dev_err(&chip->dev,
|
||||
"unable to device_register() %s, major %d, minor %d, err=%d\n",
|
||||
dev_name(&chip->devs), MAJOR(chip->devs.devt),
|
||||
MINOR(chip->devs.devt), rc);
|
||||
cdev_del(&chip->cdevs);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Make the chip available. */
|
||||
mutex_lock(&idr_lock);
|
||||
idr_replace(&dev_nums_idr, chip, chip->dev_num);
|
||||
@ -384,6 +449,10 @@ void tpm_chip_unregister(struct tpm_chip *chip)
|
||||
{
|
||||
tpm_del_legacy_sysfs(chip);
|
||||
tpm_bios_log_teardown(chip);
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
|
||||
cdev_del(&chip->cdevs);
|
||||
device_del(&chip->devs);
|
||||
}
|
||||
tpm_del_char_device(chip);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_chip_unregister);
|
||||
|
148
drivers/char/tpm/tpm-dev-common.c
Normal file
148
drivers/char/tpm/tpm-dev-common.c
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2004 IBM Corporation
|
||||
* Authors:
|
||||
* Leendert van Doorn <leendert@watson.ibm.com>
|
||||
* Dave Safford <safford@watson.ibm.com>
|
||||
* Reiner Sailer <sailer@watson.ibm.com>
|
||||
* Kylene Hall <kjhall@us.ibm.com>
|
||||
*
|
||||
* Copyright (C) 2013 Obsidian Research Corp
|
||||
* Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
|
||||
*
|
||||
* Device file system interface to the TPM
|
||||
*
|
||||
* 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/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "tpm.h"
|
||||
#include "tpm-dev.h"
|
||||
|
||||
static void user_reader_timeout(unsigned long ptr)
|
||||
{
|
||||
struct file_priv *priv = (struct file_priv *)ptr;
|
||||
|
||||
pr_warn("TPM user space timeout is deprecated (pid=%d)\n",
|
||||
task_tgid_nr(current));
|
||||
|
||||
schedule_work(&priv->work);
|
||||
}
|
||||
|
||||
static void timeout_work(struct work_struct *work)
|
||||
{
|
||||
struct file_priv *priv = container_of(work, struct file_priv, work);
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
memset(priv->data_buffer, 0, sizeof(priv->data_buffer));
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
}
|
||||
|
||||
void tpm_common_open(struct file *file, struct tpm_chip *chip,
|
||||
struct file_priv *priv)
|
||||
{
|
||||
priv->chip = chip;
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
mutex_init(&priv->buffer_mutex);
|
||||
setup_timer(&priv->user_read_timer, user_reader_timeout,
|
||||
(unsigned long)priv);
|
||||
INIT_WORK(&priv->work, timeout_work);
|
||||
|
||||
file->private_data = priv;
|
||||
}
|
||||
|
||||
ssize_t tpm_common_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct file_priv *priv = file->private_data;
|
||||
ssize_t ret_size;
|
||||
ssize_t orig_ret_size;
|
||||
int rc;
|
||||
|
||||
del_singleshot_timer_sync(&priv->user_read_timer);
|
||||
flush_work(&priv->work);
|
||||
ret_size = atomic_read(&priv->data_pending);
|
||||
if (ret_size > 0) { /* relay data */
|
||||
orig_ret_size = ret_size;
|
||||
if (size < ret_size)
|
||||
ret_size = size;
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
rc = copy_to_user(buf, priv->data_buffer, ret_size);
|
||||
memset(priv->data_buffer, 0, orig_ret_size);
|
||||
if (rc)
|
||||
ret_size = -EFAULT;
|
||||
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
}
|
||||
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
|
||||
return ret_size;
|
||||
}
|
||||
|
||||
ssize_t tpm_common_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *off, struct tpm_space *space)
|
||||
{
|
||||
struct file_priv *priv = file->private_data;
|
||||
size_t in_size = size;
|
||||
ssize_t out_size;
|
||||
|
||||
/* Cannot perform a write until the read has cleared either via
|
||||
* tpm_read or a user_read_timer timeout. This also prevents split
|
||||
* buffered writes from blocking here.
|
||||
*/
|
||||
if (atomic_read(&priv->data_pending) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
if (in_size > TPM_BUFSIZE)
|
||||
return -E2BIG;
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
|
||||
if (copy_from_user
|
||||
(priv->data_buffer, (void __user *) buf, in_size)) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* atomic tpm command send and result receive. We only hold the ops
|
||||
* lock during this period so that the tpm can be unregistered even if
|
||||
* the char dev is held open.
|
||||
*/
|
||||
if (tpm_try_get_ops(priv->chip)) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return -EPIPE;
|
||||
}
|
||||
out_size = tpm_transmit(priv->chip, space, priv->data_buffer,
|
||||
sizeof(priv->data_buffer), 0);
|
||||
|
||||
tpm_put_ops(priv->chip);
|
||||
if (out_size < 0) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return out_size;
|
||||
}
|
||||
|
||||
atomic_set(&priv->data_pending, out_size);
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
|
||||
/* Set a timeout by which the reader must come claim the result */
|
||||
mod_timer(&priv->user_read_timer, jiffies + (120 * HZ));
|
||||
|
||||
return in_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called on file close
|
||||
*/
|
||||
void tpm_common_release(struct file *file, struct file_priv *priv)
|
||||
{
|
||||
del_singleshot_timer_sync(&priv->user_read_timer);
|
||||
flush_work(&priv->work);
|
||||
file->private_data = NULL;
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
}
|
@ -18,48 +18,15 @@
|
||||
*
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "tpm.h"
|
||||
|
||||
struct file_priv {
|
||||
struct tpm_chip *chip;
|
||||
|
||||
/* Data passed to and from the tpm via the read/write calls */
|
||||
atomic_t data_pending;
|
||||
struct mutex buffer_mutex;
|
||||
|
||||
struct timer_list user_read_timer; /* user needs to claim result */
|
||||
struct work_struct work;
|
||||
|
||||
u8 data_buffer[TPM_BUFSIZE];
|
||||
};
|
||||
|
||||
static void user_reader_timeout(unsigned long ptr)
|
||||
{
|
||||
struct file_priv *priv = (struct file_priv *)ptr;
|
||||
|
||||
pr_warn("TPM user space timeout is deprecated (pid=%d)\n",
|
||||
task_tgid_nr(current));
|
||||
|
||||
schedule_work(&priv->work);
|
||||
}
|
||||
|
||||
static void timeout_work(struct work_struct *work)
|
||||
{
|
||||
struct file_priv *priv = container_of(work, struct file_priv, work);
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
memset(priv->data_buffer, 0, sizeof(priv->data_buffer));
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
}
|
||||
#include "tpm-dev.h"
|
||||
|
||||
static int tpm_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct tpm_chip *chip =
|
||||
container_of(inode->i_cdev, struct tpm_chip, cdev);
|
||||
struct tpm_chip *chip;
|
||||
struct file_priv *priv;
|
||||
|
||||
chip = container_of(inode->i_cdev, struct tpm_chip, cdev);
|
||||
|
||||
/* It's assured that the chip will be opened just once,
|
||||
* by the check of is_open variable, which is protected
|
||||
* by driver_lock. */
|
||||
@ -69,100 +36,22 @@ static int tpm_open(struct inode *inode, struct file *file)
|
||||
}
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (priv == NULL) {
|
||||
clear_bit(0, &chip->is_open);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (priv == NULL)
|
||||
goto out;
|
||||
|
||||
priv->chip = chip;
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
mutex_init(&priv->buffer_mutex);
|
||||
setup_timer(&priv->user_read_timer, user_reader_timeout,
|
||||
(unsigned long)priv);
|
||||
INIT_WORK(&priv->work, timeout_work);
|
||||
tpm_common_open(file, chip, priv);
|
||||
|
||||
file->private_data = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t tpm_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct file_priv *priv = file->private_data;
|
||||
ssize_t ret_size;
|
||||
int rc;
|
||||
|
||||
del_singleshot_timer_sync(&priv->user_read_timer);
|
||||
flush_work(&priv->work);
|
||||
ret_size = atomic_read(&priv->data_pending);
|
||||
if (ret_size > 0) { /* relay data */
|
||||
ssize_t orig_ret_size = ret_size;
|
||||
if (size < ret_size)
|
||||
ret_size = size;
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
rc = copy_to_user(buf, priv->data_buffer, ret_size);
|
||||
memset(priv->data_buffer, 0, orig_ret_size);
|
||||
if (rc)
|
||||
ret_size = -EFAULT;
|
||||
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
}
|
||||
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
|
||||
return ret_size;
|
||||
out:
|
||||
clear_bit(0, &chip->is_open);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static ssize_t tpm_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct file_priv *priv = file->private_data;
|
||||
size_t in_size = size;
|
||||
ssize_t out_size;
|
||||
|
||||
/* cannot perform a write until the read has cleared
|
||||
either via tpm_read or a user_read_timer timeout.
|
||||
This also prevents splitted buffered writes from blocking here.
|
||||
*/
|
||||
if (atomic_read(&priv->data_pending) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
if (in_size > TPM_BUFSIZE)
|
||||
return -E2BIG;
|
||||
|
||||
mutex_lock(&priv->buffer_mutex);
|
||||
|
||||
if (copy_from_user
|
||||
(priv->data_buffer, (void __user *) buf, in_size)) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* atomic tpm command send and result receive. We only hold the ops
|
||||
* lock during this period so that the tpm can be unregistered even if
|
||||
* the char dev is held open.
|
||||
*/
|
||||
if (tpm_try_get_ops(priv->chip)) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return -EPIPE;
|
||||
}
|
||||
out_size = tpm_transmit(priv->chip, priv->data_buffer,
|
||||
sizeof(priv->data_buffer), 0);
|
||||
|
||||
tpm_put_ops(priv->chip);
|
||||
if (out_size < 0) {
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
return out_size;
|
||||
}
|
||||
|
||||
atomic_set(&priv->data_pending, out_size);
|
||||
mutex_unlock(&priv->buffer_mutex);
|
||||
|
||||
/* Set a timeout by which the reader must come claim the result */
|
||||
mod_timer(&priv->user_read_timer, jiffies + (120 * HZ));
|
||||
|
||||
return in_size;
|
||||
return tpm_common_write(file, buf, size, off, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -172,12 +61,10 @@ static int tpm_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct file_priv *priv = file->private_data;
|
||||
|
||||
del_singleshot_timer_sync(&priv->user_read_timer);
|
||||
flush_work(&priv->work);
|
||||
file->private_data = NULL;
|
||||
atomic_set(&priv->data_pending, 0);
|
||||
tpm_common_release(file, priv);
|
||||
clear_bit(0, &priv->chip->is_open);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -185,9 +72,7 @@ const struct file_operations tpm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = tpm_open,
|
||||
.read = tpm_read,
|
||||
.read = tpm_common_read,
|
||||
.write = tpm_write,
|
||||
.release = tpm_release,
|
||||
};
|
||||
|
||||
|
||||
|
27
drivers/char/tpm/tpm-dev.h
Normal file
27
drivers/char/tpm/tpm-dev.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef _TPM_DEV_H
|
||||
#define _TPM_DEV_H
|
||||
|
||||
#include "tpm.h"
|
||||
|
||||
struct file_priv {
|
||||
struct tpm_chip *chip;
|
||||
|
||||
/* Data passed to and from the tpm via the read/write calls */
|
||||
atomic_t data_pending;
|
||||
struct mutex buffer_mutex;
|
||||
|
||||
struct timer_list user_read_timer; /* user needs to claim result */
|
||||
struct work_struct work;
|
||||
|
||||
u8 data_buffer[TPM_BUFSIZE];
|
||||
};
|
||||
|
||||
void tpm_common_open(struct file *file, struct tpm_chip *chip,
|
||||
struct file_priv *priv);
|
||||
ssize_t tpm_common_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *off);
|
||||
ssize_t tpm_common_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *off, struct tpm_space *space);
|
||||
void tpm_common_release(struct file *file, struct file_priv *priv);
|
||||
|
||||
#endif
|
@ -328,6 +328,47 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
|
||||
|
||||
static bool tpm_validate_command(struct tpm_chip *chip,
|
||||
struct tpm_space *space,
|
||||
const u8 *cmd,
|
||||
size_t len)
|
||||
{
|
||||
const struct tpm_input_header *header = (const void *)cmd;
|
||||
int i;
|
||||
u32 cc;
|
||||
u32 attrs;
|
||||
unsigned int nr_handles;
|
||||
|
||||
if (len < TPM_HEADER_SIZE)
|
||||
return false;
|
||||
|
||||
if (!space)
|
||||
return true;
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2 && chip->nr_commands) {
|
||||
cc = be32_to_cpu(header->ordinal);
|
||||
|
||||
i = tpm2_find_cc(chip, cc);
|
||||
if (i < 0) {
|
||||
dev_dbg(&chip->dev, "0x%04X is an invalid command\n",
|
||||
cc);
|
||||
return false;
|
||||
}
|
||||
|
||||
attrs = chip->cc_attrs_tbl[i];
|
||||
nr_handles =
|
||||
4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0));
|
||||
if (len < TPM_HEADER_SIZE + 4 * nr_handles)
|
||||
goto err_len;
|
||||
}
|
||||
|
||||
return true;
|
||||
err_len:
|
||||
dev_dbg(&chip->dev,
|
||||
"%s: insufficient command length %zu", __func__, len);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* tmp_transmit - Internal kernel interface to transmit TPM commands.
|
||||
*
|
||||
@ -340,14 +381,17 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
|
||||
* 0 when the operation is successful.
|
||||
* A negative number for system errors (errno).
|
||||
*/
|
||||
ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
|
||||
unsigned int flags)
|
||||
ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
||||
u8 *buf, size_t bufsiz, unsigned int flags)
|
||||
{
|
||||
ssize_t rc;
|
||||
struct tpm_output_header *header = (void *)buf;
|
||||
int rc;
|
||||
ssize_t len = 0;
|
||||
u32 count, ordinal;
|
||||
unsigned long stop;
|
||||
bool need_locality;
|
||||
|
||||
if (bufsiz < TPM_HEADER_SIZE)
|
||||
if (!tpm_validate_command(chip, space, buf, bufsiz))
|
||||
return -EINVAL;
|
||||
|
||||
if (bufsiz > TPM_BUFSIZE)
|
||||
@ -369,10 +413,24 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
|
||||
if (chip->dev.parent)
|
||||
pm_runtime_get_sync(chip->dev.parent);
|
||||
|
||||
/* Store the decision as chip->locality will be changed. */
|
||||
need_locality = chip->locality == -1;
|
||||
|
||||
if (need_locality && chip->ops->request_locality) {
|
||||
rc = chip->ops->request_locality(chip, 0);
|
||||
if (rc < 0)
|
||||
goto out_no_locality;
|
||||
chip->locality = rc;
|
||||
}
|
||||
|
||||
rc = tpm2_prepare_space(chip, space, ordinal, buf);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = chip->ops->send(chip, (u8 *) buf, count);
|
||||
if (rc < 0) {
|
||||
dev_err(&chip->dev,
|
||||
"tpm_transmit: tpm_send: error %zd\n", rc);
|
||||
"tpm_transmit: tpm_send: error %d\n", rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -405,17 +463,36 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
|
||||
goto out;
|
||||
|
||||
out_recv:
|
||||
rc = chip->ops->recv(chip, (u8 *) buf, bufsiz);
|
||||
if (rc < 0)
|
||||
len = chip->ops->recv(chip, (u8 *) buf, bufsiz);
|
||||
if (len < 0) {
|
||||
rc = len;
|
||||
dev_err(&chip->dev,
|
||||
"tpm_transmit: tpm_recv: error %zd\n", rc);
|
||||
"tpm_transmit: tpm_recv: error %d\n", rc);
|
||||
goto out;
|
||||
} else if (len < TPM_HEADER_SIZE) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len != be32_to_cpu(header->length)) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
|
||||
|
||||
out:
|
||||
if (need_locality && chip->ops->relinquish_locality) {
|
||||
chip->ops->relinquish_locality(chip, chip->locality);
|
||||
chip->locality = -1;
|
||||
}
|
||||
out_no_locality:
|
||||
if (chip->dev.parent)
|
||||
pm_runtime_put_sync(chip->dev.parent);
|
||||
|
||||
if (!(flags & TPM_TRANSMIT_UNLOCKED))
|
||||
mutex_unlock(&chip->tpm_mutex);
|
||||
return rc;
|
||||
return rc ? rc : len;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -434,23 +511,18 @@ out:
|
||||
* A negative number for system errors (errno).
|
||||
* A positive number for a TPM error.
|
||||
*/
|
||||
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *buf,
|
||||
size_t bufsiz, size_t min_rsp_body_length,
|
||||
unsigned int flags, const char *desc)
|
||||
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
|
||||
const void *buf, size_t bufsiz,
|
||||
size_t min_rsp_body_length, unsigned int flags,
|
||||
const char *desc)
|
||||
{
|
||||
const struct tpm_output_header *header;
|
||||
const struct tpm_output_header *header = buf;
|
||||
int err;
|
||||
ssize_t len;
|
||||
|
||||
len = tpm_transmit(chip, (const u8 *)buf, bufsiz, flags);
|
||||
len = tpm_transmit(chip, space, (u8 *)buf, bufsiz, flags);
|
||||
if (len < 0)
|
||||
return len;
|
||||
else if (len < TPM_HEADER_SIZE)
|
||||
return -EFAULT;
|
||||
|
||||
header = buf;
|
||||
if (len != be32_to_cpu(header->length))
|
||||
return -EFAULT;
|
||||
|
||||
err = be32_to_cpu(header->return_code);
|
||||
if (err != 0 && desc)
|
||||
@ -501,7 +573,7 @@ ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
|
||||
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
|
||||
tpm_cmd.params.getcap_in.subcap = cpu_to_be32(subcap_id);
|
||||
}
|
||||
rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
|
||||
min_cap_length, 0, desc);
|
||||
if (!rc)
|
||||
*cap = tpm_cmd.params.getcap_out.cap;
|
||||
@ -525,7 +597,8 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
|
||||
start_cmd.header.in = tpm_startup_header;
|
||||
|
||||
start_cmd.params.startup_in.startup_type = startup_type;
|
||||
return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
|
||||
return tpm_transmit_cmd(chip, NULL, &start_cmd,
|
||||
TPM_INTERNAL_RESULT_SIZE, 0,
|
||||
0, "attempting to start the TPM");
|
||||
}
|
||||
|
||||
@ -682,8 +755,8 @@ static int tpm_continue_selftest(struct tpm_chip *chip)
|
||||
struct tpm_cmd_t cmd;
|
||||
|
||||
cmd.header.in = continue_selftest_header;
|
||||
rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, 0, 0,
|
||||
"continue selftest");
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, CONTINUE_SELFTEST_RESULT_SIZE,
|
||||
0, 0, "continue selftest");
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -703,7 +776,7 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
|
||||
|
||||
cmd.header.in = pcrread_header;
|
||||
cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
|
||||
rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, READ_PCR_RESULT_SIZE,
|
||||
READ_PCR_RESULT_BODY_SIZE, 0,
|
||||
"attempting to read a pcr value");
|
||||
|
||||
@ -815,7 +888,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
|
||||
cmd.header.in = pcrextend_header;
|
||||
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
|
||||
memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
|
||||
rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, EXTEND_PCR_RESULT_SIZE,
|
||||
EXTEND_PCR_RESULT_BODY_SIZE, 0,
|
||||
"attempting extend a PCR value");
|
||||
|
||||
@ -920,8 +993,8 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
|
||||
if (chip == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
rc = tpm_transmit_cmd(chip, cmd, buflen, 0, 0, "attempting tpm_cmd");
|
||||
|
||||
rc = tpm_transmit_cmd(chip, NULL, cmd, buflen, 0, 0,
|
||||
"attempting tpm_cmd");
|
||||
tpm_put_ops(chip);
|
||||
return rc;
|
||||
}
|
||||
@ -1022,16 +1095,16 @@ int tpm_pm_suspend(struct device *dev)
|
||||
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr);
|
||||
memcpy(cmd.params.pcrextend_in.hash, dummy_hash,
|
||||
TPM_DIGEST_SIZE);
|
||||
rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
|
||||
EXTEND_PCR_RESULT_BODY_SIZE, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, EXTEND_PCR_RESULT_SIZE,
|
||||
EXTEND_PCR_RESULT_BODY_SIZE, 0,
|
||||
"extending dummy pcr before suspend");
|
||||
}
|
||||
|
||||
/* now do the actual savestate */
|
||||
for (try = 0; try < TPM_RETRY; try++) {
|
||||
cmd.header.in = savestate_header;
|
||||
rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, 0,
|
||||
0, NULL);
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, SAVESTATE_RESULT_SIZE,
|
||||
0, 0, NULL);
|
||||
|
||||
/*
|
||||
* If the TPM indicates that it is too busy to respond to
|
||||
@ -1114,7 +1187,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
|
||||
tpm_cmd.header.in = tpm_getrandom_header;
|
||||
tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes);
|
||||
|
||||
err = tpm_transmit_cmd(chip, &tpm_cmd,
|
||||
err = tpm_transmit_cmd(chip, NULL, &tpm_cmd,
|
||||
TPM_GETRANDOM_RESULT_SIZE + num_bytes,
|
||||
offsetof(struct tpm_getrandom_out,
|
||||
rng_data),
|
||||
@ -1205,9 +1278,17 @@ static int __init tpm_init(void)
|
||||
return PTR_ERR(tpm_class);
|
||||
}
|
||||
|
||||
rc = alloc_chrdev_region(&tpm_devt, 0, TPM_NUM_DEVICES, "tpm");
|
||||
tpmrm_class = class_create(THIS_MODULE, "tpmrm");
|
||||
if (IS_ERR(tpmrm_class)) {
|
||||
pr_err("couldn't create tpmrm class\n");
|
||||
class_destroy(tpm_class);
|
||||
return PTR_ERR(tpmrm_class);
|
||||
}
|
||||
|
||||
rc = alloc_chrdev_region(&tpm_devt, 0, 2*TPM_NUM_DEVICES, "tpm");
|
||||
if (rc < 0) {
|
||||
pr_err("tpm: failed to allocate char dev region\n");
|
||||
class_destroy(tpmrm_class);
|
||||
class_destroy(tpm_class);
|
||||
return rc;
|
||||
}
|
||||
@ -1219,7 +1300,8 @@ static void __exit tpm_exit(void)
|
||||
{
|
||||
idr_destroy(&dev_nums_idr);
|
||||
class_destroy(tpm_class);
|
||||
unregister_chrdev_region(tpm_devt, TPM_NUM_DEVICES);
|
||||
class_destroy(tpmrm_class);
|
||||
unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES);
|
||||
}
|
||||
|
||||
subsys_initcall(tpm_init);
|
||||
|
@ -40,7 +40,7 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
|
||||
struct tpm_chip *chip = to_tpm_chip(dev);
|
||||
|
||||
tpm_cmd.header.in = tpm_readpubek_header;
|
||||
err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
|
||||
err = tpm_transmit_cmd(chip, NULL, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
|
||||
READ_PUBEK_RESULT_MIN_BODY_SIZE, 0,
|
||||
"attempting to read the PUBEK");
|
||||
if (err)
|
||||
|
@ -89,10 +89,13 @@ enum tpm2_structures {
|
||||
};
|
||||
|
||||
enum tpm2_return_codes {
|
||||
TPM2_RC_SUCCESS = 0x0000,
|
||||
TPM2_RC_HASH = 0x0083, /* RC_FMT1 */
|
||||
TPM2_RC_HANDLE = 0x008B,
|
||||
TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */
|
||||
TPM2_RC_DISABLED = 0x0120,
|
||||
TPM2_RC_TESTING = 0x090A, /* RC_WARN */
|
||||
TPM2_RC_REFERENCE_H0 = 0x0910,
|
||||
};
|
||||
|
||||
enum tpm2_algorithms {
|
||||
@ -114,6 +117,8 @@ enum tpm2_command_codes {
|
||||
TPM2_CC_CREATE = 0x0153,
|
||||
TPM2_CC_LOAD = 0x0157,
|
||||
TPM2_CC_UNSEAL = 0x015E,
|
||||
TPM2_CC_CONTEXT_LOAD = 0x0161,
|
||||
TPM2_CC_CONTEXT_SAVE = 0x0162,
|
||||
TPM2_CC_FLUSH_CONTEXT = 0x0165,
|
||||
TPM2_CC_GET_CAPABILITY = 0x017A,
|
||||
TPM2_CC_GET_RANDOM = 0x017B,
|
||||
@ -127,21 +132,39 @@ enum tpm2_permanent_handles {
|
||||
};
|
||||
|
||||
enum tpm2_capabilities {
|
||||
TPM2_CAP_HANDLES = 1,
|
||||
TPM2_CAP_COMMANDS = 2,
|
||||
TPM2_CAP_PCRS = 5,
|
||||
TPM2_CAP_TPM_PROPERTIES = 6,
|
||||
};
|
||||
|
||||
enum tpm2_properties {
|
||||
TPM_PT_TOTAL_COMMANDS = 0x0129,
|
||||
};
|
||||
|
||||
enum tpm2_startup_types {
|
||||
TPM2_SU_CLEAR = 0x0000,
|
||||
TPM2_SU_STATE = 0x0001,
|
||||
};
|
||||
|
||||
enum tpm2_cc_attrs {
|
||||
TPM2_CC_ATTR_CHANDLES = 25,
|
||||
TPM2_CC_ATTR_RHANDLE = 28,
|
||||
};
|
||||
|
||||
#define TPM_VID_INTEL 0x8086
|
||||
#define TPM_VID_WINBOND 0x1050
|
||||
#define TPM_VID_STM 0x104A
|
||||
|
||||
#define TPM_PPI_VERSION_LEN 3
|
||||
|
||||
struct tpm_space {
|
||||
u32 context_tbl[3];
|
||||
u8 *context_buf;
|
||||
u32 session_tbl[3];
|
||||
u8 *session_buf;
|
||||
};
|
||||
|
||||
enum tpm_chip_flags {
|
||||
TPM_CHIP_FLAG_TPM2 = BIT(1),
|
||||
TPM_CHIP_FLAG_IRQ = BIT(2),
|
||||
@ -161,7 +184,9 @@ struct tpm_chip_seqops {
|
||||
|
||||
struct tpm_chip {
|
||||
struct device dev;
|
||||
struct device devs;
|
||||
struct cdev cdev;
|
||||
struct cdev cdevs;
|
||||
|
||||
/* A driver callback under ops cannot be run unless ops_sem is held
|
||||
* (sometimes implicitly, eg for the sysfs code). ops becomes null
|
||||
@ -199,6 +224,13 @@ struct tpm_chip {
|
||||
acpi_handle acpi_dev_handle;
|
||||
char ppi_version[TPM_PPI_VERSION_LEN + 1];
|
||||
#endif /* CONFIG_ACPI */
|
||||
|
||||
struct tpm_space work_space;
|
||||
u32 nr_commands;
|
||||
u32 *cc_attrs_tbl;
|
||||
|
||||
/* active locality */
|
||||
int locality;
|
||||
};
|
||||
|
||||
#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
|
||||
@ -485,18 +517,21 @@ static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
|
||||
}
|
||||
|
||||
extern struct class *tpm_class;
|
||||
extern struct class *tpmrm_class;
|
||||
extern dev_t tpm_devt;
|
||||
extern const struct file_operations tpm_fops;
|
||||
extern const struct file_operations tpmrm_fops;
|
||||
extern struct idr dev_nums_idr;
|
||||
|
||||
enum tpm_transmit_flags {
|
||||
TPM_TRANSMIT_UNLOCKED = BIT(0),
|
||||
};
|
||||
|
||||
ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
|
||||
unsigned int flags);
|
||||
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *buf, size_t bufsiz,
|
||||
size_t min_rsp_body_len, unsigned int flags,
|
||||
ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
|
||||
u8 *buf, size_t bufsiz, unsigned int flags);
|
||||
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
|
||||
const void *buf, size_t bufsiz,
|
||||
size_t min_rsp_body_length, unsigned int flags,
|
||||
const char *desc);
|
||||
ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
|
||||
const char *desc, size_t min_cap_length);
|
||||
@ -541,6 +576,8 @@ int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
|
||||
int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, u32 count,
|
||||
struct tpm2_digest *digests);
|
||||
int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max);
|
||||
void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
|
||||
unsigned int flags);
|
||||
int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options);
|
||||
@ -554,4 +591,11 @@ int tpm2_auto_startup(struct tpm_chip *chip);
|
||||
void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
|
||||
unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
|
||||
int tpm2_probe(struct tpm_chip *chip);
|
||||
int tpm2_find_cc(struct tpm_chip *chip, u32 cc);
|
||||
int tpm2_init_space(struct tpm_space *space);
|
||||
void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space);
|
||||
int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
|
||||
u8 *cmd);
|
||||
int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
|
||||
u32 cc, u8 *buf, size_t *bufsiz);
|
||||
#endif
|
||||
|
@ -266,7 +266,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
|
||||
sizeof(cmd.params.pcrread_in.pcr_select));
|
||||
cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd),
|
||||
TPM2_PCR_READ_RESP_BODY_SIZE,
|
||||
0, "attempting to read a pcr value");
|
||||
if (rc == 0) {
|
||||
@ -333,7 +333,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, u32 count,
|
||||
}
|
||||
}
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
|
||||
"attempting extend a PCR value");
|
||||
|
||||
tpm_buf_destroy(&buf);
|
||||
@ -382,7 +382,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
|
||||
cmd.header.in = tpm2_getrandom_header;
|
||||
cmd.params.getrandom_in.size = cpu_to_be16(num_bytes);
|
||||
|
||||
err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
|
||||
err = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd),
|
||||
offsetof(struct tpm2_get_random_out,
|
||||
buffer),
|
||||
0, "attempting get random");
|
||||
@ -418,6 +418,35 @@ static const struct tpm_input_header tpm2_get_tpm_pt_header = {
|
||||
.ordinal = cpu_to_be32(TPM2_CC_GET_CAPABILITY)
|
||||
};
|
||||
|
||||
/**
|
||||
* tpm2_flush_context_cmd() - execute a TPM2_FlushContext command
|
||||
* @chip: TPM chip to use
|
||||
* @payload: the key data in clear and encrypted form
|
||||
* @options: authentication values and other options
|
||||
*
|
||||
* Return: same as with tpm_transmit_cmd
|
||||
*/
|
||||
void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct tpm_buf buf;
|
||||
int rc;
|
||||
|
||||
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
|
||||
if (rc) {
|
||||
dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
|
||||
handle);
|
||||
return;
|
||||
}
|
||||
|
||||
tpm_buf_append_u32(&buf, handle);
|
||||
|
||||
(void) tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, flags,
|
||||
"flushing context");
|
||||
|
||||
tpm_buf_destroy(&buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
|
||||
*
|
||||
@ -528,7 +557,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 4, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, 0,
|
||||
"sealing data");
|
||||
if (rc)
|
||||
goto out;
|
||||
@ -612,7 +641,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 4, flags,
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, flags,
|
||||
"loading blob");
|
||||
if (!rc)
|
||||
*blob_handle = be32_to_cpup(
|
||||
@ -627,39 +656,6 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_flush_context_cmd() - execute a TPM2_FlushContext command
|
||||
*
|
||||
* @chip: TPM chip to use
|
||||
* @handle: the key data in clear and encrypted form
|
||||
* @flags: tpm transmit flags
|
||||
*
|
||||
* Return: Same as with tpm_transmit_cmd.
|
||||
*/
|
||||
static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct tpm_buf buf;
|
||||
int rc;
|
||||
|
||||
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
|
||||
if (rc) {
|
||||
dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
|
||||
handle);
|
||||
return;
|
||||
}
|
||||
|
||||
tpm_buf_append_u32(&buf, handle);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, flags,
|
||||
"flushing context");
|
||||
if (rc)
|
||||
dev_warn(&chip->dev, "0x%08x was not flushed, rc=%d\n", handle,
|
||||
rc);
|
||||
|
||||
tpm_buf_destroy(&buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_unseal_cmd() - execute a TPM2_Unload command
|
||||
*
|
||||
@ -697,7 +693,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
|
||||
options->blobauth /* hmac */,
|
||||
TPM_DIGEST_SIZE);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 6, flags,
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 6, flags,
|
||||
"unsealing");
|
||||
if (rc > 0)
|
||||
rc = -EPERM;
|
||||
@ -774,7 +770,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
|
||||
cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(property_id);
|
||||
cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd),
|
||||
TPM2_GET_TPM_PT_OUT_BODY_SIZE, 0, desc);
|
||||
if (!rc)
|
||||
*value = be32_to_cpu(cmd.params.get_tpm_pt_out.value);
|
||||
@ -809,7 +805,7 @@ static int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
|
||||
cmd.header.in = tpm2_startup_header;
|
||||
|
||||
cmd.params.startup_in.startup_type = cpu_to_be16(startup_type);
|
||||
return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0,
|
||||
return tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0,
|
||||
"attempting to start the TPM");
|
||||
}
|
||||
|
||||
@ -838,7 +834,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
|
||||
cmd.header.in = tpm2_shutdown_header;
|
||||
cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0,
|
||||
"stopping the TPM");
|
||||
|
||||
/* In places where shutdown command is sent there's no much we can do
|
||||
@ -902,7 +898,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
|
||||
cmd.header.in = tpm2_selftest_header;
|
||||
cmd.params.selftest_in.full_test = full;
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, 0, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, TPM2_SELF_TEST_IN_SIZE, 0, 0,
|
||||
"continue selftest");
|
||||
|
||||
/* At least some prototype chips seem to give RC_TESTING error
|
||||
@ -953,7 +949,8 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
|
||||
cmd.params.pcrread_in.pcr_select[1] = 0x00;
|
||||
cmd.params.pcrread_in.pcr_select[2] = 0x00;
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0, NULL);
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0,
|
||||
NULL);
|
||||
if (rc < 0)
|
||||
break;
|
||||
|
||||
@ -986,7 +983,7 @@ int tpm2_probe(struct tpm_chip *chip)
|
||||
cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(0x100);
|
||||
cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0, NULL);
|
||||
rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0, NULL);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
@ -1024,7 +1021,7 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
|
||||
tpm_buf_append_u32(&buf, 0);
|
||||
tpm_buf_append_u32(&buf, 1);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 9, 0,
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 9, 0,
|
||||
"get tpm pcr allocation");
|
||||
if (rc)
|
||||
goto out;
|
||||
@ -1067,15 +1064,76 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_buf buf;
|
||||
u32 nr_commands;
|
||||
u32 *attrs;
|
||||
u32 cc;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
if (nr_commands > 0xFFFFF) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
chip->cc_attrs_tbl = devm_kzalloc(&chip->dev, 4 * nr_commands,
|
||||
GFP_KERNEL);
|
||||
|
||||
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS);
|
||||
tpm_buf_append_u32(&buf, TPM2_CC_FIRST);
|
||||
tpm_buf_append_u32(&buf, nr_commands);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE,
|
||||
9 + 4 * nr_commands, 0, NULL);
|
||||
if (rc) {
|
||||
tpm_buf_destroy(&buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (nr_commands !=
|
||||
be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) {
|
||||
tpm_buf_destroy(&buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
chip->nr_commands = nr_commands;
|
||||
|
||||
attrs = (u32 *)&buf.data[TPM_HEADER_SIZE + 9];
|
||||
for (i = 0; i < nr_commands; i++, attrs++) {
|
||||
chip->cc_attrs_tbl[i] = be32_to_cpup(attrs);
|
||||
cc = chip->cc_attrs_tbl[i] & 0xFFFF;
|
||||
|
||||
if (cc == TPM2_CC_CONTEXT_SAVE || cc == TPM2_CC_FLUSH_CONTEXT) {
|
||||
chip->cc_attrs_tbl[i] &=
|
||||
~(GENMASK(2, 0) << TPM2_CC_ATTR_CHANDLES);
|
||||
chip->cc_attrs_tbl[i] |= 1 << TPM2_CC_ATTR_CHANDLES;
|
||||
}
|
||||
}
|
||||
|
||||
tpm_buf_destroy(&buf);
|
||||
|
||||
out:
|
||||
if (rc > 0)
|
||||
rc = -ENODEV;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm2_auto_startup - Perform the standard automatic TPM initialization
|
||||
* sequence
|
||||
* @chip: TPM chip to use
|
||||
*
|
||||
* Initializes timeout values for operation and command durations, conducts
|
||||
* a self-test and reads the list of active PCR banks.
|
||||
*
|
||||
* Return: 0 on success. Otherwise, a system error code is returned.
|
||||
* Returns 0 on success, < 0 in case of fatal error.
|
||||
*/
|
||||
int tpm2_auto_startup(struct tpm_chip *chip)
|
||||
{
|
||||
@ -1104,9 +1162,24 @@ int tpm2_auto_startup(struct tpm_chip *chip)
|
||||
}
|
||||
|
||||
rc = tpm2_get_pcr_allocation(chip);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = tpm2_get_cc_attrs_tbl(chip);
|
||||
|
||||
out:
|
||||
if (rc > 0)
|
||||
rc = -ENODEV;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tpm2_find_cc(struct tpm_chip *chip, u32 cc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chip->nr_commands; i++)
|
||||
if (cc == (chip->cc_attrs_tbl[i] & GENMASK(15, 0)))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
528
drivers/char/tpm/tpm2-space.c
Normal file
528
drivers/char/tpm/tpm2-space.c
Normal file
@ -0,0 +1,528 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Intel Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* This file contains TPM2 protocol implementations of the commands
|
||||
* used by the kernel internally.
|
||||
*
|
||||
* 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/gfp.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "tpm.h"
|
||||
|
||||
enum tpm2_handle_types {
|
||||
TPM2_HT_HMAC_SESSION = 0x02000000,
|
||||
TPM2_HT_POLICY_SESSION = 0x03000000,
|
||||
TPM2_HT_TRANSIENT = 0x80000000,
|
||||
};
|
||||
|
||||
struct tpm2_context {
|
||||
__be64 sequence;
|
||||
__be32 saved_handle;
|
||||
__be32 hierarchy;
|
||||
__be16 blob_size;
|
||||
} __packed;
|
||||
|
||||
static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
|
||||
if (space->session_tbl[i])
|
||||
tpm2_flush_context_cmd(chip, space->session_tbl[i],
|
||||
TPM_TRANSMIT_UNLOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
int tpm2_init_space(struct tpm_space *space)
|
||||
{
|
||||
space->context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!space->context_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
space->session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (space->session_buf == NULL) {
|
||||
kfree(space->context_buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
|
||||
{
|
||||
mutex_lock(&chip->tpm_mutex);
|
||||
tpm2_flush_sessions(chip, space);
|
||||
mutex_unlock(&chip->tpm_mutex);
|
||||
kfree(space->context_buf);
|
||||
kfree(space->session_buf);
|
||||
}
|
||||
|
||||
static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
|
||||
unsigned int *offset, u32 *handle)
|
||||
{
|
||||
struct tpm_buf tbuf;
|
||||
struct tpm2_context *ctx;
|
||||
unsigned int body_size;
|
||||
int rc;
|
||||
|
||||
rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
ctx = (struct tpm2_context *)&buf[*offset];
|
||||
body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
|
||||
tpm_buf_append(&tbuf, &buf[*offset], body_size);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4,
|
||||
TPM_TRANSMIT_UNLOCKED, NULL);
|
||||
if (rc < 0) {
|
||||
dev_warn(&chip->dev, "%s: failed with a system error %d\n",
|
||||
__func__, rc);
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -EFAULT;
|
||||
} else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
|
||||
rc == TPM2_RC_REFERENCE_H0) {
|
||||
/*
|
||||
* TPM_RC_HANDLE means that the session context can't
|
||||
* be loaded because of an internal counter mismatch
|
||||
* that makes the TPM think there might have been a
|
||||
* replay. This might happen if the context was saved
|
||||
* and loaded outside the space.
|
||||
*
|
||||
* TPM_RC_REFERENCE_H0 means the session has been
|
||||
* flushed outside the space
|
||||
*/
|
||||
rc = -ENOENT;
|
||||
tpm_buf_destroy(&tbuf);
|
||||
} else if (rc > 0) {
|
||||
dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
|
||||
__func__, rc);
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
|
||||
*offset += body_size;
|
||||
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
|
||||
unsigned int buf_size, unsigned int *offset)
|
||||
{
|
||||
struct tpm_buf tbuf;
|
||||
unsigned int body_size;
|
||||
int rc;
|
||||
|
||||
rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
tpm_buf_append_u32(&tbuf, handle);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0,
|
||||
TPM_TRANSMIT_UNLOCKED, NULL);
|
||||
if (rc < 0) {
|
||||
dev_warn(&chip->dev, "%s: failed with a system error %d\n",
|
||||
__func__, rc);
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -EFAULT;
|
||||
} else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -ENOENT;
|
||||
} else if (rc) {
|
||||
dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
|
||||
__func__, rc);
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
|
||||
if ((*offset + body_size) > buf_size) {
|
||||
dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
|
||||
*offset += body_size;
|
||||
tpm_buf_destroy(&tbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpm2_flush_space(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)
|
||||
if (space->context_tbl[i] && ~space->context_tbl[i])
|
||||
tpm2_flush_context_cmd(chip, space->context_tbl[i],
|
||||
TPM_TRANSMIT_UNLOCKED);
|
||||
|
||||
tpm2_flush_sessions(chip, space);
|
||||
}
|
||||
|
||||
static int tpm2_load_space(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
unsigned int offset;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
|
||||
if (!space->context_tbl[i])
|
||||
continue;
|
||||
|
||||
/* sanity check, should never happen */
|
||||
if (~space->context_tbl[i]) {
|
||||
dev_err(&chip->dev, "context table is inconsistent");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
rc = tpm2_load_context(chip, space->context_buf, &offset,
|
||||
&space->context_tbl[i]);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
|
||||
u32 handle;
|
||||
|
||||
if (!space->session_tbl[i])
|
||||
continue;
|
||||
|
||||
rc = tpm2_load_context(chip, space->session_buf,
|
||||
&offset, &handle);
|
||||
if (rc == -ENOENT) {
|
||||
/* load failed, just forget session */
|
||||
space->session_tbl[i] = 0;
|
||||
} else if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
if (handle != space->session_tbl[i]) {
|
||||
dev_warn(&chip->dev, "session restored to wrong handle\n");
|
||||
tpm2_flush_space(chip);
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)
|
||||
{
|
||||
u32 vhandle = be32_to_cpup((__be32 *)handle);
|
||||
u32 phandle;
|
||||
int i;
|
||||
|
||||
i = 0xFFFFFF - (vhandle & 0xFFFFFF);
|
||||
if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])
|
||||
return false;
|
||||
|
||||
phandle = space->context_tbl[i];
|
||||
*((__be32 *)handle) = cpu_to_be32(phandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
unsigned int nr_handles;
|
||||
u32 attrs;
|
||||
u32 *handle;
|
||||
int i;
|
||||
|
||||
i = tpm2_find_cc(chip, cc);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
|
||||
attrs = chip->cc_attrs_tbl[i];
|
||||
nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
|
||||
|
||||
handle = (u32 *)&cmd[TPM_HEADER_SIZE];
|
||||
for (i = 0; i < nr_handles; i++, handle++) {
|
||||
if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) {
|
||||
if (!tpm2_map_to_phandle(space, handle))
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc,
|
||||
u8 *cmd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!space)
|
||||
return 0;
|
||||
|
||||
memcpy(&chip->work_space.context_tbl, &space->context_tbl,
|
||||
sizeof(space->context_tbl));
|
||||
memcpy(&chip->work_space.session_tbl, &space->session_tbl,
|
||||
sizeof(space->session_tbl));
|
||||
memcpy(chip->work_space.context_buf, space->context_buf, PAGE_SIZE);
|
||||
memcpy(chip->work_space.session_buf, space->session_buf, PAGE_SIZE);
|
||||
|
||||
rc = tpm2_load_space(chip);
|
||||
if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = tpm2_map_command(chip, cc, cmd);
|
||||
if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)
|
||||
if (space->session_tbl[i] == 0)
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(space->session_tbl))
|
||||
return false;
|
||||
|
||||
space->session_tbl[i] = handle;
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
|
||||
if (alloc) {
|
||||
if (!space->context_tbl[i]) {
|
||||
space->context_tbl[i] = phandle;
|
||||
break;
|
||||
}
|
||||
} else if (space->context_tbl[i] == phandle)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(space->context_tbl))
|
||||
return 0;
|
||||
|
||||
return TPM2_HT_TRANSIENT | (0xFFFFFF - i);
|
||||
}
|
||||
|
||||
static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,
|
||||
size_t len)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
struct tpm_output_header *header = (void *)rsp;
|
||||
u32 phandle;
|
||||
u32 phandle_type;
|
||||
u32 vhandle;
|
||||
u32 attrs;
|
||||
int i;
|
||||
|
||||
if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS)
|
||||
return 0;
|
||||
|
||||
i = tpm2_find_cc(chip, cc);
|
||||
/* sanity check, should never happen */
|
||||
if (i < 0)
|
||||
return -EFAULT;
|
||||
|
||||
attrs = chip->cc_attrs_tbl[i];
|
||||
if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1))
|
||||
return 0;
|
||||
|
||||
phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);
|
||||
phandle_type = phandle & 0xFF000000;
|
||||
|
||||
switch (phandle_type) {
|
||||
case TPM2_HT_TRANSIENT:
|
||||
vhandle = tpm2_map_to_vhandle(space, phandle, true);
|
||||
if (!vhandle)
|
||||
goto out_no_slots;
|
||||
|
||||
*(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);
|
||||
break;
|
||||
case TPM2_HT_HMAC_SESSION:
|
||||
case TPM2_HT_POLICY_SESSION:
|
||||
if (!tpm2_add_session(chip, phandle))
|
||||
goto out_no_slots;
|
||||
break;
|
||||
default:
|
||||
dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",
|
||||
__func__, phandle);
|
||||
break;
|
||||
};
|
||||
|
||||
return 0;
|
||||
out_no_slots:
|
||||
tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_UNLOCKED);
|
||||
dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,
|
||||
phandle);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
struct tpm2_cap_handles {
|
||||
u8 more_data;
|
||||
__be32 capability;
|
||||
__be32 count;
|
||||
__be32 handles[];
|
||||
} __packed;
|
||||
|
||||
static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,
|
||||
size_t len)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
struct tpm_output_header *header = (void *)rsp;
|
||||
struct tpm2_cap_handles *data;
|
||||
u32 phandle;
|
||||
u32 phandle_type;
|
||||
u32 vhandle;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
if (cc != TPM2_CC_GET_CAPABILITY ||
|
||||
be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len < TPM_HEADER_SIZE + 9)
|
||||
return -EFAULT;
|
||||
|
||||
data = (void *)&rsp[TPM_HEADER_SIZE];
|
||||
if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES)
|
||||
return 0;
|
||||
|
||||
if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count))
|
||||
return -EFAULT;
|
||||
|
||||
for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) {
|
||||
phandle = be32_to_cpup((__be32 *)&data->handles[i]);
|
||||
phandle_type = phandle & 0xFF000000;
|
||||
|
||||
switch (phandle_type) {
|
||||
case TPM2_HT_TRANSIENT:
|
||||
vhandle = tpm2_map_to_vhandle(space, phandle, false);
|
||||
if (!vhandle)
|
||||
break;
|
||||
|
||||
data->handles[j] = cpu_to_be32(vhandle);
|
||||
j++;
|
||||
break;
|
||||
|
||||
default:
|
||||
data->handles[j] = cpu_to_be32(phandle);
|
||||
j++;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j);
|
||||
data->count = cpu_to_be32(j);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm2_save_space(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_space *space = &chip->work_space;
|
||||
unsigned int offset;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {
|
||||
if (!(space->context_tbl[i] && ~space->context_tbl[i]))
|
||||
continue;
|
||||
|
||||
rc = tpm2_save_context(chip, space->context_tbl[i],
|
||||
space->context_buf, PAGE_SIZE,
|
||||
&offset);
|
||||
if (rc == -ENOENT) {
|
||||
space->context_tbl[i] = 0;
|
||||
continue;
|
||||
} else if (rc)
|
||||
return rc;
|
||||
|
||||
tpm2_flush_context_cmd(chip, space->context_tbl[i],
|
||||
TPM_TRANSMIT_UNLOCKED);
|
||||
space->context_tbl[i] = ~0;
|
||||
}
|
||||
|
||||
for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {
|
||||
if (!space->session_tbl[i])
|
||||
continue;
|
||||
|
||||
rc = tpm2_save_context(chip, space->session_tbl[i],
|
||||
space->session_buf, PAGE_SIZE,
|
||||
&offset);
|
||||
|
||||
if (rc == -ENOENT) {
|
||||
/* handle error saving session, just forget it */
|
||||
space->session_tbl[i] = 0;
|
||||
} else if (rc < 0) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,
|
||||
u32 cc, u8 *buf, size_t *bufsiz)
|
||||
{
|
||||
struct tpm_output_header *header = (void *)buf;
|
||||
int rc;
|
||||
|
||||
if (!space)
|
||||
return 0;
|
||||
|
||||
rc = tpm2_map_response_header(chip, cc, buf, *bufsiz);
|
||||
if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = tpm2_map_response_body(chip, cc, buf, *bufsiz);
|
||||
if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = tpm2_save_space(chip);
|
||||
if (rc) {
|
||||
tpm2_flush_space(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
*bufsiz = be32_to_cpu(header->length);
|
||||
|
||||
memcpy(&space->context_tbl, &chip->work_space.context_tbl,
|
||||
sizeof(space->context_tbl));
|
||||
memcpy(&space->session_tbl, &chip->work_space.session_tbl,
|
||||
sizeof(space->session_tbl));
|
||||
memcpy(space->context_buf, chip->work_space.context_buf, PAGE_SIZE);
|
||||
memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
@ -56,18 +56,24 @@ static int calc_tpm2_event_size(struct tcg_pcr_event2 *event,
|
||||
|
||||
efispecid = (struct tcg_efi_specid_event *)event_header->event;
|
||||
|
||||
for (i = 0; (i < event->count) && (i < TPM2_ACTIVE_PCR_BANKS);
|
||||
i++) {
|
||||
/* Check if event is malformed. */
|
||||
if (event->count > efispecid->num_algs)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < event->count; i++) {
|
||||
halg_size = sizeof(event->digests[i].alg_id);
|
||||
memcpy(&halg, marker, halg_size);
|
||||
marker = marker + halg_size;
|
||||
for (j = 0; (j < efispecid->num_algs); j++) {
|
||||
for (j = 0; j < efispecid->num_algs; j++) {
|
||||
if (halg == efispecid->digest_sizes[j].alg_id) {
|
||||
marker = marker +
|
||||
marker +=
|
||||
efispecid->digest_sizes[j].digest_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Algorithm without known length. Such event is unparseable. */
|
||||
if (j == efispecid->num_algs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
event_field = (struct tcg_event_field *)marker;
|
||||
|
@ -20,6 +20,9 @@
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#ifdef CONFIG_ARM64
|
||||
#include <linux/arm-smccc.h>
|
||||
#endif
|
||||
#include "tpm.h"
|
||||
|
||||
#define ACPI_SIG_TPM2 "TPM2"
|
||||
@ -34,6 +37,16 @@ enum crb_defaults {
|
||||
CRB_ACPI_START_INDEX = 1,
|
||||
};
|
||||
|
||||
enum crb_loc_ctrl {
|
||||
CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0),
|
||||
CRB_LOC_CTRL_RELINQUISH = BIT(1),
|
||||
};
|
||||
|
||||
enum crb_loc_state {
|
||||
CRB_LOC_STATE_LOC_ASSIGNED = BIT(1),
|
||||
CRB_LOC_STATE_TPM_REG_VALID_STS = BIT(7),
|
||||
};
|
||||
|
||||
enum crb_ctrl_req {
|
||||
CRB_CTRL_REQ_CMD_READY = BIT(0),
|
||||
CRB_CTRL_REQ_GO_IDLE = BIT(1),
|
||||
@ -52,18 +65,28 @@ enum crb_cancel {
|
||||
CRB_CANCEL_INVOKE = BIT(0),
|
||||
};
|
||||
|
||||
struct crb_control_area {
|
||||
u32 req;
|
||||
u32 sts;
|
||||
u32 cancel;
|
||||
u32 start;
|
||||
u32 int_enable;
|
||||
u32 int_sts;
|
||||
u32 cmd_size;
|
||||
u32 cmd_pa_low;
|
||||
u32 cmd_pa_high;
|
||||
u32 rsp_size;
|
||||
u64 rsp_pa;
|
||||
struct crb_regs_head {
|
||||
u32 loc_state;
|
||||
u32 reserved1;
|
||||
u32 loc_ctrl;
|
||||
u32 loc_sts;
|
||||
u8 reserved2[32];
|
||||
u64 intf_id;
|
||||
u64 ctrl_ext;
|
||||
} __packed;
|
||||
|
||||
struct crb_regs_tail {
|
||||
u32 ctrl_req;
|
||||
u32 ctrl_sts;
|
||||
u32 ctrl_cancel;
|
||||
u32 ctrl_start;
|
||||
u32 ctrl_int_enable;
|
||||
u32 ctrl_int_sts;
|
||||
u32 ctrl_cmd_size;
|
||||
u32 ctrl_cmd_pa_low;
|
||||
u32 ctrl_cmd_pa_high;
|
||||
u32 ctrl_rsp_size;
|
||||
u64 ctrl_rsp_pa;
|
||||
} __packed;
|
||||
|
||||
enum crb_status {
|
||||
@ -73,15 +96,26 @@ enum crb_status {
|
||||
enum crb_flags {
|
||||
CRB_FL_ACPI_START = BIT(0),
|
||||
CRB_FL_CRB_START = BIT(1),
|
||||
CRB_FL_CRB_SMC_START = BIT(2),
|
||||
};
|
||||
|
||||
struct crb_priv {
|
||||
unsigned int flags;
|
||||
void __iomem *iobase;
|
||||
struct crb_control_area __iomem *cca;
|
||||
struct crb_regs_head __iomem *regs_h;
|
||||
struct crb_regs_tail __iomem *regs_t;
|
||||
u8 __iomem *cmd;
|
||||
u8 __iomem *rsp;
|
||||
u32 cmd_size;
|
||||
u32 smc_func_id;
|
||||
};
|
||||
|
||||
struct tpm2_crb_smc {
|
||||
u32 interrupt;
|
||||
u8 interrupt_flags;
|
||||
u8 op_flags;
|
||||
u16 reserved2;
|
||||
u32 smc_func_id;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -101,15 +135,35 @@ struct crb_priv {
|
||||
*/
|
||||
static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
|
||||
{
|
||||
if (priv->flags & CRB_FL_ACPI_START)
|
||||
if ((priv->flags & CRB_FL_ACPI_START) ||
|
||||
(priv->flags & CRB_FL_CRB_SMC_START))
|
||||
return 0;
|
||||
|
||||
iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->cca->req);
|
||||
iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req);
|
||||
/* we don't really care when this settles */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
|
||||
unsigned long timeout)
|
||||
{
|
||||
ktime_t start;
|
||||
ktime_t stop;
|
||||
|
||||
start = ktime_get();
|
||||
stop = ktime_add(start, ms_to_ktime(timeout));
|
||||
|
||||
do {
|
||||
if ((ioread32(reg) & mask) == value)
|
||||
return true;
|
||||
|
||||
usleep_range(50, 100);
|
||||
} while (ktime_before(ktime_get(), stop));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* crb_cmd_ready - request tpm crb device to enter ready state
|
||||
*
|
||||
@ -127,22 +181,15 @@ static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
|
||||
static int __maybe_unused crb_cmd_ready(struct device *dev,
|
||||
struct crb_priv *priv)
|
||||
{
|
||||
ktime_t stop, start;
|
||||
|
||||
if (priv->flags & CRB_FL_ACPI_START)
|
||||
if ((priv->flags & CRB_FL_ACPI_START) ||
|
||||
(priv->flags & CRB_FL_CRB_SMC_START))
|
||||
return 0;
|
||||
|
||||
iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->cca->req);
|
||||
|
||||
start = ktime_get();
|
||||
stop = ktime_add(start, ms_to_ktime(TPM2_TIMEOUT_C));
|
||||
do {
|
||||
if (!(ioread32(&priv->cca->req) & CRB_CTRL_REQ_CMD_READY))
|
||||
return 0;
|
||||
usleep_range(50, 100);
|
||||
} while (ktime_before(ktime_get(), stop));
|
||||
|
||||
if (ioread32(&priv->cca->req) & CRB_CTRL_REQ_CMD_READY) {
|
||||
iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->regs_t->ctrl_req);
|
||||
if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req,
|
||||
CRB_CTRL_REQ_CMD_READY /* mask */,
|
||||
0, /* value */
|
||||
TPM2_TIMEOUT_C)) {
|
||||
dev_warn(dev, "cmdReady timed out\n");
|
||||
return -ETIME;
|
||||
}
|
||||
@ -150,12 +197,41 @@ static int __maybe_unused crb_cmd_ready(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int crb_request_locality(struct tpm_chip *chip, int loc)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
u32 value = CRB_LOC_STATE_LOC_ASSIGNED |
|
||||
CRB_LOC_STATE_TPM_REG_VALID_STS;
|
||||
|
||||
if (!priv->regs_h)
|
||||
return 0;
|
||||
|
||||
iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl);
|
||||
if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value,
|
||||
TPM2_TIMEOUT_C)) {
|
||||
dev_warn(&chip->dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void crb_relinquish_locality(struct tpm_chip *chip, int loc)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
|
||||
if (!priv->regs_h)
|
||||
return;
|
||||
|
||||
iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl);
|
||||
}
|
||||
|
||||
static u8 crb_status(struct tpm_chip *chip)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
u8 sts = 0;
|
||||
|
||||
if ((ioread32(&priv->cca->start) & CRB_START_INVOKE) !=
|
||||
if ((ioread32(&priv->regs_t->ctrl_start) & CRB_START_INVOKE) !=
|
||||
CRB_START_INVOKE)
|
||||
sts |= CRB_DRV_STS_COMPLETE;
|
||||
|
||||
@ -171,13 +247,12 @@ static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
if (count < 6)
|
||||
return -EIO;
|
||||
|
||||
if (ioread32(&priv->cca->sts) & CRB_CTRL_STS_ERROR)
|
||||
if (ioread32(&priv->regs_t->ctrl_sts) & CRB_CTRL_STS_ERROR)
|
||||
return -EIO;
|
||||
|
||||
memcpy_fromio(buf, priv->rsp, 6);
|
||||
expected = be32_to_cpup((__be32 *) &buf[2]);
|
||||
|
||||
if (expected > count)
|
||||
if (expected > count || expected < 6)
|
||||
return -EIO;
|
||||
|
||||
memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6);
|
||||
@ -202,6 +277,34 @@ static int crb_do_acpi_start(struct tpm_chip *chip)
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM64
|
||||
/*
|
||||
* This is a TPM Command Response Buffer start method that invokes a
|
||||
* Secure Monitor Call to requrest the firmware to execute or cancel
|
||||
* a TPM 2.0 command.
|
||||
*/
|
||||
static int tpm_crb_smc_start(struct device *dev, unsigned long func_id)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
arm_smccc_smc(func_id, 0, 0, 0, 0, 0, 0, 0, &res);
|
||||
if (res.a0 != 0) {
|
||||
dev_err(dev,
|
||||
FW_BUG "tpm_crb_smc_start() returns res.a0 = 0x%lx\n",
|
||||
res.a0);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int tpm_crb_smc_start(struct device *dev, unsigned long func_id)
|
||||
{
|
||||
dev_err(dev, FW_BUG "tpm_crb: incorrect start method\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
@ -210,7 +313,7 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
/* Zero the cancel register so that the next command will not get
|
||||
* canceled.
|
||||
*/
|
||||
iowrite32(0, &priv->cca->cancel);
|
||||
iowrite32(0, &priv->regs_t->ctrl_cancel);
|
||||
|
||||
if (len > priv->cmd_size) {
|
||||
dev_err(&chip->dev, "invalid command count value %zd %d\n",
|
||||
@ -224,11 +327,16 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
wmb();
|
||||
|
||||
if (priv->flags & CRB_FL_CRB_START)
|
||||
iowrite32(CRB_START_INVOKE, &priv->cca->start);
|
||||
iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start);
|
||||
|
||||
if (priv->flags & CRB_FL_ACPI_START)
|
||||
rc = crb_do_acpi_start(chip);
|
||||
|
||||
if (priv->flags & CRB_FL_CRB_SMC_START) {
|
||||
iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start);
|
||||
rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -236,7 +344,7 @@ static void crb_cancel(struct tpm_chip *chip)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
|
||||
iowrite32(CRB_CANCEL_INVOKE, &priv->cca->cancel);
|
||||
iowrite32(CRB_CANCEL_INVOKE, &priv->regs_t->ctrl_cancel);
|
||||
|
||||
if ((priv->flags & CRB_FL_ACPI_START) && crb_do_acpi_start(chip))
|
||||
dev_err(&chip->dev, "ACPI Start failed\n");
|
||||
@ -245,7 +353,7 @@ static void crb_cancel(struct tpm_chip *chip)
|
||||
static bool crb_req_canceled(struct tpm_chip *chip, u8 status)
|
||||
{
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
u32 cancel = ioread32(&priv->cca->cancel);
|
||||
u32 cancel = ioread32(&priv->regs_t->ctrl_cancel);
|
||||
|
||||
return (cancel & CRB_CANCEL_INVOKE) == CRB_CANCEL_INVOKE;
|
||||
}
|
||||
@ -257,6 +365,8 @@ static const struct tpm_class_ops tpm_crb = {
|
||||
.send = crb_send,
|
||||
.cancel = crb_cancel,
|
||||
.req_canceled = crb_req_canceled,
|
||||
.request_locality = crb_request_locality,
|
||||
.relinquish_locality = crb_relinquish_locality,
|
||||
.req_complete_mask = CRB_DRV_STS_COMPLETE,
|
||||
.req_complete_val = CRB_DRV_STS_COMPLETE,
|
||||
};
|
||||
@ -295,6 +405,27 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv,
|
||||
return priv->iobase + (new_res.start - io_res->start);
|
||||
}
|
||||
|
||||
/*
|
||||
* Work around broken BIOSs that return inconsistent values from the ACPI
|
||||
* region vs the registers. Trust the ACPI region. Such broken systems
|
||||
* probably cannot send large TPM commands since the buffer will be truncated.
|
||||
*/
|
||||
static u64 crb_fixup_cmd_size(struct device *dev, struct resource *io_res,
|
||||
u64 start, u64 size)
|
||||
{
|
||||
if (io_res->start > start || io_res->end < start)
|
||||
return size;
|
||||
|
||||
if (start + size - 1 <= io_res->end)
|
||||
return size;
|
||||
|
||||
dev_err(dev,
|
||||
FW_BUG "ACPI region does not cover the entire command/response buffer. %pr vs %llx %llx\n",
|
||||
io_res, start, size);
|
||||
|
||||
return io_res->end - start + 1;
|
||||
}
|
||||
|
||||
static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
struct acpi_table_tpm2 *buf)
|
||||
{
|
||||
@ -324,10 +455,22 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
if (IS_ERR(priv->iobase))
|
||||
return PTR_ERR(priv->iobase);
|
||||
|
||||
priv->cca = crb_map_res(dev, priv, &io_res, buf->control_address,
|
||||
sizeof(struct crb_control_area));
|
||||
if (IS_ERR(priv->cca))
|
||||
return PTR_ERR(priv->cca);
|
||||
/* The ACPI IO region starts at the head area and continues to include
|
||||
* the control area, as one nice sane region except for some older
|
||||
* stuff that puts the control area outside the ACPI IO region.
|
||||
*/
|
||||
if (!(priv->flags & CRB_FL_ACPI_START)) {
|
||||
if (buf->control_address == io_res.start +
|
||||
sizeof(*priv->regs_h))
|
||||
priv->regs_h = priv->iobase;
|
||||
else
|
||||
dev_warn(dev, FW_BUG "Bad ACPI memory layout");
|
||||
}
|
||||
|
||||
priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address,
|
||||
sizeof(struct crb_regs_tail));
|
||||
if (IS_ERR(priv->regs_t))
|
||||
return PTR_ERR(priv->regs_t);
|
||||
|
||||
/*
|
||||
* PTT HW bug w/a: wake up the device to access
|
||||
@ -337,10 +480,11 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pa_high = ioread32(&priv->cca->cmd_pa_high);
|
||||
pa_low = ioread32(&priv->cca->cmd_pa_low);
|
||||
pa_high = ioread32(&priv->regs_t->ctrl_cmd_pa_high);
|
||||
pa_low = ioread32(&priv->regs_t->ctrl_cmd_pa_low);
|
||||
cmd_pa = ((u64)pa_high << 32) | pa_low;
|
||||
cmd_size = ioread32(&priv->cca->cmd_size);
|
||||
cmd_size = crb_fixup_cmd_size(dev, &io_res, cmd_pa,
|
||||
ioread32(&priv->regs_t->ctrl_cmd_size));
|
||||
|
||||
dev_dbg(dev, "cmd_hi = %X cmd_low = %X cmd_size %X\n",
|
||||
pa_high, pa_low, cmd_size);
|
||||
@ -351,9 +495,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy_fromio(&rsp_pa, &priv->cca->rsp_pa, 8);
|
||||
memcpy_fromio(&rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8);
|
||||
rsp_pa = le64_to_cpu(rsp_pa);
|
||||
rsp_size = ioread32(&priv->cca->rsp_size);
|
||||
rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa,
|
||||
ioread32(&priv->regs_t->ctrl_rsp_size));
|
||||
|
||||
if (cmd_pa != rsp_pa) {
|
||||
priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size);
|
||||
@ -386,6 +531,7 @@ static int crb_acpi_add(struct acpi_device *device)
|
||||
struct crb_priv *priv;
|
||||
struct tpm_chip *chip;
|
||||
struct device *dev = &device->dev;
|
||||
struct tpm2_crb_smc *crb_smc;
|
||||
acpi_status status;
|
||||
u32 sm;
|
||||
int rc;
|
||||
@ -418,6 +564,19 @@ static int crb_acpi_add(struct acpi_device *device)
|
||||
sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)
|
||||
priv->flags |= CRB_FL_ACPI_START;
|
||||
|
||||
if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_SMC) {
|
||||
if (buf->header.length < (sizeof(*buf) + sizeof(*crb_smc))) {
|
||||
dev_err(dev,
|
||||
FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n",
|
||||
buf->header.length,
|
||||
ACPI_TPM2_COMMAND_BUFFER_WITH_SMC);
|
||||
return -EINVAL;
|
||||
}
|
||||
crb_smc = ACPI_ADD_PTR(struct tpm2_crb_smc, buf, sizeof(*buf));
|
||||
priv->smc_func_id = crb_smc->smc_func_id;
|
||||
priv->flags |= CRB_FL_CRB_SMC_START;
|
||||
}
|
||||
|
||||
rc = crb_map_io(device, priv, buf);
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -463,8 +622,7 @@ static int crb_acpi_remove(struct acpi_device *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int crb_pm_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused crb_pm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
@ -472,17 +630,38 @@ static int crb_pm_runtime_suspend(struct device *dev)
|
||||
return crb_go_idle(dev, priv);
|
||||
}
|
||||
|
||||
static int crb_pm_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused crb_pm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
|
||||
|
||||
return crb_cmd_ready(dev, priv);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static int __maybe_unused crb_pm_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = tpm_pm_suspend(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return crb_pm_runtime_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused crb_pm_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = crb_pm_runtime_resume(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return tpm_pm_resume(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops crb_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tpm_pm_suspend, tpm_pm_resume)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(crb_pm_suspend, crb_pm_resume)
|
||||
SET_RUNTIME_PM_OPS(crb_pm_runtime_suspend, crb_pm_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
|
@ -278,22 +278,22 @@ enum tis_defaults {
|
||||
#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
|
||||
#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
|
||||
|
||||
static int check_locality(struct tpm_chip *chip, int loc)
|
||||
static bool check_locality(struct tpm_chip *chip, int loc)
|
||||
{
|
||||
u8 buf;
|
||||
int rc;
|
||||
|
||||
rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return false;
|
||||
|
||||
if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
|
||||
tpm_dev.locality = loc;
|
||||
return loc;
|
||||
return true;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* implementation similar to tpm_tis */
|
||||
@ -315,7 +315,7 @@ static int request_locality(struct tpm_chip *chip, int loc)
|
||||
unsigned long stop;
|
||||
u8 buf = TPM_ACCESS_REQUEST_USE;
|
||||
|
||||
if (check_locality(chip, loc) >= 0)
|
||||
if (check_locality(chip, loc))
|
||||
return loc;
|
||||
|
||||
iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
|
||||
@ -323,7 +323,7 @@ static int request_locality(struct tpm_chip *chip, int loc)
|
||||
/* wait for burstcount */
|
||||
stop = jiffies + chip->timeout_a;
|
||||
do {
|
||||
if (check_locality(chip, loc) >= 0)
|
||||
if (check_locality(chip, loc))
|
||||
return loc;
|
||||
usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
|
||||
} while (time_before(jiffies, stop));
|
||||
|
@ -49,9 +49,10 @@
|
||||
*/
|
||||
#define TPM_I2C_MAX_BUF_SIZE 32
|
||||
#define TPM_I2C_RETRY_COUNT 32
|
||||
#define TPM_I2C_BUS_DELAY 1 /* msec */
|
||||
#define TPM_I2C_RETRY_DELAY_SHORT 2 /* msec */
|
||||
#define TPM_I2C_RETRY_DELAY_LONG 10 /* msec */
|
||||
#define TPM_I2C_BUS_DELAY 1000 /* usec */
|
||||
#define TPM_I2C_RETRY_DELAY_SHORT (2 * 1000) /* usec */
|
||||
#define TPM_I2C_RETRY_DELAY_LONG (10 * 1000) /* usec */
|
||||
#define TPM_I2C_DELAY_RANGE 300 /* usec */
|
||||
|
||||
#define OF_IS_TPM2 ((void *)1)
|
||||
#define I2C_IS_TPM2 1
|
||||
@ -123,7 +124,9 @@ static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data)
|
||||
/* this causes the current command to be aborted */
|
||||
for (i = 0, status = -1; i < TPM_I2C_RETRY_COUNT && status < 0; i++) {
|
||||
status = i2c_nuvoton_write_buf(client, TPM_STS, 1, &data);
|
||||
msleep(TPM_I2C_BUS_DELAY);
|
||||
if (status < 0)
|
||||
usleep_range(TPM_I2C_BUS_DELAY, TPM_I2C_BUS_DELAY
|
||||
+ TPM_I2C_DELAY_RANGE);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@ -160,7 +163,8 @@ static int i2c_nuvoton_get_burstcount(struct i2c_client *client,
|
||||
burst_count = min_t(u8, TPM_I2C_MAX_BUF_SIZE, data);
|
||||
break;
|
||||
}
|
||||
msleep(TPM_I2C_BUS_DELAY);
|
||||
usleep_range(TPM_I2C_BUS_DELAY, TPM_I2C_BUS_DELAY
|
||||
+ TPM_I2C_DELAY_RANGE);
|
||||
} while (time_before(jiffies, stop));
|
||||
|
||||
return burst_count;
|
||||
@ -203,13 +207,17 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
|
||||
return 0;
|
||||
|
||||
/* use polling to wait for the event */
|
||||
ten_msec = jiffies + msecs_to_jiffies(TPM_I2C_RETRY_DELAY_LONG);
|
||||
ten_msec = jiffies + usecs_to_jiffies(TPM_I2C_RETRY_DELAY_LONG);
|
||||
stop = jiffies + timeout;
|
||||
do {
|
||||
if (time_before(jiffies, ten_msec))
|
||||
msleep(TPM_I2C_RETRY_DELAY_SHORT);
|
||||
usleep_range(TPM_I2C_RETRY_DELAY_SHORT,
|
||||
TPM_I2C_RETRY_DELAY_SHORT
|
||||
+ TPM_I2C_DELAY_RANGE);
|
||||
else
|
||||
msleep(TPM_I2C_RETRY_DELAY_LONG);
|
||||
usleep_range(TPM_I2C_RETRY_DELAY_LONG,
|
||||
TPM_I2C_RETRY_DELAY_LONG
|
||||
+ TPM_I2C_DELAY_RANGE);
|
||||
status_valid = i2c_nuvoton_check_status(chip, mask,
|
||||
value);
|
||||
if (status_valid)
|
||||
|
@ -299,6 +299,8 @@ static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
|
||||
}
|
||||
|
||||
kfree(ibmvtpm);
|
||||
/* For tpm_ibmvtpm_get_desired_dma */
|
||||
dev_set_drvdata(&vdev->dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -313,14 +315,16 @@ static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
|
||||
static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(&vdev->dev);
|
||||
struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
|
||||
struct ibmvtpm_dev *ibmvtpm;
|
||||
|
||||
/*
|
||||
* ibmvtpm initializes at probe time, so the data we are
|
||||
* asking for may not be set yet. Estimate that 4K required
|
||||
* for TCE-mapped buffer in addition to CRQ.
|
||||
*/
|
||||
if (!ibmvtpm)
|
||||
if (chip)
|
||||
ibmvtpm = dev_get_drvdata(&chip->dev);
|
||||
else
|
||||
return CRQ_RES_BUF_SIZE + PAGE_SIZE;
|
||||
|
||||
return CRQ_RES_BUF_SIZE + ibmvtpm->rtce_size;
|
||||
|
@ -56,7 +56,7 @@ static int wait_startup(struct tpm_chip *chip, int l)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int check_locality(struct tpm_chip *chip, int l)
|
||||
static bool check_locality(struct tpm_chip *chip, int l)
|
||||
{
|
||||
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
|
||||
int rc;
|
||||
@ -64,30 +64,22 @@ static int check_locality(struct tpm_chip *chip, int l)
|
||||
|
||||
rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return false;
|
||||
|
||||
if ((access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
|
||||
return priv->locality = l;
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
|
||||
priv->locality = l;
|
||||
return true;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void release_locality(struct tpm_chip *chip, int l, int force)
|
||||
static void release_locality(struct tpm_chip *chip, int l)
|
||||
{
|
||||
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
|
||||
int rc;
|
||||
u8 access;
|
||||
|
||||
rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access);
|
||||
if (rc < 0)
|
||||
return;
|
||||
|
||||
if (force || (access &
|
||||
(TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
|
||||
tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
|
||||
|
||||
tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
|
||||
}
|
||||
|
||||
static int request_locality(struct tpm_chip *chip, int l)
|
||||
@ -96,7 +88,7 @@ static int request_locality(struct tpm_chip *chip, int l)
|
||||
unsigned long stop, timeout;
|
||||
long rc;
|
||||
|
||||
if (check_locality(chip, l) >= 0)
|
||||
if (check_locality(chip, l))
|
||||
return l;
|
||||
|
||||
rc = tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_REQUEST_USE);
|
||||
@ -112,7 +104,7 @@ again:
|
||||
return -1;
|
||||
rc = wait_event_interruptible_timeout(priv->int_queue,
|
||||
(check_locality
|
||||
(chip, l) >= 0),
|
||||
(chip, l)),
|
||||
timeout);
|
||||
if (rc > 0)
|
||||
return l;
|
||||
@ -123,7 +115,7 @@ again:
|
||||
} else {
|
||||
/* wait for burstcount */
|
||||
do {
|
||||
if (check_locality(chip, l) >= 0)
|
||||
if (check_locality(chip, l))
|
||||
return l;
|
||||
msleep(TPM_TIMEOUT);
|
||||
} while (time_before(jiffies, stop));
|
||||
@ -160,8 +152,10 @@ static int get_burstcount(struct tpm_chip *chip)
|
||||
u32 value;
|
||||
|
||||
/* wait for burstcount */
|
||||
/* which timeout value, spec has 2 answers (c & d) */
|
||||
stop = jiffies + chip->timeout_d;
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
stop = jiffies + chip->timeout_a;
|
||||
else
|
||||
stop = jiffies + chip->timeout_d;
|
||||
do {
|
||||
rc = tpm_tis_read32(priv, TPM_STS(priv->locality), &value);
|
||||
if (rc < 0)
|
||||
@ -250,7 +244,6 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
|
||||
out:
|
||||
tpm_tis_ready(chip);
|
||||
release_locality(chip, priv->locality, 0);
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -266,9 +259,6 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
size_t count = 0;
|
||||
bool itpm = priv->flags & TPM_TIS_ITPM_WORKAROUND;
|
||||
|
||||
if (request_locality(chip, 0) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
status = tpm_tis_status(chip);
|
||||
if ((status & TPM_STS_COMMAND_READY) == 0) {
|
||||
tpm_tis_ready(chip);
|
||||
@ -327,7 +317,6 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
|
||||
out_err:
|
||||
tpm_tis_ready(chip);
|
||||
release_locality(chip, priv->locality, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -388,7 +377,6 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
return len;
|
||||
out_err:
|
||||
tpm_tis_ready(chip);
|
||||
release_locality(chip, priv->locality, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -475,12 +463,14 @@ static int probe_itpm(struct tpm_chip *chip)
|
||||
if (vendor != TPM_VID_INTEL)
|
||||
return 0;
|
||||
|
||||
if (request_locality(chip, 0) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
rc = tpm_tis_send_data(chip, cmd_getticks, len);
|
||||
if (rc == 0)
|
||||
goto out;
|
||||
|
||||
tpm_tis_ready(chip);
|
||||
release_locality(chip, priv->locality, 0);
|
||||
|
||||
priv->flags |= TPM_TIS_ITPM_WORKAROUND;
|
||||
|
||||
@ -494,7 +484,7 @@ static int probe_itpm(struct tpm_chip *chip)
|
||||
|
||||
out:
|
||||
tpm_tis_ready(chip);
|
||||
release_locality(chip, priv->locality, 0);
|
||||
release_locality(chip, priv->locality);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -533,7 +523,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
|
||||
wake_up_interruptible(&priv->read_queue);
|
||||
if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT)
|
||||
for (i = 0; i < 5; i++)
|
||||
if (check_locality(chip, i) >= 0)
|
||||
if (check_locality(chip, i))
|
||||
break;
|
||||
if (interrupt &
|
||||
(TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
|
||||
@ -668,7 +658,6 @@ void tpm_tis_remove(struct tpm_chip *chip)
|
||||
interrupt = 0;
|
||||
|
||||
tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt);
|
||||
release_locality(chip, priv->locality, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_tis_remove);
|
||||
|
||||
@ -682,6 +671,8 @@ static const struct tpm_class_ops tpm_tis = {
|
||||
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_canceled = tpm_tis_req_canceled,
|
||||
.request_locality = request_locality,
|
||||
.relinquish_locality = release_locality,
|
||||
};
|
||||
|
||||
int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
|
||||
@ -724,11 +715,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
|
||||
intmask &= ~TPM_GLOBAL_INT_ENABLE;
|
||||
tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
|
||||
|
||||
if (request_locality(chip, 0) != 0) {
|
||||
rc = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
rc = tpm2_probe(chip);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
|
@ -47,8 +47,8 @@ struct tpm_tis_spi_phy {
|
||||
struct tpm_tis_data priv;
|
||||
struct spi_device *spi_device;
|
||||
|
||||
u8 tx_buf[MAX_SPI_FRAMESIZE + 4];
|
||||
u8 rx_buf[MAX_SPI_FRAMESIZE + 4];
|
||||
u8 tx_buf[4];
|
||||
u8 rx_buf[4];
|
||||
};
|
||||
|
||||
static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data)
|
||||
@ -56,120 +56,96 @@ static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *da
|
||||
return container_of(data, struct tpm_tis_spi_phy, priv);
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, u8 *result)
|
||||
static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
u8 *buffer, u8 direction)
|
||||
{
|
||||
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
|
||||
int ret, i;
|
||||
int ret = 0;
|
||||
int i;
|
||||
struct spi_message m;
|
||||
struct spi_transfer spi_xfer = {
|
||||
.tx_buf = phy->tx_buf,
|
||||
.rx_buf = phy->rx_buf,
|
||||
.len = 4,
|
||||
};
|
||||
|
||||
if (len > MAX_SPI_FRAMESIZE)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->tx_buf[0] = 0x80 | (len - 1);
|
||||
phy->tx_buf[1] = 0xd4;
|
||||
phy->tx_buf[2] = (addr >> 8) & 0xFF;
|
||||
phy->tx_buf[3] = addr & 0xFF;
|
||||
|
||||
spi_xfer.cs_change = 1;
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
struct spi_transfer spi_xfer;
|
||||
u8 transfer_len;
|
||||
|
||||
spi_bus_lock(phy->spi_device->master);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
memset(phy->tx_buf, 0, len);
|
||||
while (len) {
|
||||
transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
|
||||
|
||||
phy->tx_buf[0] = direction | (transfer_len - 1);
|
||||
phy->tx_buf[1] = 0xd4;
|
||||
phy->tx_buf[2] = addr >> 8;
|
||||
phy->tx_buf[3] = addr;
|
||||
|
||||
memset(&spi_xfer, 0, sizeof(spi_xfer));
|
||||
spi_xfer.tx_buf = phy->tx_buf;
|
||||
spi_xfer.rx_buf = phy->rx_buf;
|
||||
spi_xfer.len = 4;
|
||||
spi_xfer.cs_change = 1;
|
||||
|
||||
/* According to TCG PTP specification, if there is no TPM present at
|
||||
* all, then the design has a weak pull-up on MISO. If a TPM is not
|
||||
* present, a pull-up on MISO means that the SB controller sees a 1,
|
||||
* and will latch in 0xFF on the read.
|
||||
*/
|
||||
for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) {
|
||||
spi_xfer.len = 1;
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
if ((phy->rx_buf[3] & 0x01) == 0) {
|
||||
// handle SPI wait states
|
||||
phy->tx_buf[0] = 0;
|
||||
|
||||
for (i = 0; i < TPM_RETRY; i++) {
|
||||
spi_xfer.len = 1;
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
if (phy->rx_buf[0] & 0x01)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == TPM_RETRY) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
spi_xfer.cs_change = 0;
|
||||
spi_xfer.len = transfer_len;
|
||||
spi_xfer.delay_usecs = 5;
|
||||
|
||||
if (direction) {
|
||||
spi_xfer.tx_buf = NULL;
|
||||
spi_xfer.rx_buf = buffer;
|
||||
} else {
|
||||
spi_xfer.tx_buf = buffer;
|
||||
spi_xfer.rx_buf = NULL;
|
||||
}
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
len -= transfer_len;
|
||||
buffer += transfer_len;
|
||||
}
|
||||
|
||||
spi_xfer.cs_change = 0;
|
||||
spi_xfer.len = len;
|
||||
spi_xfer.rx_buf = result;
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
|
||||
exit:
|
||||
spi_bus_unlock(phy->spi_device->master);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, u8 *result)
|
||||
{
|
||||
return tpm_tis_spi_transfer(data, addr, len, result, 0x80);
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, u8 *value)
|
||||
{
|
||||
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
|
||||
int ret, i;
|
||||
struct spi_message m;
|
||||
struct spi_transfer spi_xfer = {
|
||||
.tx_buf = phy->tx_buf,
|
||||
.rx_buf = phy->rx_buf,
|
||||
.len = 4,
|
||||
};
|
||||
|
||||
if (len > MAX_SPI_FRAMESIZE)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->tx_buf[0] = len - 1;
|
||||
phy->tx_buf[1] = 0xd4;
|
||||
phy->tx_buf[2] = (addr >> 8) & 0xFF;
|
||||
phy->tx_buf[3] = addr & 0xFF;
|
||||
|
||||
spi_xfer.cs_change = 1;
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
|
||||
spi_bus_lock(phy->spi_device->master);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
memset(phy->tx_buf, 0, len);
|
||||
|
||||
/* According to TCG PTP specification, if there is no TPM present at
|
||||
* all, then the design has a weak pull-up on MISO. If a TPM is not
|
||||
* present, a pull-up on MISO means that the SB controller sees a 1,
|
||||
* and will latch in 0xFF on the read.
|
||||
*/
|
||||
for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) {
|
||||
spi_xfer.len = 1;
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
spi_xfer.len = len;
|
||||
spi_xfer.tx_buf = value;
|
||||
spi_xfer.cs_change = 0;
|
||||
spi_xfer.tx_buf = value;
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&spi_xfer, &m);
|
||||
ret = spi_sync_locked(phy->spi_device, &m);
|
||||
|
||||
exit:
|
||||
spi_bus_unlock(phy->spi_device->master);
|
||||
return ret;
|
||||
return tpm_tis_spi_transfer(data, addr, len, value, 0);
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
|
||||
|
65
drivers/char/tpm/tpmrm-dev.c
Normal file
65
drivers/char/tpm/tpmrm-dev.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2017 James.Bottomley@HansenPartnership.com
|
||||
*
|
||||
* GPLv2
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include "tpm-dev.h"
|
||||
|
||||
struct tpmrm_priv {
|
||||
struct file_priv priv;
|
||||
struct tpm_space space;
|
||||
};
|
||||
|
||||
static int tpmrm_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct tpm_chip *chip;
|
||||
struct tpmrm_priv *priv;
|
||||
int rc;
|
||||
|
||||
chip = container_of(inode->i_cdev, struct tpm_chip, cdevs);
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = tpm2_init_space(&priv->space);
|
||||
if (rc) {
|
||||
kfree(priv);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tpm_common_open(file, chip, &priv->priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmrm_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct file_priv *fpriv = file->private_data;
|
||||
struct tpmrm_priv *priv = container_of(fpriv, struct tpmrm_priv, priv);
|
||||
|
||||
tpm_common_release(file, fpriv);
|
||||
tpm2_del_space(fpriv->chip, &priv->space);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t tpmrm_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *off)
|
||||
{
|
||||
struct file_priv *fpriv = file->private_data;
|
||||
struct tpmrm_priv *priv = container_of(fpriv, struct tpmrm_priv, priv);
|
||||
|
||||
return tpm_common_write(file, buf, size, off, &priv->space);
|
||||
}
|
||||
|
||||
const struct file_operations tpmrm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = tpmrm_open,
|
||||
.read = tpm_common_read,
|
||||
.write = tpmrm_write,
|
||||
.release = tpmrm_release,
|
||||
};
|
||||
|
20
fs/namei.c
20
fs/namei.c
@ -340,22 +340,14 @@ int generic_permission(struct inode *inode, int mask)
|
||||
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
/* DACs are overridable for directories */
|
||||
if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
|
||||
return 0;
|
||||
if (!(mask & MAY_WRITE))
|
||||
if (capable_wrt_inode_uidgid(inode,
|
||||
CAP_DAC_READ_SEARCH))
|
||||
return 0;
|
||||
return -EACCES;
|
||||
}
|
||||
/*
|
||||
* Read/write DACs are always overridable.
|
||||
* Executable DACs are overridable when there is
|
||||
* at least one exec bit set.
|
||||
*/
|
||||
if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
|
||||
if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
|
||||
return 0;
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/*
|
||||
* Searching includes executable on directories, else just read.
|
||||
@ -364,6 +356,14 @@ int generic_permission(struct inode *inode, int mask)
|
||||
if (mask == MAY_READ)
|
||||
if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH))
|
||||
return 0;
|
||||
/*
|
||||
* Read/write DACs are always overridable.
|
||||
* Executable DACs are overridable when there is
|
||||
* at least one exec bit set.
|
||||
*/
|
||||
if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
|
||||
if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
|
||||
return 0;
|
||||
|
||||
return -EACCES;
|
||||
}
|
||||
|
@ -1294,6 +1294,7 @@ struct acpi_table_tpm2 {
|
||||
#define ACPI_TPM2_MEMORY_MAPPED 6
|
||||
#define ACPI_TPM2_COMMAND_BUFFER 7
|
||||
#define ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD 8
|
||||
#define ACPI_TPM2_COMMAND_BUFFER_WITH_SMC 11
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
|
@ -50,9 +50,20 @@ struct key;
|
||||
struct key_type;
|
||||
union key_payload;
|
||||
|
||||
extern int restrict_link_by_signature(struct key *trust_keyring,
|
||||
extern int restrict_link_by_signature(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
const union key_payload *payload,
|
||||
struct key *trust_keyring);
|
||||
|
||||
extern int restrict_link_by_key_or_keyring(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *trusted);
|
||||
|
||||
extern int restrict_link_by_key_or_keyring_chain(struct key *trust_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *trusted);
|
||||
|
||||
extern int verify_signature(const struct key *key,
|
||||
const struct public_key_signature *sig);
|
||||
|
@ -18,7 +18,8 @@
|
||||
|
||||
extern int restrict_link_by_builtin_trusted(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key);
|
||||
|
||||
#else
|
||||
#define restrict_link_by_builtin_trusted restrict_link_reject
|
||||
@ -28,11 +29,24 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring,
|
||||
extern int restrict_link_by_builtin_and_secondary_trusted(
|
||||
struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key);
|
||||
#else
|
||||
#define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING
|
||||
extern int mark_hash_blacklisted(const char *hash);
|
||||
extern int is_hash_blacklisted(const u8 *hash, size_t hash_len,
|
||||
const char *type);
|
||||
#else
|
||||
static inline int is_hash_blacklisted(const u8 *hash, size_t hash_len,
|
||||
const char *type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IMA_BLACKLIST_KEYRING
|
||||
extern struct key *ima_blacklist_keyring;
|
||||
|
||||
|
@ -295,6 +295,13 @@ struct compat_old_sigaction {
|
||||
};
|
||||
#endif
|
||||
|
||||
struct compat_keyctl_kdf_params {
|
||||
compat_uptr_t hashname;
|
||||
compat_uptr_t otherinfo;
|
||||
__u32 otherinfolen;
|
||||
__u32 __spare[8];
|
||||
};
|
||||
|
||||
struct compat_statfs;
|
||||
struct compat_statfs64;
|
||||
struct compat_old_linux_dirent;
|
||||
|
@ -219,6 +219,12 @@ extern struct cred init_cred;
|
||||
# define INIT_TASK_TI(tsk)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
#define INIT_TASK_SECURITY .security = NULL,
|
||||
#else
|
||||
#define INIT_TASK_SECURITY
|
||||
#endif
|
||||
|
||||
/*
|
||||
* INIT_TASK is used to set up the first task table, touch at
|
||||
* your own risk!. Base=0, limit=0x1fffff (=2MB)
|
||||
@ -298,6 +304,7 @@ extern struct cred init_cred;
|
||||
INIT_NUMA_BALANCING(tsk) \
|
||||
INIT_KASAN(tsk) \
|
||||
INIT_LIVEPATCH(tsk) \
|
||||
INIT_TASK_SECURITY \
|
||||
}
|
||||
|
||||
|
||||
|
@ -147,6 +147,14 @@ struct key_type {
|
||||
*/
|
||||
request_key_actor_t request_key;
|
||||
|
||||
/* Look up a keyring access restriction (optional)
|
||||
*
|
||||
* - NULL is a valid return value (meaning the requested restriction
|
||||
* is known but will never block addition of a key)
|
||||
* - should return -EINVAL if the restriction is unknown
|
||||
*/
|
||||
struct key_restriction *(*lookup_restriction)(const char *params);
|
||||
|
||||
/* internal fields */
|
||||
struct list_head link; /* link in types list */
|
||||
struct lock_class_key lock_class; /* key->sem lock class */
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/assoc_array.h>
|
||||
#include <linux/refcount.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/uidgid.h>
|
||||
@ -126,6 +127,17 @@ static inline bool is_key_possessed(const key_ref_t key_ref)
|
||||
return (unsigned long) key_ref & 1UL;
|
||||
}
|
||||
|
||||
typedef int (*key_restrict_link_func_t)(struct key *dest_keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key);
|
||||
|
||||
struct key_restriction {
|
||||
key_restrict_link_func_t check;
|
||||
struct key *key;
|
||||
struct key_type *keytype;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* authentication token / access credential / keyring
|
||||
@ -135,7 +147,7 @@ static inline bool is_key_possessed(const key_ref_t key_ref)
|
||||
* - Kerberos TGTs and tickets
|
||||
*/
|
||||
struct key {
|
||||
atomic_t usage; /* number of references */
|
||||
refcount_t usage; /* number of references */
|
||||
key_serial_t serial; /* key serial number */
|
||||
union {
|
||||
struct list_head graveyard_link;
|
||||
@ -205,18 +217,17 @@ struct key {
|
||||
};
|
||||
|
||||
/* This is set on a keyring to restrict the addition of a link to a key
|
||||
* to it. If this method isn't provided then it is assumed that the
|
||||
* to it. If this structure isn't provided then it is assumed that the
|
||||
* keyring is open to any addition. It is ignored for non-keyring
|
||||
* keys.
|
||||
* keys. Only set this value using keyring_restrict(), keyring_alloc(),
|
||||
* or key_alloc().
|
||||
*
|
||||
* This is intended for use with rings of trusted keys whereby addition
|
||||
* to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION
|
||||
* overrides this, allowing the kernel to add extra keys without
|
||||
* restriction.
|
||||
*/
|
||||
int (*restrict_link)(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
struct key_restriction *restrict_link;
|
||||
};
|
||||
|
||||
extern struct key *key_alloc(struct key_type *type,
|
||||
@ -225,9 +236,7 @@ extern struct key *key_alloc(struct key_type *type,
|
||||
const struct cred *cred,
|
||||
key_perm_t perm,
|
||||
unsigned long flags,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *));
|
||||
struct key_restriction *restrict_link);
|
||||
|
||||
|
||||
#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */
|
||||
@ -242,7 +251,7 @@ extern void key_put(struct key *key);
|
||||
|
||||
static inline struct key *__key_get(struct key *key)
|
||||
{
|
||||
atomic_inc(&key->usage);
|
||||
refcount_inc(&key->usage);
|
||||
return key;
|
||||
}
|
||||
|
||||
@ -303,14 +312,13 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid
|
||||
const struct cred *cred,
|
||||
key_perm_t perm,
|
||||
unsigned long flags,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *),
|
||||
struct key_restriction *restrict_link,
|
||||
struct key *dest);
|
||||
|
||||
extern int restrict_link_reject(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload);
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key);
|
||||
|
||||
extern int keyring_clear(struct key *keyring);
|
||||
|
||||
@ -321,6 +329,9 @@ extern key_ref_t keyring_search(key_ref_t keyring,
|
||||
extern int keyring_add_key(struct key *keyring,
|
||||
struct key *key);
|
||||
|
||||
extern int keyring_restrict(key_ref_t keyring, const char *type,
|
||||
const char *restriction);
|
||||
|
||||
extern struct key *key_lookup(key_serial_t id);
|
||||
|
||||
static inline key_serial_t key_serial(const struct key *key)
|
||||
|
@ -533,8 +533,13 @@
|
||||
* manual page for definitions of the @clone_flags.
|
||||
* @clone_flags contains the flags indicating what should be shared.
|
||||
* Return 0 if permission is granted.
|
||||
* @task_alloc:
|
||||
* @task task being allocated.
|
||||
* @clone_flags contains the flags indicating what should be shared.
|
||||
* Handle allocation of task-related resources.
|
||||
* Returns a zero on success, negative values on failure.
|
||||
* @task_free:
|
||||
* @task task being freed
|
||||
* @task task about to be freed.
|
||||
* Handle release of task-related resources. (Note that this can be called
|
||||
* from interrupt context.)
|
||||
* @cred_alloc_blank:
|
||||
@ -630,10 +635,19 @@
|
||||
* Check permission before getting the ioprio value of @p.
|
||||
* @p contains the task_struct of process.
|
||||
* Return 0 if permission is granted.
|
||||
* @task_prlimit:
|
||||
* Check permission before getting and/or setting the resource limits of
|
||||
* another task.
|
||||
* @cred points to the cred structure for the current task.
|
||||
* @tcred points to the cred structure for the target task.
|
||||
* @flags contains the LSM_PRLIMIT_* flag bits indicating whether the
|
||||
* resource limits are being read, modified, or both.
|
||||
* Return 0 if permission is granted.
|
||||
* @task_setrlimit:
|
||||
* Check permission before setting the resource limits of the current
|
||||
* process for @resource to @new_rlim. The old resource limit values can
|
||||
* be examined by dereferencing (current->signal->rlim + resource).
|
||||
* Check permission before setting the resource limits of process @p
|
||||
* for @resource to @new_rlim. The old resource limit values can
|
||||
* be examined by dereferencing (p->signal->rlim + resource).
|
||||
* @p points to the task_struct for the target task's group leader.
|
||||
* @resource contains the resource whose limit is being set.
|
||||
* @new_rlim contains the new limits for @resource.
|
||||
* Return 0 if permission is granted.
|
||||
@ -1473,6 +1487,7 @@ union security_list_options {
|
||||
int (*file_open)(struct file *file, const struct cred *cred);
|
||||
|
||||
int (*task_create)(unsigned long clone_flags);
|
||||
int (*task_alloc)(struct task_struct *task, unsigned long clone_flags);
|
||||
void (*task_free)(struct task_struct *task);
|
||||
int (*cred_alloc_blank)(struct cred *cred, gfp_t gfp);
|
||||
void (*cred_free)(struct cred *cred);
|
||||
@ -1494,6 +1509,8 @@ union security_list_options {
|
||||
int (*task_setnice)(struct task_struct *p, int nice);
|
||||
int (*task_setioprio)(struct task_struct *p, int ioprio);
|
||||
int (*task_getioprio)(struct task_struct *p);
|
||||
int (*task_prlimit)(const struct cred *cred, const struct cred *tcred,
|
||||
unsigned int flags);
|
||||
int (*task_setrlimit)(struct task_struct *p, unsigned int resource,
|
||||
struct rlimit *new_rlim);
|
||||
int (*task_setscheduler)(struct task_struct *p);
|
||||
@ -1737,6 +1754,7 @@ struct security_hook_heads {
|
||||
struct list_head file_receive;
|
||||
struct list_head file_open;
|
||||
struct list_head task_create;
|
||||
struct list_head task_alloc;
|
||||
struct list_head task_free;
|
||||
struct list_head cred_alloc_blank;
|
||||
struct list_head cred_free;
|
||||
@ -1755,6 +1773,7 @@ struct security_hook_heads {
|
||||
struct list_head task_setnice;
|
||||
struct list_head task_setioprio;
|
||||
struct list_head task_getioprio;
|
||||
struct list_head task_prlimit;
|
||||
struct list_head task_setrlimit;
|
||||
struct list_head task_setscheduler;
|
||||
struct list_head task_getscheduler;
|
||||
@ -1908,6 +1927,13 @@ static inline void security_delete_hooks(struct security_hook_list *hooks,
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_SELINUX_DISABLE */
|
||||
|
||||
/* Currently required to handle SELinux runtime hook disable. */
|
||||
#ifdef CONFIG_SECURITY_WRITABLE_HOOKS
|
||||
#define __lsm_ro_after_init
|
||||
#else
|
||||
#define __lsm_ro_after_init __ro_after_init
|
||||
#endif /* CONFIG_SECURITY_WRITABLE_HOOKS */
|
||||
|
||||
extern int __init security_module_enable(const char *module);
|
||||
extern void __init capability_add_hooks(void);
|
||||
#ifdef CONFIG_SECURITY_YAMA
|
||||
|
@ -1046,6 +1046,10 @@ struct task_struct {
|
||||
#endif
|
||||
#ifdef CONFIG_LIVEPATCH
|
||||
int patch_state;
|
||||
#endif
|
||||
#ifdef CONFIG_SECURITY
|
||||
/* Used by LSM modules for access restriction: */
|
||||
void *security;
|
||||
#endif
|
||||
/* CPU-specific state of this task: */
|
||||
struct thread_struct thread;
|
||||
|
@ -133,6 +133,10 @@ extern unsigned long dac_mmap_min_addr;
|
||||
/* setfsuid or setfsgid, id0 == fsuid or fsgid */
|
||||
#define LSM_SETID_FS 8
|
||||
|
||||
/* Flags for security_task_prlimit(). */
|
||||
#define LSM_PRLIMIT_READ 1
|
||||
#define LSM_PRLIMIT_WRITE 2
|
||||
|
||||
/* forward declares to avoid warnings */
|
||||
struct sched_param;
|
||||
struct request_sock;
|
||||
@ -304,6 +308,7 @@ int security_file_send_sigiotask(struct task_struct *tsk,
|
||||
int security_file_receive(struct file *file);
|
||||
int security_file_open(struct file *file, const struct cred *cred);
|
||||
int security_task_create(unsigned long clone_flags);
|
||||
int security_task_alloc(struct task_struct *task, unsigned long clone_flags);
|
||||
void security_task_free(struct task_struct *task);
|
||||
int security_cred_alloc_blank(struct cred *cred, gfp_t gfp);
|
||||
void security_cred_free(struct cred *cred);
|
||||
@ -324,6 +329,8 @@ void security_task_getsecid(struct task_struct *p, u32 *secid);
|
||||
int security_task_setnice(struct task_struct *p, int nice);
|
||||
int security_task_setioprio(struct task_struct *p, int ioprio);
|
||||
int security_task_getioprio(struct task_struct *p);
|
||||
int security_task_prlimit(const struct cred *cred, const struct cred *tcred,
|
||||
unsigned int flags);
|
||||
int security_task_setrlimit(struct task_struct *p, unsigned int resource,
|
||||
struct rlimit *new_rlim);
|
||||
int security_task_setscheduler(struct task_struct *p);
|
||||
@ -855,6 +862,12 @@ static inline int security_task_create(unsigned long clone_flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_task_alloc(struct task_struct *task,
|
||||
unsigned long clone_flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void security_task_free(struct task_struct *task)
|
||||
{ }
|
||||
|
||||
@ -949,6 +962,13 @@ static inline int security_task_getioprio(struct task_struct *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_task_prlimit(const struct cred *cred,
|
||||
const struct cred *tcred,
|
||||
unsigned int flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_task_setrlimit(struct task_struct *p,
|
||||
unsigned int resource,
|
||||
struct rlimit *new_rlim)
|
||||
|
@ -48,7 +48,8 @@ struct tpm_class_ops {
|
||||
u8 (*status) (struct tpm_chip *chip);
|
||||
bool (*update_timeouts)(struct tpm_chip *chip,
|
||||
unsigned long *timeout_cap);
|
||||
|
||||
int (*request_locality)(struct tpm_chip *chip, int loc);
|
||||
void (*relinquish_locality)(struct tpm_chip *chip, int loc);
|
||||
};
|
||||
|
||||
#if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)
|
||||
|
@ -60,6 +60,7 @@
|
||||
#define KEYCTL_INVALIDATE 21 /* invalidate a key */
|
||||
#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
|
||||
#define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */
|
||||
#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */
|
||||
|
||||
/* keyctl structures */
|
||||
struct keyctl_dh_params {
|
||||
@ -68,4 +69,11 @@ struct keyctl_dh_params {
|
||||
__s32 base;
|
||||
};
|
||||
|
||||
struct keyctl_kdf_params {
|
||||
char *hashname;
|
||||
char *otherinfo;
|
||||
__u32 otherinfolen;
|
||||
__u32 __spare[8];
|
||||
};
|
||||
|
||||
#endif /* _LINUX_KEYCTL_H */
|
||||
|
@ -1681,9 +1681,12 @@ static __latent_entropy struct task_struct *copy_process(
|
||||
goto bad_fork_cleanup_perf;
|
||||
/* copy all the process information */
|
||||
shm_init_task(p);
|
||||
retval = copy_semundo(clone_flags, p);
|
||||
retval = security_task_alloc(p, clone_flags);
|
||||
if (retval)
|
||||
goto bad_fork_cleanup_audit;
|
||||
retval = copy_semundo(clone_flags, p);
|
||||
if (retval)
|
||||
goto bad_fork_cleanup_security;
|
||||
retval = copy_files(clone_flags, p);
|
||||
if (retval)
|
||||
goto bad_fork_cleanup_semundo;
|
||||
@ -1907,6 +1910,8 @@ bad_fork_cleanup_files:
|
||||
exit_files(p); /* blocking */
|
||||
bad_fork_cleanup_semundo:
|
||||
exit_sem(p);
|
||||
bad_fork_cleanup_security:
|
||||
security_task_free(p);
|
||||
bad_fork_cleanup_audit:
|
||||
audit_free(p);
|
||||
bad_fork_cleanup_perf:
|
||||
|
30
kernel/sys.c
30
kernel/sys.c
@ -1432,25 +1432,26 @@ out:
|
||||
}
|
||||
|
||||
/* rcu lock must be held */
|
||||
static int check_prlimit_permission(struct task_struct *task)
|
||||
static int check_prlimit_permission(struct task_struct *task,
|
||||
unsigned int flags)
|
||||
{
|
||||
const struct cred *cred = current_cred(), *tcred;
|
||||
bool id_match;
|
||||
|
||||
if (current == task)
|
||||
return 0;
|
||||
|
||||
tcred = __task_cred(task);
|
||||
if (uid_eq(cred->uid, tcred->euid) &&
|
||||
uid_eq(cred->uid, tcred->suid) &&
|
||||
uid_eq(cred->uid, tcred->uid) &&
|
||||
gid_eq(cred->gid, tcred->egid) &&
|
||||
gid_eq(cred->gid, tcred->sgid) &&
|
||||
gid_eq(cred->gid, tcred->gid))
|
||||
return 0;
|
||||
if (ns_capable(tcred->user_ns, CAP_SYS_RESOURCE))
|
||||
return 0;
|
||||
id_match = (uid_eq(cred->uid, tcred->euid) &&
|
||||
uid_eq(cred->uid, tcred->suid) &&
|
||||
uid_eq(cred->uid, tcred->uid) &&
|
||||
gid_eq(cred->gid, tcred->egid) &&
|
||||
gid_eq(cred->gid, tcred->sgid) &&
|
||||
gid_eq(cred->gid, tcred->gid));
|
||||
if (!id_match && !ns_capable(tcred->user_ns, CAP_SYS_RESOURCE))
|
||||
return -EPERM;
|
||||
|
||||
return -EPERM;
|
||||
return security_task_prlimit(cred, tcred, flags);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
|
||||
@ -1460,12 +1461,17 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
|
||||
struct rlimit64 old64, new64;
|
||||
struct rlimit old, new;
|
||||
struct task_struct *tsk;
|
||||
unsigned int checkflags = 0;
|
||||
int ret;
|
||||
|
||||
if (old_rlim)
|
||||
checkflags |= LSM_PRLIMIT_READ;
|
||||
|
||||
if (new_rlim) {
|
||||
if (copy_from_user(&new64, new_rlim, sizeof(new64)))
|
||||
return -EFAULT;
|
||||
rlim64_to_rlim(&new64, &new);
|
||||
checkflags |= LSM_PRLIMIT_WRITE;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
@ -1474,7 +1480,7 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
|
||||
rcu_read_unlock();
|
||||
return -ESRCH;
|
||||
}
|
||||
ret = check_prlimit_permission(tsk);
|
||||
ret = check_prlimit_permission(tsk, checkflags);
|
||||
if (ret) {
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
struct security_class_mapping {
|
||||
const char *name;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
static void usage(char *name)
|
||||
{
|
||||
|
@ -31,6 +31,11 @@ config SECURITY
|
||||
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_WRITABLE_HOOKS
|
||||
depends on SECURITY
|
||||
bool
|
||||
default n
|
||||
|
||||
config SECURITYFS
|
||||
bool "Enable the securityfs filesystem"
|
||||
help
|
||||
|
@ -31,10 +31,7 @@ unsigned int aa_hash_size(void)
|
||||
|
||||
char *aa_calc_hash(void *data, size_t len)
|
||||
{
|
||||
struct {
|
||||
struct shash_desc shash;
|
||||
char ctx[crypto_shash_descsize(apparmor_tfm)];
|
||||
} desc;
|
||||
SHASH_DESC_ON_STACK(desc, apparmor_tfm);
|
||||
char *hash = NULL;
|
||||
int error = -ENOMEM;
|
||||
|
||||
@ -45,16 +42,16 @@ char *aa_calc_hash(void *data, size_t len)
|
||||
if (!hash)
|
||||
goto fail;
|
||||
|
||||
desc.shash.tfm = apparmor_tfm;
|
||||
desc.shash.flags = 0;
|
||||
desc->tfm = apparmor_tfm;
|
||||
desc->flags = 0;
|
||||
|
||||
error = crypto_shash_init(&desc.shash);
|
||||
error = crypto_shash_init(desc);
|
||||
if (error)
|
||||
goto fail;
|
||||
error = crypto_shash_update(&desc.shash, (u8 *) data, len);
|
||||
error = crypto_shash_update(desc, (u8 *) data, len);
|
||||
if (error)
|
||||
goto fail;
|
||||
error = crypto_shash_final(&desc.shash, hash);
|
||||
error = crypto_shash_final(desc, hash);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
@ -69,10 +66,7 @@ fail:
|
||||
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
|
||||
size_t len)
|
||||
{
|
||||
struct {
|
||||
struct shash_desc shash;
|
||||
char ctx[crypto_shash_descsize(apparmor_tfm)];
|
||||
} desc;
|
||||
SHASH_DESC_ON_STACK(desc, apparmor_tfm);
|
||||
int error = -ENOMEM;
|
||||
__le32 le32_version = cpu_to_le32(version);
|
||||
|
||||
@ -86,19 +80,19 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
|
||||
if (!profile->hash)
|
||||
goto fail;
|
||||
|
||||
desc.shash.tfm = apparmor_tfm;
|
||||
desc.shash.flags = 0;
|
||||
desc->tfm = apparmor_tfm;
|
||||
desc->flags = 0;
|
||||
|
||||
error = crypto_shash_init(&desc.shash);
|
||||
error = crypto_shash_init(desc);
|
||||
if (error)
|
||||
goto fail;
|
||||
error = crypto_shash_update(&desc.shash, (u8 *) &le32_version, 4);
|
||||
error = crypto_shash_update(desc, (u8 *) &le32_version, 4);
|
||||
if (error)
|
||||
goto fail;
|
||||
error = crypto_shash_update(&desc.shash, (u8 *) start, len);
|
||||
error = crypto_shash_update(desc, (u8 *) start, len);
|
||||
if (error)
|
||||
goto fail;
|
||||
error = crypto_shash_final(&desc.shash, profile->hash);
|
||||
error = crypto_shash_final(desc, profile->hash);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
|
@ -57,7 +57,7 @@
|
||||
pr_err_ratelimited("AppArmor: " fmt, ##args)
|
||||
|
||||
/* Flag indicating whether initialization completed */
|
||||
extern int apparmor_initialized __initdata;
|
||||
extern int apparmor_initialized;
|
||||
|
||||
/* fn's in lib */
|
||||
char *aa_split_fqname(char *args, char **ns_name);
|
||||
|
@ -180,13 +180,13 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix,
|
||||
} else
|
||||
policy->hname = kstrdup(name, gfp);
|
||||
if (!policy->hname)
|
||||
return 0;
|
||||
return false;
|
||||
/* base.name is a substring of fqname */
|
||||
policy->name = basename(policy->hname);
|
||||
INIT_LIST_HEAD(&policy->list);
|
||||
INIT_LIST_HEAD(&policy->profiles);
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include "include/procattr.h"
|
||||
|
||||
/* Flag indicating whether initialization completed */
|
||||
int apparmor_initialized __initdata;
|
||||
int apparmor_initialized;
|
||||
|
||||
DEFINE_PER_CPU(struct aa_buffers, aa_buffers);
|
||||
|
||||
@ -587,7 +587,7 @@ static int apparmor_task_setrlimit(struct task_struct *task,
|
||||
return error;
|
||||
}
|
||||
|
||||
static struct security_hook_list apparmor_hooks[] = {
|
||||
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
|
||||
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
|
||||
LSM_HOOK_INIT(capget, apparmor_capget),
|
||||
@ -681,7 +681,7 @@ module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR);
|
||||
#endif
|
||||
|
||||
/* Debug mode */
|
||||
bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_DEBUG_MESSAGES);
|
||||
bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_APPARMOR_DEBUG_MESSAGES);
|
||||
module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR);
|
||||
|
||||
/* Audit mode */
|
||||
@ -710,7 +710,7 @@ module_param_named(logsyscall, aa_g_logsyscall, aabool, S_IRUSR | S_IWUSR);
|
||||
|
||||
/* Maximum pathname length before accesses will start getting rejected */
|
||||
unsigned int aa_g_path_max = 2 * PATH_MAX;
|
||||
module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR);
|
||||
module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR);
|
||||
|
||||
/* Determines how paranoid loading of policy is and how much verification
|
||||
* on the loaded policy is done.
|
||||
@ -738,78 +738,77 @@ __setup("apparmor=", apparmor_enabled_setup);
|
||||
/* set global flag turning off the ability to load policy */
|
||||
static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
if (!policy_admin_capable(NULL))
|
||||
if (!apparmor_enabled)
|
||||
return -EINVAL;
|
||||
if (apparmor_initialized && !policy_admin_capable(NULL))
|
||||
return -EPERM;
|
||||
return param_set_bool(val, kp);
|
||||
}
|
||||
|
||||
static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp)
|
||||
{
|
||||
if (!policy_view_capable(NULL))
|
||||
return -EPERM;
|
||||
if (!apparmor_enabled)
|
||||
return -EINVAL;
|
||||
if (apparmor_initialized && !policy_view_capable(NULL))
|
||||
return -EPERM;
|
||||
return param_get_bool(buffer, kp);
|
||||
}
|
||||
|
||||
static int param_set_aabool(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
if (!policy_admin_capable(NULL))
|
||||
return -EPERM;
|
||||
if (!apparmor_enabled)
|
||||
return -EINVAL;
|
||||
if (apparmor_initialized && !policy_admin_capable(NULL))
|
||||
return -EPERM;
|
||||
return param_set_bool(val, kp);
|
||||
}
|
||||
|
||||
static int param_get_aabool(char *buffer, const struct kernel_param *kp)
|
||||
{
|
||||
if (!policy_view_capable(NULL))
|
||||
return -EPERM;
|
||||
if (!apparmor_enabled)
|
||||
return -EINVAL;
|
||||
if (apparmor_initialized && !policy_view_capable(NULL))
|
||||
return -EPERM;
|
||||
return param_get_bool(buffer, kp);
|
||||
}
|
||||
|
||||
static int param_set_aauint(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
if (!policy_admin_capable(NULL))
|
||||
return -EPERM;
|
||||
if (!apparmor_enabled)
|
||||
return -EINVAL;
|
||||
if (apparmor_initialized && !policy_admin_capable(NULL))
|
||||
return -EPERM;
|
||||
return param_set_uint(val, kp);
|
||||
}
|
||||
|
||||
static int param_get_aauint(char *buffer, const struct kernel_param *kp)
|
||||
{
|
||||
if (!policy_view_capable(NULL))
|
||||
return -EPERM;
|
||||
if (!apparmor_enabled)
|
||||
return -EINVAL;
|
||||
if (apparmor_initialized && !policy_view_capable(NULL))
|
||||
return -EPERM;
|
||||
return param_get_uint(buffer, kp);
|
||||
}
|
||||
|
||||
static int param_get_audit(char *buffer, struct kernel_param *kp)
|
||||
{
|
||||
if (!policy_view_capable(NULL))
|
||||
return -EPERM;
|
||||
|
||||
if (!apparmor_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
if (apparmor_initialized && !policy_view_capable(NULL))
|
||||
return -EPERM;
|
||||
return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]);
|
||||
}
|
||||
|
||||
static int param_set_audit(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
int i;
|
||||
if (!policy_admin_capable(NULL))
|
||||
return -EPERM;
|
||||
|
||||
if (!apparmor_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
if (apparmor_initialized && !policy_admin_capable(NULL))
|
||||
return -EPERM;
|
||||
|
||||
for (i = 0; i < AUDIT_MAX_INDEX; i++) {
|
||||
if (strcmp(val, audit_mode_names[i]) == 0) {
|
||||
@ -823,11 +822,10 @@ static int param_set_audit(const char *val, struct kernel_param *kp)
|
||||
|
||||
static int param_get_mode(char *buffer, struct kernel_param *kp)
|
||||
{
|
||||
if (!policy_view_capable(NULL))
|
||||
return -EPERM;
|
||||
|
||||
if (!apparmor_enabled)
|
||||
return -EINVAL;
|
||||
if (apparmor_initialized && !policy_view_capable(NULL))
|
||||
return -EPERM;
|
||||
|
||||
return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]);
|
||||
}
|
||||
@ -835,14 +833,13 @@ static int param_get_mode(char *buffer, struct kernel_param *kp)
|
||||
static int param_set_mode(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
int i;
|
||||
if (!policy_admin_capable(NULL))
|
||||
return -EPERM;
|
||||
|
||||
if (!apparmor_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
if (apparmor_initialized && !policy_admin_capable(NULL))
|
||||
return -EPERM;
|
||||
|
||||
for (i = 0; i < APPARMOR_MODE_NAMES_MAX_INDEX; i++) {
|
||||
if (strcmp(val, aa_profile_mode_names[i]) == 0) {
|
||||
|
@ -876,9 +876,11 @@ ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile,
|
||||
if (ns_name) {
|
||||
ns = aa_prepare_ns(view, ns_name);
|
||||
if (IS_ERR(ns)) {
|
||||
op = OP_PROF_LOAD;
|
||||
info = "failed to prepare namespace";
|
||||
error = PTR_ERR(ns);
|
||||
ns = NULL;
|
||||
ent = NULL;
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
@ -1013,7 +1015,7 @@ fail_lock:
|
||||
/* audit cause of failure */
|
||||
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
|
||||
fail:
|
||||
audit_policy(profile, op, ns_name, ent->new->base.hname,
|
||||
audit_policy(profile, op, ns_name, ent ? ent->new->base.hname : NULL,
|
||||
info, error);
|
||||
/* audit status that rest of profiles in the atomic set failed too */
|
||||
info = "valid profile in failed atomic policy load";
|
||||
@ -1023,7 +1025,7 @@ fail:
|
||||
/* skip entry that caused failure */
|
||||
continue;
|
||||
}
|
||||
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
|
||||
op = (!tmp->old) ? OP_PROF_LOAD : OP_PROF_REPL;
|
||||
audit_policy(profile, op, ns_name,
|
||||
tmp->new->base.hname, info, error);
|
||||
}
|
||||
|
@ -1071,7 +1071,7 @@ int cap_mmap_file(struct file *file, unsigned long reqprot,
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
|
||||
struct security_hook_list capability_hooks[] = {
|
||||
struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(capable, cap_capable),
|
||||
LSM_HOOK_INIT(settime, cap_settime),
|
||||
LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check),
|
||||
|
@ -81,18 +81,25 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
int __init integrity_init_keyring(const unsigned int id)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
struct key_restriction *restriction;
|
||||
int err = 0;
|
||||
|
||||
if (!init_keyring)
|
||||
return 0;
|
||||
|
||||
restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
if (!restriction)
|
||||
return -ENOMEM;
|
||||
|
||||
restriction->check = restrict_link_to_ima;
|
||||
|
||||
keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
|
||||
KGIDT_INIT(0), cred,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_WRITE | KEY_USR_SEARCH),
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
restrict_link_to_ima, NULL);
|
||||
restriction, NULL);
|
||||
if (IS_ERR(keyring[id])) {
|
||||
err = PTR_ERR(keyring[id]);
|
||||
pr_info("Can't allocate %s keyring (%d)\n",
|
||||
|
@ -207,10 +207,11 @@ int ima_appraise_measurement(enum ima_hooks func,
|
||||
|
||||
cause = "missing-hash";
|
||||
status = INTEGRITY_NOLABEL;
|
||||
if (opened & FILE_CREATED) {
|
||||
if (opened & FILE_CREATED)
|
||||
iint->flags |= IMA_NEW_FILE;
|
||||
if ((iint->flags & IMA_NEW_FILE) &&
|
||||
!(iint->flags & IMA_DIGSIG_REQUIRED))
|
||||
status = INTEGRITY_PASS;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/cred.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <keys/system_keyring.h>
|
||||
|
||||
|
||||
@ -27,15 +28,23 @@ struct key *ima_blacklist_keyring;
|
||||
*/
|
||||
__init int ima_mok_init(void)
|
||||
{
|
||||
struct key_restriction *restriction;
|
||||
|
||||
pr_notice("Allocating IMA blacklist keyring.\n");
|
||||
|
||||
restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
if (!restriction)
|
||||
panic("Can't allocate IMA blacklist restriction.");
|
||||
|
||||
restriction->check = restrict_link_by_builtin_trusted;
|
||||
|
||||
ima_blacklist_keyring = keyring_alloc(".ima_blacklist",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_WRITE | KEY_USR_SEARCH,
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
restrict_link_by_builtin_trusted, NULL);
|
||||
restriction, NULL);
|
||||
|
||||
if (IS_ERR(ima_blacklist_keyring))
|
||||
panic("Can't allocate IMA blacklist keyring.");
|
||||
|
@ -64,6 +64,8 @@ struct ima_rule_entry {
|
||||
u8 fsuuid[16];
|
||||
kuid_t uid;
|
||||
kuid_t fowner;
|
||||
bool (*uid_op)(kuid_t, kuid_t); /* Handlers for operators */
|
||||
bool (*fowner_op)(kuid_t, kuid_t); /* uid_eq(), uid_gt(), uid_lt() */
|
||||
int pcr;
|
||||
struct {
|
||||
void *rule; /* LSM file metadata specific */
|
||||
@ -83,7 +85,7 @@ struct ima_rule_entry {
|
||||
* normal users can easily run the machine out of memory simply building
|
||||
* and running executables.
|
||||
*/
|
||||
static struct ima_rule_entry dont_measure_rules[] = {
|
||||
static struct ima_rule_entry dont_measure_rules[] __ro_after_init = {
|
||||
{.action = DONT_MEASURE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC},
|
||||
@ -97,32 +99,35 @@ static struct ima_rule_entry dont_measure_rules[] = {
|
||||
{.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC}
|
||||
};
|
||||
|
||||
static struct ima_rule_entry original_measurement_rules[] = {
|
||||
static struct ima_rule_entry original_measurement_rules[] __ro_after_init = {
|
||||
{.action = MEASURE, .func = MMAP_CHECK, .mask = MAY_EXEC,
|
||||
.flags = IMA_FUNC | IMA_MASK},
|
||||
{.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC,
|
||||
.flags = IMA_FUNC | IMA_MASK},
|
||||
{.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ,
|
||||
.uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_MASK | IMA_UID},
|
||||
.uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq,
|
||||
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
|
||||
{.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC},
|
||||
{.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC},
|
||||
};
|
||||
|
||||
static struct ima_rule_entry default_measurement_rules[] = {
|
||||
static struct ima_rule_entry default_measurement_rules[] __ro_after_init = {
|
||||
{.action = MEASURE, .func = MMAP_CHECK, .mask = MAY_EXEC,
|
||||
.flags = IMA_FUNC | IMA_MASK},
|
||||
{.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC,
|
||||
.flags = IMA_FUNC | IMA_MASK},
|
||||
{.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ,
|
||||
.uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_INMASK | IMA_EUID},
|
||||
.uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq,
|
||||
.flags = IMA_FUNC | IMA_INMASK | IMA_EUID},
|
||||
{.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ,
|
||||
.uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_INMASK | IMA_UID},
|
||||
.uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq,
|
||||
.flags = IMA_FUNC | IMA_INMASK | IMA_UID},
|
||||
{.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC},
|
||||
{.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC},
|
||||
{.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC},
|
||||
};
|
||||
|
||||
static struct ima_rule_entry default_appraise_rules[] = {
|
||||
static struct ima_rule_entry default_appraise_rules[] __ro_after_init = {
|
||||
{.action = DONT_APPRAISE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC},
|
||||
@ -139,10 +144,11 @@ static struct ima_rule_entry default_appraise_rules[] = {
|
||||
.flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
|
||||
#endif
|
||||
#ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT
|
||||
{.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER},
|
||||
{.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq,
|
||||
.flags = IMA_FOWNER},
|
||||
#else
|
||||
/* force signature */
|
||||
{.action = APPRAISE, .fowner = GLOBAL_ROOT_UID,
|
||||
{.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq,
|
||||
.flags = IMA_FOWNER | IMA_DIGSIG_REQUIRED},
|
||||
#endif
|
||||
};
|
||||
@ -240,19 +246,20 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
|
||||
if ((rule->flags & IMA_FSUUID) &&
|
||||
memcmp(rule->fsuuid, inode->i_sb->s_uuid, sizeof(rule->fsuuid)))
|
||||
return false;
|
||||
if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid))
|
||||
if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid))
|
||||
return false;
|
||||
if (rule->flags & IMA_EUID) {
|
||||
if (has_capability_noaudit(current, CAP_SETUID)) {
|
||||
if (!uid_eq(rule->uid, cred->euid)
|
||||
&& !uid_eq(rule->uid, cred->suid)
|
||||
&& !uid_eq(rule->uid, cred->uid))
|
||||
if (!rule->uid_op(cred->euid, rule->uid)
|
||||
&& !rule->uid_op(cred->suid, rule->uid)
|
||||
&& !rule->uid_op(cred->uid, rule->uid))
|
||||
return false;
|
||||
} else if (!uid_eq(rule->uid, cred->euid))
|
||||
} else if (!rule->uid_op(cred->euid, rule->uid))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid))
|
||||
if ((rule->flags & IMA_FOWNER) &&
|
||||
!rule->fowner_op(inode->i_uid, rule->fowner))
|
||||
return false;
|
||||
for (i = 0; i < MAX_LSM_RULES; i++) {
|
||||
int rc = 0;
|
||||
@ -486,7 +493,9 @@ enum {
|
||||
Opt_obj_user, Opt_obj_role, Opt_obj_type,
|
||||
Opt_subj_user, Opt_subj_role, Opt_subj_type,
|
||||
Opt_func, Opt_mask, Opt_fsmagic,
|
||||
Opt_fsuuid, Opt_uid, Opt_euid, Opt_fowner,
|
||||
Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq,
|
||||
Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt,
|
||||
Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
|
||||
Opt_appraise_type, Opt_permit_directio,
|
||||
Opt_pcr
|
||||
};
|
||||
@ -507,9 +516,15 @@ static match_table_t policy_tokens = {
|
||||
{Opt_mask, "mask=%s"},
|
||||
{Opt_fsmagic, "fsmagic=%s"},
|
||||
{Opt_fsuuid, "fsuuid=%s"},
|
||||
{Opt_uid, "uid=%s"},
|
||||
{Opt_euid, "euid=%s"},
|
||||
{Opt_fowner, "fowner=%s"},
|
||||
{Opt_uid_eq, "uid=%s"},
|
||||
{Opt_euid_eq, "euid=%s"},
|
||||
{Opt_fowner_eq, "fowner=%s"},
|
||||
{Opt_uid_gt, "uid>%s"},
|
||||
{Opt_euid_gt, "euid>%s"},
|
||||
{Opt_fowner_gt, "fowner>%s"},
|
||||
{Opt_uid_lt, "uid<%s"},
|
||||
{Opt_euid_lt, "euid<%s"},
|
||||
{Opt_fowner_lt, "fowner<%s"},
|
||||
{Opt_appraise_type, "appraise_type=%s"},
|
||||
{Opt_permit_directio, "permit_directio"},
|
||||
{Opt_pcr, "pcr=%s"},
|
||||
@ -541,24 +556,37 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
|
||||
static void ima_log_string_op(struct audit_buffer *ab, char *key, char *value,
|
||||
bool (*rule_operator)(kuid_t, kuid_t))
|
||||
{
|
||||
audit_log_format(ab, "%s=", key);
|
||||
if (rule_operator == &uid_gt)
|
||||
audit_log_format(ab, "%s>", key);
|
||||
else if (rule_operator == &uid_lt)
|
||||
audit_log_format(ab, "%s<", key);
|
||||
else
|
||||
audit_log_format(ab, "%s=", key);
|
||||
audit_log_untrustedstring(ab, value);
|
||||
audit_log_format(ab, " ");
|
||||
}
|
||||
static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
|
||||
{
|
||||
ima_log_string_op(ab, key, value, NULL);
|
||||
}
|
||||
|
||||
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
char *from;
|
||||
char *p;
|
||||
bool uid_token;
|
||||
int result = 0;
|
||||
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE);
|
||||
|
||||
entry->uid = INVALID_UID;
|
||||
entry->fowner = INVALID_UID;
|
||||
entry->uid_op = &uid_eq;
|
||||
entry->fowner_op = &uid_eq;
|
||||
entry->action = UNKNOWN;
|
||||
while ((p = strsep(&rule, " \t")) != NULL) {
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
@ -694,11 +722,21 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
if (!result)
|
||||
entry->flags |= IMA_FSUUID;
|
||||
break;
|
||||
case Opt_uid:
|
||||
ima_log_string(ab, "uid", args[0].from);
|
||||
case Opt_euid:
|
||||
if (token == Opt_euid)
|
||||
ima_log_string(ab, "euid", args[0].from);
|
||||
case Opt_uid_gt:
|
||||
case Opt_euid_gt:
|
||||
entry->uid_op = &uid_gt;
|
||||
case Opt_uid_lt:
|
||||
case Opt_euid_lt:
|
||||
if ((token == Opt_uid_lt) || (token == Opt_euid_lt))
|
||||
entry->uid_op = &uid_lt;
|
||||
case Opt_uid_eq:
|
||||
case Opt_euid_eq:
|
||||
uid_token = (token == Opt_uid_eq) ||
|
||||
(token == Opt_uid_gt) ||
|
||||
(token == Opt_uid_lt);
|
||||
|
||||
ima_log_string_op(ab, uid_token ? "uid" : "euid",
|
||||
args[0].from, entry->uid_op);
|
||||
|
||||
if (uid_valid(entry->uid)) {
|
||||
result = -EINVAL;
|
||||
@ -713,12 +751,18 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
(uid_t)lnum != lnum)
|
||||
result = -EINVAL;
|
||||
else
|
||||
entry->flags |= (token == Opt_uid)
|
||||
entry->flags |= uid_token
|
||||
? IMA_UID : IMA_EUID;
|
||||
}
|
||||
break;
|
||||
case Opt_fowner:
|
||||
ima_log_string(ab, "fowner", args[0].from);
|
||||
case Opt_fowner_gt:
|
||||
entry->fowner_op = &uid_gt;
|
||||
case Opt_fowner_lt:
|
||||
if (token == Opt_fowner_lt)
|
||||
entry->fowner_op = &uid_lt;
|
||||
case Opt_fowner_eq:
|
||||
ima_log_string_op(ab, "fowner", args[0].from,
|
||||
entry->fowner_op);
|
||||
|
||||
if (uid_valid(entry->fowner)) {
|
||||
result = -EINVAL;
|
||||
@ -1049,19 +1093,34 @@ int ima_policy_show(struct seq_file *m, void *v)
|
||||
|
||||
if (entry->flags & IMA_UID) {
|
||||
snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
|
||||
seq_printf(m, pt(Opt_uid), tbuf);
|
||||
if (entry->uid_op == &uid_gt)
|
||||
seq_printf(m, pt(Opt_uid_gt), tbuf);
|
||||
else if (entry->uid_op == &uid_lt)
|
||||
seq_printf(m, pt(Opt_uid_lt), tbuf);
|
||||
else
|
||||
seq_printf(m, pt(Opt_uid_eq), tbuf);
|
||||
seq_puts(m, " ");
|
||||
}
|
||||
|
||||
if (entry->flags & IMA_EUID) {
|
||||
snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
|
||||
seq_printf(m, pt(Opt_euid), tbuf);
|
||||
if (entry->uid_op == &uid_gt)
|
||||
seq_printf(m, pt(Opt_euid_gt), tbuf);
|
||||
else if (entry->uid_op == &uid_lt)
|
||||
seq_printf(m, pt(Opt_euid_lt), tbuf);
|
||||
else
|
||||
seq_printf(m, pt(Opt_euid_eq), tbuf);
|
||||
seq_puts(m, " ");
|
||||
}
|
||||
|
||||
if (entry->flags & IMA_FOWNER) {
|
||||
snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner));
|
||||
seq_printf(m, pt(Opt_fowner), tbuf);
|
||||
if (entry->fowner_op == &uid_gt)
|
||||
seq_printf(m, pt(Opt_fowner_gt), tbuf);
|
||||
else if (entry->fowner_op == &uid_lt)
|
||||
seq_printf(m, pt(Opt_fowner_lt), tbuf);
|
||||
else
|
||||
seq_printf(m, pt(Opt_fowner_eq), tbuf);
|
||||
seq_puts(m, " ");
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,8 @@ config KEY_DH_OPERATIONS
|
||||
bool "Diffie-Hellman operations on retained keys"
|
||||
depends on KEYS
|
||||
select MPILIB
|
||||
select CRYPTO
|
||||
select CRYPTO_HASH
|
||||
help
|
||||
This option provides support for calculating Diffie-Hellman
|
||||
public keys and shared secrets using values stored as keys
|
||||
|
@ -15,7 +15,8 @@ obj-y := \
|
||||
request_key.o \
|
||||
request_key_auth.o \
|
||||
user_defined.o
|
||||
obj-$(CONFIG_KEYS_COMPAT) += compat.o
|
||||
compat-obj-$(CONFIG_KEY_DH_OPERATIONS) += compat_dh.o
|
||||
obj-$(CONFIG_KEYS_COMPAT) += compat.o $(compat-obj-y)
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_SYSCTL) += sysctl.o
|
||||
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
|
||||
|
@ -133,8 +133,13 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
|
||||
return keyctl_get_persistent(arg2, arg3);
|
||||
|
||||
case KEYCTL_DH_COMPUTE:
|
||||
return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3),
|
||||
arg4, compat_ptr(arg5));
|
||||
return compat_keyctl_dh_compute(compat_ptr(arg2),
|
||||
compat_ptr(arg3),
|
||||
arg4, compat_ptr(arg5));
|
||||
|
||||
case KEYCTL_RESTRICT_KEYRING:
|
||||
return keyctl_restrict_keyring(arg2, compat_ptr(arg3),
|
||||
compat_ptr(arg4));
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
|
38
security/keys/compat_dh.c
Normal file
38
security/keys/compat_dh.c
Normal file
@ -0,0 +1,38 @@
|
||||
/* 32-bit compatibility syscall for 64-bit systems for DH operations
|
||||
*
|
||||
* Copyright (C) 2016 Stephan Mueller <smueller@chronox.de>
|
||||
*
|
||||
* 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/uaccess.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* Perform the DH computation or DH based key derivation.
|
||||
*
|
||||
* If successful, 0 will be returned.
|
||||
*/
|
||||
long compat_keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct compat_keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
struct keyctl_kdf_params kdfcopy;
|
||||
struct compat_keyctl_kdf_params compat_kdfcopy;
|
||||
|
||||
if (!kdf)
|
||||
return __keyctl_dh_compute(params, buffer, buflen, NULL);
|
||||
|
||||
if (copy_from_user(&compat_kdfcopy, kdf, sizeof(compat_kdfcopy)) != 0)
|
||||
return -EFAULT;
|
||||
|
||||
kdfcopy.hashname = compat_ptr(compat_kdfcopy.hashname);
|
||||
kdfcopy.otherinfo = compat_ptr(compat_kdfcopy.otherinfo);
|
||||
kdfcopy.otherinfolen = compat_kdfcopy.otherinfolen;
|
||||
|
||||
return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy);
|
||||
}
|
@ -11,6 +11,8 @@
|
||||
#include <linux/mpi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <keys/user-type.h>
|
||||
#include "internal.h"
|
||||
|
||||
@ -77,9 +79,146 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
void __user *reserved)
|
||||
struct kdf_sdesc {
|
||||
struct shash_desc shash;
|
||||
char ctx[];
|
||||
};
|
||||
|
||||
static int kdf_alloc(struct kdf_sdesc **sdesc_ret, char *hashname)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
struct kdf_sdesc *sdesc;
|
||||
int size;
|
||||
|
||||
/* allocate synchronous hash */
|
||||
tfm = crypto_alloc_shash(hashname, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_info("could not allocate digest TFM handle %s\n", hashname);
|
||||
return PTR_ERR(tfm);
|
||||
}
|
||||
|
||||
size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm);
|
||||
sdesc = kmalloc(size, GFP_KERNEL);
|
||||
if (!sdesc)
|
||||
return -ENOMEM;
|
||||
sdesc->shash.tfm = tfm;
|
||||
sdesc->shash.flags = 0x0;
|
||||
|
||||
*sdesc_ret = sdesc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kdf_dealloc(struct kdf_sdesc *sdesc)
|
||||
{
|
||||
if (!sdesc)
|
||||
return;
|
||||
|
||||
if (sdesc->shash.tfm)
|
||||
crypto_free_shash(sdesc->shash.tfm);
|
||||
|
||||
kzfree(sdesc);
|
||||
}
|
||||
|
||||
/* convert 32 bit integer into its string representation */
|
||||
static inline void crypto_kw_cpu_to_be32(u32 val, u8 *buf)
|
||||
{
|
||||
__be32 *a = (__be32 *)buf;
|
||||
|
||||
*a = cpu_to_be32(val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of the KDF in counter mode according to SP800-108 section 5.1
|
||||
* as well as SP800-56A section 5.8.1 (Single-step KDF).
|
||||
*
|
||||
* SP800-56A:
|
||||
* The src pointer is defined as Z || other info where Z is the shared secret
|
||||
* from DH and other info is an arbitrary string (see SP800-56A section
|
||||
* 5.8.1.2).
|
||||
*/
|
||||
static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen,
|
||||
u8 *dst, unsigned int dlen)
|
||||
{
|
||||
struct shash_desc *desc = &sdesc->shash;
|
||||
unsigned int h = crypto_shash_digestsize(desc->tfm);
|
||||
int err = 0;
|
||||
u8 *dst_orig = dst;
|
||||
u32 i = 1;
|
||||
u8 iteration[sizeof(u32)];
|
||||
|
||||
while (dlen) {
|
||||
err = crypto_shash_init(desc);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
crypto_kw_cpu_to_be32(i, iteration);
|
||||
err = crypto_shash_update(desc, iteration, sizeof(u32));
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
if (src && slen) {
|
||||
err = crypto_shash_update(desc, src, slen);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (dlen < h) {
|
||||
u8 tmpbuffer[h];
|
||||
|
||||
err = crypto_shash_final(desc, tmpbuffer);
|
||||
if (err)
|
||||
goto err;
|
||||
memcpy(dst, tmpbuffer, dlen);
|
||||
memzero_explicit(tmpbuffer, h);
|
||||
return 0;
|
||||
} else {
|
||||
err = crypto_shash_final(desc, dst);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
dlen -= h;
|
||||
dst += h;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
memzero_explicit(dst_orig, dlen);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc,
|
||||
char __user *buffer, size_t buflen,
|
||||
uint8_t *kbuf, size_t kbuflen)
|
||||
{
|
||||
uint8_t *outbuf = NULL;
|
||||
int ret;
|
||||
|
||||
outbuf = kmalloc(buflen, GFP_KERNEL);
|
||||
if (!outbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, buflen);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = buflen;
|
||||
if (copy_to_user(buffer, outbuf, buflen) != 0)
|
||||
ret = -EFAULT;
|
||||
|
||||
err:
|
||||
kzfree(outbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
long __keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct keyctl_kdf_params *kdfcopy)
|
||||
{
|
||||
long ret;
|
||||
MPI base, private, prime, result;
|
||||
@ -88,6 +227,7 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
uint8_t *kbuf;
|
||||
ssize_t keylen;
|
||||
size_t resultlen;
|
||||
struct kdf_sdesc *sdesc = NULL;
|
||||
|
||||
if (!params || (!buffer && buflen)) {
|
||||
ret = -EINVAL;
|
||||
@ -98,12 +238,34 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (reserved) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
if (kdfcopy) {
|
||||
char *hashname;
|
||||
|
||||
if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN ||
|
||||
kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) {
|
||||
ret = -EMSGSIZE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* get KDF name string */
|
||||
hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME);
|
||||
if (IS_ERR(hashname)) {
|
||||
ret = PTR_ERR(hashname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* allocate KDF from the kernel crypto API */
|
||||
ret = kdf_alloc(&sdesc, hashname);
|
||||
kfree(hashname);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
keylen = mpi_from_key(pcopy.prime, buflen, &prime);
|
||||
/*
|
||||
* If the caller requests postprocessing with a KDF, allow an
|
||||
* arbitrary output buffer size since the KDF ensures proper truncation.
|
||||
*/
|
||||
keylen = mpi_from_key(pcopy.prime, kdfcopy ? SIZE_MAX : buflen, &prime);
|
||||
if (keylen < 0 || !prime) {
|
||||
/* buflen == 0 may be used to query the required buffer size,
|
||||
* which is the prime key length.
|
||||
@ -133,12 +295,25 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
goto error3;
|
||||
}
|
||||
|
||||
kbuf = kmalloc(resultlen, GFP_KERNEL);
|
||||
/* allocate space for DH shared secret and SP800-56A otherinfo */
|
||||
kbuf = kmalloc(kdfcopy ? (resultlen + kdfcopy->otherinfolen) : resultlen,
|
||||
GFP_KERNEL);
|
||||
if (!kbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto error4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Concatenate SP800-56A otherinfo past DH shared secret -- the
|
||||
* input to the KDF is (DH shared secret || otherinfo)
|
||||
*/
|
||||
if (kdfcopy && kdfcopy->otherinfo &&
|
||||
copy_from_user(kbuf + resultlen, kdfcopy->otherinfo,
|
||||
kdfcopy->otherinfolen) != 0) {
|
||||
ret = -EFAULT;
|
||||
goto error5;
|
||||
}
|
||||
|
||||
ret = do_dh(result, base, private, prime);
|
||||
if (ret)
|
||||
goto error5;
|
||||
@ -147,12 +322,17 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
if (ret != 0)
|
||||
goto error5;
|
||||
|
||||
ret = nbytes;
|
||||
if (copy_to_user(buffer, kbuf, nbytes) != 0)
|
||||
ret = -EFAULT;
|
||||
if (kdfcopy) {
|
||||
ret = keyctl_dh_compute_kdf(sdesc, buffer, buflen, kbuf,
|
||||
resultlen + kdfcopy->otherinfolen);
|
||||
} else {
|
||||
ret = nbytes;
|
||||
if (copy_to_user(buffer, kbuf, nbytes) != 0)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
error5:
|
||||
kfree(kbuf);
|
||||
kzfree(kbuf);
|
||||
error4:
|
||||
mpi_free(result);
|
||||
error3:
|
||||
@ -162,5 +342,21 @@ error2:
|
||||
error1:
|
||||
mpi_free(prime);
|
||||
out:
|
||||
kdf_dealloc(sdesc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
struct keyctl_kdf_params kdfcopy;
|
||||
|
||||
if (!kdf)
|
||||
return __keyctl_dh_compute(params, buffer, buflen, NULL);
|
||||
|
||||
if (copy_from_user(&kdfcopy, kdf, sizeof(kdfcopy)) != 0)
|
||||
return -EFAULT;
|
||||
|
||||
return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy);
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ continue_scanning:
|
||||
key = rb_entry(cursor, struct key, serial_node);
|
||||
cursor = rb_next(cursor);
|
||||
|
||||
if (atomic_read(&key->usage) == 0)
|
||||
if (refcount_read(&key->usage) == 0)
|
||||
goto found_unreferenced_key;
|
||||
|
||||
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
|
||||
@ -229,6 +229,9 @@ continue_scanning:
|
||||
set_bit(KEY_FLAG_DEAD, &key->flags);
|
||||
key->perm = 0;
|
||||
goto skip_dead_key;
|
||||
} else if (key->type == &key_type_keyring &&
|
||||
key->restrict_link) {
|
||||
goto found_restricted_keyring;
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,6 +337,14 @@ found_unreferenced_key:
|
||||
gc_state |= KEY_GC_REAP_AGAIN;
|
||||
goto maybe_resched;
|
||||
|
||||
/* We found a restricted keyring and need to update the restriction if
|
||||
* it is associated with the dead key type.
|
||||
*/
|
||||
found_restricted_keyring:
|
||||
spin_unlock(&key_serial_lock);
|
||||
keyring_restriction_gc(key, key_gc_dead_keytype);
|
||||
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
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/task_work.h>
|
||||
#include <linux/keyctl.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
struct iovec;
|
||||
|
||||
@ -53,7 +55,7 @@ struct key_user {
|
||||
struct rb_node node;
|
||||
struct mutex cons_lock; /* construction initiation lock */
|
||||
spinlock_t lock;
|
||||
atomic_t usage; /* for accessing qnkeys & qnbytes */
|
||||
refcount_t usage; /* for accessing qnkeys & qnbytes */
|
||||
atomic_t nkeys; /* number of keys */
|
||||
atomic_t nikeys; /* number of instantiated keys */
|
||||
kuid_t uid;
|
||||
@ -167,6 +169,8 @@ 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 keyring_restriction_gc(struct key *keyring,
|
||||
struct key_type *dead_type);
|
||||
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);
|
||||
@ -249,6 +253,9 @@ struct iov_iter;
|
||||
extern long keyctl_instantiate_key_common(key_serial_t,
|
||||
struct iov_iter *,
|
||||
key_serial_t);
|
||||
extern long keyctl_restrict_keyring(key_serial_t id,
|
||||
const char __user *_type,
|
||||
const char __user *_restriction);
|
||||
#ifdef CONFIG_PERSISTENT_KEYRINGS
|
||||
extern long keyctl_get_persistent(uid_t, key_serial_t);
|
||||
extern unsigned persistent_keyring_expiry;
|
||||
@ -261,14 +268,33 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
|
||||
|
||||
#ifdef CONFIG_KEY_DH_OPERATIONS
|
||||
extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
|
||||
size_t, void __user *);
|
||||
size_t, struct keyctl_kdf_params __user *);
|
||||
extern long __keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
|
||||
size_t, struct keyctl_kdf_params *);
|
||||
#ifdef CONFIG_KEYS_COMPAT
|
||||
extern long compat_keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct compat_keyctl_kdf_params __user *kdf);
|
||||
#endif
|
||||
#define KEYCTL_KDF_MAX_OUTPUT_LEN 1024 /* max length of KDF output */
|
||||
#define KEYCTL_KDF_MAX_OI_LEN 64 /* max length of otherinfo */
|
||||
#else
|
||||
static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
void __user *reserved)
|
||||
struct keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEYS_COMPAT
|
||||
static inline long compat_keyctl_dh_compute(
|
||||
struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -93,7 +93,7 @@ try_again:
|
||||
|
||||
/* if we get here, then the user record still hadn't appeared on the
|
||||
* second pass - so we use the candidate record */
|
||||
atomic_set(&candidate->usage, 1);
|
||||
refcount_set(&candidate->usage, 1);
|
||||
atomic_set(&candidate->nkeys, 0);
|
||||
atomic_set(&candidate->nikeys, 0);
|
||||
candidate->uid = uid;
|
||||
@ -110,7 +110,7 @@ try_again:
|
||||
|
||||
/* okay - we found a user record for this UID */
|
||||
found:
|
||||
atomic_inc(&user->usage);
|
||||
refcount_inc(&user->usage);
|
||||
spin_unlock(&key_user_lock);
|
||||
kfree(candidate);
|
||||
out:
|
||||
@ -122,7 +122,7 @@ out:
|
||||
*/
|
||||
void key_user_put(struct key_user *user)
|
||||
{
|
||||
if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
|
||||
if (refcount_dec_and_lock(&user->usage, &key_user_lock)) {
|
||||
rb_erase(&user->node, &key_user_tree);
|
||||
spin_unlock(&key_user_lock);
|
||||
|
||||
@ -201,12 +201,15 @@ serial_exists:
|
||||
* @cred: The credentials specifying UID namespace.
|
||||
* @perm: The permissions mask of the new key.
|
||||
* @flags: Flags specifying quota properties.
|
||||
* @restrict_link: Optional link restriction method for new keyrings.
|
||||
* @restrict_link: Optional link restriction for new keyrings.
|
||||
*
|
||||
* Allocate a key of the specified type with the attributes given. The key is
|
||||
* returned in an uninstantiated state and the caller needs to instantiate the
|
||||
* key before returning.
|
||||
*
|
||||
* The restrict_link structure (if not NULL) will be freed when the
|
||||
* keyring is destroyed, so it must be dynamically allocated.
|
||||
*
|
||||
* The user's key count quota is updated to reflect the creation of the key and
|
||||
* the user's key data quota has the default for the key type reserved. The
|
||||
* instantiation function should amend this as necessary. If insufficient
|
||||
@ -225,9 +228,7 @@ serial_exists:
|
||||
struct key *key_alloc(struct key_type *type, const char *desc,
|
||||
kuid_t uid, kgid_t gid, const struct cred *cred,
|
||||
key_perm_t perm, unsigned long flags,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *))
|
||||
struct key_restriction *restrict_link)
|
||||
{
|
||||
struct key_user *user = NULL;
|
||||
struct key *key;
|
||||
@ -285,7 +286,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
|
||||
if (!key->index_key.description)
|
||||
goto no_memory_3;
|
||||
|
||||
atomic_set(&key->usage, 1);
|
||||
refcount_set(&key->usage, 1);
|
||||
init_rwsem(&key->sem);
|
||||
lockdep_set_class(&key->sem, &type->lock_class);
|
||||
key->index_key.type = type;
|
||||
@ -499,19 +500,23 @@ int key_instantiate_and_link(struct key *key,
|
||||
}
|
||||
|
||||
if (keyring) {
|
||||
if (keyring->restrict_link) {
|
||||
ret = keyring->restrict_link(keyring, key->type,
|
||||
&prep.payload);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
ret = __key_link_begin(keyring, &key->index_key, &edit);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
if (keyring->restrict_link && keyring->restrict_link->check) {
|
||||
struct key_restriction *keyres = keyring->restrict_link;
|
||||
|
||||
ret = keyres->check(keyring, key->type, &prep.payload,
|
||||
keyres->key);
|
||||
if (ret < 0)
|
||||
goto error_link_end;
|
||||
}
|
||||
}
|
||||
|
||||
ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
|
||||
|
||||
error_link_end:
|
||||
if (keyring)
|
||||
__key_link_end(keyring, &key->index_key, edit);
|
||||
|
||||
@ -621,7 +626,7 @@ void key_put(struct key *key)
|
||||
if (key) {
|
||||
key_check(key);
|
||||
|
||||
if (atomic_dec_and_test(&key->usage))
|
||||
if (refcount_dec_and_test(&key->usage))
|
||||
schedule_work(&key_gc_work);
|
||||
}
|
||||
}
|
||||
@ -656,7 +661,7 @@ not_found:
|
||||
|
||||
found:
|
||||
/* pretend it doesn't exist if it is awaiting deletion */
|
||||
if (atomic_read(&key->usage) == 0)
|
||||
if (refcount_read(&key->usage) == 0)
|
||||
goto not_found;
|
||||
|
||||
/* this races with key_put(), but that doesn't matter since key_put()
|
||||
@ -806,9 +811,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
struct key *keyring, *key = NULL;
|
||||
key_ref_t key_ref;
|
||||
int ret;
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *) = NULL;
|
||||
struct key_restriction *restrict_link = NULL;
|
||||
|
||||
/* look up the key type to see if it's one of the registered kernel
|
||||
* types */
|
||||
@ -854,20 +857,21 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
}
|
||||
index_key.desc_len = strlen(index_key.description);
|
||||
|
||||
if (restrict_link) {
|
||||
ret = restrict_link(keyring, index_key.type, &prep.payload);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_free_prep;
|
||||
}
|
||||
}
|
||||
|
||||
ret = __key_link_begin(keyring, &index_key, &edit);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_free_prep;
|
||||
}
|
||||
|
||||
if (restrict_link && restrict_link->check) {
|
||||
ret = restrict_link->check(keyring, index_key.type,
|
||||
&prep.payload, restrict_link->key);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_link_end;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we're going to allocate a new key, we're going to have
|
||||
* to modify the keyring */
|
||||
ret = key_permission(keyring_ref, KEY_NEED_WRITE);
|
||||
|
@ -1584,6 +1584,59 @@ error_keyring:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply a restriction to a given keyring.
|
||||
*
|
||||
* The caller must have Setattr permission to change keyring restrictions.
|
||||
*
|
||||
* The requested type name may be a NULL pointer to reject all attempts
|
||||
* to link to the keyring. If _type is non-NULL, _restriction can be
|
||||
* NULL or a pointer to a string describing the restriction. If _type is
|
||||
* NULL, _restriction must also be NULL.
|
||||
*
|
||||
* Returns 0 if successful.
|
||||
*/
|
||||
long keyctl_restrict_keyring(key_serial_t id, const char __user *_type,
|
||||
const char __user *_restriction)
|
||||
{
|
||||
key_ref_t key_ref;
|
||||
bool link_reject = !_type;
|
||||
char type[32];
|
||||
char *restriction = NULL;
|
||||
long ret;
|
||||
|
||||
key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR);
|
||||
if (IS_ERR(key_ref))
|
||||
return PTR_ERR(key_ref);
|
||||
|
||||
if (_type) {
|
||||
ret = key_get_type_from_user(type, _type, sizeof(type));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (_restriction) {
|
||||
if (!_type) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
restriction = strndup_user(_restriction, PAGE_SIZE);
|
||||
if (IS_ERR(restriction)) {
|
||||
ret = PTR_ERR(restriction);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
ret = keyring_restrict(key_ref, link_reject ? NULL : type, restriction);
|
||||
kfree(restriction);
|
||||
|
||||
error:
|
||||
key_ref_put(key_ref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The key control system call
|
||||
*/
|
||||
@ -1693,7 +1746,12 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||
case KEYCTL_DH_COMPUTE:
|
||||
return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2,
|
||||
(char __user *) arg3, (size_t) arg4,
|
||||
(void __user *) arg5);
|
||||
(struct keyctl_kdf_params __user *) arg5);
|
||||
|
||||
case KEYCTL_RESTRICT_KEYRING:
|
||||
return keyctl_restrict_keyring((key_serial_t) arg2,
|
||||
(const char __user *) arg3,
|
||||
(const char __user *) arg4);
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
|
@ -394,6 +394,13 @@ static void keyring_destroy(struct key *keyring)
|
||||
write_unlock(&keyring_name_lock);
|
||||
}
|
||||
|
||||
if (keyring->restrict_link) {
|
||||
struct key_restriction *keyres = keyring->restrict_link;
|
||||
|
||||
key_put(keyres->key);
|
||||
kfree(keyres);
|
||||
}
|
||||
|
||||
assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops);
|
||||
}
|
||||
|
||||
@ -492,9 +499,7 @@ static long keyring_read(const struct key *keyring,
|
||||
struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
|
||||
const struct cred *cred, key_perm_t perm,
|
||||
unsigned long flags,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *),
|
||||
struct key_restriction *restrict_link,
|
||||
struct key *dest)
|
||||
{
|
||||
struct key *keyring;
|
||||
@ -519,17 +524,19 @@ EXPORT_SYMBOL(keyring_alloc);
|
||||
* @keyring: The keyring being added to.
|
||||
* @type: The type of key being added.
|
||||
* @payload: The payload of the key intended to be added.
|
||||
* @data: Additional data for evaluating restriction.
|
||||
*
|
||||
* Reject the addition of any links to a keyring. It can be overridden by
|
||||
* passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when
|
||||
* adding a key to a keyring.
|
||||
*
|
||||
* This is meant to be passed as the restrict_link parameter to
|
||||
* keyring_alloc().
|
||||
* This is meant to be stored in a key_restriction structure which is passed
|
||||
* in the restrict_link parameter to keyring_alloc().
|
||||
*/
|
||||
int restrict_link_reject(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
@ -940,6 +947,111 @@ key_ref_t keyring_search(key_ref_t keyring,
|
||||
}
|
||||
EXPORT_SYMBOL(keyring_search);
|
||||
|
||||
static struct key_restriction *keyring_restriction_alloc(
|
||||
key_restrict_link_func_t check)
|
||||
{
|
||||
struct key_restriction *keyres =
|
||||
kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
|
||||
if (!keyres)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
keyres->check = check;
|
||||
|
||||
return keyres;
|
||||
}
|
||||
|
||||
/*
|
||||
* Semaphore to serialise restriction setup to prevent reference count
|
||||
* cycles through restriction key pointers.
|
||||
*/
|
||||
static DECLARE_RWSEM(keyring_serialise_restrict_sem);
|
||||
|
||||
/*
|
||||
* Check for restriction cycles that would prevent keyring garbage collection.
|
||||
* keyring_serialise_restrict_sem must be held.
|
||||
*/
|
||||
static bool keyring_detect_restriction_cycle(const struct key *dest_keyring,
|
||||
struct key_restriction *keyres)
|
||||
{
|
||||
while (keyres && keyres->key &&
|
||||
keyres->key->type == &key_type_keyring) {
|
||||
if (keyres->key == dest_keyring)
|
||||
return true;
|
||||
|
||||
keyres = keyres->key->restrict_link;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* keyring_restrict - Look up and apply a restriction to a keyring
|
||||
*
|
||||
* @keyring: The keyring to be restricted
|
||||
* @restriction: The restriction options to apply to the keyring
|
||||
*/
|
||||
int keyring_restrict(key_ref_t keyring_ref, const char *type,
|
||||
const char *restriction)
|
||||
{
|
||||
struct key *keyring;
|
||||
struct key_type *restrict_type = NULL;
|
||||
struct key_restriction *restrict_link;
|
||||
int ret = 0;
|
||||
|
||||
keyring = key_ref_to_ptr(keyring_ref);
|
||||
key_check(keyring);
|
||||
|
||||
if (keyring->type != &key_type_keyring)
|
||||
return -ENOTDIR;
|
||||
|
||||
if (!type) {
|
||||
restrict_link = keyring_restriction_alloc(restrict_link_reject);
|
||||
} else {
|
||||
restrict_type = key_type_lookup(type);
|
||||
|
||||
if (IS_ERR(restrict_type))
|
||||
return PTR_ERR(restrict_type);
|
||||
|
||||
if (!restrict_type->lookup_restriction) {
|
||||
ret = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
restrict_link = restrict_type->lookup_restriction(restriction);
|
||||
}
|
||||
|
||||
if (IS_ERR(restrict_link)) {
|
||||
ret = PTR_ERR(restrict_link);
|
||||
goto error;
|
||||
}
|
||||
|
||||
down_write(&keyring->sem);
|
||||
down_write(&keyring_serialise_restrict_sem);
|
||||
|
||||
if (keyring->restrict_link)
|
||||
ret = -EEXIST;
|
||||
else if (keyring_detect_restriction_cycle(keyring, restrict_link))
|
||||
ret = -EDEADLK;
|
||||
else
|
||||
keyring->restrict_link = restrict_link;
|
||||
|
||||
up_write(&keyring_serialise_restrict_sem);
|
||||
up_write(&keyring->sem);
|
||||
|
||||
if (ret < 0) {
|
||||
key_put(restrict_link->key);
|
||||
kfree(restrict_link);
|
||||
}
|
||||
|
||||
error:
|
||||
if (restrict_type)
|
||||
key_type_put(restrict_type);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(keyring_restrict);
|
||||
|
||||
/*
|
||||
* Search the given keyring for a key that might be updated.
|
||||
*
|
||||
@ -1033,7 +1145,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
|
||||
/* we've got a match but we might end up racing with
|
||||
* key_cleanup() if the keyring is currently 'dead'
|
||||
* (ie. it has a zero usage count) */
|
||||
if (!atomic_inc_not_zero(&keyring->usage))
|
||||
if (!refcount_inc_not_zero(&keyring->usage))
|
||||
continue;
|
||||
keyring->last_used_at = current_kernel_time().tv_sec;
|
||||
goto out;
|
||||
@ -1220,9 +1332,10 @@ void __key_link_end(struct key *keyring,
|
||||
*/
|
||||
static int __key_link_check_restriction(struct key *keyring, struct key *key)
|
||||
{
|
||||
if (!keyring->restrict_link)
|
||||
if (!keyring->restrict_link || !keyring->restrict_link->check)
|
||||
return 0;
|
||||
return keyring->restrict_link(keyring, key->type, &key->payload);
|
||||
return keyring->restrict_link->check(keyring, key->type, &key->payload,
|
||||
keyring->restrict_link->key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1250,14 +1363,14 @@ int key_link(struct key *keyring, struct key *key)
|
||||
struct assoc_array_edit *edit;
|
||||
int ret;
|
||||
|
||||
kenter("{%d,%d}", keyring->serial, atomic_read(&keyring->usage));
|
||||
kenter("{%d,%d}", keyring->serial, refcount_read(&keyring->usage));
|
||||
|
||||
key_check(keyring);
|
||||
key_check(key);
|
||||
|
||||
ret = __key_link_begin(keyring, &key->index_key, &edit);
|
||||
if (ret == 0) {
|
||||
kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));
|
||||
kdebug("begun {%d,%d}", keyring->serial, refcount_read(&keyring->usage));
|
||||
ret = __key_link_check_restriction(keyring, key);
|
||||
if (ret == 0)
|
||||
ret = __key_link_check_live_key(keyring, key);
|
||||
@ -1266,7 +1379,7 @@ int key_link(struct key *keyring, struct key *key)
|
||||
__key_link_end(keyring, &key->index_key, edit);
|
||||
}
|
||||
|
||||
kleave(" = %d {%d,%d}", ret, keyring->serial, atomic_read(&keyring->usage));
|
||||
kleave(" = %d {%d,%d}", ret, keyring->serial, refcount_read(&keyring->usage));
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(key_link);
|
||||
@ -1426,3 +1539,53 @@ do_gc:
|
||||
up_write(&keyring->sem);
|
||||
kleave(" [gc]");
|
||||
}
|
||||
|
||||
/*
|
||||
* Garbage collect restriction pointers from a keyring.
|
||||
*
|
||||
* Keyring restrictions are associated with a key type, and must be cleaned
|
||||
* up if the key type is unregistered. The restriction is altered to always
|
||||
* reject additional keys so a keyring cannot be opened up by unregistering
|
||||
* a key type.
|
||||
*
|
||||
* Not called with any keyring locks held. The keyring's key struct will not
|
||||
* be deallocated under us as only our caller may deallocate it.
|
||||
*
|
||||
* The caller is required to hold key_types_sem and dead_type->sem. This is
|
||||
* fulfilled by key_gc_keytype() holding the locks on behalf of
|
||||
* key_garbage_collector(), which it invokes on a workqueue.
|
||||
*/
|
||||
void keyring_restriction_gc(struct key *keyring, struct key_type *dead_type)
|
||||
{
|
||||
struct key_restriction *keyres;
|
||||
|
||||
kenter("%x{%s}", keyring->serial, keyring->description ?: "");
|
||||
|
||||
/*
|
||||
* keyring->restrict_link is only assigned at key allocation time
|
||||
* or with the key type locked, so the only values that could be
|
||||
* concurrently assigned to keyring->restrict_link are for key
|
||||
* types other than dead_type. Given this, it's ok to check
|
||||
* the key type before acquiring keyring->sem.
|
||||
*/
|
||||
if (!dead_type || !keyring->restrict_link ||
|
||||
keyring->restrict_link->keytype != dead_type) {
|
||||
kleave(" [no restriction gc]");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Lock the keyring to ensure that a link is not in progress */
|
||||
down_write(&keyring->sem);
|
||||
|
||||
keyres = keyring->restrict_link;
|
||||
|
||||
keyres->check = restrict_link_reject;
|
||||
|
||||
key_put(keyres->key);
|
||||
keyres->key = NULL;
|
||||
keyres->keytype = NULL;
|
||||
|
||||
up_write(&keyring->sem);
|
||||
|
||||
kleave(" [restriction gc]");
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
|
||||
showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
|
||||
showflag(key, 'N', KEY_FLAG_NEGATIVE),
|
||||
showflag(key, 'i', KEY_FLAG_INVALIDATED),
|
||||
atomic_read(&key->usage),
|
||||
refcount_read(&key->usage),
|
||||
xbuf,
|
||||
key->perm,
|
||||
from_kuid_munged(seq_user_ns(m), key->uid),
|
||||
@ -340,7 +340,7 @@ static int proc_key_users_show(struct seq_file *m, void *v)
|
||||
|
||||
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),
|
||||
refcount_read(&user->usage),
|
||||
atomic_read(&user->nkeys),
|
||||
atomic_read(&user->nikeys),
|
||||
user->qnkeys,
|
||||
|
@ -30,7 +30,7 @@ static DEFINE_MUTEX(key_user_keyring_mutex);
|
||||
|
||||
/* The root user's tracking struct */
|
||||
struct key_user root_key_user = {
|
||||
.usage = ATOMIC_INIT(3),
|
||||
.usage = REFCOUNT_INIT(3),
|
||||
.cons_lock = __MUTEX_INITIALIZER(root_key_user.cons_lock),
|
||||
.lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock),
|
||||
.nkeys = ATOMIC_INIT(2),
|
||||
|
@ -213,7 +213,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
|
||||
if (ret < 0)
|
||||
goto error_inst;
|
||||
|
||||
kleave(" = {%d,%d}", authkey->serial, atomic_read(&authkey->usage));
|
||||
kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage));
|
||||
return authkey;
|
||||
|
||||
auth_key_revoked:
|
||||
|
@ -174,7 +174,7 @@ static int loadpin_read_file(struct file *file, enum kernel_read_file_id id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct security_hook_list loadpin_hooks[] = {
|
||||
static struct security_hook_list loadpin_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security),
|
||||
LSM_HOOK_INIT(kernel_read_file, loadpin_read_file),
|
||||
};
|
||||
|
@ -32,6 +32,7 @@
|
||||
/* Maximum number of letters for an LSM name string */
|
||||
#define SECURITY_NAME_MAX 10
|
||||
|
||||
struct security_hook_heads security_hook_heads __lsm_ro_after_init;
|
||||
char *lsm_names;
|
||||
/* Boot-time LSM user choice */
|
||||
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
|
||||
@ -54,6 +55,12 @@ static void __init do_security_initcalls(void)
|
||||
*/
|
||||
int __init security_init(void)
|
||||
{
|
||||
int i;
|
||||
struct list_head *list = (struct list_head *) &security_hook_heads;
|
||||
|
||||
for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct list_head);
|
||||
i++)
|
||||
INIT_LIST_HEAD(&list[i]);
|
||||
pr_info("Security Framework initialized\n");
|
||||
|
||||
/*
|
||||
@ -934,6 +941,11 @@ int security_task_create(unsigned long clone_flags)
|
||||
return call_int_hook(task_create, 0, clone_flags);
|
||||
}
|
||||
|
||||
int security_task_alloc(struct task_struct *task, unsigned long clone_flags)
|
||||
{
|
||||
return call_int_hook(task_alloc, 0, task, clone_flags);
|
||||
}
|
||||
|
||||
void security_task_free(struct task_struct *task)
|
||||
{
|
||||
call_void_hook(task_free, task);
|
||||
@ -1040,6 +1052,12 @@ int security_task_getioprio(struct task_struct *p)
|
||||
return call_int_hook(task_getioprio, 0, p);
|
||||
}
|
||||
|
||||
int security_task_prlimit(const struct cred *cred, const struct cred *tcred,
|
||||
unsigned int flags)
|
||||
{
|
||||
return call_int_hook(task_prlimit, 0, cred, tcred, flags);
|
||||
}
|
||||
|
||||
int security_task_setrlimit(struct task_struct *p, unsigned int resource,
|
||||
struct rlimit *new_rlim)
|
||||
{
|
||||
@ -1625,355 +1643,3 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
|
||||
actx);
|
||||
}
|
||||
#endif /* CONFIG_AUDIT */
|
||||
|
||||
struct security_hook_heads security_hook_heads = {
|
||||
.binder_set_context_mgr =
|
||||
LIST_HEAD_INIT(security_hook_heads.binder_set_context_mgr),
|
||||
.binder_transaction =
|
||||
LIST_HEAD_INIT(security_hook_heads.binder_transaction),
|
||||
.binder_transfer_binder =
|
||||
LIST_HEAD_INIT(security_hook_heads.binder_transfer_binder),
|
||||
.binder_transfer_file =
|
||||
LIST_HEAD_INIT(security_hook_heads.binder_transfer_file),
|
||||
|
||||
.ptrace_access_check =
|
||||
LIST_HEAD_INIT(security_hook_heads.ptrace_access_check),
|
||||
.ptrace_traceme =
|
||||
LIST_HEAD_INIT(security_hook_heads.ptrace_traceme),
|
||||
.capget = LIST_HEAD_INIT(security_hook_heads.capget),
|
||||
.capset = LIST_HEAD_INIT(security_hook_heads.capset),
|
||||
.capable = LIST_HEAD_INIT(security_hook_heads.capable),
|
||||
.quotactl = LIST_HEAD_INIT(security_hook_heads.quotactl),
|
||||
.quota_on = LIST_HEAD_INIT(security_hook_heads.quota_on),
|
||||
.syslog = LIST_HEAD_INIT(security_hook_heads.syslog),
|
||||
.settime = LIST_HEAD_INIT(security_hook_heads.settime),
|
||||
.vm_enough_memory =
|
||||
LIST_HEAD_INIT(security_hook_heads.vm_enough_memory),
|
||||
.bprm_set_creds =
|
||||
LIST_HEAD_INIT(security_hook_heads.bprm_set_creds),
|
||||
.bprm_check_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.bprm_check_security),
|
||||
.bprm_secureexec =
|
||||
LIST_HEAD_INIT(security_hook_heads.bprm_secureexec),
|
||||
.bprm_committing_creds =
|
||||
LIST_HEAD_INIT(security_hook_heads.bprm_committing_creds),
|
||||
.bprm_committed_creds =
|
||||
LIST_HEAD_INIT(security_hook_heads.bprm_committed_creds),
|
||||
.sb_alloc_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.sb_alloc_security),
|
||||
.sb_free_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.sb_free_security),
|
||||
.sb_copy_data = LIST_HEAD_INIT(security_hook_heads.sb_copy_data),
|
||||
.sb_remount = LIST_HEAD_INIT(security_hook_heads.sb_remount),
|
||||
.sb_kern_mount =
|
||||
LIST_HEAD_INIT(security_hook_heads.sb_kern_mount),
|
||||
.sb_show_options =
|
||||
LIST_HEAD_INIT(security_hook_heads.sb_show_options),
|
||||
.sb_statfs = LIST_HEAD_INIT(security_hook_heads.sb_statfs),
|
||||
.sb_mount = LIST_HEAD_INIT(security_hook_heads.sb_mount),
|
||||
.sb_umount = LIST_HEAD_INIT(security_hook_heads.sb_umount),
|
||||
.sb_pivotroot = LIST_HEAD_INIT(security_hook_heads.sb_pivotroot),
|
||||
.sb_set_mnt_opts =
|
||||
LIST_HEAD_INIT(security_hook_heads.sb_set_mnt_opts),
|
||||
.sb_clone_mnt_opts =
|
||||
LIST_HEAD_INIT(security_hook_heads.sb_clone_mnt_opts),
|
||||
.sb_parse_opts_str =
|
||||
LIST_HEAD_INIT(security_hook_heads.sb_parse_opts_str),
|
||||
.dentry_init_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.dentry_init_security),
|
||||
.dentry_create_files_as =
|
||||
LIST_HEAD_INIT(security_hook_heads.dentry_create_files_as),
|
||||
#ifdef CONFIG_SECURITY_PATH
|
||||
.path_unlink = LIST_HEAD_INIT(security_hook_heads.path_unlink),
|
||||
.path_mkdir = LIST_HEAD_INIT(security_hook_heads.path_mkdir),
|
||||
.path_rmdir = LIST_HEAD_INIT(security_hook_heads.path_rmdir),
|
||||
.path_mknod = LIST_HEAD_INIT(security_hook_heads.path_mknod),
|
||||
.path_truncate =
|
||||
LIST_HEAD_INIT(security_hook_heads.path_truncate),
|
||||
.path_symlink = LIST_HEAD_INIT(security_hook_heads.path_symlink),
|
||||
.path_link = LIST_HEAD_INIT(security_hook_heads.path_link),
|
||||
.path_rename = LIST_HEAD_INIT(security_hook_heads.path_rename),
|
||||
.path_chmod = LIST_HEAD_INIT(security_hook_heads.path_chmod),
|
||||
.path_chown = LIST_HEAD_INIT(security_hook_heads.path_chown),
|
||||
.path_chroot = LIST_HEAD_INIT(security_hook_heads.path_chroot),
|
||||
#endif
|
||||
.inode_alloc_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_alloc_security),
|
||||
.inode_free_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_free_security),
|
||||
.inode_init_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_init_security),
|
||||
.inode_create = LIST_HEAD_INIT(security_hook_heads.inode_create),
|
||||
.inode_link = LIST_HEAD_INIT(security_hook_heads.inode_link),
|
||||
.inode_unlink = LIST_HEAD_INIT(security_hook_heads.inode_unlink),
|
||||
.inode_symlink =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_symlink),
|
||||
.inode_mkdir = LIST_HEAD_INIT(security_hook_heads.inode_mkdir),
|
||||
.inode_rmdir = LIST_HEAD_INIT(security_hook_heads.inode_rmdir),
|
||||
.inode_mknod = LIST_HEAD_INIT(security_hook_heads.inode_mknod),
|
||||
.inode_rename = LIST_HEAD_INIT(security_hook_heads.inode_rename),
|
||||
.inode_readlink =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_readlink),
|
||||
.inode_follow_link =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_follow_link),
|
||||
.inode_permission =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_permission),
|
||||
.inode_setattr =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_setattr),
|
||||
.inode_getattr =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_getattr),
|
||||
.inode_setxattr =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_setxattr),
|
||||
.inode_post_setxattr =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_post_setxattr),
|
||||
.inode_getxattr =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_getxattr),
|
||||
.inode_listxattr =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_listxattr),
|
||||
.inode_removexattr =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_removexattr),
|
||||
.inode_need_killpriv =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_need_killpriv),
|
||||
.inode_killpriv =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_killpriv),
|
||||
.inode_getsecurity =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_getsecurity),
|
||||
.inode_setsecurity =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_setsecurity),
|
||||
.inode_listsecurity =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_listsecurity),
|
||||
.inode_getsecid =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_getsecid),
|
||||
.inode_copy_up =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_copy_up),
|
||||
.inode_copy_up_xattr =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_copy_up_xattr),
|
||||
.file_permission =
|
||||
LIST_HEAD_INIT(security_hook_heads.file_permission),
|
||||
.file_alloc_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.file_alloc_security),
|
||||
.file_free_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.file_free_security),
|
||||
.file_ioctl = LIST_HEAD_INIT(security_hook_heads.file_ioctl),
|
||||
.mmap_addr = LIST_HEAD_INIT(security_hook_heads.mmap_addr),
|
||||
.mmap_file = LIST_HEAD_INIT(security_hook_heads.mmap_file),
|
||||
.file_mprotect =
|
||||
LIST_HEAD_INIT(security_hook_heads.file_mprotect),
|
||||
.file_lock = LIST_HEAD_INIT(security_hook_heads.file_lock),
|
||||
.file_fcntl = LIST_HEAD_INIT(security_hook_heads.file_fcntl),
|
||||
.file_set_fowner =
|
||||
LIST_HEAD_INIT(security_hook_heads.file_set_fowner),
|
||||
.file_send_sigiotask =
|
||||
LIST_HEAD_INIT(security_hook_heads.file_send_sigiotask),
|
||||
.file_receive = LIST_HEAD_INIT(security_hook_heads.file_receive),
|
||||
.file_open = LIST_HEAD_INIT(security_hook_heads.file_open),
|
||||
.task_create = LIST_HEAD_INIT(security_hook_heads.task_create),
|
||||
.task_free = LIST_HEAD_INIT(security_hook_heads.task_free),
|
||||
.cred_alloc_blank =
|
||||
LIST_HEAD_INIT(security_hook_heads.cred_alloc_blank),
|
||||
.cred_free = LIST_HEAD_INIT(security_hook_heads.cred_free),
|
||||
.cred_prepare = LIST_HEAD_INIT(security_hook_heads.cred_prepare),
|
||||
.cred_transfer =
|
||||
LIST_HEAD_INIT(security_hook_heads.cred_transfer),
|
||||
.kernel_act_as =
|
||||
LIST_HEAD_INIT(security_hook_heads.kernel_act_as),
|
||||
.kernel_create_files_as =
|
||||
LIST_HEAD_INIT(security_hook_heads.kernel_create_files_as),
|
||||
.kernel_module_request =
|
||||
LIST_HEAD_INIT(security_hook_heads.kernel_module_request),
|
||||
.kernel_read_file =
|
||||
LIST_HEAD_INIT(security_hook_heads.kernel_read_file),
|
||||
.kernel_post_read_file =
|
||||
LIST_HEAD_INIT(security_hook_heads.kernel_post_read_file),
|
||||
.task_fix_setuid =
|
||||
LIST_HEAD_INIT(security_hook_heads.task_fix_setuid),
|
||||
.task_setpgid = LIST_HEAD_INIT(security_hook_heads.task_setpgid),
|
||||
.task_getpgid = LIST_HEAD_INIT(security_hook_heads.task_getpgid),
|
||||
.task_getsid = LIST_HEAD_INIT(security_hook_heads.task_getsid),
|
||||
.task_getsecid =
|
||||
LIST_HEAD_INIT(security_hook_heads.task_getsecid),
|
||||
.task_setnice = LIST_HEAD_INIT(security_hook_heads.task_setnice),
|
||||
.task_setioprio =
|
||||
LIST_HEAD_INIT(security_hook_heads.task_setioprio),
|
||||
.task_getioprio =
|
||||
LIST_HEAD_INIT(security_hook_heads.task_getioprio),
|
||||
.task_setrlimit =
|
||||
LIST_HEAD_INIT(security_hook_heads.task_setrlimit),
|
||||
.task_setscheduler =
|
||||
LIST_HEAD_INIT(security_hook_heads.task_setscheduler),
|
||||
.task_getscheduler =
|
||||
LIST_HEAD_INIT(security_hook_heads.task_getscheduler),
|
||||
.task_movememory =
|
||||
LIST_HEAD_INIT(security_hook_heads.task_movememory),
|
||||
.task_kill = LIST_HEAD_INIT(security_hook_heads.task_kill),
|
||||
.task_prctl = LIST_HEAD_INIT(security_hook_heads.task_prctl),
|
||||
.task_to_inode =
|
||||
LIST_HEAD_INIT(security_hook_heads.task_to_inode),
|
||||
.ipc_permission =
|
||||
LIST_HEAD_INIT(security_hook_heads.ipc_permission),
|
||||
.ipc_getsecid = LIST_HEAD_INIT(security_hook_heads.ipc_getsecid),
|
||||
.msg_msg_alloc_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.msg_msg_alloc_security),
|
||||
.msg_msg_free_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.msg_msg_free_security),
|
||||
.msg_queue_alloc_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.msg_queue_alloc_security),
|
||||
.msg_queue_free_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.msg_queue_free_security),
|
||||
.msg_queue_associate =
|
||||
LIST_HEAD_INIT(security_hook_heads.msg_queue_associate),
|
||||
.msg_queue_msgctl =
|
||||
LIST_HEAD_INIT(security_hook_heads.msg_queue_msgctl),
|
||||
.msg_queue_msgsnd =
|
||||
LIST_HEAD_INIT(security_hook_heads.msg_queue_msgsnd),
|
||||
.msg_queue_msgrcv =
|
||||
LIST_HEAD_INIT(security_hook_heads.msg_queue_msgrcv),
|
||||
.shm_alloc_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.shm_alloc_security),
|
||||
.shm_free_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.shm_free_security),
|
||||
.shm_associate =
|
||||
LIST_HEAD_INIT(security_hook_heads.shm_associate),
|
||||
.shm_shmctl = LIST_HEAD_INIT(security_hook_heads.shm_shmctl),
|
||||
.shm_shmat = LIST_HEAD_INIT(security_hook_heads.shm_shmat),
|
||||
.sem_alloc_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.sem_alloc_security),
|
||||
.sem_free_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.sem_free_security),
|
||||
.sem_associate =
|
||||
LIST_HEAD_INIT(security_hook_heads.sem_associate),
|
||||
.sem_semctl = LIST_HEAD_INIT(security_hook_heads.sem_semctl),
|
||||
.sem_semop = LIST_HEAD_INIT(security_hook_heads.sem_semop),
|
||||
.netlink_send = LIST_HEAD_INIT(security_hook_heads.netlink_send),
|
||||
.d_instantiate =
|
||||
LIST_HEAD_INIT(security_hook_heads.d_instantiate),
|
||||
.getprocattr = LIST_HEAD_INIT(security_hook_heads.getprocattr),
|
||||
.setprocattr = LIST_HEAD_INIT(security_hook_heads.setprocattr),
|
||||
.ismaclabel = LIST_HEAD_INIT(security_hook_heads.ismaclabel),
|
||||
.secid_to_secctx =
|
||||
LIST_HEAD_INIT(security_hook_heads.secid_to_secctx),
|
||||
.secctx_to_secid =
|
||||
LIST_HEAD_INIT(security_hook_heads.secctx_to_secid),
|
||||
.release_secctx =
|
||||
LIST_HEAD_INIT(security_hook_heads.release_secctx),
|
||||
.inode_invalidate_secctx =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_invalidate_secctx),
|
||||
.inode_notifysecctx =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_notifysecctx),
|
||||
.inode_setsecctx =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_setsecctx),
|
||||
.inode_getsecctx =
|
||||
LIST_HEAD_INIT(security_hook_heads.inode_getsecctx),
|
||||
#ifdef CONFIG_SECURITY_NETWORK
|
||||
.unix_stream_connect =
|
||||
LIST_HEAD_INIT(security_hook_heads.unix_stream_connect),
|
||||
.unix_may_send =
|
||||
LIST_HEAD_INIT(security_hook_heads.unix_may_send),
|
||||
.socket_create =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_create),
|
||||
.socket_post_create =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_post_create),
|
||||
.socket_bind = LIST_HEAD_INIT(security_hook_heads.socket_bind),
|
||||
.socket_connect =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_connect),
|
||||
.socket_listen =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_listen),
|
||||
.socket_accept =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_accept),
|
||||
.socket_sendmsg =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_sendmsg),
|
||||
.socket_recvmsg =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_recvmsg),
|
||||
.socket_getsockname =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_getsockname),
|
||||
.socket_getpeername =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_getpeername),
|
||||
.socket_getsockopt =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_getsockopt),
|
||||
.socket_setsockopt =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_setsockopt),
|
||||
.socket_shutdown =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_shutdown),
|
||||
.socket_sock_rcv_skb =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_sock_rcv_skb),
|
||||
.socket_getpeersec_stream =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_getpeersec_stream),
|
||||
.socket_getpeersec_dgram =
|
||||
LIST_HEAD_INIT(security_hook_heads.socket_getpeersec_dgram),
|
||||
.sk_alloc_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.sk_alloc_security),
|
||||
.sk_free_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.sk_free_security),
|
||||
.sk_clone_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.sk_clone_security),
|
||||
.sk_getsecid = LIST_HEAD_INIT(security_hook_heads.sk_getsecid),
|
||||
.sock_graft = LIST_HEAD_INIT(security_hook_heads.sock_graft),
|
||||
.inet_conn_request =
|
||||
LIST_HEAD_INIT(security_hook_heads.inet_conn_request),
|
||||
.inet_csk_clone =
|
||||
LIST_HEAD_INIT(security_hook_heads.inet_csk_clone),
|
||||
.inet_conn_established =
|
||||
LIST_HEAD_INIT(security_hook_heads.inet_conn_established),
|
||||
.secmark_relabel_packet =
|
||||
LIST_HEAD_INIT(security_hook_heads.secmark_relabel_packet),
|
||||
.secmark_refcount_inc =
|
||||
LIST_HEAD_INIT(security_hook_heads.secmark_refcount_inc),
|
||||
.secmark_refcount_dec =
|
||||
LIST_HEAD_INIT(security_hook_heads.secmark_refcount_dec),
|
||||
.req_classify_flow =
|
||||
LIST_HEAD_INIT(security_hook_heads.req_classify_flow),
|
||||
.tun_dev_alloc_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.tun_dev_alloc_security),
|
||||
.tun_dev_free_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.tun_dev_free_security),
|
||||
.tun_dev_create =
|
||||
LIST_HEAD_INIT(security_hook_heads.tun_dev_create),
|
||||
.tun_dev_attach_queue =
|
||||
LIST_HEAD_INIT(security_hook_heads.tun_dev_attach_queue),
|
||||
.tun_dev_attach =
|
||||
LIST_HEAD_INIT(security_hook_heads.tun_dev_attach),
|
||||
.tun_dev_open = LIST_HEAD_INIT(security_hook_heads.tun_dev_open),
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
.xfrm_policy_alloc_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.xfrm_policy_alloc_security),
|
||||
.xfrm_policy_clone_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.xfrm_policy_clone_security),
|
||||
.xfrm_policy_free_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.xfrm_policy_free_security),
|
||||
.xfrm_policy_delete_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.xfrm_policy_delete_security),
|
||||
.xfrm_state_alloc =
|
||||
LIST_HEAD_INIT(security_hook_heads.xfrm_state_alloc),
|
||||
.xfrm_state_alloc_acquire =
|
||||
LIST_HEAD_INIT(security_hook_heads.xfrm_state_alloc_acquire),
|
||||
.xfrm_state_free_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.xfrm_state_free_security),
|
||||
.xfrm_state_delete_security =
|
||||
LIST_HEAD_INIT(security_hook_heads.xfrm_state_delete_security),
|
||||
.xfrm_policy_lookup =
|
||||
LIST_HEAD_INIT(security_hook_heads.xfrm_policy_lookup),
|
||||
.xfrm_state_pol_flow_match =
|
||||
LIST_HEAD_INIT(security_hook_heads.xfrm_state_pol_flow_match),
|
||||
.xfrm_decode_session =
|
||||
LIST_HEAD_INIT(security_hook_heads.xfrm_decode_session),
|
||||
#endif /* CONFIG_SECURITY_NETWORK_XFRM */
|
||||
#ifdef CONFIG_KEYS
|
||||
.key_alloc = LIST_HEAD_INIT(security_hook_heads.key_alloc),
|
||||
.key_free = LIST_HEAD_INIT(security_hook_heads.key_free),
|
||||
.key_permission =
|
||||
LIST_HEAD_INIT(security_hook_heads.key_permission),
|
||||
.key_getsecurity =
|
||||
LIST_HEAD_INIT(security_hook_heads.key_getsecurity),
|
||||
#endif /* CONFIG_KEYS */
|
||||
#ifdef CONFIG_AUDIT
|
||||
.audit_rule_init =
|
||||
LIST_HEAD_INIT(security_hook_heads.audit_rule_init),
|
||||
.audit_rule_known =
|
||||
LIST_HEAD_INIT(security_hook_heads.audit_rule_known),
|
||||
.audit_rule_match =
|
||||
LIST_HEAD_INIT(security_hook_heads.audit_rule_match),
|
||||
.audit_rule_free =
|
||||
LIST_HEAD_INIT(security_hook_heads.audit_rule_free),
|
||||
#endif /* CONFIG_AUDIT */
|
||||
};
|
||||
|
@ -40,6 +40,7 @@ config SECURITY_SELINUX_BOOTPARAM_VALUE
|
||||
config SECURITY_SELINUX_DISABLE
|
||||
bool "NSA SELinux runtime disable"
|
||||
depends on SECURITY_SELINUX
|
||||
select SECURITY_WRITABLE_HOOKS
|
||||
default n
|
||||
help
|
||||
This option enables writing to a selinuxfs node 'disable', which
|
||||
@ -50,6 +51,11 @@ config SECURITY_SELINUX_DISABLE
|
||||
portability across platforms where boot parameters are difficult
|
||||
to employ.
|
||||
|
||||
NOTE: selecting this option will disable the '__ro_after_init'
|
||||
kernel hardening feature for security hooks. Please consider
|
||||
using the selinux=0 boot parameter instead of enabling this
|
||||
option.
|
||||
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_SELINUX_DEVELOP
|
||||
|
@ -3920,6 +3920,21 @@ static int selinux_task_getioprio(struct task_struct *p)
|
||||
PROCESS__GETSCHED, NULL);
|
||||
}
|
||||
|
||||
int selinux_task_prlimit(const struct cred *cred, const struct cred *tcred,
|
||||
unsigned int flags)
|
||||
{
|
||||
u32 av = 0;
|
||||
|
||||
if (!flags)
|
||||
return 0;
|
||||
if (flags & LSM_PRLIMIT_WRITE)
|
||||
av |= PROCESS__SETRLIMIT;
|
||||
if (flags & LSM_PRLIMIT_READ)
|
||||
av |= PROCESS__GETRLIMIT;
|
||||
return avc_has_perm(cred_sid(cred), cred_sid(tcred),
|
||||
SECCLASS_PROCESS, av, NULL);
|
||||
}
|
||||
|
||||
static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource,
|
||||
struct rlimit *new_rlim)
|
||||
{
|
||||
@ -4352,10 +4367,18 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
|
||||
u32 sid, node_perm;
|
||||
|
||||
if (family == PF_INET) {
|
||||
if (addrlen < sizeof(struct sockaddr_in)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
addr4 = (struct sockaddr_in *)address;
|
||||
snum = ntohs(addr4->sin_port);
|
||||
addrp = (char *)&addr4->sin_addr.s_addr;
|
||||
} else {
|
||||
if (addrlen < SIN6_LEN_RFC2133) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
addr6 = (struct sockaddr_in6 *)address;
|
||||
snum = ntohs(addr6->sin6_port);
|
||||
addrp = (char *)&addr6->sin6_addr.s6_addr;
|
||||
@ -6108,7 +6131,7 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
|
||||
|
||||
#endif
|
||||
|
||||
static struct security_hook_list selinux_hooks[] = {
|
||||
static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
|
||||
LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
|
||||
LSM_HOOK_INIT(binder_transfer_binder, selinux_binder_transfer_binder),
|
||||
@ -6206,6 +6229,7 @@ static struct security_hook_list selinux_hooks[] = {
|
||||
LSM_HOOK_INIT(task_setnice, selinux_task_setnice),
|
||||
LSM_HOOK_INIT(task_setioprio, selinux_task_setioprio),
|
||||
LSM_HOOK_INIT(task_getioprio, selinux_task_getioprio),
|
||||
LSM_HOOK_INIT(task_prlimit, selinux_task_prlimit),
|
||||
LSM_HOOK_INIT(task_setrlimit, selinux_task_setrlimit),
|
||||
LSM_HOOK_INIT(task_setscheduler, selinux_task_setscheduler),
|
||||
LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler),
|
||||
|
@ -47,7 +47,7 @@ struct security_class_mapping secclass_map[] = {
|
||||
"getattr", "setexec", "setfscreate", "noatsecure", "siginh",
|
||||
"setrlimit", "rlimitinh", "dyntransition", "setcurrent",
|
||||
"execmem", "execstack", "execheap", "setkeycreate",
|
||||
"setsockcreate", NULL } },
|
||||
"setsockcreate", "getrlimit", NULL } },
|
||||
{ "system",
|
||||
{ "ipc_info", "syslog_read", "syslog_mod",
|
||||
"syslog_console", "module_request", "module_load", NULL } },
|
||||
|
@ -28,7 +28,7 @@ struct nlmsg_perm {
|
||||
u32 perm;
|
||||
};
|
||||
|
||||
static struct nlmsg_perm nlmsg_route_perms[] =
|
||||
static const struct nlmsg_perm nlmsg_route_perms[] =
|
||||
{
|
||||
{ RTM_NEWLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
|
||||
{ RTM_DELLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
|
||||
@ -81,7 +81,7 @@ static struct nlmsg_perm nlmsg_route_perms[] =
|
||||
{ RTM_GETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
|
||||
};
|
||||
|
||||
static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
|
||||
static const struct nlmsg_perm nlmsg_tcpdiag_perms[] =
|
||||
{
|
||||
{ TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
|
||||
{ DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
|
||||
@ -89,7 +89,7 @@ static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
|
||||
{ SOCK_DESTROY, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE },
|
||||
};
|
||||
|
||||
static struct nlmsg_perm nlmsg_xfrm_perms[] =
|
||||
static const struct nlmsg_perm nlmsg_xfrm_perms[] =
|
||||
{
|
||||
{ XFRM_MSG_NEWSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
|
||||
{ XFRM_MSG_DELSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
|
||||
@ -116,7 +116,7 @@ static struct nlmsg_perm nlmsg_xfrm_perms[] =
|
||||
{ XFRM_MSG_MAPPING, NETLINK_XFRM_SOCKET__NLMSG_READ },
|
||||
};
|
||||
|
||||
static struct nlmsg_perm nlmsg_audit_perms[] =
|
||||
static const struct nlmsg_perm nlmsg_audit_perms[] =
|
||||
{
|
||||
{ AUDIT_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
|
||||
{ AUDIT_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
|
||||
@ -137,7 +137,7 @@ static struct nlmsg_perm nlmsg_audit_perms[] =
|
||||
};
|
||||
|
||||
|
||||
static int nlmsg_perm(u16 nlmsg_type, u32 *perm, struct nlmsg_perm *tab, size_t tabsize)
|
||||
static int nlmsg_perm(u16 nlmsg_type, u32 *perm, const struct nlmsg_perm *tab, size_t tabsize)
|
||||
{
|
||||
int i, err = -EINVAL;
|
||||
|
||||
|
@ -1456,10 +1456,10 @@ static int sel_avc_stats_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct avc_cache_stats *st = v;
|
||||
|
||||
if (v == SEQ_START_TOKEN)
|
||||
seq_printf(seq, "lookups hits misses allocations reclaims "
|
||||
"frees\n");
|
||||
else {
|
||||
if (v == SEQ_START_TOKEN) {
|
||||
seq_puts(seq,
|
||||
"lookups hits misses allocations reclaims frees\n");
|
||||
} else {
|
||||
unsigned int lookups = st->lookups;
|
||||
unsigned int misses = st->misses;
|
||||
unsigned int hits = lookups - misses;
|
||||
|
@ -176,8 +176,9 @@ void cond_policydb_destroy(struct policydb *p)
|
||||
int cond_init_bool_indexes(struct policydb *p)
|
||||
{
|
||||
kfree(p->bool_val_to_struct);
|
||||
p->bool_val_to_struct =
|
||||
kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum *), GFP_KERNEL);
|
||||
p->bool_val_to_struct = kmalloc_array(p->p_bools.nprim,
|
||||
sizeof(*p->bool_val_to_struct),
|
||||
GFP_KERNEL);
|
||||
if (!p->bool_val_to_struct)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
@ -226,7 +227,7 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp)
|
||||
u32 len;
|
||||
int rc;
|
||||
|
||||
booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL);
|
||||
booldatum = kzalloc(sizeof(*booldatum), GFP_KERNEL);
|
||||
if (!booldatum)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -331,7 +332,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum
|
||||
goto err;
|
||||
}
|
||||
|
||||
list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL);
|
||||
list = kzalloc(sizeof(*list), GFP_KERNEL);
|
||||
if (!list) {
|
||||
rc = -ENOMEM;
|
||||
goto err;
|
||||
@ -420,7 +421,7 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
|
||||
goto err;
|
||||
|
||||
rc = -ENOMEM;
|
||||
expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL);
|
||||
expr = kzalloc(sizeof(*expr), GFP_KERNEL);
|
||||
if (!expr)
|
||||
goto err;
|
||||
|
||||
@ -471,7 +472,7 @@ int cond_read_list(struct policydb *p, void *fp)
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
rc = -ENOMEM;
|
||||
node = kzalloc(sizeof(struct cond_node), GFP_KERNEL);
|
||||
node = kzalloc(sizeof(*node), GFP_KERNEL);
|
||||
if (!node)
|
||||
goto err;
|
||||
|
||||
@ -663,5 +664,4 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
|
||||
(node->key.specified & AVTAB_XPERMS))
|
||||
services_compute_xperms_drivers(xperms, node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -17,15 +17,15 @@ struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *
|
||||
u32 i;
|
||||
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (p == NULL)
|
||||
if (!p)
|
||||
return p;
|
||||
|
||||
p->size = size;
|
||||
p->nel = 0;
|
||||
p->hash_value = hash_value;
|
||||
p->keycmp = keycmp;
|
||||
p->htable = kmalloc(sizeof(*(p->htable)) * size, GFP_KERNEL);
|
||||
if (p->htable == NULL) {
|
||||
p->htable = kmalloc_array(size, sizeof(*p->htable), GFP_KERNEL);
|
||||
if (!p->htable) {
|
||||
kfree(p);
|
||||
return NULL;
|
||||
}
|
||||
@ -58,7 +58,7 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum)
|
||||
return -EEXIST;
|
||||
|
||||
newnode = kzalloc(sizeof(*newnode), GFP_KERNEL);
|
||||
if (newnode == NULL)
|
||||
if (!newnode)
|
||||
return -ENOMEM;
|
||||
newnode->key = key;
|
||||
newnode->datum = datum;
|
||||
@ -87,7 +87,7 @@ void *hashtab_search(struct hashtab *h, const void *key)
|
||||
while (cur && h->keycmp(h, key, cur->key) > 0)
|
||||
cur = cur->next;
|
||||
|
||||
if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
|
||||
if (!cur || (h->keycmp(h, key, cur->key) != 0))
|
||||
return NULL;
|
||||
|
||||
return cur->datum;
|
||||
|
@ -178,10 +178,9 @@ static int roles_init(struct policydb *p)
|
||||
int rc;
|
||||
struct role_datum *role;
|
||||
|
||||
rc = -ENOMEM;
|
||||
role = kzalloc(sizeof(*role), GFP_KERNEL);
|
||||
if (!role)
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
|
||||
rc = -EINVAL;
|
||||
role->value = ++p->p_roles.nprim;
|
||||
@ -540,23 +539,23 @@ static int policydb_index(struct policydb *p)
|
||||
#endif
|
||||
|
||||
rc = -ENOMEM;
|
||||
p->class_val_to_struct =
|
||||
kzalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)),
|
||||
GFP_KERNEL);
|
||||
p->class_val_to_struct = kcalloc(p->p_classes.nprim,
|
||||
sizeof(*p->class_val_to_struct),
|
||||
GFP_KERNEL);
|
||||
if (!p->class_val_to_struct)
|
||||
goto out;
|
||||
|
||||
rc = -ENOMEM;
|
||||
p->role_val_to_struct =
|
||||
kzalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)),
|
||||
GFP_KERNEL);
|
||||
p->role_val_to_struct = kcalloc(p->p_roles.nprim,
|
||||
sizeof(*p->role_val_to_struct),
|
||||
GFP_KERNEL);
|
||||
if (!p->role_val_to_struct)
|
||||
goto out;
|
||||
|
||||
rc = -ENOMEM;
|
||||
p->user_val_to_struct =
|
||||
kzalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)),
|
||||
GFP_KERNEL);
|
||||
p->user_val_to_struct = kcalloc(p->p_users.nprim,
|
||||
sizeof(*p->user_val_to_struct),
|
||||
GFP_KERNEL);
|
||||
if (!p->user_val_to_struct)
|
||||
goto out;
|
||||
|
||||
@ -880,8 +879,6 @@ void policydb_destroy(struct policydb *p)
|
||||
ebitmap_destroy(&p->filename_trans_ttypes);
|
||||
ebitmap_destroy(&p->policycaps);
|
||||
ebitmap_destroy(&p->permissive_map);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1120,10 +1117,9 @@ static int perm_read(struct policydb *p, struct hashtab *h, void *fp)
|
||||
__le32 buf[2];
|
||||
u32 len;
|
||||
|
||||
rc = -ENOMEM;
|
||||
perdatum = kzalloc(sizeof(*perdatum), GFP_KERNEL);
|
||||
if (!perdatum)
|
||||
goto bad;
|
||||
return -ENOMEM;
|
||||
|
||||
rc = next_entry(buf, fp, sizeof buf);
|
||||
if (rc)
|
||||
@ -1154,10 +1150,9 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp)
|
||||
u32 len, nel;
|
||||
int i, rc;
|
||||
|
||||
rc = -ENOMEM;
|
||||
comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
|
||||
if (!comdatum)
|
||||
goto bad;
|
||||
return -ENOMEM;
|
||||
|
||||
rc = next_entry(buf, fp, sizeof buf);
|
||||
if (rc)
|
||||
@ -1320,10 +1315,9 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
|
||||
u32 len, len2, ncons, nel;
|
||||
int i, rc;
|
||||
|
||||
rc = -ENOMEM;
|
||||
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
|
||||
if (!cladatum)
|
||||
goto bad;
|
||||
return -ENOMEM;
|
||||
|
||||
rc = next_entry(buf, fp, sizeof(u32)*6);
|
||||
if (rc)
|
||||
@ -1414,10 +1408,9 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
|
||||
__le32 buf[3];
|
||||
u32 len;
|
||||
|
||||
rc = -ENOMEM;
|
||||
role = kzalloc(sizeof(*role), GFP_KERNEL);
|
||||
if (!role)
|
||||
goto bad;
|
||||
return -ENOMEM;
|
||||
|
||||
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
|
||||
to_read = 3;
|
||||
@ -1471,10 +1464,9 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp)
|
||||
__le32 buf[4];
|
||||
u32 len;
|
||||
|
||||
rc = -ENOMEM;
|
||||
typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL);
|
||||
if (!typdatum)
|
||||
goto bad;
|
||||
return -ENOMEM;
|
||||
|
||||
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
|
||||
to_read = 4;
|
||||
@ -1546,10 +1538,9 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
|
||||
__le32 buf[3];
|
||||
u32 len;
|
||||
|
||||
rc = -ENOMEM;
|
||||
usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL);
|
||||
if (!usrdatum)
|
||||
goto bad;
|
||||
return -ENOMEM;
|
||||
|
||||
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
|
||||
to_read = 3;
|
||||
@ -1597,10 +1588,9 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp)
|
||||
__le32 buf[2];
|
||||
u32 len;
|
||||
|
||||
rc = -ENOMEM;
|
||||
levdatum = kzalloc(sizeof(*levdatum), GFP_ATOMIC);
|
||||
if (!levdatum)
|
||||
goto bad;
|
||||
return -ENOMEM;
|
||||
|
||||
rc = next_entry(buf, fp, sizeof buf);
|
||||
if (rc)
|
||||
@ -1614,7 +1604,7 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp)
|
||||
goto bad;
|
||||
|
||||
rc = -ENOMEM;
|
||||
levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC);
|
||||
levdatum->level = kmalloc(sizeof(*levdatum->level), GFP_ATOMIC);
|
||||
if (!levdatum->level)
|
||||
goto bad;
|
||||
|
||||
@ -1639,10 +1629,9 @@ static int cat_read(struct policydb *p, struct hashtab *h, void *fp)
|
||||
__le32 buf[3];
|
||||
u32 len;
|
||||
|
||||
rc = -ENOMEM;
|
||||
catdatum = kzalloc(sizeof(*catdatum), GFP_ATOMIC);
|
||||
if (!catdatum)
|
||||
goto bad;
|
||||
return -ENOMEM;
|
||||
|
||||
rc = next_entry(buf, fp, sizeof buf);
|
||||
if (rc)
|
||||
@ -1854,7 +1843,7 @@ static int range_read(struct policydb *p, void *fp)
|
||||
|
||||
rc = next_entry(buf, fp, sizeof(u32));
|
||||
if (rc)
|
||||
goto out;
|
||||
return rc;
|
||||
|
||||
nel = le32_to_cpu(buf[0]);
|
||||
for (i = 0; i < nel; i++) {
|
||||
@ -1931,7 +1920,6 @@ static int filename_trans_read(struct policydb *p, void *fp)
|
||||
nel = le32_to_cpu(buf[0]);
|
||||
|
||||
for (i = 0; i < nel; i++) {
|
||||
ft = NULL;
|
||||
otype = NULL;
|
||||
name = NULL;
|
||||
|
||||
@ -2008,7 +1996,7 @@ static int genfs_read(struct policydb *p, void *fp)
|
||||
|
||||
rc = next_entry(buf, fp, sizeof(u32));
|
||||
if (rc)
|
||||
goto out;
|
||||
return rc;
|
||||
nel = le32_to_cpu(buf[0]);
|
||||
|
||||
for (i = 0; i < nel; i++) {
|
||||
@ -2100,9 +2088,10 @@ static int genfs_read(struct policydb *p, void *fp)
|
||||
}
|
||||
rc = 0;
|
||||
out:
|
||||
if (newgenfs)
|
||||
if (newgenfs) {
|
||||
kfree(newgenfs->fstype);
|
||||
kfree(newgenfs);
|
||||
kfree(newgenfs);
|
||||
}
|
||||
ocontext_destroy(newc, OCON_FSUSE);
|
||||
|
||||
return rc;
|
||||
|
@ -157,7 +157,7 @@ static int selinux_set_mapping(struct policydb *pol,
|
||||
}
|
||||
|
||||
k = 0;
|
||||
while (p_in->perms && p_in->perms[k]) {
|
||||
while (p_in->perms[k]) {
|
||||
/* An empty permission string skips ahead */
|
||||
if (!*p_in->perms[k]) {
|
||||
k++;
|
||||
|
@ -18,7 +18,7 @@ int sidtab_init(struct sidtab *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
s->htable = kmalloc(sizeof(*(s->htable)) * SIDTAB_SIZE, GFP_ATOMIC);
|
||||
s->htable = kmalloc_array(SIDTAB_SIZE, sizeof(*s->htable), GFP_ATOMIC);
|
||||
if (!s->htable)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < SIDTAB_SIZE; i++)
|
||||
@ -54,7 +54,7 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
|
||||
}
|
||||
|
||||
newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
|
||||
if (newnode == NULL) {
|
||||
if (!newnode) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
@ -98,7 +98,7 @@ static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
|
||||
if (force && cur && sid == cur->sid && cur->context.len)
|
||||
return &cur->context;
|
||||
|
||||
if (cur == NULL || sid != cur->sid || cur->context.len) {
|
||||
if (!cur || sid != cur->sid || cur->context.len) {
|
||||
/* Remap invalid SIDs to the unlabeled SID. */
|
||||
sid = SECINITSID_UNLABELED;
|
||||
hvalue = SIDTAB_HASH(sid);
|
||||
|
@ -504,7 +504,7 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap,
|
||||
if ((m & *cp) == 0)
|
||||
continue;
|
||||
rc = netlbl_catmap_setbit(&sap->attr.mls.cat,
|
||||
cat, GFP_ATOMIC);
|
||||
cat, GFP_KERNEL);
|
||||
if (rc < 0) {
|
||||
netlbl_catmap_free(sap->attr.mls.cat);
|
||||
return rc;
|
||||
|
@ -695,10 +695,8 @@ static int smack_parse_opts_str(char *options,
|
||||
|
||||
opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
|
||||
GFP_KERNEL);
|
||||
if (!opts->mnt_opts_flags) {
|
||||
kfree(opts->mnt_opts);
|
||||
if (!opts->mnt_opts_flags)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (fsdefault) {
|
||||
opts->mnt_opts[num_mnt_opts] = fsdefault;
|
||||
@ -4633,7 +4631,7 @@ static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct security_hook_list smack_hooks[] = {
|
||||
static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
|
||||
LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
|
||||
LSM_HOOK_INIT(syslog, smack_syslog),
|
||||
|
@ -692,7 +692,7 @@ int tomoyo_path_number_perm(const u8 type, const struct path *path,
|
||||
{
|
||||
struct tomoyo_request_info r;
|
||||
struct tomoyo_obj_info obj = {
|
||||
.path1 = *path,
|
||||
.path1 = { .mnt = path->mnt, .dentry = path->dentry },
|
||||
};
|
||||
int error = -ENOMEM;
|
||||
struct tomoyo_path_info buf;
|
||||
@ -740,7 +740,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
|
||||
struct tomoyo_path_info buf;
|
||||
struct tomoyo_request_info r;
|
||||
struct tomoyo_obj_info obj = {
|
||||
.path1 = *path,
|
||||
.path1 = { .mnt = path->mnt, .dentry = path->dentry },
|
||||
};
|
||||
int idx;
|
||||
|
||||
@ -786,7 +786,7 @@ int tomoyo_path_perm(const u8 operation, const struct path *path, const char *ta
|
||||
{
|
||||
struct tomoyo_request_info r;
|
||||
struct tomoyo_obj_info obj = {
|
||||
.path1 = *path,
|
||||
.path1 = { .mnt = path->mnt, .dentry = path->dentry },
|
||||
};
|
||||
int error;
|
||||
struct tomoyo_path_info buf;
|
||||
@ -843,7 +843,7 @@ int tomoyo_mkdev_perm(const u8 operation, const struct path *path,
|
||||
{
|
||||
struct tomoyo_request_info r;
|
||||
struct tomoyo_obj_info obj = {
|
||||
.path1 = *path,
|
||||
.path1 = { .mnt = path->mnt, .dentry = path->dentry },
|
||||
};
|
||||
int error = -ENOMEM;
|
||||
struct tomoyo_path_info buf;
|
||||
@ -890,8 +890,8 @@ int tomoyo_path2_perm(const u8 operation, const struct path *path1,
|
||||
struct tomoyo_path_info buf2;
|
||||
struct tomoyo_request_info r;
|
||||
struct tomoyo_obj_info obj = {
|
||||
.path1 = *path1,
|
||||
.path2 = *path2,
|
||||
.path1 = { .mnt = path1->mnt, .dentry = path1->dentry },
|
||||
.path2 = { .mnt = path2->mnt, .dentry = path2->dentry }
|
||||
};
|
||||
int idx;
|
||||
|
||||
|
@ -165,7 +165,7 @@ static int tomoyo_path_truncate(const struct path *path)
|
||||
*/
|
||||
static int tomoyo_path_unlink(const struct path *parent, struct dentry *dentry)
|
||||
{
|
||||
struct path path = { parent->mnt, dentry };
|
||||
struct path path = { .mnt = parent->mnt, .dentry = dentry };
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);
|
||||
}
|
||||
|
||||
@ -181,7 +181,7 @@ static int tomoyo_path_unlink(const struct path *parent, struct dentry *dentry)
|
||||
static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry,
|
||||
umode_t mode)
|
||||
{
|
||||
struct path path = { parent->mnt, dentry };
|
||||
struct path path = { .mnt = parent->mnt, .dentry = dentry };
|
||||
return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path,
|
||||
mode & S_IALLUGO);
|
||||
}
|
||||
@ -196,7 +196,7 @@ static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry,
|
||||
*/
|
||||
static int tomoyo_path_rmdir(const struct path *parent, struct dentry *dentry)
|
||||
{
|
||||
struct path path = { parent->mnt, dentry };
|
||||
struct path path = { .mnt = parent->mnt, .dentry = dentry };
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);
|
||||
}
|
||||
|
||||
@ -212,7 +212,7 @@ static int tomoyo_path_rmdir(const struct path *parent, struct dentry *dentry)
|
||||
static int tomoyo_path_symlink(const struct path *parent, struct dentry *dentry,
|
||||
const char *old_name)
|
||||
{
|
||||
struct path path = { parent->mnt, dentry };
|
||||
struct path path = { .mnt = parent->mnt, .dentry = dentry };
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);
|
||||
}
|
||||
|
||||
@ -229,7 +229,7 @@ static int tomoyo_path_symlink(const struct path *parent, struct dentry *dentry,
|
||||
static int tomoyo_path_mknod(const struct path *parent, struct dentry *dentry,
|
||||
umode_t mode, unsigned int dev)
|
||||
{
|
||||
struct path path = { parent->mnt, dentry };
|
||||
struct path path = { .mnt = parent->mnt, .dentry = dentry };
|
||||
int type = TOMOYO_TYPE_CREATE;
|
||||
const unsigned int perm = mode & S_IALLUGO;
|
||||
|
||||
@ -268,8 +268,8 @@ static int tomoyo_path_mknod(const struct path *parent, struct dentry *dentry,
|
||||
static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_dir,
|
||||
struct dentry *new_dentry)
|
||||
{
|
||||
struct path path1 = { new_dir->mnt, old_dentry };
|
||||
struct path path2 = { new_dir->mnt, new_dentry };
|
||||
struct path path1 = { .mnt = new_dir->mnt, .dentry = old_dentry };
|
||||
struct path path2 = { .mnt = new_dir->mnt, .dentry = new_dentry };
|
||||
return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
|
||||
}
|
||||
|
||||
@ -288,8 +288,8 @@ static int tomoyo_path_rename(const struct path *old_parent,
|
||||
const struct path *new_parent,
|
||||
struct dentry *new_dentry)
|
||||
{
|
||||
struct path path1 = { old_parent->mnt, old_dentry };
|
||||
struct path path2 = { new_parent->mnt, new_dentry };
|
||||
struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry };
|
||||
struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry };
|
||||
return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
|
||||
}
|
||||
|
||||
@ -417,7 +417,7 @@ static int tomoyo_sb_mount(const char *dev_name, const struct path *path,
|
||||
*/
|
||||
static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
|
||||
{
|
||||
struct path path = { mnt, mnt->mnt_root };
|
||||
struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path, NULL);
|
||||
}
|
||||
|
||||
@ -496,7 +496,7 @@ static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
* tomoyo_security_ops is a "struct security_operations" which is used for
|
||||
* registering TOMOYO.
|
||||
*/
|
||||
static struct security_hook_list tomoyo_hooks[] = {
|
||||
static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(cred_alloc_blank, tomoyo_cred_alloc_blank),
|
||||
LSM_HOOK_INIT(cred_prepare, tomoyo_cred_prepare),
|
||||
LSM_HOOK_INIT(cred_transfer, tomoyo_cred_transfer),
|
||||
|
@ -428,7 +428,7 @@ int yama_ptrace_traceme(struct task_struct *parent)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct security_hook_list yama_hooks[] = {
|
||||
static struct security_hook_list yama_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(ptrace_access_check, yama_ptrace_access_check),
|
||||
LSM_HOOK_INIT(ptrace_traceme, yama_ptrace_traceme),
|
||||
LSM_HOOK_INIT(task_prctl, yama_task_prctl),
|
||||
|
Loading…
Reference in New Issue
Block a user