Commit f4f27d00 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull security subsystem updates from James Morris:
 "Highlights:

   - A new LSM, "LoadPin", from Kees Cook is added, which allows forcing
     of modules and firmware to be loaded from a specific device (this
     is from ChromeOS, where the device as a whole is verified
     cryptographically via dm-verity).

     This is disabled by default but can be configured to be enabled by
     default (don't do this if you don't know what you're doing).

   - Keys: allow authentication data to be stored in an asymmetric key.
     Lots of general fixes and updates.

   - SELinux: add restrictions for loading of kernel modules via
     finit_module().  Distinguish non-init user namespace capability
     checks.  Apply execstack check on thread stacks"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (48 commits)
  LSM: LoadPin: provide enablement CONFIG
  Yama: use atomic allocations when reporting
  seccomp: Fix comment typo
  ima: add support for creating files using the mknodat syscall
  ima: fix ima_inode_post_setattr
  vfs: forbid write access when reading a file into memory
  fs: fix over-zealous use of "const"
  selinux: apply execstack check on thread stacks
  selinux: distinguish non-init user namespace capability checks
  LSM: LoadPin for kernel file loading restrictions
  fs: define a string representation of the kernel_read_file_id enumeration
  Yama: consolidate error reporting
  string_helpers: add kstrdup_quotable_file
  string_helpers: add kstrdup_quotable_cmdline
  string_helpers: add kstrdup_quotable
  selinux: check ss_initialized before revalidating an inode label
  selinux: delay inode label lookup as long as possible
  selinux: don't revalidate an inode's label when explicitly setting it
  selinux: Change bool variable name to index.
  KEYS: Add KEYCTL_DH_COMPUTE command
  ...
parents 2600a46e b937190c
LoadPin is a Linux Security Module that ensures all kernel-loaded files
(modules, firmware, etc) all originate from the same filesystem, with
the expectation that such a filesystem is backed by a read-only device
such as dm-verity or CDROM. This allows systems that have a verified
and/or unchangeable filesystem to enforce module and firmware loading
restrictions without needing to sign the files individually.
The LSM is selectable at build-time with CONFIG_SECURITY_LOADPIN, and
can be controlled at boot-time with the kernel command line option
"loadpin.enabled". By default, it is enabled, but can be disabled at
boot ("loadpin.enabled=0").
LoadPin starts pinning when it sees the first file loaded. If the
block device backing the filesystem is not read-only, a sysctl is
created to toggle pinning: /proc/sys/kernel/loadpin/enabled. (Having
a mutable filesystem means pinning is mutable too, but having the
sysctl allows for easy testing on systems with a mutable filesystem.)
...@@ -823,6 +823,36 @@ The keyctl syscall functions are: ...@@ -823,6 +823,36 @@ The keyctl syscall functions are:
A process must have search permission on the key for this function to be A process must have search permission on the key for this function to be
successful. successful.
(*) Compute a Diffie-Hellman shared secret or public key
long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params,
char *buffer, size_t buflen);
The params struct contains serial numbers for three keys:
- The prime, p, known to both parties
- The local private key
- The base integer, which is either a shared generator or the
remote public key
The value computed is:
result = base ^ private (mod prime)
If the base is the shared generator, the result is the local
public key. If the base is the remote public key, the result is
the shared secret.
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.
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.
=============== ===============
KERNEL SERVICES KERNEL SERVICES
...@@ -999,6 +1029,10 @@ payload contents" for more information. ...@@ -999,6 +1029,10 @@ payload contents" for more information.
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
const struct cred *cred, const struct cred *cred,
key_perm_t perm, key_perm_t perm,
int (*restrict_link)(struct key *,
const struct key_type *,
unsigned long,
const union key_payload *),
unsigned long flags, unsigned long flags,
struct key *dest); struct key *dest);
...@@ -1010,6 +1044,24 @@ payload contents" for more information. ...@@ -1010,6 +1044,24 @@ payload contents" for more information.
KEY_ALLOC_NOT_IN_QUOTA in flags if the keyring shouldn't be accounted 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. 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.
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.
A convenience function, restrict_link_reject, exists to always return
-EPERM to in this case.
(*) To check the validity of a key, this function can be called: (*) To check the validity of a key, this function can be called:
......
...@@ -10025,6 +10025,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git ...@@ -10025,6 +10025,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git
S: Supported S: Supported
F: security/apparmor/ F: security/apparmor/
LOADPIN SECURITY MODULE
M: Kees Cook <keescook@chromium.org>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git lsm/loadpin
S: Supported
F: security/loadpin/
YAMA SECURITY MODULE YAMA SECURITY MODULE
M: Kees Cook <keescook@chromium.org> M: Kees Cook <keescook@chromium.org>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip
......
...@@ -19,8 +19,7 @@ ...@@ -19,8 +19,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/verify_pefile.h> #include <linux/verification.h>
#include <keys/system_keyring.h>
#include <asm/bootparam.h> #include <asm/bootparam.h>
#include <asm/setup.h> #include <asm/setup.h>
...@@ -529,18 +528,9 @@ static int bzImage64_cleanup(void *loader_data) ...@@ -529,18 +528,9 @@ static int bzImage64_cleanup(void *loader_data)
#ifdef CONFIG_KEXEC_BZIMAGE_VERIFY_SIG #ifdef CONFIG_KEXEC_BZIMAGE_VERIFY_SIG
static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len) static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len)
{ {
bool trusted; return verify_pefile_signature(kernel, kernel_len,
int ret; NULL,
VERIFYING_KEXEC_PE_SIGNATURE);
ret = verify_pefile_signature(kernel, kernel_len,
system_trusted_keyring,
VERIFYING_KEXEC_PE_SIGNATURE,
&trusted);
if (ret < 0)
return ret;
if (!trusted)
return -EKEYREJECTED;
return 0;
} }
#endif #endif
......
...@@ -17,6 +17,7 @@ config MODULE_SIG_KEY ...@@ -17,6 +17,7 @@ config MODULE_SIG_KEY
config SYSTEM_TRUSTED_KEYRING config SYSTEM_TRUSTED_KEYRING
bool "Provide system-wide ring of trusted keys" bool "Provide system-wide ring of trusted keys"
depends on KEYS depends on KEYS
depends on ASYMMETRIC_KEY_TYPE
help help
Provide a system keyring to which trusted keys can be added. Keys in Provide a system keyring to which trusted keys can be added. Keys in
the keyring are considered to be trusted. Keys may be added at will the keyring are considered to be trusted. Keys may be added at will
...@@ -55,4 +56,12 @@ config SYSTEM_EXTRA_CERTIFICATE_SIZE ...@@ -55,4 +56,12 @@ config SYSTEM_EXTRA_CERTIFICATE_SIZE
This is the number of bytes reserved in the kernel image for a This is the number of bytes reserved in the kernel image for a
certificate to be inserted. certificate to be inserted.
config SECONDARY_TRUSTED_KEYRING
bool "Provide a keyring to which extra trustable keys may be added"
depends on SYSTEM_TRUSTED_KEYRING
help
If set, provide a keyring to which extra keys may be added, provided
those keys are not blacklisted and are vouched for by a key built
into the kernel or already in the secondary trusted keyring.
endmenu endmenu
...@@ -18,29 +18,88 @@ ...@@ -18,29 +18,88 @@
#include <keys/system_keyring.h> #include <keys/system_keyring.h>
#include <crypto/pkcs7.h> #include <crypto/pkcs7.h>
struct key *system_trusted_keyring; static struct key *builtin_trusted_keys;
EXPORT_SYMBOL_GPL(system_trusted_keyring); #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
static struct key *secondary_trusted_keys;
#endif
extern __initconst const u8 system_certificate_list[]; extern __initconst const u8 system_certificate_list[];
extern __initconst const unsigned long system_certificate_list_size; extern __initconst const unsigned long system_certificate_list_size;
/**
* restrict_link_to_builtin_trusted - Restrict keyring addition by built in CA
*
* 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,
const struct key_type *type,
const union key_payload *payload)
{
return restrict_link_by_signature(builtin_trusted_keys, type, payload);
}
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
/**
* restrict_link_by_builtin_and_secondary_trusted - Restrict keyring
* addition by both builtin and secondary keyrings
*
* Restrict the addition of keys into a keyring based on the key-to-be-added
* being vouched for by a key in either the built-in or the secondary system
* keyrings.
*/
int restrict_link_by_builtin_and_secondary_trusted(
struct key *keyring,
const struct key_type *type,
const union key_payload *payload)
{
/* 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 &&
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);
}
#endif
/* /*
* Load the compiled-in keys * Create the trusted keyrings
*/ */
static __init int system_trusted_keyring_init(void) static __init int system_trusted_keyring_init(void)
{ {
pr_notice("Initialise system trusted keyring\n"); pr_notice("Initialise system trusted keyrings\n");
system_trusted_keyring = builtin_trusted_keys =
keyring_alloc(".system_keyring", keyring_alloc(".builtin_trusted_keys",
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
((KEY_POS_ALL & ~KEY_POS_SETATTR) | ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH), KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH),
KEY_ALLOC_NOT_IN_QUOTA, NULL); KEY_ALLOC_NOT_IN_QUOTA,
if (IS_ERR(system_trusted_keyring)) NULL, NULL);
panic("Can't allocate system trusted keyring\n"); if (IS_ERR(builtin_trusted_keys))
panic("Can't allocate builtin trusted keyring\n");
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
secondary_trusted_keys =
keyring_alloc(".secondary_trusted_keys",
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH |
KEY_USR_WRITE),
KEY_ALLOC_NOT_IN_QUOTA,
restrict_link_by_builtin_and_secondary_trusted,
NULL);
if (IS_ERR(secondary_trusted_keys))
panic("Can't allocate secondary trusted keyring\n");
if (key_link(secondary_trusted_keys, builtin_trusted_keys) < 0)
panic("Can't link trusted keyrings\n");
#endif
set_bit(KEY_FLAG_TRUSTED_ONLY, &system_trusted_keyring->flags);
return 0; return 0;
} }
...@@ -76,7 +135,7 @@ static __init int load_system_certificate_list(void) ...@@ -76,7 +135,7 @@ static __init int load_system_certificate_list(void)
if (plen > end - p) if (plen > end - p)
goto dodgy_cert; goto dodgy_cert;
key = key_create_or_update(make_key_ref(system_trusted_keyring, 1), key = key_create_or_update(make_key_ref(builtin_trusted_keys, 1),
"asymmetric", "asymmetric",
NULL, NULL,
p, p,
...@@ -84,8 +143,8 @@ static __init int load_system_certificate_list(void) ...@@ -84,8 +143,8 @@ static __init int load_system_certificate_list(void)
((KEY_POS_ALL & ~KEY_POS_SETATTR) | ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ), KEY_USR_VIEW | KEY_USR_READ),
KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_NOT_IN_QUOTA |
KEY_ALLOC_TRUSTED | KEY_ALLOC_BUILT_IN |
KEY_ALLOC_BUILT_IN); KEY_ALLOC_BYPASS_RESTRICTION);
if (IS_ERR(key)) { if (IS_ERR(key)) {
pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
PTR_ERR(key)); PTR_ERR(key));
...@@ -108,19 +167,27 @@ late_initcall(load_system_certificate_list); ...@@ -108,19 +167,27 @@ late_initcall(load_system_certificate_list);
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION #ifdef CONFIG_SYSTEM_DATA_VERIFICATION
/** /**
* Verify a PKCS#7-based signature on system data. * verify_pkcs7_signature - Verify a PKCS#7-based signature on system data.
* @data: The data to be verified. * @data: The data to be verified (NULL if expecting internal data).
* @len: Size of @data. * @len: Size of @data.
* @raw_pkcs7: The PKCS#7 message that is the signature. * @raw_pkcs7: The PKCS#7 message that is the signature.
* @pkcs7_len: The size of @raw_pkcs7. * @pkcs7_len: The size of @raw_pkcs7.
* @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only,
* (void *)1UL for all trusted keys).
* @usage: The use to which the key is being put. * @usage: The use to which the key is being put.
* @view_content: Callback to gain access to content.
* @ctx: Context for callback.
*/ */
int system_verify_data(const void *data, unsigned long len, int verify_pkcs7_signature(const void *data, size_t len,
const void *raw_pkcs7, size_t pkcs7_len, const void *raw_pkcs7, size_t pkcs7_len,
enum key_being_used_for usage) struct key *trusted_keys,
enum key_being_used_for usage,
int (*view_content)(void *ctx,
const void *data, size_t len,
size_t asn1hdrlen),
void *ctx)
{ {
struct pkcs7_message *pkcs7; struct pkcs7_message *pkcs7;
bool trusted;
int ret; int ret;
pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len); pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
...@@ -128,7 +195,7 @@ int system_verify_data(const void *data, unsigned long len, ...@@ -128,7 +195,7 @@ int system_verify_data(const void *data, unsigned long len,
return PTR_ERR(pkcs7); return PTR_ERR(pkcs7);
/* The data should be detached - so we need to supply it. */ /* The data should be detached - so we need to supply it. */
if (pkcs7_supply_detached_data(pkcs7, data, len) < 0) { if (data && pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
pr_err("PKCS#7 signature with non-detached data\n"); pr_err("PKCS#7 signature with non-detached data\n");
ret = -EBADMSG; ret = -EBADMSG;
goto error; goto error;
...@@ -138,13 +205,33 @@ int system_verify_data(const void *data, unsigned long len, ...@@ -138,13 +205,33 @@ int system_verify_data(const void *data, unsigned long len,
if (ret < 0) if (ret < 0)
goto error; goto error;
ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted); if (!trusted_keys) {
if (ret < 0) trusted_keys = builtin_trusted_keys;
} else if (trusted_keys == (void *)1UL) {
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
trusted_keys = secondary_trusted_keys;
#else
trusted_keys = builtin_trusted_keys;
#endif
}
ret = pkcs7_validate_trust(pkcs7, trusted_keys);
if (ret < 0) {
if (ret == -ENOKEY)
pr_err("PKCS#7 signature not signed with a trusted key\n");
goto error; goto error;
}
if (!trusted) { if (view_content) {
pr_err("PKCS#7 signature not signed with a trusted key\n"); size_t asn1hdrlen;
ret = -ENOKEY;
ret = pkcs7_get_content_data(pkcs7, &data, &len, &asn1hdrlen);
if (ret < 0) {
if (ret == -ENODATA)
pr_devel("PKCS#7 message does not contain data\n");
goto error;
}
ret = view_content(ctx, data, len, asn1hdrlen);
} }
error: error:
...@@ -152,6 +239,6 @@ int system_verify_data(const void *data, unsigned long len, ...@@ -152,6 +239,6 @@ int system_verify_data(const void *data, unsigned long len,
pr_devel("<==%s() = %d\n", __func__, ret); pr_devel("<==%s() = %d\n", __func__, ret);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(system_verify_data); EXPORT_SYMBOL_GPL(verify_pkcs7_signature);
#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
menuconfig ASYMMETRIC_KEY_TYPE menuconfig ASYMMETRIC_KEY_TYPE
tristate "Asymmetric (public-key cryptographic) key type" bool "Asymmetric (public-key cryptographic) key type"
depends on KEYS depends on KEYS
help help
This option provides support for a key type that holds the data for This option provides support for a key type that holds the data for
...@@ -40,8 +40,7 @@ config PKCS7_MESSAGE_PARSER ...@@ -40,8 +40,7 @@ config PKCS7_MESSAGE_PARSER
config PKCS7_TEST_KEY config PKCS7_TEST_KEY
tristate "PKCS#7 testing key type" tristate "PKCS#7 testing key type"
depends on PKCS7_MESSAGE_PARSER depends on SYSTEM_DATA_VERIFICATION
select SYSTEM_TRUSTED_KEYRING
help help
This option provides a type of key that can be loaded up from a This option provides a type of key that can be loaded up from a
PKCS#7 message - provided the message is signed by a trusted key. If PKCS#7 message - provided the message is signed by a trusted key. If
...@@ -54,6 +53,7 @@ config PKCS7_TEST_KEY ...@@ -54,6 +53,7 @@ config PKCS7_TEST_KEY
config SIGNED_PE_FILE_VERIFICATION config SIGNED_PE_FILE_VERIFICATION
bool "Support for PE file signature verification" bool "Support for PE file signature verification"
depends on PKCS7_MESSAGE_PARSER=y depends on PKCS7_MESSAGE_PARSER=y
depends on SYSTEM_DATA_VERIFICATION
select ASN1 select ASN1
select OID_REGISTRY select OID_REGISTRY
help help
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
asymmetric_keys-y := asymmetric_type.o signature.o asymmetric_keys-y := \
asymmetric_type.o \
restrict.o \
signature.o
obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
* 2 of the Licence, or (at your option) any later version. * 2 of the Licence, or (at your option) any later version.
*/ */
#include <keys/asymmetric-type.h>
extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id); extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id);
extern int __asymmetric_key_hex_to_key_id(const char *id, extern int __asymmetric_key_hex_to_key_id(const char *id,
......
...@@ -34,6 +34,95 @@ EXPORT_SYMBOL_GPL(key_being_used_for); ...@@ -34,6 +34,95 @@ EXPORT_SYMBOL_GPL(key_being_used_for);
static LIST_HEAD(asymmetric_key_parsers); static LIST_HEAD(asymmetric_key_parsers);
static DECLARE_RWSEM(asymmetric_key_parsers_sem); static DECLARE_RWSEM(asymmetric_key_parsers_sem);
/**
* find_asymmetric_key - Find a key by ID.
* @keyring: The keys to search.
* @id_0: The first ID to look for or NULL.
* @id_1: The second ID to look for or NULL.
* @partial: Use partial match if true, exact if false.
*
* Find a key in the given keyring by identifier. The preferred identifier is
* the id_0 and the fallback identifier is the id_1. If both are given, the
* lookup is by the former, but the latter must also match.
*/
struct key *find_asymmetric_key(struct key *keyring,
const struct asymmetric_key_id *id_0,
const struct asymmetric_key_id *id_1,
bool partial)
{
struct key *key;
key_ref_t ref;
const char *lookup;
char *req, *p;
int len;
if (id_0) {
lookup = id_0->data;
len = id_0->len;
} else {
lookup = id_1->data;
len = id_1->len;
}
/* Construct an identifier "id:<keyid>". */
p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
if (!req)
return ERR_PTR(-ENOMEM);
if (partial) {
*p++ = 'i';
*p++ = 'd';
} else {
*p++ = 'e';
*p++ = 'x';
}
*p++ = ':';
p = bin2hex(p, lookup, len);
*p = 0;
pr_debug("Look up: \"%s\"\n", req);
ref = keyring_search(make_key_ref(keyring, 1),
&key_type_asymmetric, req);
if (IS_ERR(ref))
pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
kfree(req);
if (IS_ERR(ref)) {
switch (PTR_ERR(ref)) {
/* Hide some search errors */
case -EACCES:
case -ENOTDIR:
case -EAGAIN:
return ERR_PTR(-ENOKEY);
default:
return ERR_CAST(ref);
}
}
key = key_ref_to_ptr(ref);
if (id_0 && id_1) {
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
if (!kids->id[0]) {
pr_debug("First ID matches, but second is missing\n");
goto reject;
}
if (!asymmetric_key_id_same(id_1, kids->id[1])) {
pr_debug("First ID matches, but second does not\n");
goto reject;
}
}
pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
return key;
reject:
key_put(key);
return ERR_PTR(-EKEYREJECTED);
}
EXPORT_SYMBOL_GPL(find_asymmetric_key);
/** /**
* asymmetric_key_generate_id: Construct an asymmetric key ID * asymmetric_key_generate_id: Construct an asymmetric key ID
* @val_1: First binary blob * @val_1: First binary blob
...@@ -331,7 +420,8 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) ...@@ -331,7 +420,8 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
pr_devel("==>%s()\n", __func__); pr_devel("==>%s()\n", __func__);
if (subtype) { if (subtype) {
subtype->destroy(prep->payload.data[asym_crypto]); subtype->destroy(prep->payload.data[asym_crypto],
prep->payload.data[asym_auth]);
module_put(subtype->owner); module_put(subtype->owner);
} }
asymmetric_key_free_kids(kids); asymmetric_key_free_kids(kids);
...@@ -346,13 +436,15 @@ static void asymmetric_key_destroy(struct key *key) ...@@ -346,13 +436,15 @@ static void asymmetric_key_destroy(struct key *key)
struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids]; struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids];
void *data = key->payload.data[asym_crypto]; void *data = key->payload.data[asym_crypto];
void *auth = key->payload.data[asym_auth];
key->payload.data[asym_crypto] = NULL; key->payload.data[asym_crypto] = NULL;
key->payload.data[asym_subtype] = NULL; key->payload.data[asym_subtype] = NULL;
key->payload.data[asym_key_ids] = NULL; key->payload.data[asym_key_ids] = NULL;
key->payload.data[asym_auth] = NULL;
if (subtype) { if (subtype) {
subtype->destroy(data); subtype->destroy(data, auth);
module_put(subtype->owner); module_put(subtype->owner);
} }
......
...@@ -21,19 +21,13 @@ ...@@ -21,19 +21,13 @@
/* /*
* Parse a Microsoft Individual Code Signing blob * Parse a Microsoft Individual Code Signing blob
*/ */
int mscode_parse(struct pefile_context *ctx) int mscode_parse(void *_ctx, const void *content_data, size_t data_len,
size_t asn1hdrlen)
{ {
const void *content_data; struct pefile_context *ctx = _ctx;
size_t data_len;
int ret;
ret = pkcs7_get_content_data(ctx->pkcs7, &content_data, &data_len, 1);
if (ret) {
pr_debug("PKCS#7 message does not contain data\n");
return ret;
}
content_data -= asn1hdrlen;
data_len += asn1hdrlen;
pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len), pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len),
content_data); content_data);
...@@ -129,7 +123,6 @@ int mscode_note_digest(void *context, size_t hdrlen, ...@@ -129,7 +123,6 @@ int mscode_note_digest(void *context, size_t hdrlen,
{ {
struct pefile_context *ctx = context; struct pefile_context *ctx = context;
ctx->digest = value; ctx->digest = kmemdup(value, vlen, GFP_KERNEL);
ctx->digest_len = vlen; return ctx->digest ? 0 : -ENOMEM;
return 0;
} }
...@@ -13,12 +13,9 @@ ...@@ -13,12 +13,9 @@
#include <linux/key.h> #include <linux/key.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/verification.h>
#include <linux/key-type.h> #include <linux/key-type.h>
#include <keys/asymmetric-type.h>
#include <crypto/pkcs7.h>
#include <keys/user-type.h> #include <keys/user-type.h>
#include <keys/system_keyring.h>
#include "pkcs7_parser.h"
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PKCS#7 testing key type"); MODULE_DESCRIPTION("PKCS#7 testing key type");
...@@ -29,59 +26,46 @@ MODULE_PARM_DESC(pkcs7_usage, ...@@ -29,59 +26,46 @@ MODULE_PARM_DESC(pkcs7_usage,
"Usage to specify when verifying the PKCS#7 message"); "Usage to specify when verifying the PKCS#7 message");
/* /*
* Preparse a PKCS#7 wrapped and validated data blob. * Retrieve the PKCS#7 message content.
*/ */
static int pkcs7_preparse(struct key_preparsed_payload *prep) static int pkcs7_view_content(void *ctx, const void *data, size_t len,
size_t asn1hdrlen)
{ {
enum key_being_used_for usage = pkcs7_usage; struct key_preparsed_payload *prep = ctx;
struct pkcs7_message *pkcs7; const void *saved_prep_data;
const void *data, *saved_prep_data; size_t saved_prep_datalen;
size_t datalen, saved_prep_datalen;
bool trusted;
int ret; int ret;
kenter("");
if (usage >= NR__KEY_BEING_USED_FOR) {
pr_err("Invalid usage type %d\n", usage);
return -EINVAL;
}
saved_prep_data = prep->data; saved_prep_data = prep->data;
saved_prep_datalen = prep->datalen; saved_prep_datalen = prep->datalen;
pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen);
if (IS_ERR(pkcs7)) {
ret = PTR_ERR(pkcs7);
goto error;
}
ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error_free;
ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted);
if (ret < 0)
goto error_free;
if (!trusted)
pr_warn("PKCS#7 message doesn't chain back to a trusted key\n");
ret = pkcs7_get_content_data(pkcs7, &data, &datalen, false);
if (ret < 0)
goto error_free;
prep->data = data; prep->data = data;
prep->datalen = datalen; prep->datalen = len;
ret = user_preparse(prep); ret = user_preparse(prep);
prep->data = saved_prep_data; prep->data = saved_prep_data;
prep->datalen = saved_prep_datalen; prep->datalen = saved_prep_datalen;
error_free:
pkcs7_free_message(pkcs7);
error:
kleave(" = %d", ret);
return ret; return ret;
} }
/*
* Preparse a PKCS#7 wrapped and validated data blob.
*/
static int pkcs7_preparse(struct key_preparsed_payload *prep)
{
enum key_being_used_for usage = pkcs7_usage;
if (usage >= NR__KEY_BEING_USED_FOR) {
pr_err("Invalid usage type %d\n", usage);
return -EINVAL;
}
return verify_pkcs7_signature(NULL, 0,
prep->data, prep->datalen,
NULL, usage,
pkcs7_view_content, prep);
}
/* /*
* user defined keys take an arbitrary string as the description and an * user defined keys take an arbitrary string as the description and an
* arbitrary blob of data as the payload * arbitrary blob of data as the payload
......
...@@ -44,9 +44,7 @@ struct pkcs7_parse_context { ...@@ -44,9 +44,7 @@ struct pkcs7_parse_context {
static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
{ {
if (sinfo) { if (sinfo) {
kfree(sinfo->sig.s); public_key_signature_free(sinfo->sig);
kfree(sinfo->sig.digest);
kfree(sinfo->signing_cert_id);
kfree(sinfo); kfree(sinfo);
} }
} }
...@@ -125,6 +123,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) ...@@ -125,6 +123,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
if (!ctx->sinfo) if (!ctx->sinfo)
goto out_no_sinfo; goto out_no_sinfo;
ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
GFP_KERNEL);
if (!ctx->sinfo->sig)
goto out_no_sig;
ctx->data = (unsigned long)data; ctx->data = (unsigned long)data;
ctx->ppcerts = &ctx->certs; ctx->ppcerts = &ctx->certs;
...@@ -150,6 +152,7 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) ...@@ -150,6 +152,7 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
ctx->certs = cert->next; ctx->certs = cert->next;
x509_free_certificate(cert); x509_free_certificate(cert);
} }
out_no_sig:
pkcs7_free_signed_info(ctx->sinfo); pkcs7_free_signed_info(ctx->sinfo);
out_no_sinfo: out_no_sinfo:
pkcs7_free_message(ctx->msg); pkcs7_free_message(ctx->msg);
...@@ -165,24 +168,25 @@ EXPORT_SYMBOL_GPL(pkcs7_parse_message); ...@@ -165,24 +168,25 @@ EXPORT_SYMBOL_GPL(pkcs7_parse_message);
* @pkcs7: The preparsed PKCS#7 message to access * @pkcs7: The preparsed PKCS#7 message to access
* @_data: Place to return a pointer to the data * @_data: Place to return a pointer to the data
* @_data_len: Place to return the data length * @_data_len: Place to return the data length
* @want_wrapper: True if the ASN.1 object header should be included in the data * @_headerlen: Size of ASN.1 header not included in _data
* *
* Get access to the data content of the PKCS#7 message, including, optionally, * Get access to the data content of the PKCS#7 message. The size of the
* the header of the ASN.1 object that contains it. Returns -ENODATA if the * header of the ASN.1 object that contains it is also provided and can be used
* data object was missing from the message. * to adjust *_data and *_data_len to get the entire object.
*
* Returns -ENODATA if the data object was missing from the message.
*/ */
int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
const void **_data, size_t *_data_len, const void **_data, size_t *_data_len,
bool want_wrapper) size_t *_headerlen)
{ {
size_t wrapper;
if (!pkcs7->data) if (!pkcs7->data)
return -ENODATA; return -ENODATA;
wrapper = want_wrapper ? pkcs7->data_hdrlen : 0; *_data = pkcs7->data;
*_data = pkcs7->data - wrapper; *_data_len = pkcs7->data_len;
*_data_len = pkcs7->data_len + wrapper; if (_headerlen)
*_headerlen = pkcs7->data_hdrlen;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(pkcs7_get_content_data); EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
...@@ -218,25 +222,25 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen, ...@@ -218,25 +222,25 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
switch (ctx->last_oid) { switch (ctx->last_oid) {
case OID_md4: case OID_md4:
ctx->sinfo->sig.hash_algo = "md4"; ctx->sinfo->sig->hash_algo = "md4";
break; break;
case OID_md5: case OID_md5:
ctx->sinfo->sig.hash_algo = "md5"; ctx->sinfo->sig->hash_algo = "md5";
break; break;
case OID_sha1: case OID_sha1:
ctx->sinfo->sig.hash_algo = "sha1"; ctx->sinfo->sig->hash_algo = "sha1";
break; break;
case OID_sha256: case OID_sha256:
ctx->sinfo->sig.hash_algo = "sha256"; ctx->sinfo->sig->hash_algo = "sha256";
break; break;
case OID_sha384: case OID_sha384:
ctx->sinfo->sig.hash_algo = "sha384"; ctx->sinfo->sig->hash_algo = "sha384";
break; break;
case OID_sha512: case OID_sha512:
ctx->sinfo->sig.hash_algo = "sha512"; ctx->sinfo->sig->hash_algo = "sha512";
break; break;
case OID_sha224: case OID_sha224:
ctx->sinfo->sig.hash_algo = "sha224"; ctx->sinfo->sig->hash_algo = "sha224";
break; break;
default: default:
printk("Unsupported digest algo: %u\n", ctx->last_oid); printk("Unsupported digest algo: %u\n", ctx->last_oid);
...@@ -256,7 +260,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, ...@@ -256,7 +260,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
switch (ctx->last_oid) { switch (ctx->last_oid) {
case OID_rsaEncryption: case OID_rsaEncryption:
ctx->sinfo->sig.pkey_algo = "rsa"; ctx->sinfo->sig->pkey_algo = "rsa";
break; break;
default: default:
printk("Unsupported pkey algo: %u\n", ctx->last_oid); printk("Unsupported pkey algo: %u\n", ctx->last_oid);
...@@ -616,11 +620,11 @@ int pkcs7_sig_note_signature(void *context, size_t hdrlen, ...@@ -616,11 +620,11 @@ int pkcs7_sig_note_signature(void *context, size_t hdrlen,
{ {
struct pkcs7_parse_context *ctx = context; struct pkcs7_parse_context *ctx = context;
ctx->sinfo->sig.s = kmemdup(value, vlen, GFP_KERNEL); ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);
if (!ctx->sinfo->sig.s) if (!ctx->sinfo->sig->s)
return -ENOMEM; return -ENOMEM;
ctx->sinfo->sig.s_size = vlen; ctx->sinfo->sig->s_size = vlen;
return 0; return 0;
} }
...@@ -656,12 +660,16 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen, ...@@ -656,12 +660,16 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen,
pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data); pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);
sinfo->signing_cert_id = kid; sinfo->sig->auth_ids[0] = kid;
sinfo->index = ++ctx->sinfo_index; sinfo->index = ++ctx->sinfo_index;
*ctx->ppsinfo = sinfo; *ctx->ppsinfo = sinfo;
ctx->ppsinfo = &sinfo->next; ctx->ppsinfo = &sinfo->next;
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
if (!ctx->sinfo) if (!ctx->sinfo)
return -ENOMEM; return -ENOMEM;
ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
GFP_KERNEL);
if (!ctx->sinfo->sig)
return -ENOMEM;
return 0; return 0;
} }
...@@ -22,7 +22,6 @@ struct pkcs7_signed_info { ...@@ -22,7 +22,6 @@ struct pkcs7_signed_info {
struct pkcs7_signed_info *next; struct pkcs7_signed_info *next;
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
unsigned index; unsigned index;
bool trusted;
bool unsupported_crypto; /* T if not usable due to missing crypto */ bool unsupported_crypto; /* T if not usable due to missing crypto */
/* Message digest - the digest of the Content Data (or NULL) */ /* Message digest - the digest of the Content Data (or NULL) */
...@@ -41,19 +40,17 @@ struct pkcs7_signed_info { ...@@ -41,19 +40,17 @@ struct pkcs7_signed_info {
#define sinfo_has_ms_statement_type 5 #define sinfo_has_ms_statement_type 5
time64_t signing_time; time64_t signing_time;
/* Issuing cert serial number and issuer's name [PKCS#7 or CMS ver 1]
* or issuing cert's SKID [CMS ver 3].
*/
struct asymmetric_key_id *signing_cert_id;
/* Message signature. /* Message signature.
* *
* This contains the generated digest of _either_ the Content Data or * This contains the generated digest of _either_ the Content Data or
* the Authenticated Attributes [RFC2315 9.3]. If the latter, one of * the Authenticated Attributes [RFC2315 9.3]. If the latter, one of
* the attributes contains the digest of the the Content Data within * the attributes contains the digest of the the Content Data within
* it. * it.
*
* THis also contains the issuing cert serial number and issuer's name
* [PKCS#7 or CMS ver 1] or issuing cert's SKID [CMS ver 3].
*/ */
struct public_key_signature sig; struct public_key_signature *sig;
}; };
struct pkcs7_message { struct pkcs7_message {
......
...@@ -27,10 +27,9 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, ...@@ -27,10 +27,9 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
struct pkcs7_signed_info *sinfo, struct pkcs7_signed_info *sinfo,
struct key *trust_keyring) struct key *trust_keyring)
{ {
struct public_key_signature *sig = &sinfo->sig; struct public_key_signature *sig = sinfo->sig;
struct x509_certificate *x509, *last = NULL, *p; struct x509_certificate *x509, *last = NULL, *p;
struct key *key; struct key *key;
bool trusted;
int ret; int ret;
kenter(",%u,", sinfo->index); kenter(",%u,", sinfo->index);
...@@ -42,10 +41,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, ...@@ -42,10 +41,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
for (x509 = sinfo->signer; x509; x509 = x509->signer) { for (x509 = sinfo->signer; x509; x509 = x509->signer) {
if (x509->seen) { if (x509->seen) {
if (x509->verified) { if (x509->verified)
trusted = x509->trusted;
goto verified; goto verified;
}
kleave(" = -ENOKEY [cached]"); kleave(" = -ENOKEY [cached]");
return -ENOKEY; return -ENOKEY;
} }
...@@ -54,9 +51,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, ...@@ -54,9 +51,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
/* Look to see if this certificate is present in the trusted /* Look to see if this certificate is present in the trusted
* keys. * keys.
*/ */
key = x509_request_asymmetric_key(trust_keyring, key = find_asymmetric_key(trust_keyring,
x509->id, x509->skid, x509->id, x509->skid, false);
false);
if (!IS_ERR(key)) { if (!IS_ERR(key)) {
/* One of the X.509 certificates in the PKCS#7 message /* One of the X.509 certificates in the PKCS#7 message
* is apparently the same as one we already trust. * is apparently the same as one we already trust.
...@@ -80,16 +76,16 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, ...@@ -80,16 +76,16 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
might_sleep(); might_sleep();
last = x509; last = x509;
sig = &last->sig; sig = last->sig;
} }
/* No match - see if the root certificate has a signer amongst the /* No match - see if the root certificate has a signer amongst the
* trusted keys. * trusted keys.
*/ */
if (last && (last->akid_id || last->akid_skid)) { if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) {
key = x509_request_asymmetric_key(trust_keyring, key = find_asymmetric_key(trust_keyring,
last->akid_id, last->sig->auth_ids[0],
last->akid_skid, last->sig->auth_ids[1],
false); false);
if (!IS_ERR(key)) { if (!IS_ERR(key)) {
x509 = last; x509 = last;
...@@ -104,10 +100,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, ...@@ -104,10 +100,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
/* As a last resort, see if we have a trusted public key that matches /* As a last resort, see if we have a trusted public key that matches
* the signed info directly. * the signed info directly.
*/ */
key = x509_request_asymmetric_key(trust_keyring, key = find_asymmetric_key(trust_keyring,
sinfo->signing_cert_id, sinfo->sig->auth_ids[0], NULL, false);
NULL,
false);
if (!IS_ERR(key)) { if (!IS_ERR(key)) {
pr_devel("sinfo %u: Direct signer is key %x\n", pr_devel("sinfo %u: Direct signer is key %x\n",
sinfo->index, key_serial(key)); sinfo->index, key_serial(key));
...@@ -122,7 +116,6 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, ...@@ -122,7 +116,6 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
matched: matched:
ret = verify_signature(key, sig); ret = verify_signature(key, sig);
trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags);
key_put(key); key_put(key);
if (ret < 0) { if (ret < 0) {
if (ret == -ENOMEM) if (ret == -ENOMEM)
...@@ -134,12 +127,9 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, ...@@ -134,12 +127,9 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
verified: verified:
if (x509) { if (x509) {
x509->verified = true; x509->verified = true;
for (p = sinfo->signer; p != x509; p = p->signer) { for (p = sinfo->signer; p != x509; p = p->signer)
p->verified = true; p->verified = true;
p->trusted = trusted;
} }
}
sinfo->trusted = trusted;
kleave(" = 0"); kleave(" = 0");
return 0; return 0;
} }
...@@ -148,7 +138,6 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, ...@@ -148,7 +138,6 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
* pkcs7_validate_trust - Validate PKCS#7 trust chain * pkcs7_validate_trust - Validate PKCS#7 trust chain
* @pkcs7: The PKCS#7 certificate to validate * @pkcs7: The PKCS#7 certificate to validate
* @trust_keyring: Signing certificates to use as starting points * @trust_keyring: Signing certificates to use as starting points
* @_trusted: Set to true if trustworth, false otherwise
* *
* Validate that the certificate chain inside the PKCS#7 message intersects * Validate that the certificate chain inside the PKCS#7 message intersects
* keys we already know and trust. * keys we already know and trust.
...@@ -170,16 +159,13 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, ...@@ -170,16 +159,13 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
* May also return -ENOMEM. * May also return -ENOMEM.
*/ */
int pkcs7_validate_trust(struct pkcs7_message *pkcs7, int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
struct key *trust_keyring, struct key *trust_keyring)
bool *_trusted)
{ {
struct pkcs7_signed_info *sinfo; struct pkcs7_signed_info *sinfo;
struct x509_certificate *p; struct x509_certificate *p;
int cached_ret = -ENOKEY; int cached_ret = -ENOKEY;
int ret; int ret;
*_trusted = false;
for (p = pkcs7->certs; p; p = p->next) for (p = pkcs7->certs; p; p = p->next)
p->seen = false; p->seen = false;
...@@ -193,7 +179,6 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, ...@@ -193,7 +179,6 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
cached_ret = -ENOPKG; cached_ret = -ENOPKG;
continue; continue;
case 0: case 0:
*_trusted |= sinfo->trusted;
cached_ret = 0; cached_ret = 0;
continue; continue;
default: default:
......
...@@ -25,34 +25,36 @@ ...@@ -25,34 +25,36 @@
static int pkcs7_digest(struct pkcs7_message *pkcs7, static int pkcs7_digest(struct pkcs7_message *pkcs7,
struct pkcs7_signed_info *sinfo) struct pkcs7_signed_info *sinfo)
{ {
struct public_key_signature *sig = sinfo->sig;
struct crypto_shash *tfm; struct crypto_shash *tfm;
struct shash_desc *desc; struct shash_desc *desc;
size_t digest_size, desc_size; size_t desc_size;
void *digest;
int ret; int ret;
kenter(",%u,%s", sinfo->index, sinfo->sig.hash_algo); kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo);
if (!sinfo->sig.hash_algo) if (!sinfo->sig->hash_algo)
return -ENOPKG; return -ENOPKG;
/* Allocate the hashing algorithm we're going to need and find out how /* Allocate the hashing algorithm we're going to need and find out how
* big the hash operational data will be. * big the hash operational data will be.
*/ */
tfm = crypto_alloc_shash(sinfo->sig.hash_algo, 0, 0); tfm = crypto_alloc_shash(sinfo->sig->hash_algo, 0, 0);
if (IS_ERR(tfm)) if (IS_ERR(tfm))
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm); sig->digest_size = crypto_shash_digestsize(tfm);
ret = -ENOMEM; ret = -ENOMEM;
digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size, sig->digest = kmalloc(sig->digest_size, GFP_KERNEL);
GFP_KERNEL); if (!sig->digest)
if (!digest) goto error_no_desc;
desc = kzalloc(desc_size, GFP_KERNEL);
if (!desc)
goto error_no_desc; goto error_no_desc;
desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc));
desc->tfm = tfm; desc->tfm = tfm;
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
...@@ -60,10 +62,11 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, ...@@ -60,10 +62,11 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
ret = crypto_shash_init(desc); ret = crypto_shash_init(desc);
if (ret < 0) if (ret < 0)
goto error; goto error;
ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest); ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len,
sig->digest);
if (ret < 0) if (ret < 0)
goto error; goto error;
pr_devel("MsgDigest = [%*ph]\n", 8, digest); pr_devel("MsgDigest = [%*ph]\n", 8, sig->digest);
/* However, if there are authenticated attributes, there must be a /* However, if there are authenticated attributes, there must be a
* message digest attribute amongst them which corresponds to the * message digest attribute amongst them which corresponds to the
...@@ -78,14 +81,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, ...@@ -78,14 +81,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
goto error; goto error;
} }
if (sinfo->msgdigest_len != sinfo->sig.digest_size) { if (sinfo->msgdigest_len != sig->digest_size) {
pr_debug("Sig %u: Invalid digest size (%u)\n", pr_debug("Sig %u: Invalid digest size (%u)\n",
sinfo->index, sinfo->msgdigest_len); sinfo->index, sinfo->msgdigest_len);
ret = -EBADMSG; ret = -EBADMSG;
goto error; goto error;
} }
if (memcmp(digest, sinfo->msgdigest, sinfo->msgdigest_len) != 0) { if (memcmp(sig->digest, sinfo->msgdigest,
sinfo->msgdigest_len) != 0) {
pr_debug("Sig %u: Message digest doesn't match\n", pr_debug("Sig %u: Message digest doesn't match\n",
sinfo->index); sinfo->index);
ret = -EKEYREJECTED; ret = -EKEYREJECTED;
...@@ -97,7 +101,7 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, ...@@ -97,7 +101,7 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
* convert the attributes from a CONT.0 into a SET before we * convert the attributes from a CONT.0 into a SET before we
* hash it. * hash it.
*/ */
memset(digest, 0, sinfo->sig.digest_size); memset(sig->digest, 0, sig->digest_size);
ret = crypto_shash_init(desc); ret = crypto_shash_init(desc);
if (ret < 0) if (ret < 0)
...@@ -107,17 +111,14 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, ...@@ -107,17 +111,14 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
if (ret < 0) if (ret < 0)
goto error; goto error;
ret = crypto_shash_finup(desc, sinfo->authattrs, ret = crypto_shash_finup(desc, sinfo->authattrs,
sinfo->authattrs_len, digest); sinfo->authattrs_len, sig->digest);
if (ret < 0) if (ret < 0)
goto error; goto error;
pr_devel("AADigest = [%*ph]\n", 8, digest); pr_devel("AADigest = [%*ph]\n", 8, sig->digest);
} }
sinfo->sig.digest = digest;
digest = NULL;
error: error:
kfree(digest); kfree(desc);
error_no_desc: error_no_desc:
crypto_free_shash(tfm); crypto_free_shash(tfm);
kleave(" = %d", ret); kleave(" = %d", ret);
...@@ -144,12 +145,12 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, ...@@ -144,12 +145,12 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
* PKCS#7 message - but I can't be 100% sure of that. It's * PKCS#7 message - but I can't be 100% sure of that. It's
* possible this will need element-by-element comparison. * possible this will need element-by-element comparison.
*/ */
if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id)) if (!asymmetric_key_id_same(x509->id, sinfo->sig->auth_ids[0]))
continue; continue;
pr_devel("Sig %u: Found cert serial match X.509[%u]\n", pr_devel("Sig %u: Found cert serial match X.509[%u]\n",
sinfo->index, certix); sinfo->index, certix);
if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) { if (x509->pub->pkey_algo != sinfo->sig->pkey_algo) {
pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n", pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n",
sinfo->index); sinfo->index);
continue; continue;
...@@ -164,7 +165,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, ...@@ -164,7 +165,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
*/ */
pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n", pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n",
sinfo->index, sinfo->index,
sinfo->signing_cert_id->len, sinfo->signing_cert_id->data); sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data);
return 0; return 0;
} }
...@@ -174,6 +175,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, ...@@ -174,6 +175,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
struct pkcs7_signed_info *sinfo) struct pkcs7_signed_info *sinfo)
{ {
struct public_key_signature *sig;
struct x509_certificate *x509 = sinfo->signer, *p; struct x509_certificate *x509 = sinfo->signer, *p;
struct asymmetric_key_id *auth; struct asymmetric_key_id *auth;
int ret; int ret;
...@@ -188,34 +190,26 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, ...@@ -188,34 +190,26 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
x509->subject, x509->subject,
x509->raw_serial_size, x509->raw_serial); x509->raw_serial_size, x509->raw_serial);
x509->seen = true; x509->seen = true;
ret = x509_get_sig_params(x509); if (x509->unsupported_key)
if (ret < 0) goto unsupported_crypto_in_x509;
goto maybe_missing_crypto_in_x509;
pr_debug("- issuer %s\n", x509->issuer); pr_debug("- issuer %s\n", x509->issuer);
if (x509->akid_id) sig = x509->sig;
if (sig->auth_ids[0])
pr_debug("- authkeyid.id %*phN\n", pr_debug("- authkeyid.id %*phN\n",
x509->akid_id->len, x509->akid_id->data); sig->auth_ids[0]->len, sig->auth_ids[0]->data);
if (x509->akid_skid) if (sig->auth_ids[1])
pr_debug("- authkeyid.skid %*phN\n", pr_debug("- authkeyid.skid %*phN\n",
x509->akid_skid->len, x509->akid_skid->data); sig->auth_ids[1]->len, sig->auth_ids[1]->data);
if ((!x509->akid_id && !x509->akid_skid) || if (x509->self_signed) {
strcmp(x509->subject, x509->issuer) == 0) {
/* If there's no authority certificate specified, then /* If there's no authority certificate specified, then
* the certificate must be self-signed and is the root * the certificate must be self-signed and is the root
* of the chain. Likewise if the cert is its own * of the chain. Likewise if the cert is its own
* authority. * authority.
*/ */
pr_debug("- no auth?\n"); if (x509->unsupported_sig)
if (x509->raw_subject_size != x509->raw_issuer_size || goto unsupported_crypto_in_x509;
memcmp(x509->raw_subject, x509->raw_issuer,
x509->raw_issuer_size) != 0)
return 0;
ret = x509_check_signature(x509->pub, x509);
if (ret < 0)
goto maybe_missing_crypto_in_x509;
x509->signer = x509; x509->signer = x509;
pr_debug("- self-signed\n"); pr_debug("- self-signed\n");
return 0; return 0;
...@@ -224,7 +218,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, ...@@ -224,7 +218,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
/* Look through the X.509 certificates in the PKCS#7 message's /* Look through the X.509 certificates in the PKCS#7 message's
* list to see if the next one is there. * list to see if the next one is there.
*/ */
auth = x509->akid_id; auth = sig->auth_ids[0];
if (auth) { if (auth) {
pr_debug("- want %*phN\n", auth->len, auth->data); pr_debug("- want %*phN\n", auth->len, auth->data);
for (p = pkcs7->certs; p; p = p->next) { for (p = pkcs7->certs; p; p = p->next) {
...@@ -234,7 +228,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, ...@@ -234,7 +228,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
goto found_issuer_check_skid; goto found_issuer_check_skid;
} }
} else { } else {
auth = x509->akid_skid; auth = sig->auth_ids[1];
pr_debug("- want %*phN\n", auth->len, auth->data); pr_debug("- want %*phN\n", auth->len, auth->data);
for (p = pkcs7->certs; p; p = p->next) { for (p = pkcs7->certs; p; p = p->next) {
if (!p->skid) if (!p->skid)
...@@ -254,8 +248,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, ...@@ -254,8 +248,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
/* We matched issuer + serialNumber, but if there's an /* We matched issuer + serialNumber, but if there's an
* authKeyId.keyId, that must match the CA subjKeyId also. * authKeyId.keyId, that must match the CA subjKeyId also.
*/ */
if (x509->akid_skid && if (sig->auth_ids[1] &&
!asymmetric_key_id_same(p->skid, x509->akid_skid)) { !asymmetric_key_id_same(p->skid, sig->auth_ids[1])) {
pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n", pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n",
sinfo->index, x509->index, p->index); sinfo->index, x509->index, p->index);
return -EKEYREJECTED; return -EKEYREJECTED;
...@@ -267,7 +261,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, ...@@ -267,7 +261,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
sinfo->index); sinfo->index);
return 0; return 0;
} }
ret = x509_check_signature(p->pub, x509); ret = public_key_verify_signature(p->pub, p->sig);
if (ret < 0) if (ret < 0)
return ret; return ret;
x509->signer = p; x509->signer = p;
...@@ -279,16 +273,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, ...@@ -279,16 +273,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
might_sleep(); might_sleep();
} }
maybe_missing_crypto_in_x509: unsupported_crypto_in_x509:
/* Just prune the certificate chain at this point if we lack some /* Just prune the certificate chain at this point if we lack some
* crypto module to go further. Note, however, we don't want to set * crypto module to go further. Note, however, we don't want to set
* sinfo->missing_crypto as the signed info block may still be * sinfo->unsupported_crypto as the signed info block may still be
* validatable against an X.509 cert lower in the chain that we have a * validatable against an X.509 cert lower in the chain that we have a
* trusted copy of. * trusted copy of.
*/ */
if (ret == -ENOPKG)
return 0; return 0;
return ret;
} }
/* /*
...@@ -332,7 +324,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, ...@@ -332,7 +324,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
} }
/* Verify the PKCS#7 binary against the key */ /* Verify the PKCS#7 binary against the key */
ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig); ret = public_key_verify_signature(sinfo->signer->pub, sinfo->sig);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -375,9 +367,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, ...@@ -375,9 +367,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
enum key_being_used_for usage) enum key_being_used_for usage)
{ {
struct pkcs7_signed_info *sinfo; struct pkcs7_signed_info *sinfo;
struct x509_certificate *x509;
int enopkg = -ENOPKG; int enopkg = -ENOPKG;
int ret, n; int ret;
kenter(""); kenter("");
...@@ -419,12 +410,6 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, ...@@ -419,12 +410,6 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
return -EINVAL; return -EINVAL;
} }
for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) {
ret = x509_get_sig_params(x509);
if (ret < 0)
return ret;
}
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
ret = pkcs7_verify_one(pkcs7, sinfo); ret = pkcs7_verify_one(pkcs7, sinfo);
if (ret < 0) { if (ret < 0) {
......
...@@ -39,15 +39,23 @@ static void public_key_describe(const struct key *asymmetric_key, ...@@ -39,15 +39,23 @@ static void public_key_describe(const struct key *asymmetric_key,
/* /*
* Destroy a public key algorithm key. * Destroy a public key algorithm key.
*/ */
void public_key_destroy(void *payload) void public_key_free(struct public_key *key)
{ {
struct public_key *key = payload; if (key) {
if (key)
kfree(key->key); kfree(key->key);
kfree(key); kfree(key);
}
}
EXPORT_SYMBOL_GPL(public_key_free);
/*
* Destroy a public key algorithm key.
*/
static void public_key_destroy(void *payload0, void *payload3)
{
public_key_free(payload0);
public_key_signature_free(payload3);
} }
EXPORT_SYMBOL_GPL(public_key_destroy);
struct public_key_completion { struct public_key_completion {
struct completion completion; struct completion completion;
......
/* Instantiate a public key crypto key from an X.509 Certificate
*
* Copyright (C) 2012, 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) "ASYM: "fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <crypto/public_key.h>
#include "asymmetric_keys.h"
static bool use_builtin_keys;
static struct asymmetric_key_id *ca_keyid;
#ifndef MODULE
static struct {
struct asymmetric_key_id id;
unsigned char data[10];
} cakey;
static int __init ca_keys_setup(char *str)
{
if (!str) /* default system keyring */
return 1;
if (strncmp(str, "id:", 3) == 0) {
struct asymmetric_key_id *p = &cakey.id;
size_t hexlen = (strlen(str) - 3) / 2;
int ret;
if (hexlen == 0 || hexlen > sizeof(cakey.data)) {
pr_err("Missing or invalid ca_keys id\n");
return 1;
}
ret = __asymmetric_key_hex_to_key_id(str + 3, p, hexlen);
if (ret < 0)
pr_err("Unparsable ca_keys id hex string\n");
else
ca_keyid = p; /* owner key 'id:xxxxxx' */
} else if (strcmp(str, "builtin") == 0) {
use_builtin_keys = true;
}
return 1;
}
__setup("ca_keys=", ca_keys_setup);
#endif
/**
* 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.
* @type: The type of key being added.
* @payload: The payload of the new key.
*
* 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
* new certificate as being trusted.
*
* 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 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,
const struct key_type *type,
const union key_payload *payload)
{
const struct public_key_signature *sig;
struct key *key;
int ret;
pr_devel("==>%s()\n", __func__);
if (!trust_keyring)
return -ENOKEY;
if (type != &key_type_asymmetric)
return -EOPNOTSUPP;
sig = payload->data[asym_auth];
if (!sig->auth_ids[0] && !sig->auth_ids[1])
return 0;
if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid))
return -EPERM;
/* See if we have a key that signed this one. */
key = find_asymmetric_key(trust_keyring,
sig->auth_ids[0], sig->auth_ids[1],
false);
if (IS_ERR(key))
return -ENOKEY;
if (use_builtin_keys && !test_bit(KEY_FLAG_BUILTIN, &key->flags))
ret = -ENOKEY;
else
ret = verify_signature(key, sig);
key_put(key);
return ret;
}
...@@ -15,9 +15,27 @@ ...@@ -15,9 +15,27 @@
#include <keys/asymmetric-subtype.h> #include <keys/asymmetric-subtype.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h>
#include <crypto/public_key.h> #include <crypto/public_key.h>
#include "asymmetric_keys.h" #include "asymmetric_keys.h"
/*
* Destroy a public key signature.
*/
void public_key_signature_free(struct public_key_signature *sig)
{
int i;
if (sig) {
for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++)
kfree(sig->auth_ids[i]);
kfree(sig->s);
kfree(sig->digest);
kfree(sig);
}
}
EXPORT_SYMBOL_GPL(public_key_signature_free);
/** /**
* verify_signature - Initiate the use of an asymmetric key to verify a signature * verify_signature - Initiate the use of an asymmetric key to verify a signature
* @key: The asymmetric key to verify against * @key: The asymmetric key to verify against
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/pe.h> #include <linux/pe.h>
#include <linux/asn1.h> #include <linux/asn1.h>
#include <crypto/pkcs7.h> #include <linux/verification.h>
#include <crypto/hash.h> #include <crypto/hash.h>
#include "verify_pefile.h" #include "verify_pefile.h"
...@@ -392,9 +392,8 @@ static int pefile_digest_pe(const void *pebuf, unsigned int pelen, ...@@ -392,9 +392,8 @@ static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
* verify_pefile_signature - Verify the signature on a PE binary image * verify_pefile_signature - Verify the signature on a PE binary image
* @pebuf: Buffer containing the PE binary image * @pebuf: Buffer containing the PE binary image
* @pelen: Length of the binary image * @pelen: Length of the binary image
* @trust_keyring: Signing certificates to use as starting points * @trust_keys: Signing certificate(s) to use as starting points
* @usage: The use to which the key is being put. * @usage: The use to which the key is being put.
* @_trusted: Set to true if trustworth, false otherwise
* *
* Validate that the certificate chain inside the PKCS#7 message inside the PE * Validate that the certificate chain inside the PKCS#7 message inside the PE
* binary image intersects keys we already know and trust. * binary image intersects keys we already know and trust.
...@@ -418,14 +417,10 @@ static int pefile_digest_pe(const void *pebuf, unsigned int pelen, ...@@ -418,14 +417,10 @@ static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
* May also return -ENOMEM. * May also return -ENOMEM.
*/ */
int verify_pefile_signature(const void *pebuf, unsigned pelen, int verify_pefile_signature(const void *pebuf, unsigned pelen,
struct key *trusted_keyring, struct key *trusted_keys,
enum key_being_used_for usage, enum key_being_used_for usage)
bool *_trusted)
{ {
struct pkcs7_message *pkcs7;
struct pefile_context ctx; struct pefile_context ctx;
const void *data;
size_t datalen;
int ret; int ret;
kenter(""); kenter("");
...@@ -439,19 +434,10 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, ...@@ -439,19 +434,10 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
if (ret < 0) if (ret < 0)
return ret; return ret;
pkcs7 = pkcs7_parse_message(pebuf + ctx.sig_offset, ctx.sig_len); ret = verify_pkcs7_signature(NULL, 0,
if (IS_ERR(pkcs7)) pebuf + ctx.sig_offset, ctx.sig_len,
return PTR_ERR(pkcs7); trusted_keys, usage,
ctx.pkcs7 = pkcs7; mscode_parse, &ctx);
ret = pkcs7_get_content_data(ctx.pkcs7, &data, &datalen, false);
if (ret < 0 || datalen == 0) {
pr_devel("PKCS#7 message does not contain data\n");
ret = -EBADMSG;
goto error;
}
ret = mscode_parse(&ctx);
if (ret < 0) if (ret < 0)
goto error; goto error;
...@@ -462,16 +448,8 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, ...@@ -462,16 +448,8 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
* contents. * contents.
*/ */
ret = pefile_digest_pe(pebuf, pelen, &ctx); ret = pefile_digest_pe(pebuf, pelen, &ctx);
if (ret < 0)
goto error;
ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error;
ret = pkcs7_validate_trust(pkcs7, trusted_keyring, _trusted);
error: error:
pkcs7_free_message(ctx.pkcs7); kfree(ctx.digest);
return ret; return ret;
} }
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
* 2 of the Licence, or (at your option) any later version. * 2 of the Licence, or (at your option) any later version.
*/ */
#include <linux/verify_pefile.h>
#include <crypto/pkcs7.h> #include <crypto/pkcs7.h>
#include <crypto/hash_info.h> #include <crypto/hash_info.h>
...@@ -23,7 +22,6 @@ struct pefile_context { ...@@ -23,7 +22,6 @@ struct pefile_context {
unsigned sig_offset; unsigned sig_offset;
unsigned sig_len; unsigned sig_len;
const struct section_header *secs; const struct section_header *secs;
struct pkcs7_message *pkcs7;
/* PKCS#7 MS Individual Code Signing content */ /* PKCS#7 MS Individual Code Signing content */
const void *digest; /* Digest */ const void *digest; /* Digest */
...@@ -39,4 +37,5 @@ struct pefile_context { ...@@ -39,4 +37,5 @@ struct pefile_context {
/* /*
* mscode_parser.c * mscode_parser.c
*/ */
extern int mscode_parse(struct pefile_context *ctx); extern int mscode_parse(void *_ctx, const void *content_data, size_t data_len,
size_t asn1hdrlen);
...@@ -47,15 +47,12 @@ struct x509_parse_context { ...@@ -47,15 +47,12 @@ struct x509_parse_context {
void x509_free_certificate(struct x509_certificate *cert) void x509_free_certificate(struct x509_certificate *cert)
{ {
if (cert) { if (cert) {
public_key_destroy(cert->pub); public_key_free(cert->pub);
public_key_signature_free(cert->sig);
kfree(cert->issuer); kfree(cert->issuer);
kfree(cert->subject); kfree(cert->subject);
kfree(cert->id); kfree(cert->id);
kfree(cert->skid); kfree(cert->skid);
kfree(cert->akid_id);
kfree(cert->akid_skid);
kfree(cert->sig.digest);
kfree(cert->sig.s);
kfree(cert); kfree(cert);
} }
} }
...@@ -78,6 +75,9 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) ...@@ -78,6 +75,9 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
if (!cert->pub) if (!cert->pub)
goto error_no_ctx; goto error_no_ctx;
cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL);
if (!cert->sig)
goto error_no_ctx;
ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL); ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
if (!ctx) if (!ctx)
goto error_no_ctx; goto error_no_ctx;
...@@ -108,6 +108,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) ...@@ -108,6 +108,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
cert->pub->keylen = ctx->key_size; cert->pub->keylen = ctx->key_size;
/* Grab the signature bits */
ret = x509_get_sig_params(cert);
if (ret < 0)
goto error_decode;
/* Generate cert issuer + serial number key ID */ /* Generate cert issuer + serial number key ID */
kid = asymmetric_key_generate_id(cert->raw_serial, kid = asymmetric_key_generate_id(cert->raw_serial,
cert->raw_serial_size, cert->raw_serial_size,
...@@ -119,6 +124,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) ...@@ -119,6 +124,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
} }
cert->id = kid; cert->id = kid;
/* Detect self-signed certificates */
ret = x509_check_for_self_signed(cert);
if (ret < 0)
goto error_decode;
kfree(ctx); kfree(ctx);
return cert; return cert;
...@@ -188,33 +198,33 @@ int x509_note_pkey_algo(void *context, size_t hdrlen, ...@@ -188,33 +198,33 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
return -ENOPKG; /* Unsupported combination */ return -ENOPKG; /* Unsupported combination */
case OID_md4WithRSAEncryption: case OID_md4WithRSAEncryption:
ctx->cert->sig.hash_algo = "md4"; ctx->cert->sig->hash_algo = "md4";
ctx->cert->sig.pkey_algo = "rsa"; ctx->cert->sig->pkey_algo = "rsa";
break; break;
case OID_sha1WithRSAEncryption: case OID_sha1WithRSAEncryption:
ctx->cert->sig.hash_algo = "sha1"; ctx->cert->sig->hash_algo = "sha1";
ctx->cert->sig.pkey_algo = "rsa"; ctx->cert->sig->pkey_algo = "rsa";
break; break;
case OID_sha256WithRSAEncryption: case OID_sha256WithRSAEncryption:
ctx->cert->sig.hash_algo = "sha256"; ctx->cert->sig->hash_algo = "sha256";
ctx->cert->sig.pkey_algo = "rsa"; ctx->cert->sig->pkey_algo = "rsa";
break; break;
case OID_sha384WithRSAEncryption: case OID_sha384WithRSAEncryption:
ctx->cert->sig.hash_algo = "sha384"; ctx->cert->sig->hash_algo = "sha384";
ctx->cert->sig.pkey_algo = "rsa"; ctx->cert->sig->pkey_algo = "rsa";
break; break;
case OID_sha512WithRSAEncryption: case OID_sha512WithRSAEncryption:
ctx->cert->sig.hash_algo = "sha512"; ctx->cert->sig->hash_algo = "sha512";
ctx->cert->sig.pkey_algo = "rsa"; ctx->cert->sig->pkey_algo = "rsa";
break; break;
case OID_sha224WithRSAEncryption: case OID_sha224WithRSAEncryption:
ctx->cert->sig.hash_algo = "sha224"; ctx->cert->sig->hash_algo = "sha224";
ctx->cert->sig.pkey_algo = "rsa"; ctx->cert->sig->pkey_algo = "rsa";
break; break;
} }
...@@ -572,14 +582,14 @@ int x509_akid_note_kid(void *context, size_t hdrlen, ...@@ -572,14 +582,14 @@ int x509_akid_note_kid(void *context, size_t hdrlen,
pr_debug("AKID: keyid: %*phN\n", (int)vlen, value); pr_debug("AKID: keyid: %*phN\n", (int)vlen, value);
if (ctx->cert->akid_skid) if (ctx->cert->sig->auth_ids[1])
return 0; return 0;
kid = asymmetric_key_generate_id(value, vlen, "", 0); kid = asymmetric_key_generate_id(value, vlen, "", 0);
if (IS_ERR(kid)) if (IS_ERR(kid))
return PTR_ERR(kid); return PTR_ERR(kid);
pr_debug("authkeyid %*phN\n", kid->len, kid->data); pr_debug("authkeyid %*phN\n", kid->len, kid->data);
ctx->cert->akid_skid = kid; ctx->cert->sig->auth_ids[1] = kid;
return 0; return 0;
} }
...@@ -611,7 +621,7 @@ int x509_akid_note_serial(void *context, size_t hdrlen, ...@@ -611,7 +621,7 @@ int x509_akid_note_serial(void *context, size_t hdrlen,
pr_debug("AKID: serial: %*phN\n", (int)vlen, value); pr_debug("AKID: serial: %*phN\n", (int)vlen, value);
if (!ctx->akid_raw_issuer || ctx->cert->akid_id) if (!ctx->akid_raw_issuer || ctx->cert->sig->auth_ids[0])
return 0; return 0;
kid = asymmetric_key_generate_id(value, kid = asymmetric_key_generate_id(value,
...@@ -622,6 +632,6 @@ int x509_akid_note_serial(void *context, size_t hdrlen, ...@@ -622,6 +632,6 @@ int x509_akid_note_serial(void *context, size_t hdrlen,
return PTR_ERR(kid); return PTR_ERR(kid);
pr_debug("authkeyid %*phN\n", kid->len, kid->data); pr_debug("authkeyid %*phN\n", kid->len, kid->data);
ctx->cert->akid_id = kid; ctx->cert->sig->auth_ids[0] = kid;
return 0; return 0;
} }
...@@ -17,13 +17,11 @@ struct x509_certificate { ...@@ -17,13 +17,11 @@ struct x509_certificate {
struct x509_certificate *next; struct x509_certificate *next;
struct x509_certificate *signer; /* Certificate that signed this one */ struct x509_certificate *signer; /* Certificate that signed this one */
struct public_key *pub; /* Public key details */ struct public_key *pub; /* Public key details */
struct public_key_signature sig; /* Signature parameters */ struct public_key_signature *sig; /* Signature parameters */
char *issuer; /* Name of certificate issuer */ char *issuer; /* Name of certificate issuer */
char *subject; /* Name of certificate subject */ char *subject; /* Name of certificate subject */
struct asymmetric_key_id *id; /* Issuer + Serial number */ struct asymmetric_key_id *id; /* Issuer + Serial number */
struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */
struct asymmetric_key_id *akid_id; /* CA AuthKeyId matching ->id (optional) */
struct asymmetric_key_id *akid_skid; /* CA AuthKeyId matching ->skid (optional) */
time64_t valid_from; time64_t valid_from;
time64_t valid_to; time64_t valid_to;
const void *tbs; /* Signed data */ const void *tbs; /* Signed data */
...@@ -41,8 +39,9 @@ struct x509_certificate { ...@@ -41,8 +39,9 @@ struct x509_certificate {
unsigned index; unsigned index;
bool seen; /* Infinite recursion prevention */ bool seen; /* Infinite recursion prevention */
bool verified; bool verified;
bool trusted; bool self_signed; /* T if self-signed (check unsupported_sig too) */
bool unsupported_crypto; /* T if can't be verified due to missing crypto */ bool unsupported_key; /* T if key uses unsupported crypto */
bool unsupported_sig; /* T if signature uses unsupported crypto */
}; };
/* /*
...@@ -58,5 +57,4 @@ extern int x509_decode_time(time64_t *_t, size_t hdrlen, ...@@ -58,5 +57,4 @@ extern int x509_decode_time(time64_t *_t, size_t hdrlen,
* x509_public_key.c * x509_public_key.c
*/ */
extern int x509_get_sig_params(struct x509_certificate *cert); extern int x509_get_sig_params(struct x509_certificate *cert);
extern int x509_check_signature(const struct public_key *pub, extern int x509_check_for_self_signed(struct x509_certificate *cert);
struct x509_certificate *cert);
This diff is collapsed.
...@@ -360,7 +360,7 @@ init_cifs_idmap(void) ...@@ -360,7 +360,7 @@ init_cifs_idmap(void)
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
(KEY_POS_ALL & ~KEY_POS_SETATTR) | (KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ, KEY_USR_VIEW | KEY_USR_READ,
KEY_ALLOC_NOT_IN_QUOTA, NULL); KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
if (IS_ERR(keyring)) { if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring); ret = PTR_ERR(keyring);
goto failed_put_cred; goto failed_put_cred;
......
...@@ -850,15 +850,25 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size, ...@@ -850,15 +850,25 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size,
if (ret) if (ret)
return ret; return ret;
ret = deny_write_access(file);
if (ret)
return ret;
i_size = i_size_read(file_inode(file)); i_size = i_size_read(file_inode(file));
if (max_size > 0 && i_size > max_size) if (max_size > 0 && i_size > max_size) {
return -EFBIG; ret = -EFBIG;
if (i_size <= 0) goto out;
return -EINVAL; }
if (i_size <= 0) {
ret = -EINVAL;
goto out;
}
*buf = vmalloc(i_size); *buf = vmalloc(i_size);
if (!*buf) if (!*buf) {
return -ENOMEM; ret = -ENOMEM;
goto out;
}
pos = 0; pos = 0;
while (pos < i_size) { while (pos < i_size) {
...@@ -876,18 +886,21 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size, ...@@ -876,18 +886,21 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size,
if (pos != i_size) { if (pos != i_size) {
ret = -EIO; ret = -EIO;
goto out; goto out_free;
} }
ret = security_kernel_post_read_file(file, *buf, i_size, id); ret = security_kernel_post_read_file(file, *buf, i_size, id);
if (!ret) if (!ret)
*size = pos; *size = pos;
out: out_free:
if (ret < 0) { if (ret < 0) {
vfree(*buf); vfree(*buf);
*buf = NULL; *buf = NULL;
} }
out:
allow_write_access(file);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(kernel_read_file); EXPORT_SYMBOL_GPL(kernel_read_file);
......
...@@ -3627,6 +3627,8 @@ SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode, ...@@ -3627,6 +3627,8 @@ SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode,
switch (mode & S_IFMT) { switch (mode & S_IFMT) {
case 0: case S_IFREG: case 0: case S_IFREG:
error = vfs_create(path.dentry->d_inode,dentry,mode,true); error = vfs_create(path.dentry->d_inode,dentry,mode,true);
if (!error)
ima_post_path_mknod(dentry);
break; break;
case S_IFCHR: case S_IFBLK: case S_IFCHR: case S_IFBLK:
error = vfs_mknod(path.dentry->d_inode,dentry,mode, error = vfs_mknod(path.dentry->d_inode,dentry,mode,
......
...@@ -201,7 +201,7 @@ int nfs_idmap_init(void) ...@@ -201,7 +201,7 @@ int nfs_idmap_init(void)
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
(KEY_POS_ALL & ~KEY_POS_SETATTR) | (KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ, KEY_USR_VIEW | KEY_USR_READ,
KEY_ALLOC_NOT_IN_QUOTA, NULL); KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
if (IS_ERR(keyring)) { if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring); ret = PTR_ERR(keyring);
goto failed_put_cred; goto failed_put_cred;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#ifndef _CRYPTO_PKCS7_H #ifndef _CRYPTO_PKCS7_H
#define _CRYPTO_PKCS7_H #define _CRYPTO_PKCS7_H
#include <linux/verification.h>
#include <crypto/public_key.h> #include <crypto/public_key.h>
struct key; struct key;
...@@ -26,14 +27,13 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7); ...@@ -26,14 +27,13 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
const void **_data, size_t *_datalen, const void **_data, size_t *_datalen,
bool want_wrapper); size_t *_headerlen);
/* /*
* pkcs7_trust.c * pkcs7_trust.c
*/ */
extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
struct key *trust_keyring, struct key *trust_keyring);
bool *_trusted);
/* /*
* pkcs7_verify.c * pkcs7_verify.c
......
...@@ -14,20 +14,6 @@ ...@@ -14,20 +14,6 @@
#ifndef _LINUX_PUBLIC_KEY_H #ifndef _LINUX_PUBLIC_KEY_H
#define _LINUX_PUBLIC_KEY_H #define _LINUX_PUBLIC_KEY_H
/*
* The use to which an asymmetric key is being put.
*/
enum key_being_used_for {
VERIFYING_MODULE_SIGNATURE,
VERIFYING_FIRMWARE_SIGNATURE,
VERIFYING_KEXEC_PE_SIGNATURE,
VERIFYING_KEY_SIGNATURE,
VERIFYING_KEY_SELF_SIGNATURE,
VERIFYING_UNSPECIFIED_SIGNATURE,
NR__KEY_BEING_USED_FOR
};
extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
/* /*
* Cryptographic data for the public-key subtype of the asymmetric key type. * Cryptographic data for the public-key subtype of the asymmetric key type.
* *
...@@ -41,12 +27,13 @@ struct public_key { ...@@ -41,12 +27,13 @@ struct public_key {
const char *pkey_algo; const char *pkey_algo;
}; };
extern void public_key_destroy(void *payload); extern void public_key_free(struct public_key *key);
/* /*
* Public key cryptography signature data * Public key cryptography signature data
*/ */
struct public_key_signature { struct public_key_signature {
struct asymmetric_key_id *auth_ids[2];
u8 *s; /* Signature */ u8 *s; /* Signature */
u32 s_size; /* Number of bytes in signature */ u32 s_size; /* Number of bytes in signature */
u8 *digest; u8 *digest;
...@@ -55,17 +42,21 @@ struct public_key_signature { ...@@ -55,17 +42,21 @@ struct public_key_signature {
const char *hash_algo; const char *hash_algo;
}; };
extern void public_key_signature_free(struct public_key_signature *sig);
extern struct asymmetric_key_subtype public_key_subtype; extern struct asymmetric_key_subtype public_key_subtype;
struct key; struct key;
struct key_type;
union key_payload;
extern int restrict_link_by_signature(struct key *trust_keyring,
const struct key_type *type,
const union key_payload *payload);
extern int verify_signature(const struct key *key, extern int verify_signature(const struct key *key,
const struct public_key_signature *sig); const struct public_key_signature *sig);
struct asymmetric_key_id;
extern struct key *x509_request_asymmetric_key(struct key *keyring,
const struct asymmetric_key_id *id,
const struct asymmetric_key_id *skid,
bool partial);
int public_key_verify_signature(const struct public_key *pkey, int public_key_verify_signature(const struct public_key *pkey,
const struct public_key_signature *sig); const struct public_key_signature *sig);
......
...@@ -32,7 +32,7 @@ struct asymmetric_key_subtype { ...@@ -32,7 +32,7 @@ struct asymmetric_key_subtype {
void (*describe)(const struct key *key, struct seq_file *m); void (*describe)(const struct key *key, struct seq_file *m);
/* Destroy a key of this subtype */ /* Destroy a key of this subtype */
void (*destroy)(void *payload); void (*destroy)(void *payload_crypto, void *payload_auth);
/* Verify the signature on a key of this subtype (optional) */ /* Verify the signature on a key of this subtype (optional) */
int (*verify_signature)(const struct key *key, int (*verify_signature)(const struct key *key,
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#define _KEYS_ASYMMETRIC_TYPE_H #define _KEYS_ASYMMETRIC_TYPE_H
#include <linux/key-type.h> #include <linux/key-type.h>
#include <linux/verification.h>
extern struct key_type key_type_asymmetric; extern struct key_type key_type_asymmetric;
...@@ -23,9 +24,10 @@ extern struct key_type key_type_asymmetric; ...@@ -23,9 +24,10 @@ extern struct key_type key_type_asymmetric;
* follows: * follows:
*/ */
enum asymmetric_payload_bits { enum asymmetric_payload_bits {
asym_crypto, asym_crypto, /* The data representing the key */
asym_subtype, asym_subtype, /* Pointer to an asymmetric_key_subtype struct */
asym_key_ids, asym_key_ids, /* Pointer to an asymmetric_key_ids struct */
asym_auth /* The key's authorisation (signature, parent key ID) */
}; };
/* /*
...@@ -74,6 +76,11 @@ const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key) ...@@ -74,6 +76,11 @@ const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key)
return key->payload.data[asym_key_ids]; return key->payload.data[asym_key_ids];
} }
extern struct key *find_asymmetric_key(struct key *keyring,
const struct asymmetric_key_id *id_0,
const struct asymmetric_key_id *id_1,
bool partial);
/* /*
* The payload is at the discretion of the subtype. * The payload is at the discretion of the subtype.
*/ */
......
...@@ -12,51 +12,40 @@ ...@@ -12,51 +12,40 @@
#ifndef _KEYS_SYSTEM_KEYRING_H #ifndef _KEYS_SYSTEM_KEYRING_H
#define _KEYS_SYSTEM_KEYRING_H #define _KEYS_SYSTEM_KEYRING_H
#include <linux/key.h>
#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING #ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
#include <linux/key.h> extern int restrict_link_by_builtin_trusted(struct key *keyring,
#include <crypto/public_key.h> const struct key_type *type,
const union key_payload *payload);
extern struct key *system_trusted_keyring;
static inline struct key *get_system_trusted_keyring(void)
{
return system_trusted_keyring;
}
#else #else
static inline struct key *get_system_trusted_keyring(void) #define restrict_link_by_builtin_trusted restrict_link_reject
{
return NULL;
}
#endif #endif
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
extern int system_verify_data(const void *data, unsigned long len, extern int restrict_link_by_builtin_and_secondary_trusted(
const void *raw_pkcs7, size_t pkcs7_len, struct key *keyring,
enum key_being_used_for usage); const struct key_type *type,
const union key_payload *payload);
#else
#define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted
#endif #endif
#ifdef CONFIG_IMA_MOK_KEYRING #ifdef CONFIG_IMA_BLACKLIST_KEYRING
extern struct key *ima_mok_keyring;
extern struct key *ima_blacklist_keyring; extern struct key *ima_blacklist_keyring;
static inline struct key *get_ima_mok_keyring(void)
{
return ima_mok_keyring;
}
static inline struct key *get_ima_blacklist_keyring(void) static inline struct key *get_ima_blacklist_keyring(void)
{ {
return ima_blacklist_keyring; return ima_blacklist_keyring;
} }
#else #else
static inline struct key *get_ima_mok_keyring(void)
{
return NULL;
}
static inline struct key *get_ima_blacklist_keyring(void) static inline struct key *get_ima_blacklist_keyring(void)
{ {
return NULL; return NULL;
} }
#endif /* CONFIG_IMA_MOK_KEYRING */ #endif /* CONFIG_IMA_BLACKLIST_KEYRING */
#endif /* _KEYS_SYSTEM_KEYRING_H */ #endif /* _KEYS_SYSTEM_KEYRING_H */
...@@ -2634,15 +2634,34 @@ static inline void i_readcount_inc(struct inode *inode) ...@@ -2634,15 +2634,34 @@ static inline void i_readcount_inc(struct inode *inode)
#endif #endif
extern int do_pipe_flags(int *, int); extern int do_pipe_flags(int *, int);
#define __kernel_read_file_id(id) \
id(UNKNOWN, unknown) \
id(FIRMWARE, firmware) \
id(MODULE, kernel-module) \
id(KEXEC_IMAGE, kexec-image) \
id(KEXEC_INITRAMFS, kexec-initramfs) \
id(POLICY, security-policy) \
id(MAX_ID, )
#define __fid_enumify(ENUM, dummy) READING_ ## ENUM,
#define __fid_stringify(dummy, str) #str,
enum kernel_read_file_id { enum kernel_read_file_id {
READING_FIRMWARE = 1, __kernel_read_file_id(__fid_enumify)
READING_MODULE, };
READING_KEXEC_IMAGE,
READING_KEXEC_INITRAMFS, static const char * const kernel_read_file_str[] = {
READING_POLICY, __kernel_read_file_id(__fid_stringify)
READING_MAX_ID
}; };
static inline const char *kernel_read_file_id_str(enum kernel_read_file_id id)
{
if (id < 0 || id >= READING_MAX_ID)
return kernel_read_file_str[READING_UNKNOWN];
return kernel_read_file_str[id];
}
extern int kernel_read(struct file *, loff_t, char *, unsigned long); extern int kernel_read(struct file *, loff_t, char *, unsigned long);
extern int kernel_read_file(struct file *, void **, loff_t *, loff_t, extern int kernel_read_file(struct file *, void **, loff_t *, loff_t,
enum kernel_read_file_id); enum kernel_read_file_id);
......
...@@ -21,6 +21,7 @@ extern int ima_file_mmap(struct file *file, unsigned long prot); ...@@ -21,6 +21,7 @@ extern int ima_file_mmap(struct file *file, unsigned long prot);
extern int ima_read_file(struct file *file, enum kernel_read_file_id id); extern int ima_read_file(struct file *file, enum kernel_read_file_id id);
extern int ima_post_read_file(struct file *file, void *buf, loff_t size, extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
enum kernel_read_file_id id); enum kernel_read_file_id id);
extern void ima_post_path_mknod(struct dentry *dentry);
#else #else
static inline int ima_bprm_check(struct linux_binprm *bprm) static inline int ima_bprm_check(struct linux_binprm *bprm)
...@@ -54,6 +55,11 @@ static inline int ima_post_read_file(struct file *file, void *buf, loff_t size, ...@@ -54,6 +55,11 @@ static inline int ima_post_read_file(struct file *file, void *buf, loff_t size,
return 0; return 0;
} }
static inline void ima_post_path_mknod(struct dentry *dentry)
{
return;
}
#endif /* CONFIG_IMA */ #endif /* CONFIG_IMA */
#ifdef CONFIG_IMA_APPRAISE #ifdef CONFIG_IMA_APPRAISE
......
...@@ -45,7 +45,6 @@ struct key_preparsed_payload { ...@@ -45,7 +45,6 @@ struct key_preparsed_payload {
size_t datalen; /* Raw datalen */ size_t datalen; /* Raw datalen */
size_t quotalen; /* Quota length for proposed payload */ size_t quotalen; /* Quota length for proposed payload */
time_t expiry; /* Expiry time of key */ time_t expiry; /* Expiry time of key */
bool trusted; /* True if key is trusted */
}; };
typedef int (*request_key_actor_t)(struct key_construction *key, typedef int (*request_key_actor_t)(struct key_construction *key,
......
...@@ -173,11 +173,9 @@ struct key { ...@@ -173,11 +173,9 @@ struct key {
#define KEY_FLAG_NEGATIVE 5 /* set if key is negative */ #define KEY_FLAG_NEGATIVE 5 /* set if key is negative */
#define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */ #define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */
#define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */
#define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ #define KEY_FLAG_BUILTIN 8 /* set if key is built in to the kernel */
#define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ #define KEY_FLAG_ROOT_CAN_INVAL 9 /* set if key can be invalidated by root without permission */
#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ #define KEY_FLAG_KEEP 10 /* set if key should not be removed */
#define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */
#define KEY_FLAG_KEEP 12 /* set if key should not be removed */
/* the key type and key description string /* the key type and key description string
* - the desc is used to match a key against search criteria * - the desc is used to match a key against search criteria
...@@ -205,6 +203,20 @@ struct key { ...@@ -205,6 +203,20 @@ struct key {
}; };
int reject_error; int reject_error;
}; };
/* 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
* keyring is open to any addition. It is ignored for non-keyring
* keys.
*
* 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);
}; };
extern struct key *key_alloc(struct key_type *type, extern struct key *key_alloc(struct key_type *type,
...@@ -212,14 +224,17 @@ extern struct key *key_alloc(struct key_type *type, ...@@ -212,14 +224,17 @@ extern struct key *key_alloc(struct key_type *type,
kuid_t uid, kgid_t gid, kuid_t uid, kgid_t gid,
const struct cred *cred, const struct cred *cred,
key_perm_t perm, key_perm_t perm,
unsigned long flags); unsigned long flags,
int (*restrict_link)(struct key *,
const struct key_type *,
const union key_payload *));
#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */
#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ #define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */
#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ #define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */
#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */ #define KEY_ALLOC_BUILT_IN 0x0004 /* Key is built into kernel */
#define KEY_ALLOC_BUILT_IN 0x0008 /* Key is built into kernel */ #define KEY_ALLOC_BYPASS_RESTRICTION 0x0008 /* Override the check on restricted keyrings */
extern void key_revoke(struct key *key); extern void key_revoke(struct key *key);
extern void key_invalidate(struct key *key); extern void key_invalidate(struct key *key);
...@@ -288,8 +303,15 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid ...@@ -288,8 +303,15 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid
const struct cred *cred, const struct cred *cred,
key_perm_t perm, key_perm_t perm,
unsigned long flags, unsigned long flags,
int (*restrict_link)(struct key *,
const struct key_type *,
const union key_payload *),
struct key *dest); struct key *dest);
extern int restrict_link_reject(struct key *keyring,
const struct key_type *type,
const union key_payload *payload);
extern int keyring_clear(struct key *keyring); extern int keyring_clear(struct key *keyring);
extern key_ref_t keyring_search(key_ref_t keyring, extern key_ref_t keyring_search(key_ref_t keyring,
......
...@@ -1805,7 +1805,6 @@ struct security_hook_heads { ...@@ -1805,7 +1805,6 @@ struct security_hook_heads {
struct list_head tun_dev_attach_queue; struct list_head tun_dev_attach_queue;
struct list_head tun_dev_attach; struct list_head tun_dev_attach;
struct list_head tun_dev_open; struct list_head tun_dev_open;
struct list_head skb_owned_by;
#endif /* CONFIG_SECURITY_NETWORK */ #endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM #ifdef CONFIG_SECURITY_NETWORK_XFRM
struct list_head xfrm_policy_alloc_security; struct list_head xfrm_policy_alloc_security;
...@@ -1894,5 +1893,10 @@ extern void __init yama_add_hooks(void); ...@@ -1894,5 +1893,10 @@ extern void __init yama_add_hooks(void);
#else #else
static inline void __init yama_add_hooks(void) { } static inline void __init yama_add_hooks(void) { }
#endif #endif
#ifdef CONFIG_SECURITY_LOADPIN
void __init loadpin_add_hooks(void);
#else
static inline void loadpin_add_hooks(void) { };
#endif
#endif /* ! __LINUX_LSM_HOOKS_H */ #endif /* ! __LINUX_LSM_HOOKS_H */
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
#include <linux/types.h> #include <linux/types.h>
struct file;
/* Descriptions of the types of units to /* Descriptions of the types of units to
* print in */ * print in */
enum string_size_units { enum string_size_units {
...@@ -68,4 +70,8 @@ static inline int string_escape_str_any_np(const char *src, char *dst, ...@@ -68,4 +70,8 @@ static inline int string_escape_str_any_np(const char *src, char *dst,
return string_escape_str(src, dst, sz, ESCAPE_ANY_NP, only); return string_escape_str(src, dst, sz, ESCAPE_ANY_NP, only);
} }
char *kstrdup_quotable(const char *src, gfp_t gfp);
char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp);
char *kstrdup_quotable_file(struct file *file, gfp_t gfp);
#endif #endif
/* Signed PE file verification /* Signature verification
* *
* Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
...@@ -9,14 +9,41 @@ ...@@ -9,14 +9,41 @@
* 2 of the Licence, or (at your option) any later version. * 2 of the Licence, or (at your option) any later version.
*/ */
#ifndef _LINUX_VERIFY_PEFILE_H #ifndef _LINUX_VERIFICATION_H
#define _LINUX_VERIFY_PEFILE_H #define _LINUX_VERIFICATION_H
#include <crypto/public_key.h> /*
* The use to which an asymmetric key is being put.
*/
enum key_being_used_for {
VERIFYING_MODULE_SIGNATURE,
VERIFYING_FIRMWARE_SIGNATURE,
VERIFYING_KEXEC_PE_SIGNATURE,
VERIFYING_KEY_SIGNATURE,
VERIFYING_KEY_SELF_SIGNATURE,
VERIFYING_UNSPECIFIED_SIGNATURE,
NR__KEY_BEING_USED_FOR
};
extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
extern int verify_pefile_signature(const void *pebuf, unsigned pelen, #ifdef CONFIG_SYSTEM_DATA_VERIFICATION
struct key *trusted_keyring,
struct key;
extern int verify_pkcs7_signature(const void *data, size_t len,
const void *raw_pkcs7, size_t pkcs7_len,
struct key *trusted_keys,
enum key_being_used_for usage, enum key_being_used_for usage,
bool *_trusted); int (*view_content)(void *ctx,
const void *data, size_t len,
size_t asn1hdrlen),
void *ctx);
#ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION
extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
struct key *trusted_keys,
enum key_being_used_for usage);
#endif
#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
#endif /* _LINUX_VERIFY_PEFILE_H */ #endif /* _LINUX_VERIFY_PEFILE_H */
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#ifndef _LINUX_KEYCTL_H #ifndef _LINUX_KEYCTL_H
#define _LINUX_KEYCTL_H #define _LINUX_KEYCTL_H
#include <linux/types.h>
/* special process keyring shortcut IDs */ /* special process keyring shortcut IDs */
#define KEY_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific keyring */ #define KEY_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific keyring */
#define KEY_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific keyring */ #define KEY_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific keyring */
...@@ -57,5 +59,13 @@ ...@@ -57,5 +59,13 @@
#define KEYCTL_INSTANTIATE_IOV 20 /* instantiate a partially constructed key */ #define KEYCTL_INSTANTIATE_IOV 20 /* instantiate a partially constructed key */
#define KEYCTL_INVALIDATE 21 /* invalidate a key */ #define KEYCTL_INVALIDATE 21 /* invalidate a key */
#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */ #define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
#define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */
/* keyctl structures */
struct keyctl_dh_params {
__s32 private;
__s32 prime;
__s32 base;
};
#endif /* _LINUX_KEYCTL_H */ #endif /* _LINUX_KEYCTL_H */
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/string.h> #include <linux/string.h>
#include <keys/system_keyring.h> #include <linux/verification.h>
#include <crypto/public_key.h> #include <crypto/public_key.h>
#include "module-internal.h" #include "module-internal.h"
...@@ -80,6 +80,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) ...@@ -80,6 +80,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
return -EBADMSG; return -EBADMSG;
} }
return system_verify_data(mod, modlen, mod + modlen, sig_len, return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
VERIFYING_MODULE_SIGNATURE); NULL, VERIFYING_MODULE_SIGNATURE,
NULL, NULL);
} }
...@@ -915,7 +915,7 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off, ...@@ -915,7 +915,7 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
fprog = filter->prog->orig_prog; fprog = filter->prog->orig_prog;
if (!fprog) { if (!fprog) {
/* This must be a new non-cBPF filter, since we save every /* This must be a new non-cBPF filter, since we save
* every cBPF filter's orig_prog above when * every cBPF filter's orig_prog above when
* CONFIG_CHECKPOINT_RESTORE is enabled. * CONFIG_CHECKPOINT_RESTORE is enabled.
*/ */
......
...@@ -10,6 +10,10 @@ ...@@ -10,6 +10,10 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/fs.h>
#include <linux/limits.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/string_helpers.h> #include <linux/string_helpers.h>
...@@ -534,3 +538,91 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz, ...@@ -534,3 +538,91 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
return p - dst; return p - dst;
} }
EXPORT_SYMBOL(string_escape_mem); EXPORT_SYMBOL(string_escape_mem);
/*
* Return an allocated string that has been escaped of special characters
* and double quotes, making it safe to log in quotes.
*/
char *kstrdup_quotable(const char *src, gfp_t gfp)
{
size_t slen, dlen;
char *dst;
const int flags = ESCAPE_HEX;
const char esc[] = "\f\n\r\t\v\a\e\\\"";
if (!src)
return NULL;
slen = strlen(src);
dlen = string_escape_mem(src, slen, NULL, 0, flags, esc);
dst = kmalloc(dlen + 1, gfp);
if (!dst)
return NULL;
WARN_ON(string_escape_mem(src, slen, dst, dlen, flags, esc) != dlen);
dst[dlen] = '\0';
return dst;
}
EXPORT_SYMBOL_GPL(kstrdup_quotable);
/*
* Returns allocated NULL-terminated string containing process
* command line, with inter-argument NULLs replaced with spaces,
* and other special characters escaped.
*/
char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp)
{
char *buffer, *quoted;
int i, res;
buffer = kmalloc(PAGE_SIZE, GFP_TEMPORARY);
if (!buffer)
return NULL;
res = get_cmdline(task, buffer, PAGE_SIZE - 1);
buffer[res] = '\0';
/* Collapse trailing NULLs, leave res pointing to last non-NULL. */
while (--res >= 0 && buffer[res] == '\0')
;
/* Replace inter-argument NULLs. */
for (i = 0; i <= res; i++)
if (buffer[i] == '\0')
buffer[i] = ' ';
/* Make sure result is printable. */
quoted = kstrdup_quotable(buffer, gfp);
kfree(buffer);
return quoted;
}
EXPORT_SYMBOL_GPL(kstrdup_quotable_cmdline);
/*
* Returns allocated NULL-terminated string containing pathname,
* with special characters escaped, able to be safely logged. If
* there is an error, the leading character will be "<".
*/
char *kstrdup_quotable_file(struct file *file, gfp_t gfp)
{
char *temp, *pathname;
if (!file)
return kstrdup("<unknown>", gfp);
/* We add 11 spaces for ' (deleted)' to be appended */
temp = kmalloc(PATH_MAX + 11, GFP_TEMPORARY);
if (!temp)
return kstrdup("<no_memory>", gfp);
pathname = file_path(file, temp, PATH_MAX + 11);
if (IS_ERR(pathname))
pathname = kstrdup("<too_long>", gfp);
else
pathname = kstrdup_quotable(pathname, gfp);
kfree(temp);
return pathname;
}
EXPORT_SYMBOL_GPL(kstrdup_quotable_file);
...@@ -281,7 +281,7 @@ static int __init init_dns_resolver(void) ...@@ -281,7 +281,7 @@ static int __init init_dns_resolver(void)
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
(KEY_POS_ALL & ~KEY_POS_SETATTR) | (KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ, KEY_USR_VIEW | KEY_USR_READ,
KEY_ALLOC_NOT_IN_QUOTA, NULL); KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
if (IS_ERR(keyring)) { if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring); ret = PTR_ERR(keyring);
goto failed_put_cred; goto failed_put_cred;
......
...@@ -677,7 +677,7 @@ int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap, ...@@ -677,7 +677,7 @@ int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap,
u32 spot = start; u32 spot = start;
while (rc == 0 && spot <= end) { while (rc == 0 && spot <= end) {
if (((spot & (BITS_PER_LONG - 1)) != 0) && if (((spot & (BITS_PER_LONG - 1)) == 0) &&
((end - spot) > BITS_PER_LONG)) { ((end - spot) > BITS_PER_LONG)) {
rc = netlbl_catmap_setlong(catmap, rc = netlbl_catmap_setlong(catmap,
spot, spot,
......
...@@ -965,7 +965,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn, ...@@ -965,7 +965,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
key = key_alloc(&key_type_rxrpc, "x", key = key_alloc(&key_type_rxrpc, "x",
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0,
KEY_ALLOC_NOT_IN_QUOTA); KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(key)) { if (IS_ERR(key)) {
_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key)); _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
return -ENOMEM; return -ENOMEM;
...@@ -1012,7 +1012,7 @@ struct key *rxrpc_get_null_key(const char *keyname) ...@@ -1012,7 +1012,7 @@ struct key *rxrpc_get_null_key(const char *keyname)
key = key_alloc(&key_type_rxrpc, keyname, key = key_alloc(&key_type_rxrpc, keyname,
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA); KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(key)) if (IS_ERR(key))
return key; return key;
......
...@@ -122,6 +122,7 @@ source security/selinux/Kconfig ...@@ -122,6 +122,7 @@ source security/selinux/Kconfig
source security/smack/Kconfig source security/smack/Kconfig
source security/tomoyo/Kconfig source security/tomoyo/Kconfig
source security/apparmor/Kconfig source security/apparmor/Kconfig
source security/loadpin/Kconfig
source security/yama/Kconfig source security/yama/Kconfig
source security/integrity/Kconfig source security/integrity/Kconfig
......
...@@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack ...@@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack
subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
subdir-$(CONFIG_SECURITY_YAMA) += yama subdir-$(CONFIG_SECURITY_YAMA) += yama
subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
# always enable default capabilities # always enable default capabilities
obj-y += commoncap.o obj-y += commoncap.o
...@@ -22,6 +23,7 @@ obj-$(CONFIG_AUDIT) += lsm_audit.o ...@@ -22,6 +23,7 @@ obj-$(CONFIG_AUDIT) += lsm_audit.o
obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/ obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/
obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
obj-$(CONFIG_SECURITY_YAMA) += yama/ obj-$(CONFIG_SECURITY_YAMA) += yama/
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
# Object integrity file lists # Object integrity file lists
......
...@@ -35,7 +35,6 @@ config INTEGRITY_ASYMMETRIC_KEYS ...@@ -35,7 +35,6 @@ config INTEGRITY_ASYMMETRIC_KEYS
default n default n
select ASYMMETRIC_KEY_TYPE select ASYMMETRIC_KEY_TYPE
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
select PUBLIC_KEY_ALGO_RSA
select CRYPTO_RSA select CRYPTO_RSA
select X509_CERTIFICATE_PARSER select X509_CERTIFICATE_PARSER
help help
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/key-type.h> #include <linux/key-type.h>
#include <linux/digsig.h> #include <linux/digsig.h>
#include <crypto/public_key.h>
#include <keys/system_keyring.h>
#include "integrity.h" #include "integrity.h"
...@@ -40,6 +42,12 @@ static bool init_keyring __initdata = true; ...@@ -40,6 +42,12 @@ static bool init_keyring __initdata = true;
static bool init_keyring __initdata; static bool init_keyring __initdata;
#endif #endif
#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
#define restrict_link_to_ima restrict_link_by_builtin_and_secondary_trusted
#else
#define restrict_link_to_ima restrict_link_by_builtin_trusted
#endif
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen) const char *digest, int digestlen)
{ {
...@@ -83,10 +91,9 @@ int __init integrity_init_keyring(const unsigned int id) ...@@ -83,10 +91,9 @@ int __init integrity_init_keyring(const unsigned int id)
((KEY_POS_ALL & ~KEY_POS_SETATTR) | ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_VIEW | KEY_USR_READ |
KEY_USR_WRITE | KEY_USR_SEARCH), KEY_USR_WRITE | KEY_USR_SEARCH),
KEY_ALLOC_NOT_IN_QUOTA, NULL); KEY_ALLOC_NOT_IN_QUOTA,
if (!IS_ERR(keyring[id])) restrict_link_to_ima, NULL);
set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags); if (IS_ERR(keyring[id])) {
else {
err = PTR_ERR(keyring[id]); err = PTR_ERR(keyring[id]);
pr_info("Can't allocate %s keyring (%d)\n", pr_info("Can't allocate %s keyring (%d)\n",
keyring_name[id], err); keyring_name[id], err);
......
...@@ -155,23 +155,33 @@ config IMA_TRUSTED_KEYRING ...@@ -155,23 +155,33 @@ config IMA_TRUSTED_KEYRING
This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING
config IMA_MOK_KEYRING config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
bool "Create IMA machine owner keys (MOK) and blacklist keyrings" bool "Permit keys validly signed by a built-in or secondary CA cert (EXPERIMENTAL)"
depends on SYSTEM_TRUSTED_KEYRING
depends on SECONDARY_TRUSTED_KEYRING
depends on INTEGRITY_ASYMMETRIC_KEYS
select INTEGRITY_TRUSTED_KEYRING
default n
help
Keys may be added to the IMA or IMA blacklist keyrings, if the
key is validly signed by a CA cert in the system built-in or
secondary trusted keyrings.
Intermediate keys between those the kernel has compiled in and the
IMA keys to be added may be added to the system secondary keyring,
provided they are validly signed by a key already resident in the
built-in or secondary trusted keyrings.
config IMA_BLACKLIST_KEYRING
bool "Create IMA machine owner blacklist keyrings (EXPERIMENTAL)"
depends on SYSTEM_TRUSTED_KEYRING depends on SYSTEM_TRUSTED_KEYRING
depends on IMA_TRUSTED_KEYRING depends on IMA_TRUSTED_KEYRING
default n default n
help help
This option creates IMA MOK and blacklist keyrings. IMA MOK is an This option creates an IMA blacklist keyring, which contains all
intermediate keyring that sits between .system and .ima keyrings, revoked IMA keys. It is consulted before any other keyring. If
effectively forming a simple CA hierarchy. To successfully import a the search is successful the requested operation is rejected and
key into .ima_mok it must be signed by a key which CA is in .system an error is returned to the caller.
keyring. On turn any key that needs to go in .ima keyring must be
signed by CA in either .system or .ima_mok keyrings. IMA MOK is empty
at kernel boot.
IMA blacklist keyring contains all revoked IMA keys. It is consulted
before any other keyring. If the search is successful the requested
operation is rejected and error is returned to the caller.
config IMA_LOAD_X509 config IMA_LOAD_X509
bool "Load X509 certificate onto the '.ima' trusted keyring" bool "Load X509 certificate onto the '.ima' trusted keyring"
......
...@@ -8,4 +8,4 @@ obj-$(CONFIG_IMA) += ima.o ...@@ -8,4 +8,4 @@ obj-$(CONFIG_IMA) += ima.o
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
ima_policy.o ima_template.o ima_template_lib.o ima_policy.o ima_template.o ima_template_lib.o
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
obj-$(CONFIG_IMA_MOK_KEYRING) += ima_mok.o obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
...@@ -275,6 +275,11 @@ int ima_appraise_measurement(enum ima_hooks func, ...@@ -275,6 +275,11 @@ int ima_appraise_measurement(enum ima_hooks func,
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) { xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
if (!ima_fix_xattr(dentry, iint)) if (!ima_fix_xattr(dentry, iint))
status = INTEGRITY_PASS; status = INTEGRITY_PASS;
} else if ((inode->i_size == 0) &&
(iint->flags & IMA_NEW_FILE) &&
(xattr_value &&
xattr_value->type == EVM_IMA_XATTR_DIGSIG)) {
status = INTEGRITY_PASS;
} }
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
op, cause, rc, 0); op, cause, rc, 0);
...@@ -328,7 +333,7 @@ void ima_inode_post_setattr(struct dentry *dentry) ...@@ -328,7 +333,7 @@ void ima_inode_post_setattr(struct dentry *dentry)
if (iint) { if (iint) {
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED | iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK | IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
IMA_ACTION_FLAGS); IMA_ACTION_RULE_FLAGS);
if (must_appraise) if (must_appraise)
iint->flags |= IMA_APPRAISE; iint->flags |= IMA_APPRAISE;
} }
......
...@@ -246,7 +246,8 @@ static int process_measurement(struct file *file, char *buf, loff_t size, ...@@ -246,7 +246,8 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
ima_audit_measurement(iint, pathname); ima_audit_measurement(iint, pathname);
out_digsig: out_digsig:
if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG)) if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG) &&
!(iint->flags & IMA_NEW_FILE))
rc = -EACCES; rc = -EACCES;
kfree(xattr_value); kfree(xattr_value);
out_free: out_free:
...@@ -315,6 +316,28 @@ int ima_file_check(struct file *file, int mask, int opened) ...@@ -315,6 +316,28 @@ int ima_file_check(struct file *file, int mask, int opened)
} }
EXPORT_SYMBOL_GPL(ima_file_check); EXPORT_SYMBOL_GPL(ima_file_check);
/**
* ima_post_path_mknod - mark as a new inode
* @dentry: newly created dentry
*
* Mark files created via the mknodat syscall as new, so that the
* file data can be written later.
*/
void ima_post_path_mknod(struct dentry *dentry)
{
struct integrity_iint_cache *iint;
struct inode *inode = dentry->d_inode;
int must_appraise;
must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK);
if (!must_appraise)
return;
iint = integrity_inode_get(inode);
if (iint)
iint->flags |= IMA_NEW_FILE;
}
/** /**
* ima_read_file - pre-measure/appraise hook decision based on policy * ima_read_file - pre-measure/appraise hook decision based on policy
* @file: pointer to the file to be measured/appraised/audit * @file: pointer to the file to be measured/appraised/audit
......
...@@ -17,38 +17,29 @@ ...@@ -17,38 +17,29 @@
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
#include <keys/asymmetric-type.h> #include <keys/system_keyring.h>
struct key *ima_mok_keyring;
struct key *ima_blacklist_keyring; struct key *ima_blacklist_keyring;
/* /*
* Allocate the IMA MOK and blacklist keyrings * Allocate the IMA blacklist keyring
*/ */
__init int ima_mok_init(void) __init int ima_mok_init(void)
{ {
pr_notice("Allocating IMA MOK and blacklist keyrings.\n"); pr_notice("Allocating IMA blacklist keyring.\n");
ima_mok_keyring = keyring_alloc(".ima_mok",
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, NULL);
ima_blacklist_keyring = keyring_alloc(".ima_blacklist", ima_blacklist_keyring = keyring_alloc(".ima_blacklist",
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
(KEY_POS_ALL & ~KEY_POS_SETATTR) | (KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_VIEW | KEY_USR_READ |
KEY_USR_WRITE | KEY_USR_SEARCH, KEY_USR_WRITE | KEY_USR_SEARCH,
KEY_ALLOC_NOT_IN_QUOTA, NULL); KEY_ALLOC_NOT_IN_QUOTA,
restrict_link_by_builtin_trusted, NULL);
if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring)) if (IS_ERR(ima_blacklist_keyring))
panic("Can't allocate IMA MOK or blacklist keyrings."); panic("Can't allocate IMA blacklist keyring.");
set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_mok_keyring->flags);
set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_blacklist_keyring->flags);
set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags); set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags);
return 0; return 0;
} }
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
/* iint cache flags */ /* iint cache flags */
#define IMA_ACTION_FLAGS 0xff000000 #define IMA_ACTION_FLAGS 0xff000000
#define IMA_ACTION_RULE_FLAGS 0x06000000
#define IMA_DIGSIG 0x01000000 #define IMA_DIGSIG 0x01000000
#define IMA_DIGSIG_REQUIRED 0x02000000 #define IMA_DIGSIG_REQUIRED 0x02000000
#define IMA_PERMIT_DIRECTIO 0x04000000 #define IMA_PERMIT_DIRECTIO 0x04000000
......
...@@ -41,6 +41,10 @@ config BIG_KEYS ...@@ -41,6 +41,10 @@ config BIG_KEYS
bool "Large payload keys" bool "Large payload keys"
depends on KEYS depends on KEYS
depends on TMPFS depends on TMPFS
select CRYPTO
select CRYPTO_AES
select CRYPTO_ECB
select CRYPTO_RNG
help help
This option provides support for holding large keys within the kernel This option provides support for holding large keys within the kernel
(for example Kerberos ticket caches). The data may be stored out to (for example Kerberos ticket caches). The data may be stored out to
...@@ -81,3 +85,14 @@ config ENCRYPTED_KEYS ...@@ -81,3 +85,14 @@ config ENCRYPTED_KEYS
Userspace only ever sees/stores encrypted blobs. Userspace only ever sees/stores encrypted blobs.
If you are unsure as to whether this is required, answer N. If you are unsure as to whether this is required, answer N.
config KEY_DH_OPERATIONS
bool "Diffie-Hellman operations on retained keys"
depends on KEYS
select MPILIB
help
This option provides support for calculating Diffie-Hellman
public keys and shared secrets using values stored as keys
in the kernel.
If you are unsure as to whether this is required, answer N.
...@@ -19,6 +19,7 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o ...@@ -19,6 +19,7 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o obj-$(CONFIG_SYSCTL) += sysctl.o
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
# #
# Key types # Key types
......
...@@ -14,8 +14,10 @@ ...@@ -14,8 +14,10 @@
#include <linux/file.h> #include <linux/file.h>
#include <linux/shmem_fs.h> #include <linux/shmem_fs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/scatterlist.h>
#include <keys/user-type.h> #include <keys/user-type.h>
#include <keys/big_key-type.h> #include <keys/big_key-type.h>
#include <crypto/rng.h>
/* /*
* Layout of key payload words. * Layout of key payload words.
...@@ -27,6 +29,14 @@ enum { ...@@ -27,6 +29,14 @@ enum {
big_key_len, big_key_len,
}; };
/*
* Crypto operation with big_key data
*/
enum big_key_op {
BIG_KEY_ENC,
BIG_KEY_DEC,
};
/* /*
* If the data is under this limit, there's no point creating a shm file to * If the data is under this limit, there's no point creating a shm file to
* hold it as the permanently resident metadata for the shmem fs will be at * hold it as the permanently resident metadata for the shmem fs will be at
...@@ -34,6 +44,11 @@ enum { ...@@ -34,6 +44,11 @@ enum {
*/ */
#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) #define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
/*
* Key size for big_key data encryption
*/
#define ENC_KEY_SIZE 16
/* /*
* big_key defined keys take an arbitrary string as the description and an * big_key defined keys take an arbitrary string as the description and an
* arbitrary blob of data as the payload * arbitrary blob of data as the payload
...@@ -49,6 +64,54 @@ struct key_type key_type_big_key = { ...@@ -49,6 +64,54 @@ struct key_type key_type_big_key = {
.read = big_key_read, .read = big_key_read,
}; };
/*
* Crypto names for big_key data encryption
*/
static const char big_key_rng_name[] = "stdrng";
static const char big_key_alg_name[] = "ecb(aes)";
/*
* Crypto algorithms for big_key data encryption
*/
static struct crypto_rng *big_key_rng;
static struct crypto_blkcipher *big_key_blkcipher;
/*
* Generate random key to encrypt big_key data
*/
static inline int big_key_gen_enckey(u8 *key)
{
return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE);
}
/*
* Encrypt/decrypt big_key data
*/
static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key)
{
int ret = -EINVAL;
struct scatterlist sgio;
struct blkcipher_desc desc;
if (crypto_blkcipher_setkey(big_key_blkcipher, key, ENC_KEY_SIZE)) {
ret = -EAGAIN;
goto error;
}
desc.flags = 0;
desc.tfm = big_key_blkcipher;
sg_init_one(&sgio, data, datalen);
if (op == BIG_KEY_ENC)
ret = crypto_blkcipher_encrypt(&desc, &sgio, &sgio, datalen);
else
ret = crypto_blkcipher_decrypt(&desc, &sgio, &sgio, datalen);
error:
return ret;
}
/* /*
* Preparse a big key * Preparse a big key
*/ */
...@@ -56,6 +119,8 @@ int big_key_preparse(struct key_preparsed_payload *prep) ...@@ -56,6 +119,8 @@ int big_key_preparse(struct key_preparsed_payload *prep)
{ {
struct path *path = (struct path *)&prep->payload.data[big_key_path]; struct path *path = (struct path *)&prep->payload.data[big_key_path];
struct file *file; struct file *file;
u8 *enckey;
u8 *data = NULL;
ssize_t written; ssize_t written;
size_t datalen = prep->datalen; size_t datalen = prep->datalen;
int ret; int ret;
...@@ -73,16 +138,43 @@ int big_key_preparse(struct key_preparsed_payload *prep) ...@@ -73,16 +138,43 @@ int big_key_preparse(struct key_preparsed_payload *prep)
/* Create a shmem file to store the data in. This will permit the data /* Create a shmem file to store the data in. This will permit the data
* to be swapped out if needed. * to be swapped out if needed.
* *
* TODO: Encrypt the stored data with a temporary key. * File content is stored encrypted with randomly generated key.
*/ */
file = shmem_kernel_file_setup("", datalen, 0); size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher));
/* prepare aligned data to encrypt */
data = kmalloc(enclen, GFP_KERNEL);
if (!data)
return -ENOMEM;
memcpy(data, prep->data, datalen);
memset(data + datalen, 0x00, enclen - datalen);
/* generate random key */
enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL);
if (!enckey) {
ret = -ENOMEM;
goto error;
}
ret = big_key_gen_enckey(enckey);
if (ret)
goto err_enckey;
/* encrypt aligned data */
ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey);
if (ret)
goto err_enckey;
/* save aligned data to file */
file = shmem_kernel_file_setup("", enclen, 0);
if (IS_ERR(file)) { if (IS_ERR(file)) {
ret = PTR_ERR(file); ret = PTR_ERR(file);
goto error; goto err_enckey;
} }
written = kernel_write(file, prep->data, prep->datalen, 0); written = kernel_write(file, data, enclen, 0);
if (written != datalen) { if (written != enclen) {
ret = written; ret = written;
if (written >= 0) if (written >= 0)
ret = -ENOMEM; ret = -ENOMEM;
...@@ -92,12 +184,15 @@ int big_key_preparse(struct key_preparsed_payload *prep) ...@@ -92,12 +184,15 @@ int big_key_preparse(struct key_preparsed_payload *prep)
/* Pin the mount and dentry to the key so that we can open it again /* Pin the mount and dentry to the key so that we can open it again
* later * later
*/ */
prep->payload.data[big_key_data] = enckey;
*path = file->f_path; *path = file->f_path;
path_get(path); path_get(path);
fput(file); fput(file);
kfree(data);
} else { } else {
/* Just store the data in a buffer */ /* Just store the data in a buffer */
void *data = kmalloc(datalen, GFP_KERNEL); void *data = kmalloc(datalen, GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
...@@ -108,7 +203,10 @@ int big_key_preparse(struct key_preparsed_payload *prep) ...@@ -108,7 +203,10 @@ int big_key_preparse(struct key_preparsed_payload *prep)
err_fput: err_fput:
fput(file); fput(file);
err_enckey:
kfree(enckey);
error: error:
kfree(data);
return ret; return ret;
} }
...@@ -119,10 +217,10 @@ void big_key_free_preparse(struct key_preparsed_payload *prep) ...@@ -119,10 +217,10 @@ void big_key_free_preparse(struct key_preparsed_payload *prep)
{ {
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&prep->payload.data[big_key_path]; struct path *path = (struct path *)&prep->payload.data[big_key_path];
path_put(path); path_put(path);
} else {
kfree(prep->payload.data[big_key_data]);
} }
kfree(prep->payload.data[big_key_data]);
} }
/* /*
...@@ -147,15 +245,15 @@ void big_key_destroy(struct key *key) ...@@ -147,15 +245,15 @@ void big_key_destroy(struct key *key)
{ {
size_t datalen = (size_t)key->payload.data[big_key_len]; size_t datalen = (size_t)key->payload.data[big_key_len];
if (datalen) { if (datalen > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&key->payload.data[big_key_path]; struct path *path = (struct path *)&key->payload.data[big_key_path];
path_put(path); path_put(path);
path->mnt = NULL; path->mnt = NULL;
path->dentry = NULL; path->dentry = NULL;
} else { }
kfree(key->payload.data[big_key_data]); kfree(key->payload.data[big_key_data]);
key->payload.data[big_key_data] = NULL; key->payload.data[big_key_data] = NULL;
}
} }
/* /*
...@@ -188,17 +286,41 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) ...@@ -188,17 +286,41 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
if (datalen > BIG_KEY_FILE_THRESHOLD) { if (datalen > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&key->payload.data[big_key_path]; struct path *path = (struct path *)&key->payload.data[big_key_path];
struct file *file; struct file *file;
loff_t pos; u8 *data;
u8 *enckey = (u8 *)key->payload.data[big_key_data];
size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher));
data = kmalloc(enclen, GFP_KERNEL);
if (!data)
return -ENOMEM;
file = dentry_open(path, O_RDONLY, current_cred()); file = dentry_open(path, O_RDONLY, current_cred());
if (IS_ERR(file)) if (IS_ERR(file)) {
return PTR_ERR(file); ret = PTR_ERR(file);
goto error;
}
pos = 0; /* read file to kernel and decrypt */
ret = vfs_read(file, buffer, datalen, &pos); ret = kernel_read(file, 0, data, enclen);
fput(file); if (ret >= 0 && ret != enclen) {
if (ret >= 0 && ret != datalen)
ret = -EIO; ret = -EIO;
goto err_fput;
}
ret = big_key_crypt(BIG_KEY_DEC, data, enclen, enckey);
if (ret)
goto err_fput;
ret = datalen;
/* copy decrypted data to user */
if (copy_to_user(buffer, data, datalen) != 0)
ret = -EFAULT;
err_fput:
fput(file);
error:
kfree(data);
} else { } else {
ret = datalen; ret = datalen;
if (copy_to_user(buffer, key->payload.data[big_key_data], if (copy_to_user(buffer, key->payload.data[big_key_data],
...@@ -209,8 +331,48 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) ...@@ -209,8 +331,48 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
return ret; return ret;
} }
/*
* Register key type
*/
static int __init big_key_init(void) static int __init big_key_init(void)
{ {
return register_key_type(&key_type_big_key); return register_key_type(&key_type_big_key);
} }
/*
* Initialize big_key crypto and RNG algorithms
*/
static int __init big_key_crypto_init(void)
{
int ret = -EINVAL;
/* init RNG */
big_key_rng = crypto_alloc_rng(big_key_rng_name, 0, 0);
if (IS_ERR(big_key_rng)) {
big_key_rng = NULL;
return -EFAULT;
}
/* seed RNG */
ret = crypto_rng_reset(big_key_rng, NULL, crypto_rng_seedsize(big_key_rng));
if (ret)
goto error;
/* init block cipher */
big_key_blkcipher = crypto_alloc_blkcipher(big_key_alg_name, 0, 0);
if (IS_ERR(big_key_blkcipher)) {
big_key_blkcipher = NULL;
ret = -EFAULT;
goto error;
}
return 0;
error:
crypto_free_rng(big_key_rng);
big_key_rng = NULL;
return ret;
}
device_initcall(big_key_init); device_initcall(big_key_init);
late_initcall(big_key_crypto_init);
...@@ -132,6 +132,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, ...@@ -132,6 +132,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
case KEYCTL_GET_PERSISTENT: case KEYCTL_GET_PERSISTENT:
return keyctl_get_persistent(arg2, arg3); return keyctl_get_persistent(arg2, arg3);
case KEYCTL_DH_COMPUTE:
return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3),
arg4);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
/* Crypto operations using stored keys
*
* Copyright (c) 2016, Intel Corporation
*
* 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/mpi.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <keys/user-type.h>
#include "internal.h"
/*
* Public key or shared secret generation function [RFC2631 sec 2.1.1]
*
* ya = g^xa mod p;
* or
* ZZ = yb^xa mod p;
*
* where xa is the local private key, ya is the local public key, g is
* the generator, p is the prime, yb is the remote public key, and ZZ
* is the shared secret.
*
* Both are the same calculation, so g or yb are the "base" and ya or
* ZZ are the "result".
*/
static int do_dh(MPI result, MPI base, MPI xa, MPI p)
{
return mpi_powm(result, base, xa, p);
}
static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi)
{
struct key *key;
key_ref_t key_ref;
long status;
ssize_t ret;
key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ);
if (IS_ERR(key_ref)) {
ret = -ENOKEY;
goto error;
}
key = key_ref_to_ptr(key_ref);
ret = -EOPNOTSUPP;
if (key->type == &key_type_user) {
down_read(&key->sem);
status = key_validate(key);
if (status == 0) {
const struct user_key_payload *payload;
payload = user_key_payload(key);
if (maxlen == 0) {
*mpi = NULL;
ret = payload->datalen;
} else if (payload->datalen <= maxlen) {
*mpi = mpi_read_raw_data(payload->data,
payload->datalen);
if (*mpi)
ret = payload->datalen;
} else {
ret = -EINVAL;
}
}
up_read(&key->sem);
}
key_put(key);
error:
return ret;
}
long keyctl_dh_compute(struct keyctl_dh_params __user *params,
char __user *buffer, size_t buflen)
{
long ret;
MPI base, private, prime, result;
unsigned nbytes;
struct keyctl_dh_params pcopy;
uint8_t *kbuf;
ssize_t keylen;
size_t resultlen;
if (!params || (!buffer && buflen)) {
ret = -EINVAL;
goto out;
}
if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
ret = -EFAULT;
goto out;
}
keylen = mpi_from_key(pcopy.prime, buflen, &prime);
if (keylen < 0 || !prime) {
/* buflen == 0 may be used to query the required buffer size,
* which is the prime key length.
*/
ret = keylen;
goto out;
}
/* The result is never longer than the prime */
resultlen = keylen;
keylen = mpi_from_key(pcopy.base, SIZE_MAX, &base);
if (keylen < 0 || !base) {
ret = keylen;
goto error1;
}
keylen = mpi_from_key(pcopy.private, SIZE_MAX, &private);
if (keylen < 0 || !private) {
ret = keylen;
goto error2;
}
result = mpi_alloc(0);
if (!result) {
ret = -ENOMEM;
goto error3;
}
kbuf = kmalloc(resultlen, GFP_KERNEL);
if (!kbuf) {
ret = -ENOMEM;
goto error4;
}
ret = do_dh(result, base, private, prime);
if (ret)
goto error5;
ret = mpi_read_buffer(result, kbuf, resultlen, &nbytes, NULL);
if (ret != 0)
goto error5;
ret = nbytes;
if (copy_to_user(buffer, kbuf, nbytes) != 0)
ret = -EFAULT;
error5:
kfree(kbuf);
error4:
mpi_free(result);
error3:
mpi_free(private);
error2:
mpi_free(base);
error1:
mpi_free(prime);
out:
return ret;
}
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/key-type.h> #include <linux/key-type.h>
#include <linux/task_work.h> #include <linux/task_work.h>
#include <linux/keyctl.h>
struct iovec; struct iovec;
...@@ -257,6 +258,17 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring) ...@@ -257,6 +258,17 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
} }
#endif #endif
#ifdef CONFIG_KEY_DH_OPERATIONS
extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
size_t);
#else
static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
char __user *buffer, size_t buflen)
{
return -EOPNOTSUPP;
}
#endif
/* /*
* Debugging key validation * Debugging key validation
*/ */
......
...@@ -201,6 +201,7 @@ static inline void key_alloc_serial(struct key *key) ...@@ -201,6 +201,7 @@ static inline void key_alloc_serial(struct key *key)
* @cred: The credentials specifying UID namespace. * @cred: The credentials specifying UID namespace.
* @perm: The permissions mask of the new key. * @perm: The permissions mask of the new key.
* @flags: Flags specifying quota properties. * @flags: Flags specifying quota properties.
* @restrict_link: Optional link restriction method for new keyrings.
* *
* Allocate a key of the specified type with the attributes given. The key is * 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 * returned in an uninstantiated state and the caller needs to instantiate the
...@@ -223,7 +224,10 @@ static inline void key_alloc_serial(struct key *key) ...@@ -223,7 +224,10 @@ static inline void key_alloc_serial(struct key *key)
*/ */
struct key *key_alloc(struct key_type *type, const char *desc, struct key *key_alloc(struct key_type *type, const char *desc,
kuid_t uid, kgid_t gid, const struct cred *cred, kuid_t uid, kgid_t gid, const struct cred *cred,
key_perm_t perm, unsigned long flags) key_perm_t perm, unsigned long flags,
int (*restrict_link)(struct key *,
const struct key_type *,
const union key_payload *))
{ {
struct key_user *user = NULL; struct key_user *user = NULL;
struct key *key; struct key *key;
...@@ -291,11 +295,10 @@ struct key *key_alloc(struct key_type *type, const char *desc, ...@@ -291,11 +295,10 @@ struct key *key_alloc(struct key_type *type, const char *desc,
key->uid = uid; key->uid = uid;
key->gid = gid; key->gid = gid;
key->perm = perm; key->perm = perm;
key->restrict_link = restrict_link;
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
key->flags |= 1 << KEY_FLAG_IN_QUOTA; key->flags |= 1 << KEY_FLAG_IN_QUOTA;
if (flags & KEY_ALLOC_TRUSTED)
key->flags |= 1 << KEY_FLAG_TRUSTED;
if (flags & KEY_ALLOC_BUILT_IN) if (flags & KEY_ALLOC_BUILT_IN)
key->flags |= 1 << KEY_FLAG_BUILTIN; key->flags |= 1 << KEY_FLAG_BUILTIN;
...@@ -496,6 +499,12 @@ int key_instantiate_and_link(struct key *key, ...@@ -496,6 +499,12 @@ int key_instantiate_and_link(struct key *key,
} }
if (keyring) { 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); ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret < 0) if (ret < 0)
goto error; goto error;
...@@ -551,8 +560,12 @@ int key_reject_and_link(struct key *key, ...@@ -551,8 +560,12 @@ int key_reject_and_link(struct key *key,
awaken = 0; awaken = 0;
ret = -EBUSY; ret = -EBUSY;
if (keyring) if (keyring) {
if (keyring->restrict_link)
return -EPERM;
link_ret = __key_link_begin(keyring, &key->index_key, &edit); link_ret = __key_link_begin(keyring, &key->index_key, &edit);
}
mutex_lock(&key_construction_mutex); mutex_lock(&key_construction_mutex);
...@@ -793,6 +806,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -793,6 +806,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
struct key *keyring, *key = NULL; struct key *keyring, *key = NULL;
key_ref_t key_ref; key_ref_t key_ref;
int ret; int ret;
int (*restrict_link)(struct key *,
const struct key_type *,
const union key_payload *) = NULL;
/* look up the key type to see if it's one of the registered kernel /* look up the key type to see if it's one of the registered kernel
* types */ * types */
...@@ -811,6 +827,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -811,6 +827,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_check(keyring); key_check(keyring);
key_ref = ERR_PTR(-EPERM);
if (!(flags & KEY_ALLOC_BYPASS_RESTRICTION))
restrict_link = keyring->restrict_link;
key_ref = ERR_PTR(-ENOTDIR); key_ref = ERR_PTR(-ENOTDIR);
if (keyring->type != &key_type_keyring) if (keyring->type != &key_type_keyring)
goto error_put_type; goto error_put_type;
...@@ -819,7 +839,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -819,7 +839,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
prep.data = payload; prep.data = payload;
prep.datalen = plen; prep.datalen = plen;
prep.quotalen = index_key.type->def_datalen; prep.quotalen = index_key.type->def_datalen;
prep.trusted = flags & KEY_ALLOC_TRUSTED;
prep.expiry = TIME_T_MAX; prep.expiry = TIME_T_MAX;
if (index_key.type->preparse) { if (index_key.type->preparse) {
ret = index_key.type->preparse(&prep); ret = index_key.type->preparse(&prep);
...@@ -835,10 +854,13 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -835,10 +854,13 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
} }
index_key.desc_len = strlen(index_key.description); index_key.desc_len = strlen(index_key.description);
key_ref = ERR_PTR(-EPERM); if (restrict_link) {
if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags)) ret = restrict_link(keyring, index_key.type, &prep.payload);
if (ret < 0) {
key_ref = ERR_PTR(ret);
goto error_free_prep; goto error_free_prep;
flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0; }
}
ret = __key_link_begin(keyring, &index_key, &edit); ret = __key_link_begin(keyring, &index_key, &edit);
if (ret < 0) { if (ret < 0) {
...@@ -879,7 +901,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ...@@ -879,7 +901,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
/* allocate a new key */ /* allocate a new key */
key = key_alloc(index_key.type, index_key.description, key = key_alloc(index_key.type, index_key.description,
cred->fsuid, cred->fsgid, cred, perm, flags); cred->fsuid, cred->fsgid, cred, perm, flags, NULL);
if (IS_ERR(key)) { if (IS_ERR(key)) {
key_ref = ERR_CAST(key); key_ref = ERR_CAST(key);
goto error_link_end; goto error_link_end;
......
...@@ -1686,6 +1686,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, ...@@ -1686,6 +1686,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
case KEYCTL_GET_PERSISTENT: case KEYCTL_GET_PERSISTENT:
return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3); return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
case KEYCTL_DH_COMPUTE:
return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2,
(char __user *) arg3,
(size_t) arg4);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -491,13 +491,17 @@ static long keyring_read(const struct key *keyring, ...@@ -491,13 +491,17 @@ static long keyring_read(const struct key *keyring,
*/ */
struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
const struct cred *cred, key_perm_t perm, const struct cred *cred, key_perm_t perm,
unsigned long flags, struct key *dest) unsigned long flags,
int (*restrict_link)(struct key *,
const struct key_type *,
const union key_payload *),
struct key *dest)
{ {
struct key *keyring; struct key *keyring;
int ret; int ret;
keyring = key_alloc(&key_type_keyring, description, keyring = key_alloc(&key_type_keyring, description,
uid, gid, cred, perm, flags); uid, gid, cred, perm, flags, restrict_link);
if (!IS_ERR(keyring)) { if (!IS_ERR(keyring)) {
ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL); ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
if (ret < 0) { if (ret < 0) {
...@@ -510,6 +514,26 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, ...@@ -510,6 +514,26 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
} }
EXPORT_SYMBOL(keyring_alloc); EXPORT_SYMBOL(keyring_alloc);
/**
* restrict_link_reject - Give -EPERM to restrict link
* @keyring: The keyring being added to.
* @type: The type of key being added.
* @payload: The payload of the key intended to be added.
*
* 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().
*/
int restrict_link_reject(struct key *keyring,
const struct key_type *type,
const union key_payload *payload)
{
return -EPERM;
}
/* /*
* By default, we keys found by getting an exact match on their descriptions. * By default, we keys found by getting an exact match on their descriptions.
*/ */
...@@ -1191,6 +1215,16 @@ void __key_link_end(struct key *keyring, ...@@ -1191,6 +1215,16 @@ void __key_link_end(struct key *keyring,
up_write(&keyring->sem); up_write(&keyring->sem);
} }
/*
* Check addition of keys to restricted keyrings.
*/
static int __key_link_check_restriction(struct key *keyring, struct key *key)
{
if (!keyring->restrict_link)
return 0;
return keyring->restrict_link(keyring, key->type, &key->payload);
}
/** /**
* key_link - Link a key to a keyring * key_link - Link a key to a keyring
* @keyring: The keyring to make the link in. * @keyring: The keyring to make the link in.
...@@ -1221,13 +1255,11 @@ int key_link(struct key *keyring, struct key *key) ...@@ -1221,13 +1255,11 @@ int key_link(struct key *keyring, struct key *key)
key_check(keyring); key_check(keyring);
key_check(key); key_check(key);
if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) &&
!test_bit(KEY_FLAG_TRUSTED, &key->flags))
return -EPERM;
ret = __key_link_begin(keyring, &key->index_key, &edit); ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret == 0) { if (ret == 0) {
kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage)); kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));
ret = __key_link_check_restriction(keyring, key);
if (ret == 0)
ret = __key_link_check_live_key(keyring, key); ret = __key_link_check_live_key(keyring, key);
if (ret == 0) if (ret == 0)
__key_link(key, &edit); __key_link(key, &edit);
......
...@@ -26,7 +26,7 @@ static int key_create_persistent_register(struct user_namespace *ns) ...@@ -26,7 +26,7 @@ static int key_create_persistent_register(struct user_namespace *ns)
current_cred(), current_cred(),
((KEY_POS_ALL & ~KEY_POS_SETATTR) | ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ), KEY_USR_VIEW | KEY_USR_READ),
KEY_ALLOC_NOT_IN_QUOTA, NULL); KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
if (IS_ERR(reg)) if (IS_ERR(reg))
return PTR_ERR(reg); return PTR_ERR(reg);
...@@ -60,7 +60,7 @@ static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid, ...@@ -60,7 +60,7 @@ static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
uid, INVALID_GID, current_cred(), uid, INVALID_GID, current_cred(),
((KEY_POS_ALL & ~KEY_POS_SETATTR) | ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ), KEY_USR_VIEW | KEY_USR_READ),
KEY_ALLOC_NOT_IN_QUOTA, KEY_ALLOC_NOT_IN_QUOTA, NULL,
ns->persistent_keyring_register); ns->persistent_keyring_register);
if (IS_ERR(persistent)) if (IS_ERR(persistent))
return ERR_CAST(persistent); return ERR_CAST(persistent);
......
...@@ -76,7 +76,8 @@ int install_user_keyrings(void) ...@@ -76,7 +76,8 @@ int install_user_keyrings(void)
if (IS_ERR(uid_keyring)) { if (IS_ERR(uid_keyring)) {
uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
cred, user_keyring_perm, cred, user_keyring_perm,
KEY_ALLOC_IN_QUOTA, NULL); KEY_ALLOC_IN_QUOTA,
NULL, NULL);
if (IS_ERR(uid_keyring)) { if (IS_ERR(uid_keyring)) {
ret = PTR_ERR(uid_keyring); ret = PTR_ERR(uid_keyring);
goto error; goto error;
...@@ -92,7 +93,8 @@ int install_user_keyrings(void) ...@@ -92,7 +93,8 @@ int install_user_keyrings(void)
session_keyring = session_keyring =
keyring_alloc(buf, user->uid, INVALID_GID, keyring_alloc(buf, user->uid, INVALID_GID,
cred, user_keyring_perm, cred, user_keyring_perm,
KEY_ALLOC_IN_QUOTA, NULL); KEY_ALLOC_IN_QUOTA,
NULL, NULL);
if (IS_ERR(session_keyring)) { if (IS_ERR(session_keyring)) {
ret = PTR_ERR(session_keyring); ret = PTR_ERR(session_keyring);
goto error_release; goto error_release;
...@@ -134,7 +136,8 @@ int install_thread_keyring_to_cred(struct cred *new) ...@@ -134,7 +136,8 @@ int install_thread_keyring_to_cred(struct cred *new)
keyring = keyring_alloc("_tid", new->uid, new->gid, new, keyring = keyring_alloc("_tid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW, KEY_POS_ALL | KEY_USR_VIEW,
KEY_ALLOC_QUOTA_OVERRUN, NULL); KEY_ALLOC_QUOTA_OVERRUN,
NULL, NULL);
if (IS_ERR(keyring)) if (IS_ERR(keyring))
return PTR_ERR(keyring); return PTR_ERR(keyring);
...@@ -180,7 +183,8 @@ int install_process_keyring_to_cred(struct cred *new) ...@@ -180,7 +183,8 @@ int install_process_keyring_to_cred(struct cred *new)
keyring = keyring_alloc("_pid", new->uid, new->gid, new, keyring = keyring_alloc("_pid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW, KEY_POS_ALL | KEY_USR_VIEW,
KEY_ALLOC_QUOTA_OVERRUN, NULL); KEY_ALLOC_QUOTA_OVERRUN,
NULL, NULL);
if (IS_ERR(keyring)) if (IS_ERR(keyring))
return PTR_ERR(keyring); return PTR_ERR(keyring);
...@@ -231,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) ...@@ -231,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred, keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred,
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
flags, NULL); flags, NULL, NULL);
if (IS_ERR(keyring)) if (IS_ERR(keyring))
return PTR_ERR(keyring); return PTR_ERR(keyring);
} else { } else {
...@@ -785,7 +789,7 @@ long join_session_keyring(const char *name) ...@@ -785,7 +789,7 @@ long join_session_keyring(const char *name)
keyring = keyring_alloc( keyring = keyring_alloc(
name, old->uid, old->gid, old, name, old->uid, old->gid, old,
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK,
KEY_ALLOC_IN_QUOTA, NULL); KEY_ALLOC_IN_QUOTA, NULL, NULL);
if (IS_ERR(keyring)) { if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring); ret = PTR_ERR(keyring);
goto error2; goto error2;
......
...@@ -116,7 +116,7 @@ static int call_sbin_request_key(struct key_construction *cons, ...@@ -116,7 +116,7 @@ static int call_sbin_request_key(struct key_construction *cons,
cred = get_current_cred(); cred = get_current_cred();
keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred, keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred,
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
KEY_ALLOC_QUOTA_OVERRUN, NULL); KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL);
put_cred(cred); put_cred(cred);
if (IS_ERR(keyring)) { if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring); ret = PTR_ERR(keyring);
...@@ -355,7 +355,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx, ...@@ -355,7 +355,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
key = key_alloc(ctx->index_key.type, ctx->index_key.description, key = key_alloc(ctx->index_key.type, ctx->index_key.description,
ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred, ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred,
perm, flags); perm, flags, NULL);
if (IS_ERR(key)) if (IS_ERR(key))
goto alloc_failed; goto alloc_failed;
......
...@@ -202,7 +202,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, ...@@ -202,7 +202,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
authkey = key_alloc(&key_type_request_key_auth, desc, authkey = key_alloc(&key_type_request_key_auth, desc,
cred->fsuid, cred->fsgid, cred, cred->fsuid, cred->fsgid, cred,
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA); KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(authkey)) { if (IS_ERR(authkey)) {
ret = PTR_ERR(authkey); ret = PTR_ERR(authkey);
goto error_alloc; goto error_alloc;
......
...@@ -96,45 +96,25 @@ EXPORT_SYMBOL_GPL(user_free_preparse); ...@@ -96,45 +96,25 @@ EXPORT_SYMBOL_GPL(user_free_preparse);
*/ */
int user_update(struct key *key, struct key_preparsed_payload *prep) int user_update(struct key *key, struct key_preparsed_payload *prep)
{ {
struct user_key_payload *upayload, *zap; struct user_key_payload *zap = NULL;
size_t datalen = prep->datalen;
int ret; int ret;
ret = -EINVAL;
if (datalen <= 0 || datalen > 32767 || !prep->data)
goto error;
/* construct a replacement payload */
ret = -ENOMEM;
upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
if (!upayload)
goto error;
upayload->datalen = datalen;
memcpy(upayload->data, prep->data, datalen);
/* check the quota and attach the new data */ /* check the quota and attach the new data */
zap = upayload; ret = key_payload_reserve(key, prep->datalen);
if (ret < 0)
ret = key_payload_reserve(key, datalen); return ret;
if (ret == 0) {
/* attach the new data, displacing the old */ /* attach the new data, displacing the old */
key->expiry = prep->expiry;
if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
zap = key->payload.data[0]; zap = rcu_dereference_key(key);
else rcu_assign_keypointer(key, prep->payload.data[0]);
zap = NULL; prep->payload.data[0] = NULL;
rcu_assign_keypointer(key, upayload);
key->expiry = 0;
}
if (zap) if (zap)
kfree_rcu(zap, rcu); kfree_rcu(zap, rcu);
error:
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(user_update); EXPORT_SYMBOL_GPL(user_update);
/* /*
......
config SECURITY_LOADPIN
bool "Pin load of kernel files (modules, fw, etc) to one filesystem"
depends on SECURITY && BLOCK
help
Any files read through the kernel file reading interface
(kernel modules, firmware, kexec images, security policy)
can be pinned to the first filesystem used for loading. When
enabled, any files that come from other filesystems will be
rejected. This is best used on systems without an initrd that
have a root filesystem backed by a read-only device such as
dm-verity or a CDROM.
config SECURITY_LOADPIN_ENABLED
bool "Enforce LoadPin at boot"
depends on SECURITY_LOADPIN
help
If selected, LoadPin will enforce pinning at boot. If not
selected, it can be enabled at boot with the kernel parameter
"loadpin.enabled=1".
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin.o
/*
* Module and Firmware Pinning Security Module
*
* Copyright 2011-2016 Google Inc.
*
* Author: Kees Cook <keescook@chromium.org>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "LoadPin: " fmt
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/lsm_hooks.h>
#include <linux/mount.h>
#include <linux/path.h>
#include <linux/sched.h> /* current */
#include <linux/string_helpers.h>
static void report_load(const char *origin, struct file *file, char *operation)
{
char *cmdline, *pathname;
pathname = kstrdup_quotable_file(file, GFP_KERNEL);
cmdline = kstrdup_quotable_cmdline(current, GFP_KERNEL);
pr_notice("%s %s obj=%s%s%s pid=%d cmdline=%s%s%s\n",
origin, operation,
(pathname && pathname[0] != '<') ? "\"" : "",
pathname,
(pathname && pathname[0] != '<') ? "\"" : "",
task_pid_nr(current),
cmdline ? "\"" : "", cmdline, cmdline ? "\"" : "");
kfree(cmdline);
kfree(pathname);
}
static int enabled = IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENABLED);
static struct super_block *pinned_root;
static DEFINE_SPINLOCK(pinned_root_spinlock);
#ifdef CONFIG_SYSCTL
static int zero;
static int one = 1;
static struct ctl_path loadpin_sysctl_path[] = {
{ .procname = "kernel", },
{ .procname = "loadpin", },
{ }
};
static struct ctl_table loadpin_sysctl_table[] = {
{
.procname = "enabled",
.data = &enabled,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &zero,
.extra2 = &one,
},
{ }
};
/*
* This must be called after early kernel init, since then the rootdev
* is available.
*/
static void check_pinning_enforcement(struct super_block *mnt_sb)
{
bool ro = false;
/*
* If load pinning is not enforced via a read-only block
* device, allow sysctl to change modes for testing.
*/
if (mnt_sb->s_bdev) {
ro = bdev_read_only(mnt_sb->s_bdev);
pr_info("dev(%u,%u): %s\n",
MAJOR(mnt_sb->s_bdev->bd_dev),
MINOR(mnt_sb->s_bdev->bd_dev),
ro ? "read-only" : "writable");
} else
pr_info("mnt_sb lacks block device, treating as: writable\n");
if (!ro) {
if (!register_sysctl_paths(loadpin_sysctl_path,
loadpin_sysctl_table))
pr_notice("sysctl registration failed!\n");
else
pr_info("load pinning can be disabled.\n");
} else
pr_info("load pinning engaged.\n");
}
#else
static void check_pinning_enforcement(struct super_block *mnt_sb)
{
pr_info("load pinning engaged.\n");
}
#endif
static void loadpin_sb_free_security(struct super_block *mnt_sb)
{
/*
* When unmounting the filesystem we were using for load
* pinning, we acknowledge the superblock release, but make sure
* no other modules or firmware can be loaded.
*/
if (!IS_ERR_OR_NULL(pinned_root) && mnt_sb == pinned_root) {
pinned_root = ERR_PTR(-EIO);
pr_info("umount pinned fs: refusing further loads\n");
}
}
static int loadpin_read_file(struct file *file, enum kernel_read_file_id id)
{
struct super_block *load_root;
const char *origin = kernel_read_file_id_str(id);
/* This handles the older init_module API that has a NULL file. */
if (!file) {
if (!enabled) {
report_load(origin, NULL, "old-api-pinning-ignored");
return 0;
}
report_load(origin, NULL, "old-api-denied");
return -EPERM;
}
load_root = file->f_path.mnt->mnt_sb;
/* First loaded module/firmware defines the root for all others. */
spin_lock(&pinned_root_spinlock);
/*
* pinned_root is only NULL at startup. Otherwise, it is either
* a valid reference, or an ERR_PTR.
*/
if (!pinned_root) {
pinned_root = load_root;
/*
* Unlock now since it's only pinned_root we care about.
* In the worst case, we will (correctly) report pinning
* failures before we have announced that pinning is
* enabled. This would be purely cosmetic.
*/
spin_unlock(&pinned_root_spinlock);
check_pinning_enforcement(pinned_root);
report_load(origin, file, "pinned");
} else {
spin_unlock(&pinned_root_spinlock);
}
if (IS_ERR_OR_NULL(pinned_root) || load_root != pinned_root) {
if (unlikely(!enabled)) {
report_load(origin, file, "pinning-ignored");
return 0;
}
report_load(origin, file, "denied");
return -EPERM;
}
return 0;
}
static struct security_hook_list loadpin_hooks[] = {
LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security),
LSM_HOOK_INIT(kernel_read_file, loadpin_read_file),
};
void __init loadpin_add_hooks(void)
{
pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
}
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
module_param(enabled, int, 0);
MODULE_PARM_DESC(enabled, "Pin module/firmware loading (default: true)");
...@@ -60,6 +60,7 @@ int __init security_init(void) ...@@ -60,6 +60,7 @@ int __init security_init(void)
*/ */
capability_add_hooks(); capability_add_hooks();
yama_add_hooks(); yama_add_hooks();
loadpin_add_hooks();
/* /*
* Load all the remaining security modules. * Load all the remaining security modules.
...@@ -1848,7 +1849,6 @@ struct security_hook_heads security_hook_heads = { ...@@ -1848,7 +1849,6 @@ struct security_hook_heads security_hook_heads = {
.tun_dev_attach = .tun_dev_attach =
LIST_HEAD_INIT(security_hook_heads.tun_dev_attach), LIST_HEAD_INIT(security_hook_heads.tun_dev_attach),
.tun_dev_open = LIST_HEAD_INIT(security_hook_heads.tun_dev_open), .tun_dev_open = LIST_HEAD_INIT(security_hook_heads.tun_dev_open),
.skb_owned_by = LIST_HEAD_INIT(security_hook_heads.skb_owned_by),
#endif /* CONFIG_SECURITY_NETWORK */ #endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM #ifdef CONFIG_SECURITY_NETWORK_XFRM
.xfrm_policy_alloc_security = .xfrm_policy_alloc_security =
......
This diff is collapsed.
...@@ -12,6 +12,18 @@ ...@@ -12,6 +12,18 @@
#define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \ #define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \
"write", "associate", "unix_read", "unix_write" "write", "associate", "unix_read", "unix_write"
#define COMMON_CAP_PERMS "chown", "dac_override", "dac_read_search", \
"fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", \
"linux_immutable", "net_bind_service", "net_broadcast", \
"net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module", \
"sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin", \
"sys_boot", "sys_nice", "sys_resource", "sys_time", \
"sys_tty_config", "mknod", "lease", "audit_write", \
"audit_control", "setfcap"
#define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \
"wake_alarm", "block_suspend", "audit_read"
/* /*
* Note: The name for any socket class should be suffixed by "socket", * Note: The name for any socket class should be suffixed by "socket",
* and doesn't contain more than one substr of "socket". * and doesn't contain more than one substr of "socket".
...@@ -32,16 +44,9 @@ struct security_class_mapping secclass_map[] = { ...@@ -32,16 +44,9 @@ struct security_class_mapping secclass_map[] = {
"setsockcreate", NULL } }, "setsockcreate", NULL } },
{ "system", { "system",
{ "ipc_info", "syslog_read", "syslog_mod", { "ipc_info", "syslog_read", "syslog_mod",
"syslog_console", "module_request", NULL } }, "syslog_console", "module_request", "module_load", NULL } },
{ "capability", { "capability",
{ "chown", "dac_override", "dac_read_search", { COMMON_CAP_PERMS, NULL } },
"fowner", "fsetid", "kill", "setgid", "setuid", "setpcap",
"linux_immutable", "net_bind_service", "net_broadcast",
"net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module",
"sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin",
"sys_boot", "sys_nice", "sys_resource", "sys_time",
"sys_tty_config", "mknod", "lease", "audit_write",
"audit_control", "setfcap", NULL } },
{ "filesystem", { "filesystem",
{ "mount", "remount", "unmount", "getattr", { "mount", "remount", "unmount", "getattr",
"relabelfrom", "relabelto", "associate", "quotamod", "relabelfrom", "relabelto", "associate", "quotamod",
...@@ -150,12 +155,15 @@ struct security_class_mapping secclass_map[] = { ...@@ -150,12 +155,15 @@ struct security_class_mapping secclass_map[] = {
{ "memprotect", { "mmap_zero", NULL } }, { "memprotect", { "mmap_zero", NULL } },
{ "peer", { "recv", NULL } }, { "peer", { "recv", NULL } },
{ "capability2", { "capability2",
{ "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", { COMMON_CAP2_PERMS, NULL } },
"audit_read", NULL } },
{ "kernel_service", { "use_as_override", "create_files_as", NULL } }, { "kernel_service", { "use_as_override", "create_files_as", NULL } },
{ "tun_socket", { "tun_socket",
{ COMMON_SOCK_PERMS, "attach_queue", NULL } }, { COMMON_SOCK_PERMS, "attach_queue", NULL } },
{ "binder", { "impersonate", "call", "set_context_mgr", "transfer", { "binder", { "impersonate", "call", "set_context_mgr", "transfer",
NULL } }, NULL } },
{ "cap_userns",
{ COMMON_CAP_PERMS, NULL } },
{ "cap2_userns",
{ COMMON_CAP2_PERMS, NULL } },
{ NULL } { NULL }
}; };
...@@ -17,6 +17,6 @@ int security_get_bools(int *len, char ***names, int **values); ...@@ -17,6 +17,6 @@ int security_get_bools(int *len, char ***names, int **values);
int security_set_bools(int len, int *values); int security_set_bools(int len, int *values);
int security_get_bool_value(int bool); int security_get_bool_value(int index);
#endif #endif
...@@ -38,9 +38,8 @@ struct task_security_struct { ...@@ -38,9 +38,8 @@ struct task_security_struct {
}; };
enum label_initialized { enum label_initialized {
LABEL_MISSING, /* not initialized */ LABEL_INVALID, /* invalid or not initialized */
LABEL_INITIALIZED, /* inizialized */ LABEL_INITIALIZED /* initialized */
LABEL_INVALID /* invalid */
}; };
struct inode_security_struct { struct inode_security_struct {
......
...@@ -2696,7 +2696,7 @@ int security_set_bools(int len, int *values) ...@@ -2696,7 +2696,7 @@ int security_set_bools(int len, int *values)
return rc; return rc;
} }
int security_get_bool_value(int bool) int security_get_bool_value(int index)
{ {
int rc; int rc;
int len; int len;
...@@ -2705,10 +2705,10 @@ int security_get_bool_value(int bool) ...@@ -2705,10 +2705,10 @@ int security_get_bool_value(int bool)
rc = -EFAULT; rc = -EFAULT;
len = policydb.p_bools.nprim; len = policydb.p_bools.nprim;
if (bool >= len) if (index >= len)
goto out; goto out;
rc = policydb.bool_val_to_struct[bool]->state; rc = policydb.bool_val_to_struct[index]->state;
out: out:
read_unlock(&policy_rwlock); read_unlock(&policy_rwlock);
return rc; return rc;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/prctl.h> #include <linux/prctl.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/string_helpers.h>
#define YAMA_SCOPE_DISABLED 0 #define YAMA_SCOPE_DISABLED 0
#define YAMA_SCOPE_RELATIONAL 1 #define YAMA_SCOPE_RELATIONAL 1
...@@ -41,6 +42,22 @@ static DEFINE_SPINLOCK(ptracer_relations_lock); ...@@ -41,6 +42,22 @@ static DEFINE_SPINLOCK(ptracer_relations_lock);
static void yama_relation_cleanup(struct work_struct *work); static void yama_relation_cleanup(struct work_struct *work);
static DECLARE_WORK(yama_relation_work, yama_relation_cleanup); static DECLARE_WORK(yama_relation_work, yama_relation_cleanup);
static void report_access(const char *access, struct task_struct *target,
struct task_struct *agent)
{
char *target_cmd, *agent_cmd;
target_cmd = kstrdup_quotable_cmdline(target, GFP_ATOMIC);
agent_cmd = kstrdup_quotable_cmdline(agent, GFP_ATOMIC);
pr_notice_ratelimited(
"ptrace %s of \"%s\"[%d] was attempted by \"%s\"[%d]\n",
access, target_cmd, target->pid, agent_cmd, agent->pid);
kfree(agent_cmd);
kfree(target_cmd);
}
/** /**
* yama_relation_cleanup - remove invalid entries from the relation list * yama_relation_cleanup - remove invalid entries from the relation list
* *
...@@ -307,11 +324,8 @@ static int yama_ptrace_access_check(struct task_struct *child, ...@@ -307,11 +324,8 @@ static int yama_ptrace_access_check(struct task_struct *child,
} }
} }
if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0) { if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0)
printk_ratelimited(KERN_NOTICE report_access("attach", child, current);
"ptrace of pid %d was attempted by: %s (pid %d)\n",
child->pid, current->comm, current->pid);
}
return rc; return rc;
} }
...@@ -337,11 +351,8 @@ int yama_ptrace_traceme(struct task_struct *parent) ...@@ -337,11 +351,8 @@ int yama_ptrace_traceme(struct task_struct *parent)
break; break;
} }
if (rc) { if (rc)
printk_ratelimited(KERN_NOTICE report_access("traceme", current, parent);
"ptraceme of pid %d was attempted by: %s (pid %d)\n",
current->pid, parent->comm, parent->pid);
}
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment