mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "In this release: - PKCS#7 parser for the key management subsystem from David Howells - appoint Kees Cook as seccomp maintainer - bugfixes and general maintenance across the subsystem" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (94 commits) X.509: Need to export x509_request_asymmetric_key() netlabel: shorter names for the NetLabel catmap funcs/structs netlabel: fix the catmap walking functions netlabel: fix the horribly broken catmap functions netlabel: fix a problem when setting bits below the previously lowest bit PKCS#7: X.509 certificate issuer and subject are mandatory fields in the ASN.1 tpm: simplify code by using %*phN specifier tpm: Provide a generic means to override the chip returned timeouts tpm: missing tpm_chip_put in tpm_get_random() tpm: Properly clean sysfs entries in error path tpm: Add missing tpm_do_selftest to ST33 I2C driver PKCS#7: Use x509_request_asymmetric_key() Revert "selinux: fix the default socket labeling in sock_graft()" X.509: x509_request_asymmetric_keys() doesn't need string length arguments PKCS#7: fix sparse non static symbol warning KEYS: revert encrypted key change ima: add support for measuring and appraising firmware firmware_class: perform new LSM checks security: introduce kernel_fw_from_file hook PKCS#7: Missing inclusion of linux/err.h ...
This commit is contained in:
commit
bb2cbf5e93
@ -26,6 +26,7 @@ Description:
|
||||
option: [[appraise_type=]] [permit_directio]
|
||||
|
||||
base: func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK]
|
||||
[FIRMWARE_CHECK]
|
||||
mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
|
||||
fsmagic:= hex value
|
||||
fsuuid:= file system UUID (e.g 8bcbe394-4f13-4144-be8e-5aa9ea2ce2f6)
|
||||
@ -57,7 +58,8 @@ Description:
|
||||
measure func=BPRM_CHECK
|
||||
measure func=FILE_MMAP mask=MAY_EXEC
|
||||
measure func=FILE_CHECK mask=MAY_READ uid=0
|
||||
measure func=MODULE_CHECK uid=0
|
||||
measure func=MODULE_CHECK
|
||||
measure func=FIRMWARE_CHECK
|
||||
appraise fowner=0
|
||||
|
||||
The default policy measures all executables in bprm_check,
|
||||
|
@ -566,6 +566,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
possible to determine what the correct size should be.
|
||||
This option provides an override for these situations.
|
||||
|
||||
ca_keys= [KEYS] This parameter identifies a specific key(s) on
|
||||
the system trusted keyring to be used for certificate
|
||||
trust validation.
|
||||
format: { id:<keyid> | builtin }
|
||||
|
||||
ccw_timeout_log [S390]
|
||||
See Documentation/s390/CommonIO for details.
|
||||
|
||||
@ -1319,6 +1324,23 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
Formats: { "ima" | "ima-ng" }
|
||||
Default: "ima-ng"
|
||||
|
||||
ima.ahash_minsize= [IMA] Minimum file size for asynchronous hash usage
|
||||
Format: <min_file_size>
|
||||
Set the minimal file size for using asynchronous hash.
|
||||
If left unspecified, ahash usage is disabled.
|
||||
|
||||
ahash performance varies for different data sizes on
|
||||
different crypto accelerators. This option can be used
|
||||
to achieve the best performance for a particular HW.
|
||||
|
||||
ima.ahash_bufsize= [IMA] Asynchronous hash buffer size
|
||||
Format: <bufsize>
|
||||
Set hashing buffer size. Default: 4k.
|
||||
|
||||
ahash performance varies for different chunk sizes on
|
||||
different crypto accelerators. This option can be used
|
||||
to achieve best performance for particular HW.
|
||||
|
||||
init= [KNL]
|
||||
Format: <full_path>
|
||||
Run specified binary instead of /sbin/init as init
|
||||
|
@ -1150,20 +1150,24 @@ The structure has a number of fields, some of which are mandatory:
|
||||
const void *data;
|
||||
size_t datalen;
|
||||
size_t quotalen;
|
||||
time_t expiry;
|
||||
};
|
||||
|
||||
Before calling the method, the caller will fill in data and datalen with
|
||||
the payload blob parameters; quotalen will be filled in with the default
|
||||
quota size from the key type and the rest will be cleared.
|
||||
quota size from the key type; expiry will be set to TIME_T_MAX and the
|
||||
rest will be cleared.
|
||||
|
||||
If a description can be proposed from the payload contents, that should be
|
||||
attached as a string to the description field. This will be used for the
|
||||
key description if the caller of add_key() passes NULL or "".
|
||||
|
||||
The method can attach anything it likes to type_data[] and payload. These
|
||||
are merely passed along to the instantiate() or update() operations.
|
||||
are merely passed along to the instantiate() or update() operations. If
|
||||
set, the expiry time will be applied to the key if it is instantiated from
|
||||
this data.
|
||||
|
||||
The method should return 0 if success ful or a negative error code
|
||||
The method should return 0 if successful or a negative error code
|
||||
otherwise.
|
||||
|
||||
|
||||
@ -1172,7 +1176,9 @@ The structure has a number of fields, some of which are mandatory:
|
||||
This method is only required if the preparse() method is provided,
|
||||
otherwise it is unused. It cleans up anything attached to the
|
||||
description, type_data and payload fields of the key_preparsed_payload
|
||||
struct as filled in by the preparse() method.
|
||||
struct as filled in by the preparse() method. It will always be called
|
||||
after preparse() returns successfully, even if instantiate() or update()
|
||||
succeed.
|
||||
|
||||
|
||||
(*) int (*instantiate)(struct key *key, struct key_preparsed_payload *prep);
|
||||
|
10
MAINTAINERS
10
MAINTAINERS
@ -8002,6 +8002,16 @@ S: Maintained
|
||||
F: drivers/mmc/host/sdhci.*
|
||||
F: drivers/mmc/host/sdhci-pltfm.[ch]
|
||||
|
||||
SECURE COMPUTING
|
||||
M: Kees Cook <keescook@chromium.org>
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git seccomp
|
||||
S: Supported
|
||||
F: kernel/seccomp.c
|
||||
F: include/uapi/linux/seccomp.h
|
||||
F: include/linux/seccomp.h
|
||||
K: \bsecure_computing
|
||||
K: \bTIF_SECCOMP\b
|
||||
|
||||
SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF)
|
||||
M: Anton Vorontsov <anton@enomsg.org>
|
||||
L: linuxppc-dev@lists.ozlabs.org
|
||||
|
@ -321,6 +321,7 @@ config HAVE_ARCH_SECCOMP_FILTER
|
||||
- secure_computing is called from a ptrace_event()-safe context
|
||||
- secure_computing return value is checked and a return value of -1
|
||||
results in the system call being skipped immediately.
|
||||
- seccomp syscall wired up
|
||||
|
||||
config SECCOMP_FILTER
|
||||
def_bool y
|
||||
|
@ -409,6 +409,7 @@
|
||||
#define __NR_sched_setattr (__NR_SYSCALL_BASE+380)
|
||||
#define __NR_sched_getattr (__NR_SYSCALL_BASE+381)
|
||||
#define __NR_renameat2 (__NR_SYSCALL_BASE+382)
|
||||
#define __NR_seccomp (__NR_SYSCALL_BASE+383)
|
||||
|
||||
/*
|
||||
* The following SWIs are ARM private.
|
||||
|
@ -392,6 +392,7 @@
|
||||
/* 380 */ CALL(sys_sched_setattr)
|
||||
CALL(sys_sched_getattr)
|
||||
CALL(sys_renameat2)
|
||||
CALL(sys_seccomp)
|
||||
#ifndef syscalls_counted
|
||||
.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
|
||||
#define syscalls_counted
|
||||
|
@ -372,16 +372,17 @@
|
||||
#define __NR_sched_setattr (__NR_Linux + 349)
|
||||
#define __NR_sched_getattr (__NR_Linux + 350)
|
||||
#define __NR_renameat2 (__NR_Linux + 351)
|
||||
#define __NR_seccomp (__NR_Linux + 352)
|
||||
|
||||
/*
|
||||
* Offset of the last Linux o32 flavoured syscall
|
||||
*/
|
||||
#define __NR_Linux_syscalls 351
|
||||
#define __NR_Linux_syscalls 352
|
||||
|
||||
#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
|
||||
|
||||
#define __NR_O32_Linux 4000
|
||||
#define __NR_O32_Linux_syscalls 351
|
||||
#define __NR_O32_Linux_syscalls 352
|
||||
|
||||
#if _MIPS_SIM == _MIPS_SIM_ABI64
|
||||
|
||||
@ -701,16 +702,17 @@
|
||||
#define __NR_sched_setattr (__NR_Linux + 309)
|
||||
#define __NR_sched_getattr (__NR_Linux + 310)
|
||||
#define __NR_renameat2 (__NR_Linux + 311)
|
||||
#define __NR_seccomp (__NR_Linux + 312)
|
||||
|
||||
/*
|
||||
* Offset of the last Linux 64-bit flavoured syscall
|
||||
*/
|
||||
#define __NR_Linux_syscalls 311
|
||||
#define __NR_Linux_syscalls 312
|
||||
|
||||
#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */
|
||||
|
||||
#define __NR_64_Linux 5000
|
||||
#define __NR_64_Linux_syscalls 311
|
||||
#define __NR_64_Linux_syscalls 312
|
||||
|
||||
#if _MIPS_SIM == _MIPS_SIM_NABI32
|
||||
|
||||
@ -1034,15 +1036,16 @@
|
||||
#define __NR_sched_setattr (__NR_Linux + 313)
|
||||
#define __NR_sched_getattr (__NR_Linux + 314)
|
||||
#define __NR_renameat2 (__NR_Linux + 315)
|
||||
#define __NR_seccomp (__NR_Linux + 316)
|
||||
|
||||
/*
|
||||
* Offset of the last N32 flavoured syscall
|
||||
*/
|
||||
#define __NR_Linux_syscalls 315
|
||||
#define __NR_Linux_syscalls 316
|
||||
|
||||
#endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */
|
||||
|
||||
#define __NR_N32_Linux 6000
|
||||
#define __NR_N32_Linux_syscalls 315
|
||||
#define __NR_N32_Linux_syscalls 316
|
||||
|
||||
#endif /* _UAPI_ASM_UNISTD_H */
|
||||
|
@ -578,3 +578,4 @@ EXPORT(sys_call_table)
|
||||
PTR sys_sched_setattr
|
||||
PTR sys_sched_getattr /* 4350 */
|
||||
PTR sys_renameat2
|
||||
PTR sys_seccomp
|
||||
|
@ -431,4 +431,5 @@ EXPORT(sys_call_table)
|
||||
PTR sys_sched_setattr
|
||||
PTR sys_sched_getattr /* 5310 */
|
||||
PTR sys_renameat2
|
||||
PTR sys_seccomp
|
||||
.size sys_call_table,.-sys_call_table
|
||||
|
@ -424,4 +424,5 @@ EXPORT(sysn32_call_table)
|
||||
PTR sys_sched_setattr
|
||||
PTR sys_sched_getattr
|
||||
PTR sys_renameat2 /* 6315 */
|
||||
PTR sys_seccomp
|
||||
.size sysn32_call_table,.-sysn32_call_table
|
||||
|
@ -557,4 +557,5 @@ EXPORT(sys32_call_table)
|
||||
PTR sys_sched_setattr
|
||||
PTR sys_sched_getattr /* 4350 */
|
||||
PTR sys_renameat2
|
||||
PTR sys_seccomp
|
||||
.size sys32_call_table,.-sys32_call_table
|
||||
|
@ -360,3 +360,4 @@
|
||||
351 i386 sched_setattr sys_sched_setattr
|
||||
352 i386 sched_getattr sys_sched_getattr
|
||||
353 i386 renameat2 sys_renameat2
|
||||
354 i386 seccomp sys_seccomp
|
||||
|
@ -323,6 +323,7 @@
|
||||
314 common sched_setattr sys_sched_setattr
|
||||
315 common sched_getattr sys_sched_getattr
|
||||
316 common renameat2 sys_renameat2
|
||||
317 common seccomp sys_seccomp
|
||||
|
||||
#
|
||||
# x32-specific system call numbers start at 512 to avoid cache impact
|
||||
|
@ -22,7 +22,6 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
|
||||
config PUBLIC_KEY_ALGO_RSA
|
||||
tristate "RSA public-key algorithm"
|
||||
select MPILIB_EXTRA
|
||||
select MPILIB
|
||||
help
|
||||
This option enables support for the RSA algorithm (PKCS#1, RFC3447).
|
||||
@ -33,8 +32,39 @@ config X509_CERTIFICATE_PARSER
|
||||
select ASN1
|
||||
select OID_REGISTRY
|
||||
help
|
||||
This option procides support for parsing X.509 format blobs for key
|
||||
This option provides support for parsing X.509 format blobs for key
|
||||
data and provides the ability to instantiate a crypto key from a
|
||||
public key packet found inside the certificate.
|
||||
|
||||
config PKCS7_MESSAGE_PARSER
|
||||
tristate "PKCS#7 message parser"
|
||||
depends on X509_CERTIFICATE_PARSER
|
||||
select ASN1
|
||||
select OID_REGISTRY
|
||||
help
|
||||
This option provides support for parsing PKCS#7 format messages for
|
||||
signature data and provides the ability to verify the signature.
|
||||
|
||||
config PKCS7_TEST_KEY
|
||||
tristate "PKCS#7 testing key type"
|
||||
depends on PKCS7_MESSAGE_PARSER
|
||||
select SYSTEM_TRUSTED_KEYRING
|
||||
help
|
||||
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
|
||||
it is, the PKCS#7 wrapper is discarded and reading the key returns
|
||||
just the payload. If it isn't, adding the key will fail with an
|
||||
error.
|
||||
|
||||
This is intended for testing the PKCS#7 parser.
|
||||
|
||||
config SIGNED_PE_FILE_VERIFICATION
|
||||
bool "Support for PE file signature verification"
|
||||
depends on PKCS7_MESSAGE_PARSER=y
|
||||
select ASN1
|
||||
select OID_REGISTRY
|
||||
help
|
||||
This option provides support for verifying the signature(s) on a
|
||||
signed PE binary.
|
||||
|
||||
endif # ASYMMETRIC_KEY_TYPE
|
||||
|
@ -25,3 +25,40 @@ $(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h
|
||||
|
||||
clean-files += x509-asn1.c x509-asn1.h
|
||||
clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h
|
||||
|
||||
#
|
||||
# PKCS#7 message handling
|
||||
#
|
||||
obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
|
||||
pkcs7_message-y := \
|
||||
pkcs7-asn1.o \
|
||||
pkcs7_parser.o \
|
||||
pkcs7_trust.o \
|
||||
pkcs7_verify.o
|
||||
|
||||
$(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h
|
||||
$(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h
|
||||
|
||||
clean-files += pkcs7-asn1.c pkcs7-asn1.h
|
||||
|
||||
#
|
||||
# PKCS#7 parser testing key
|
||||
#
|
||||
obj-$(CONFIG_PKCS7_TEST_KEY) += pkcs7_test_key.o
|
||||
pkcs7_test_key-y := \
|
||||
pkcs7_key_type.o
|
||||
|
||||
#
|
||||
# Signed PE binary-wrapped key handling
|
||||
#
|
||||
obj-$(CONFIG_SIGNED_PE_FILE_VERIFICATION) += verify_signed_pefile.o
|
||||
|
||||
verify_signed_pefile-y := \
|
||||
verify_pefile.o \
|
||||
mscode_parser.o \
|
||||
mscode-asn1.o
|
||||
|
||||
$(obj)/mscode_parser.o: $(obj)/mscode-asn1.h $(obj)/mscode-asn1.h
|
||||
$(obj)/mscode-asn1.o: $(obj)/mscode-asn1.c $(obj)/mscode-asn1.h
|
||||
|
||||
clean-files += mscode-asn1.c mscode-asn1.h
|
||||
|
@ -9,6 +9,8 @@
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
int asymmetric_keyid_match(const char *kid, const char *id);
|
||||
|
||||
static inline const char *asymmetric_key_id(const struct key *key)
|
||||
{
|
||||
return key->type_data.p[1];
|
||||
|
@ -22,6 +22,35 @@ MODULE_LICENSE("GPL");
|
||||
static LIST_HEAD(asymmetric_key_parsers);
|
||||
static DECLARE_RWSEM(asymmetric_key_parsers_sem);
|
||||
|
||||
/*
|
||||
* Match asymmetric key id with partial match
|
||||
* @id: key id to match in a form "id:<id>"
|
||||
*/
|
||||
int asymmetric_keyid_match(const char *kid, const char *id)
|
||||
{
|
||||
size_t idlen, kidlen;
|
||||
|
||||
if (!kid || !id)
|
||||
return 0;
|
||||
|
||||
/* make it possible to use id as in the request: "id:<id>" */
|
||||
if (strncmp(id, "id:", 3) == 0)
|
||||
id += 3;
|
||||
|
||||
/* Anything after here requires a partial match on the ID string */
|
||||
idlen = strlen(id);
|
||||
kidlen = strlen(kid);
|
||||
if (idlen > kidlen)
|
||||
return 0;
|
||||
|
||||
kid += kidlen - idlen;
|
||||
if (strcasecmp(id, kid) != 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asymmetric_keyid_match);
|
||||
|
||||
/*
|
||||
* Match asymmetric keys on (part of) their name
|
||||
* We have some shorthand methods for matching keys. We allow:
|
||||
@ -34,9 +63,8 @@ static int asymmetric_key_match(const struct key *key, const void *description)
|
||||
{
|
||||
const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
|
||||
const char *spec = description;
|
||||
const char *id, *kid;
|
||||
const char *id;
|
||||
ptrdiff_t speclen;
|
||||
size_t idlen, kidlen;
|
||||
|
||||
if (!subtype || !spec || !*spec)
|
||||
return 0;
|
||||
@ -55,23 +83,8 @@ static int asymmetric_key_match(const struct key *key, const void *description)
|
||||
speclen = id - spec;
|
||||
id++;
|
||||
|
||||
/* Anything after here requires a partial match on the ID string */
|
||||
kid = asymmetric_key_id(key);
|
||||
if (!kid)
|
||||
return 0;
|
||||
|
||||
idlen = strlen(id);
|
||||
kidlen = strlen(kid);
|
||||
if (idlen > kidlen)
|
||||
return 0;
|
||||
|
||||
kid += kidlen - idlen;
|
||||
if (strcasecmp(id, kid) != 0)
|
||||
return 0;
|
||||
|
||||
if (speclen == 2 &&
|
||||
memcmp(spec, "id", 2) == 0)
|
||||
return 1;
|
||||
if (speclen == 2 && memcmp(spec, "id", 2) == 0)
|
||||
return asymmetric_keyid_match(asymmetric_key_id(key), id);
|
||||
|
||||
if (speclen == subtype->name_len &&
|
||||
memcmp(spec, subtype->name, speclen) == 0)
|
||||
@ -156,36 +169,13 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (subtype) {
|
||||
subtype->destroy(prep->payload);
|
||||
subtype->destroy(prep->payload[0]);
|
||||
module_put(subtype->owner);
|
||||
}
|
||||
kfree(prep->type_data[1]);
|
||||
kfree(prep->description);
|
||||
}
|
||||
|
||||
/*
|
||||
* Instantiate a asymmetric_key defined key. The key was preparsed, so we just
|
||||
* have to transfer the data here.
|
||||
*/
|
||||
static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
ret = key_payload_reserve(key, prep->quotalen);
|
||||
if (ret == 0) {
|
||||
key->type_data.p[0] = prep->type_data[0];
|
||||
key->type_data.p[1] = prep->type_data[1];
|
||||
key->payload.data = prep->payload;
|
||||
prep->type_data[0] = NULL;
|
||||
prep->type_data[1] = NULL;
|
||||
prep->payload = NULL;
|
||||
}
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a asymmetric key
|
||||
*/
|
||||
@ -205,7 +195,7 @@ struct key_type key_type_asymmetric = {
|
||||
.name = "asymmetric",
|
||||
.preparse = asymmetric_key_preparse,
|
||||
.free_preparse = asymmetric_key_free_preparse,
|
||||
.instantiate = asymmetric_key_instantiate,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = asymmetric_key_match,
|
||||
.destroy = asymmetric_key_destroy,
|
||||
.describe = asymmetric_key_describe,
|
||||
|
28
crypto/asymmetric_keys/mscode.asn1
Normal file
28
crypto/asymmetric_keys/mscode.asn1
Normal file
@ -0,0 +1,28 @@
|
||||
--- Microsoft individual code signing data blob parser
|
||||
---
|
||||
--- Copyright (C) 2012 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.
|
||||
---
|
||||
|
||||
MSCode ::= SEQUENCE {
|
||||
type SEQUENCE {
|
||||
contentType ContentType,
|
||||
parameters ANY
|
||||
},
|
||||
content SEQUENCE {
|
||||
digestAlgorithm DigestAlgorithmIdentifier,
|
||||
digest OCTET STRING ({ mscode_note_digest })
|
||||
}
|
||||
}
|
||||
|
||||
ContentType ::= OBJECT IDENTIFIER ({ mscode_note_content_type })
|
||||
|
||||
DigestAlgorithmIdentifier ::= SEQUENCE {
|
||||
algorithm OBJECT IDENTIFIER ({ mscode_note_digest_algo }),
|
||||
parameters ANY OPTIONAL
|
||||
}
|
126
crypto/asymmetric_keys/mscode_parser.c
Normal file
126
crypto/asymmetric_keys/mscode_parser.c
Normal file
@ -0,0 +1,126 @@
|
||||
/* Parse a Microsoft Individual Code Signing blob
|
||||
*
|
||||
* Copyright (C) 2014 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) "MSCODE: "fmt
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/oid_registry.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include "verify_pefile.h"
|
||||
#include "mscode-asn1.h"
|
||||
|
||||
/*
|
||||
* Parse a Microsoft Individual Code Signing blob
|
||||
*/
|
||||
int mscode_parse(struct pefile_context *ctx)
|
||||
{
|
||||
const void *content_data;
|
||||
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;
|
||||
}
|
||||
|
||||
pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len),
|
||||
content_data);
|
||||
|
||||
return asn1_ber_decoder(&mscode_decoder, ctx, content_data, data_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the content type OID
|
||||
*/
|
||||
int mscode_note_content_type(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
enum OID oid;
|
||||
|
||||
oid = look_up_OID(value, vlen);
|
||||
if (oid == OID__NR) {
|
||||
char buffer[50];
|
||||
|
||||
sprint_oid(value, vlen, buffer, sizeof(buffer));
|
||||
pr_err("Unknown OID: %s\n", buffer);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/*
|
||||
* pesign utility had a bug where it was putting
|
||||
* OID_msIndividualSPKeyPurpose instead of OID_msPeImageDataObjId
|
||||
* So allow both OIDs.
|
||||
*/
|
||||
if (oid != OID_msPeImageDataObjId &&
|
||||
oid != OID_msIndividualSPKeyPurpose) {
|
||||
pr_err("Unexpected content type OID %u\n", oid);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the digest algorithm OID
|
||||
*/
|
||||
int mscode_note_digest_algo(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pefile_context *ctx = context;
|
||||
char buffer[50];
|
||||
enum OID oid;
|
||||
|
||||
oid = look_up_OID(value, vlen);
|
||||
switch (oid) {
|
||||
case OID_md4:
|
||||
ctx->digest_algo = HASH_ALGO_MD4;
|
||||
break;
|
||||
case OID_md5:
|
||||
ctx->digest_algo = HASH_ALGO_MD5;
|
||||
break;
|
||||
case OID_sha1:
|
||||
ctx->digest_algo = HASH_ALGO_SHA1;
|
||||
break;
|
||||
case OID_sha256:
|
||||
ctx->digest_algo = HASH_ALGO_SHA256;
|
||||
break;
|
||||
|
||||
case OID__NR:
|
||||
sprint_oid(value, vlen, buffer, sizeof(buffer));
|
||||
pr_err("Unknown OID: %s\n", buffer);
|
||||
return -EBADMSG;
|
||||
|
||||
default:
|
||||
pr_err("Unsupported content type: %u\n", oid);
|
||||
return -ENOPKG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the digest we're guaranteeing with this certificate
|
||||
*/
|
||||
int mscode_note_digest(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pefile_context *ctx = context;
|
||||
|
||||
ctx->digest = value;
|
||||
ctx->digest_len = vlen;
|
||||
return 0;
|
||||
}
|
127
crypto/asymmetric_keys/pkcs7.asn1
Normal file
127
crypto/asymmetric_keys/pkcs7.asn1
Normal file
@ -0,0 +1,127 @@
|
||||
PKCS7ContentInfo ::= SEQUENCE {
|
||||
contentType ContentType,
|
||||
content [0] EXPLICIT SignedData OPTIONAL
|
||||
}
|
||||
|
||||
ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID })
|
||||
|
||||
SignedData ::= SEQUENCE {
|
||||
version INTEGER,
|
||||
digestAlgorithms DigestAlgorithmIdentifiers,
|
||||
contentInfo ContentInfo,
|
||||
certificates CHOICE {
|
||||
certSet [0] IMPLICIT ExtendedCertificatesAndCertificates,
|
||||
certSequence [2] IMPLICIT Certificates
|
||||
} OPTIONAL ({ pkcs7_note_certificate_list }),
|
||||
crls CHOICE {
|
||||
crlSet [1] IMPLICIT CertificateRevocationLists,
|
||||
crlSequence [3] IMPLICIT CRLSequence
|
||||
} OPTIONAL,
|
||||
signerInfos SignerInfos
|
||||
}
|
||||
|
||||
ContentInfo ::= SEQUENCE {
|
||||
contentType ContentType,
|
||||
content [0] EXPLICIT Data OPTIONAL
|
||||
}
|
||||
|
||||
Data ::= ANY ({ pkcs7_note_data })
|
||||
|
||||
DigestAlgorithmIdentifiers ::= CHOICE {
|
||||
daSet SET OF DigestAlgorithmIdentifier,
|
||||
daSequence SEQUENCE OF DigestAlgorithmIdentifier
|
||||
}
|
||||
|
||||
DigestAlgorithmIdentifier ::= SEQUENCE {
|
||||
algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
parameters ANY OPTIONAL
|
||||
}
|
||||
|
||||
--
|
||||
-- Certificates and certificate lists
|
||||
--
|
||||
ExtendedCertificatesAndCertificates ::= SET OF ExtendedCertificateOrCertificate
|
||||
|
||||
ExtendedCertificateOrCertificate ::= CHOICE {
|
||||
certificate Certificate, -- X.509
|
||||
extendedCertificate [0] IMPLICIT ExtendedCertificate -- PKCS#6
|
||||
}
|
||||
|
||||
ExtendedCertificate ::= Certificate -- cheating
|
||||
|
||||
Certificates ::= SEQUENCE OF Certificate
|
||||
|
||||
CertificateRevocationLists ::= SET OF CertificateList
|
||||
|
||||
CertificateList ::= SEQUENCE OF Certificate -- This may be defined incorrectly
|
||||
|
||||
CRLSequence ::= SEQUENCE OF CertificateList
|
||||
|
||||
Certificate ::= ANY ({ pkcs7_extract_cert }) -- X.509
|
||||
|
||||
--
|
||||
-- Signer information
|
||||
--
|
||||
SignerInfos ::= CHOICE {
|
||||
siSet SET OF SignerInfo,
|
||||
siSequence SEQUENCE OF SignerInfo
|
||||
}
|
||||
|
||||
SignerInfo ::= SEQUENCE {
|
||||
version INTEGER,
|
||||
issuerAndSerialNumber IssuerAndSerialNumber,
|
||||
digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }),
|
||||
authenticatedAttributes CHOICE {
|
||||
aaSet [0] IMPLICIT SetOfAuthenticatedAttribute
|
||||
({ pkcs7_sig_note_set_of_authattrs }),
|
||||
aaSequence [2] EXPLICIT SEQUENCE OF AuthenticatedAttribute
|
||||
-- Explicit because easier to compute digest on
|
||||
-- sequence of attributes and then reuse encoded
|
||||
-- sequence in aaSequence.
|
||||
} OPTIONAL,
|
||||
digestEncryptionAlgorithm
|
||||
DigestEncryptionAlgorithmIdentifier ({ pkcs7_sig_note_pkey_algo }),
|
||||
encryptedDigest EncryptedDigest,
|
||||
unauthenticatedAttributes CHOICE {
|
||||
uaSet [1] IMPLICIT SET OF UnauthenticatedAttribute,
|
||||
uaSequence [3] IMPLICIT SEQUENCE OF UnauthenticatedAttribute
|
||||
} OPTIONAL
|
||||
} ({ pkcs7_note_signed_info })
|
||||
|
||||
IssuerAndSerialNumber ::= SEQUENCE {
|
||||
issuer Name ({ pkcs7_sig_note_issuer }),
|
||||
serialNumber CertificateSerialNumber ({ pkcs7_sig_note_serial })
|
||||
}
|
||||
|
||||
CertificateSerialNumber ::= INTEGER
|
||||
|
||||
SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute
|
||||
|
||||
AuthenticatedAttribute ::= SEQUENCE {
|
||||
type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
values SET OF ANY ({ pkcs7_sig_note_authenticated_attr })
|
||||
}
|
||||
|
||||
UnauthenticatedAttribute ::= SEQUENCE {
|
||||
type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
values SET OF ANY
|
||||
}
|
||||
|
||||
DigestEncryptionAlgorithmIdentifier ::= SEQUENCE {
|
||||
algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
parameters ANY OPTIONAL
|
||||
}
|
||||
|
||||
EncryptedDigest ::= OCTET STRING ({ pkcs7_sig_note_signature })
|
||||
|
||||
---
|
||||
--- X.500 Name
|
||||
---
|
||||
Name ::= SEQUENCE OF RelativeDistinguishedName
|
||||
|
||||
RelativeDistinguishedName ::= SET OF AttributeValueAssertion
|
||||
|
||||
AttributeValueAssertion ::= SEQUENCE {
|
||||
attributeType OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
attributeValue ANY
|
||||
}
|
100
crypto/asymmetric_keys/pkcs7_key_type.c
Normal file
100
crypto/asymmetric_keys/pkcs7_key_type.c
Normal file
@ -0,0 +1,100 @@
|
||||
/* Testing module to load key from trusted PKCS#7 message
|
||||
*
|
||||
* Copyright (C) 2014 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) "PKCS7key: "fmt
|
||||
#include <linux/key.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "pkcs7_parser.h"
|
||||
|
||||
/*
|
||||
* Preparse a PKCS#7 wrapped and validated data blob.
|
||||
*/
|
||||
static int pkcs7_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct pkcs7_message *pkcs7;
|
||||
const void *data, *saved_prep_data;
|
||||
size_t datalen, saved_prep_datalen;
|
||||
bool trusted;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
|
||||
saved_prep_data = prep->data;
|
||||
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);
|
||||
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->datalen = datalen;
|
||||
ret = user_preparse(prep);
|
||||
prep->data = saved_prep_data;
|
||||
prep->datalen = saved_prep_datalen;
|
||||
|
||||
error_free:
|
||||
pkcs7_free_message(pkcs7);
|
||||
error:
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* user defined keys take an arbitrary string as the description and an
|
||||
* arbitrary blob of data as the payload
|
||||
*/
|
||||
static struct key_type key_type_pkcs7 = {
|
||||
.name = "pkcs7_test",
|
||||
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.preparse = pkcs7_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.revoke = user_revoke,
|
||||
.destroy = user_destroy,
|
||||
.describe = user_describe,
|
||||
.read = user_read,
|
||||
};
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
static int __init pkcs7_key_init(void)
|
||||
{
|
||||
return register_key_type(&key_type_pkcs7);
|
||||
}
|
||||
|
||||
static void __exit pkcs7_key_cleanup(void)
|
||||
{
|
||||
unregister_key_type(&key_type_pkcs7);
|
||||
}
|
||||
|
||||
module_init(pkcs7_key_init);
|
||||
module_exit(pkcs7_key_cleanup);
|
396
crypto/asymmetric_keys/pkcs7_parser.c
Normal file
396
crypto/asymmetric_keys/pkcs7_parser.c
Normal file
@ -0,0 +1,396 @@
|
||||
/* PKCS#7 parser
|
||||
*
|
||||
* Copyright (C) 2012 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) "PKCS7: "fmt
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/oid_registry.h>
|
||||
#include "public_key.h"
|
||||
#include "pkcs7_parser.h"
|
||||
#include "pkcs7-asn1.h"
|
||||
|
||||
struct pkcs7_parse_context {
|
||||
struct pkcs7_message *msg; /* Message being constructed */
|
||||
struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */
|
||||
struct pkcs7_signed_info **ppsinfo;
|
||||
struct x509_certificate *certs; /* Certificate cache */
|
||||
struct x509_certificate **ppcerts;
|
||||
unsigned long data; /* Start of data */
|
||||
enum OID last_oid; /* Last OID encountered */
|
||||
unsigned x509_index;
|
||||
unsigned sinfo_index;
|
||||
};
|
||||
|
||||
/**
|
||||
* pkcs7_free_message - Free a PKCS#7 message
|
||||
* @pkcs7: The PKCS#7 message to free
|
||||
*/
|
||||
void pkcs7_free_message(struct pkcs7_message *pkcs7)
|
||||
{
|
||||
struct x509_certificate *cert;
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
|
||||
if (pkcs7) {
|
||||
while (pkcs7->certs) {
|
||||
cert = pkcs7->certs;
|
||||
pkcs7->certs = cert->next;
|
||||
x509_free_certificate(cert);
|
||||
}
|
||||
while (pkcs7->crl) {
|
||||
cert = pkcs7->crl;
|
||||
pkcs7->crl = cert->next;
|
||||
x509_free_certificate(cert);
|
||||
}
|
||||
while (pkcs7->signed_infos) {
|
||||
sinfo = pkcs7->signed_infos;
|
||||
pkcs7->signed_infos = sinfo->next;
|
||||
mpi_free(sinfo->sig.mpi[0]);
|
||||
kfree(sinfo->sig.digest);
|
||||
kfree(sinfo);
|
||||
}
|
||||
kfree(pkcs7);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_free_message);
|
||||
|
||||
/**
|
||||
* pkcs7_parse_message - Parse a PKCS#7 message
|
||||
* @data: The raw binary ASN.1 encoded message to be parsed
|
||||
* @datalen: The size of the encoded message
|
||||
*/
|
||||
struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx;
|
||||
struct pkcs7_message *msg;
|
||||
long ret;
|
||||
|
||||
ret = -ENOMEM;
|
||||
msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
|
||||
if (!msg)
|
||||
goto error_no_sig;
|
||||
ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
goto error_no_ctx;
|
||||
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
|
||||
if (!ctx->sinfo)
|
||||
goto error_no_sinfo;
|
||||
|
||||
ctx->msg = msg;
|
||||
ctx->data = (unsigned long)data;
|
||||
ctx->ppcerts = &ctx->certs;
|
||||
ctx->ppsinfo = &ctx->msg->signed_infos;
|
||||
|
||||
/* Attempt to decode the signature */
|
||||
ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
|
||||
if (ret < 0)
|
||||
goto error_decode;
|
||||
|
||||
while (ctx->certs) {
|
||||
struct x509_certificate *cert = ctx->certs;
|
||||
ctx->certs = cert->next;
|
||||
x509_free_certificate(cert);
|
||||
}
|
||||
mpi_free(ctx->sinfo->sig.mpi[0]);
|
||||
kfree(ctx->sinfo->sig.digest);
|
||||
kfree(ctx->sinfo);
|
||||
kfree(ctx);
|
||||
return msg;
|
||||
|
||||
error_decode:
|
||||
mpi_free(ctx->sinfo->sig.mpi[0]);
|
||||
kfree(ctx->sinfo->sig.digest);
|
||||
kfree(ctx->sinfo);
|
||||
error_no_sinfo:
|
||||
kfree(ctx);
|
||||
error_no_ctx:
|
||||
pkcs7_free_message(msg);
|
||||
error_no_sig:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_parse_message);
|
||||
|
||||
/**
|
||||
* pkcs7_get_content_data - Get access to the PKCS#7 content
|
||||
* @pkcs7: The preparsed PKCS#7 message to access
|
||||
* @_data: Place to return a pointer to the data
|
||||
* @_data_len: Place to return the data length
|
||||
* @want_wrapper: True if the ASN.1 object header should be included in the data
|
||||
*
|
||||
* Get access to the data content of the PKCS#7 message, including, optionally,
|
||||
* the header of the ASN.1 object that contains it. Returns -ENODATA if the
|
||||
* data object was missing from the message.
|
||||
*/
|
||||
int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
|
||||
const void **_data, size_t *_data_len,
|
||||
bool want_wrapper)
|
||||
{
|
||||
size_t wrapper;
|
||||
|
||||
if (!pkcs7->data)
|
||||
return -ENODATA;
|
||||
|
||||
wrapper = want_wrapper ? pkcs7->data_hdrlen : 0;
|
||||
*_data = pkcs7->data - wrapper;
|
||||
*_data_len = pkcs7->data_len + wrapper;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
|
||||
|
||||
/*
|
||||
* Note an OID when we find one for later processing when we know how
|
||||
* to interpret it.
|
||||
*/
|
||||
int pkcs7_note_OID(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
ctx->last_oid = look_up_OID(value, vlen);
|
||||
if (ctx->last_oid == OID__NR) {
|
||||
char buffer[50];
|
||||
sprint_oid(value, vlen, buffer, sizeof(buffer));
|
||||
printk("PKCS7: Unknown OID: [%lu] %s\n",
|
||||
(unsigned long)value - ctx->data, buffer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the digest algorithm for the signature.
|
||||
*/
|
||||
int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_md4:
|
||||
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD4;
|
||||
break;
|
||||
case OID_md5:
|
||||
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD5;
|
||||
break;
|
||||
case OID_sha1:
|
||||
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA1;
|
||||
break;
|
||||
case OID_sha256:
|
||||
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA256;
|
||||
break;
|
||||
default:
|
||||
printk("Unsupported digest algo: %u\n", ctx->last_oid);
|
||||
return -ENOPKG;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the public key algorithm for the signature.
|
||||
*/
|
||||
int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_rsaEncryption:
|
||||
ctx->sinfo->sig.pkey_algo = PKEY_ALGO_RSA;
|
||||
break;
|
||||
default:
|
||||
printk("Unsupported pkey algo: %u\n", ctx->last_oid);
|
||||
return -ENOPKG;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a certificate and store it in the context.
|
||||
*/
|
||||
int pkcs7_extract_cert(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
struct x509_certificate *x509;
|
||||
|
||||
if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {
|
||||
pr_debug("Cert began with tag %02x at %lu\n",
|
||||
tag, (unsigned long)ctx - ctx->data);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* We have to correct for the header so that the X.509 parser can start
|
||||
* from the beginning. Note that since X.509 stipulates DER, there
|
||||
* probably shouldn't be an EOC trailer - but it is in PKCS#7 (which
|
||||
* stipulates BER).
|
||||
*/
|
||||
value -= hdrlen;
|
||||
vlen += hdrlen;
|
||||
|
||||
if (((u8*)value)[1] == 0x80)
|
||||
vlen += 2; /* Indefinite length - there should be an EOC */
|
||||
|
||||
x509 = x509_cert_parse(value, vlen);
|
||||
if (IS_ERR(x509))
|
||||
return PTR_ERR(x509);
|
||||
|
||||
pr_debug("Got cert for %s\n", x509->subject);
|
||||
pr_debug("- fingerprint %s\n", x509->fingerprint);
|
||||
|
||||
x509->index = ++ctx->x509_index;
|
||||
*ctx->ppcerts = x509;
|
||||
ctx->ppcerts = &x509->next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the certificate list
|
||||
*/
|
||||
int pkcs7_note_certificate_list(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
pr_devel("Got cert list (%02x)\n", tag);
|
||||
|
||||
*ctx->ppcerts = ctx->msg->certs;
|
||||
ctx->msg->certs = ctx->certs;
|
||||
ctx->certs = NULL;
|
||||
ctx->ppcerts = &ctx->certs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the data from the message and store that and its content type OID in
|
||||
* the context.
|
||||
*/
|
||||
int pkcs7_note_data(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
pr_debug("Got data\n");
|
||||
|
||||
ctx->msg->data = value;
|
||||
ctx->msg->data_len = vlen;
|
||||
ctx->msg->data_hdrlen = hdrlen;
|
||||
ctx->msg->data_type = ctx->last_oid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse authenticated attributes
|
||||
*/
|
||||
int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_messageDigest:
|
||||
if (tag != ASN1_OTS)
|
||||
return -EBADMSG;
|
||||
ctx->sinfo->msgdigest = value;
|
||||
ctx->sinfo->msgdigest_len = vlen;
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the set of auth attributes for digestion purposes [RFC2315 9.3]
|
||||
*/
|
||||
int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
|
||||
ctx->sinfo->authattrs = value - (hdrlen - 1);
|
||||
ctx->sinfo->authattrs_len = vlen + (hdrlen - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the issuing certificate serial number
|
||||
*/
|
||||
int pkcs7_sig_note_serial(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
ctx->sinfo->raw_serial = value;
|
||||
ctx->sinfo->raw_serial_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the issuer's name
|
||||
*/
|
||||
int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
ctx->sinfo->raw_issuer = value;
|
||||
ctx->sinfo->raw_issuer_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the signature data
|
||||
*/
|
||||
int pkcs7_sig_note_signature(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
MPI mpi;
|
||||
|
||||
BUG_ON(ctx->sinfo->sig.pkey_algo != PKEY_ALGO_RSA);
|
||||
|
||||
mpi = mpi_read_raw_data(value, vlen);
|
||||
if (!mpi)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->sinfo->sig.mpi[0] = mpi;
|
||||
ctx->sinfo->sig.nr_mpi = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note a signature information block
|
||||
*/
|
||||
int pkcs7_note_signed_info(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
ctx->sinfo->index = ++ctx->sinfo_index;
|
||||
*ctx->ppsinfo = ctx->sinfo;
|
||||
ctx->ppsinfo = &ctx->sinfo->next;
|
||||
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
|
||||
if (!ctx->sinfo)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
61
crypto/asymmetric_keys/pkcs7_parser.h
Normal file
61
crypto/asymmetric_keys/pkcs7_parser.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* PKCS#7 crypto data parser internal definitions
|
||||
*
|
||||
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/oid_registry.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include "x509_parser.h"
|
||||
|
||||
#define kenter(FMT, ...) \
|
||||
pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
|
||||
#define kleave(FMT, ...) \
|
||||
pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
|
||||
|
||||
struct pkcs7_signed_info {
|
||||
struct pkcs7_signed_info *next;
|
||||
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
|
||||
unsigned index;
|
||||
bool trusted;
|
||||
|
||||
/* Message digest - the digest of the Content Data (or NULL) */
|
||||
const void *msgdigest;
|
||||
unsigned msgdigest_len;
|
||||
|
||||
/* Authenticated Attribute data (or NULL) */
|
||||
unsigned authattrs_len;
|
||||
const void *authattrs;
|
||||
|
||||
/* Issuing cert serial number and issuer's name */
|
||||
const void *raw_serial;
|
||||
unsigned raw_serial_size;
|
||||
unsigned raw_issuer_size;
|
||||
const void *raw_issuer;
|
||||
|
||||
/* Message signature.
|
||||
*
|
||||
* This contains the generated digest of _either_ the Content Data or
|
||||
* the Authenticated Attributes [RFC2315 9.3]. If the latter, one of
|
||||
* the attributes contains the digest of the the Content Data within
|
||||
* it.
|
||||
*/
|
||||
struct public_key_signature sig;
|
||||
};
|
||||
|
||||
struct pkcs7_message {
|
||||
struct x509_certificate *certs; /* Certificate list */
|
||||
struct x509_certificate *crl; /* Revocation list */
|
||||
struct pkcs7_signed_info *signed_infos;
|
||||
|
||||
/* Content Data (or NULL) */
|
||||
enum OID data_type; /* Type of Data */
|
||||
size_t data_len; /* Length of Data */
|
||||
size_t data_hdrlen; /* Length of Data ASN.1 header */
|
||||
const void *data; /* Content Data (or 0) */
|
||||
};
|
166
crypto/asymmetric_keys/pkcs7_trust.c
Normal file
166
crypto/asymmetric_keys/pkcs7_trust.c
Normal file
@ -0,0 +1,166 @@
|
||||
/* Validate the trust chain of a PKCS#7 message.
|
||||
*
|
||||
* Copyright (C) 2012 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) "PKCS7: "fmt
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/asn1.h>
|
||||
#include <linux/key.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include "public_key.h"
|
||||
#include "pkcs7_parser.h"
|
||||
|
||||
/**
|
||||
* Check the trust on one PKCS#7 SignedInfo block.
|
||||
*/
|
||||
int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo,
|
||||
struct key *trust_keyring)
|
||||
{
|
||||
struct public_key_signature *sig = &sinfo->sig;
|
||||
struct x509_certificate *x509, *last = NULL, *p;
|
||||
struct key *key;
|
||||
bool trusted;
|
||||
int ret;
|
||||
|
||||
kenter(",%u,", sinfo->index);
|
||||
|
||||
for (x509 = sinfo->signer; x509; x509 = x509->signer) {
|
||||
if (x509->seen) {
|
||||
if (x509->verified) {
|
||||
trusted = x509->trusted;
|
||||
goto verified;
|
||||
}
|
||||
kleave(" = -ENOKEY [cached]");
|
||||
return -ENOKEY;
|
||||
}
|
||||
x509->seen = true;
|
||||
|
||||
/* Look to see if this certificate is present in the trusted
|
||||
* keys.
|
||||
*/
|
||||
key = x509_request_asymmetric_key(trust_keyring, x509->subject,
|
||||
x509->fingerprint);
|
||||
if (!IS_ERR(key))
|
||||
/* One of the X.509 certificates in the PKCS#7 message
|
||||
* is apparently the same as one we already trust.
|
||||
* Verify that the trusted variant can also validate
|
||||
* the signature on the descendant.
|
||||
*/
|
||||
goto matched;
|
||||
if (key == ERR_PTR(-ENOMEM))
|
||||
return -ENOMEM;
|
||||
|
||||
/* Self-signed certificates form roots of their own, and if we
|
||||
* don't know them, then we can't accept them.
|
||||
*/
|
||||
if (x509->next == x509) {
|
||||
kleave(" = -ENOKEY [unknown self-signed]");
|
||||
return -ENOKEY;
|
||||
}
|
||||
|
||||
might_sleep();
|
||||
last = x509;
|
||||
sig = &last->sig;
|
||||
}
|
||||
|
||||
/* No match - see if the root certificate has a signer amongst the
|
||||
* trusted keys.
|
||||
*/
|
||||
if (!last || !last->issuer || !last->authority) {
|
||||
kleave(" = -ENOKEY [no backref]");
|
||||
return -ENOKEY;
|
||||
}
|
||||
|
||||
key = x509_request_asymmetric_key(trust_keyring, last->issuer,
|
||||
last->authority);
|
||||
if (IS_ERR(key))
|
||||
return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY;
|
||||
x509 = last;
|
||||
|
||||
matched:
|
||||
ret = verify_signature(key, sig);
|
||||
trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags);
|
||||
key_put(key);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOMEM)
|
||||
return ret;
|
||||
kleave(" = -EKEYREJECTED [verify %d]", ret);
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
|
||||
verified:
|
||||
x509->verified = true;
|
||||
for (p = sinfo->signer; p != x509; p = p->signer) {
|
||||
p->verified = true;
|
||||
p->trusted = trusted;
|
||||
}
|
||||
sinfo->trusted = trusted;
|
||||
kleave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pkcs7_validate_trust - Validate PKCS#7 trust chain
|
||||
* @pkcs7: The PKCS#7 certificate to validate
|
||||
* @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
|
||||
* keys we already know and trust.
|
||||
*
|
||||
* Returns, in order of descending priority:
|
||||
*
|
||||
* (*) -EKEYREJECTED if a signature failed to match for which we have a valid
|
||||
* key, or:
|
||||
*
|
||||
* (*) 0 if at least one signature chain intersects with the keys in the trust
|
||||
* keyring, or:
|
||||
*
|
||||
* (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
|
||||
* chain.
|
||||
*
|
||||
* (*) -ENOKEY if we couldn't find a match for any of the signature chains in
|
||||
* the message.
|
||||
*
|
||||
* May also return -ENOMEM.
|
||||
*/
|
||||
int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
|
||||
struct key *trust_keyring,
|
||||
bool *_trusted)
|
||||
{
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
struct x509_certificate *p;
|
||||
int cached_ret = 0, ret;
|
||||
|
||||
for (p = pkcs7->certs; p; p = p->next)
|
||||
p->seen = false;
|
||||
|
||||
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
|
||||
ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOPKG) {
|
||||
cached_ret = -ENOPKG;
|
||||
} else if (ret == -ENOKEY) {
|
||||
if (cached_ret == 0)
|
||||
cached_ret = -ENOKEY;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
*_trusted |= sinfo->trusted;
|
||||
}
|
||||
|
||||
return cached_ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_validate_trust);
|
321
crypto/asymmetric_keys/pkcs7_verify.c
Normal file
321
crypto/asymmetric_keys/pkcs7_verify.c
Normal file
@ -0,0 +1,321 @@
|
||||
/* Verify the signature on a PKCS#7 message.
|
||||
*
|
||||
* Copyright (C) 2012 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) "PKCS7: "fmt
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/asn1.h>
|
||||
#include <crypto/hash.h>
|
||||
#include "public_key.h"
|
||||
#include "pkcs7_parser.h"
|
||||
|
||||
/*
|
||||
* Digest the relevant parts of the PKCS#7 data
|
||||
*/
|
||||
static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *desc;
|
||||
size_t digest_size, desc_size;
|
||||
void *digest;
|
||||
int ret;
|
||||
|
||||
kenter(",%u,%u", sinfo->index, sinfo->sig.pkey_hash_algo);
|
||||
|
||||
if (sinfo->sig.pkey_hash_algo >= PKEY_HASH__LAST ||
|
||||
!hash_algo_name[sinfo->sig.pkey_hash_algo])
|
||||
return -ENOPKG;
|
||||
|
||||
/* Allocate the hashing algorithm we're going to need and find out how
|
||||
* big the hash operational data will be.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(hash_algo_name[sinfo->sig.pkey_hash_algo],
|
||||
0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
|
||||
|
||||
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
||||
sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm);
|
||||
|
||||
ret = -ENOMEM;
|
||||
digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
|
||||
if (!digest)
|
||||
goto error_no_desc;
|
||||
|
||||
desc = digest + digest_size;
|
||||
desc->tfm = tfm;
|
||||
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
/* Digest the message [RFC2315 9.3] */
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
pr_devel("MsgDigest = [%*ph]\n", 8, digest);
|
||||
|
||||
/* However, if there are authenticated attributes, there must be a
|
||||
* message digest attribute amongst them which corresponds to the
|
||||
* digest we just calculated.
|
||||
*/
|
||||
if (sinfo->msgdigest) {
|
||||
u8 tag;
|
||||
|
||||
if (sinfo->msgdigest_len != sinfo->sig.digest_size) {
|
||||
pr_debug("Sig %u: Invalid digest size (%u)\n",
|
||||
sinfo->index, sinfo->msgdigest_len);
|
||||
ret = -EBADMSG;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (memcmp(digest, sinfo->msgdigest, sinfo->msgdigest_len) != 0) {
|
||||
pr_debug("Sig %u: Message digest doesn't match\n",
|
||||
sinfo->index);
|
||||
ret = -EKEYREJECTED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* We then calculate anew, using the authenticated attributes
|
||||
* as the contents of the digest instead. Note that we need to
|
||||
* convert the attributes from a CONT.0 into a SET before we
|
||||
* hash it.
|
||||
*/
|
||||
memset(digest, 0, sinfo->sig.digest_size);
|
||||
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
tag = ASN1_CONS_BIT | ASN1_SET;
|
||||
ret = crypto_shash_update(desc, &tag, 1);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
ret = crypto_shash_finup(desc, sinfo->authattrs,
|
||||
sinfo->authattrs_len, digest);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
pr_devel("AADigest = [%*ph]\n", 8, digest);
|
||||
}
|
||||
|
||||
sinfo->sig.digest = digest;
|
||||
digest = NULL;
|
||||
|
||||
error:
|
||||
kfree(digest);
|
||||
error_no_desc:
|
||||
crypto_free_shash(tfm);
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7
|
||||
* uses the issuer's name and the issuing certificate serial number for
|
||||
* matching purposes. These must match the certificate issuer's name (not
|
||||
* subject's name) and the certificate serial number [RFC 2315 6.7].
|
||||
*/
|
||||
static int pkcs7_find_key(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
struct x509_certificate *x509;
|
||||
unsigned certix = 1;
|
||||
|
||||
kenter("%u,%u,%u",
|
||||
sinfo->index, sinfo->raw_serial_size, sinfo->raw_issuer_size);
|
||||
|
||||
for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) {
|
||||
/* I'm _assuming_ that the generator of the PKCS#7 message will
|
||||
* encode the fields from the X.509 cert in the same way in the
|
||||
* PKCS#7 message - but I can't be 100% sure of that. It's
|
||||
* possible this will need element-by-element comparison.
|
||||
*/
|
||||
if (x509->raw_serial_size != sinfo->raw_serial_size ||
|
||||
memcmp(x509->raw_serial, sinfo->raw_serial,
|
||||
sinfo->raw_serial_size) != 0)
|
||||
continue;
|
||||
pr_devel("Sig %u: Found cert serial match X.509[%u]\n",
|
||||
sinfo->index, certix);
|
||||
|
||||
if (x509->raw_issuer_size != sinfo->raw_issuer_size ||
|
||||
memcmp(x509->raw_issuer, sinfo->raw_issuer,
|
||||
sinfo->raw_issuer_size) != 0) {
|
||||
pr_warn("Sig %u: X.509 subject and PKCS#7 issuer don't match\n",
|
||||
sinfo->index);
|
||||
continue;
|
||||
}
|
||||
|
||||
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",
|
||||
sinfo->index);
|
||||
continue;
|
||||
}
|
||||
|
||||
sinfo->signer = x509;
|
||||
return 0;
|
||||
}
|
||||
pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n",
|
||||
sinfo->index, sinfo->raw_serial_size, sinfo->raw_serial);
|
||||
return -ENOKEY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the internal certificate chain as best we can.
|
||||
*/
|
||||
static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
struct x509_certificate *x509 = sinfo->signer, *p;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
|
||||
for (p = pkcs7->certs; p; p = p->next)
|
||||
p->seen = false;
|
||||
|
||||
for (;;) {
|
||||
pr_debug("verify %s: %s\n", x509->subject, x509->fingerprint);
|
||||
x509->seen = true;
|
||||
ret = x509_get_sig_params(x509);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pr_debug("- issuer %s\n", x509->issuer);
|
||||
if (x509->authority)
|
||||
pr_debug("- authkeyid %s\n", x509->authority);
|
||||
|
||||
if (!x509->authority ||
|
||||
strcmp(x509->subject, x509->issuer) == 0) {
|
||||
/* If there's no authority certificate specified, then
|
||||
* the certificate must be self-signed and is the root
|
||||
* of the chain. Likewise if the cert is its own
|
||||
* authority.
|
||||
*/
|
||||
pr_debug("- no auth?\n");
|
||||
if (x509->raw_subject_size != x509->raw_issuer_size ||
|
||||
memcmp(x509->raw_subject, x509->raw_issuer,
|
||||
x509->raw_issuer_size) != 0)
|
||||
return 0;
|
||||
|
||||
ret = x509_check_signature(x509->pub, x509);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
x509->signer = x509;
|
||||
pr_debug("- self-signed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Look through the X.509 certificates in the PKCS#7 message's
|
||||
* list to see if the next one is there.
|
||||
*/
|
||||
pr_debug("- want %s\n", x509->authority);
|
||||
for (p = pkcs7->certs; p; p = p->next) {
|
||||
pr_debug("- cmp [%u] %s\n", p->index, p->fingerprint);
|
||||
if (p->raw_subject_size == x509->raw_issuer_size &&
|
||||
strcmp(p->fingerprint, x509->authority) == 0 &&
|
||||
memcmp(p->raw_subject, x509->raw_issuer,
|
||||
x509->raw_issuer_size) == 0)
|
||||
goto found_issuer;
|
||||
}
|
||||
|
||||
/* We didn't find the root of this chain */
|
||||
pr_debug("- top\n");
|
||||
return 0;
|
||||
|
||||
found_issuer:
|
||||
pr_debug("- issuer %s\n", p->subject);
|
||||
if (p->seen) {
|
||||
pr_warn("Sig %u: X.509 chain contains loop\n",
|
||||
sinfo->index);
|
||||
return 0;
|
||||
}
|
||||
ret = x509_check_signature(p->pub, x509);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
x509->signer = p;
|
||||
if (x509 == p) {
|
||||
pr_debug("- self-signed\n");
|
||||
return 0;
|
||||
}
|
||||
x509 = p;
|
||||
might_sleep();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify one signed information block from a PKCS#7 message.
|
||||
*/
|
||||
static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
int ret;
|
||||
|
||||
kenter(",%u", sinfo->index);
|
||||
|
||||
/* First of all, digest the data in the PKCS#7 message and the
|
||||
* signed information block
|
||||
*/
|
||||
ret = pkcs7_digest(pkcs7, sinfo);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Find the key for the signature */
|
||||
ret = pkcs7_find_key(pkcs7, sinfo);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pr_devel("Using X.509[%u] for sig %u\n",
|
||||
sinfo->signer->index, sinfo->index);
|
||||
|
||||
/* Verify the PKCS#7 binary against the key */
|
||||
ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pr_devel("Verified signature %u\n", sinfo->index);
|
||||
|
||||
/* Verify the internal certificate chain */
|
||||
return pkcs7_verify_sig_chain(pkcs7, sinfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* pkcs7_verify - Verify a PKCS#7 message
|
||||
* @pkcs7: The PKCS#7 message to be verified
|
||||
*/
|
||||
int pkcs7_verify(struct pkcs7_message *pkcs7)
|
||||
{
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
struct x509_certificate *x509;
|
||||
int ret, n;
|
||||
|
||||
kenter("");
|
||||
|
||||
for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) {
|
||||
ret = x509_get_sig_params(x509);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pr_debug("X.509[%u] %s\n", n, x509->authority);
|
||||
}
|
||||
|
||||
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
|
||||
ret = pkcs7_verify_one(pkcs7, sinfo);
|
||||
if (ret < 0) {
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
kleave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_verify);
|
457
crypto/asymmetric_keys/verify_pefile.c
Normal file
457
crypto/asymmetric_keys/verify_pefile.c
Normal file
@ -0,0 +1,457 @@
|
||||
/* Parse a signed PE binary
|
||||
*
|
||||
* Copyright (C) 2014 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) "PEFILE: "fmt
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pe.h>
|
||||
#include <linux/asn1.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include <crypto/hash.h>
|
||||
#include "verify_pefile.h"
|
||||
|
||||
/*
|
||||
* Parse a PE binary.
|
||||
*/
|
||||
static int pefile_parse_binary(const void *pebuf, unsigned int pelen,
|
||||
struct pefile_context *ctx)
|
||||
{
|
||||
const struct mz_hdr *mz = pebuf;
|
||||
const struct pe_hdr *pe;
|
||||
const struct pe32_opt_hdr *pe32;
|
||||
const struct pe32plus_opt_hdr *pe64;
|
||||
const struct data_directory *ddir;
|
||||
const struct data_dirent *dde;
|
||||
const struct section_header *secs, *sec;
|
||||
size_t cursor, datalen = pelen;
|
||||
|
||||
kenter("");
|
||||
|
||||
#define chkaddr(base, x, s) \
|
||||
do { \
|
||||
if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
|
||||
return -ELIBBAD; \
|
||||
} while (0)
|
||||
|
||||
chkaddr(0, 0, sizeof(*mz));
|
||||
if (mz->magic != MZ_MAGIC)
|
||||
return -ELIBBAD;
|
||||
cursor = sizeof(*mz);
|
||||
|
||||
chkaddr(cursor, mz->peaddr, sizeof(*pe));
|
||||
pe = pebuf + mz->peaddr;
|
||||
if (pe->magic != PE_MAGIC)
|
||||
return -ELIBBAD;
|
||||
cursor = mz->peaddr + sizeof(*pe);
|
||||
|
||||
chkaddr(0, cursor, sizeof(pe32->magic));
|
||||
pe32 = pebuf + cursor;
|
||||
pe64 = pebuf + cursor;
|
||||
|
||||
switch (pe32->magic) {
|
||||
case PE_OPT_MAGIC_PE32:
|
||||
chkaddr(0, cursor, sizeof(*pe32));
|
||||
ctx->image_checksum_offset =
|
||||
(unsigned long)&pe32->csum - (unsigned long)pebuf;
|
||||
ctx->header_size = pe32->header_size;
|
||||
cursor += sizeof(*pe32);
|
||||
ctx->n_data_dirents = pe32->data_dirs;
|
||||
break;
|
||||
|
||||
case PE_OPT_MAGIC_PE32PLUS:
|
||||
chkaddr(0, cursor, sizeof(*pe64));
|
||||
ctx->image_checksum_offset =
|
||||
(unsigned long)&pe64->csum - (unsigned long)pebuf;
|
||||
ctx->header_size = pe64->header_size;
|
||||
cursor += sizeof(*pe64);
|
||||
ctx->n_data_dirents = pe64->data_dirs;
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_debug("Unknown PEOPT magic = %04hx\n", pe32->magic);
|
||||
return -ELIBBAD;
|
||||
}
|
||||
|
||||
pr_debug("checksum @ %x\n", ctx->image_checksum_offset);
|
||||
pr_debug("header size = %x\n", ctx->header_size);
|
||||
|
||||
if (cursor >= ctx->header_size || ctx->header_size >= datalen)
|
||||
return -ELIBBAD;
|
||||
|
||||
if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde))
|
||||
return -ELIBBAD;
|
||||
|
||||
ddir = pebuf + cursor;
|
||||
cursor += sizeof(*dde) * ctx->n_data_dirents;
|
||||
|
||||
ctx->cert_dirent_offset =
|
||||
(unsigned long)&ddir->certs - (unsigned long)pebuf;
|
||||
ctx->certs_size = ddir->certs.size;
|
||||
|
||||
if (!ddir->certs.virtual_address || !ddir->certs.size) {
|
||||
pr_debug("Unsigned PE binary\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
|
||||
chkaddr(ctx->header_size, ddir->certs.virtual_address,
|
||||
ddir->certs.size);
|
||||
ctx->sig_offset = ddir->certs.virtual_address;
|
||||
ctx->sig_len = ddir->certs.size;
|
||||
pr_debug("cert = %x @%x [%*ph]\n",
|
||||
ctx->sig_len, ctx->sig_offset,
|
||||
ctx->sig_len, pebuf + ctx->sig_offset);
|
||||
|
||||
ctx->n_sections = pe->sections;
|
||||
if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec))
|
||||
return -ELIBBAD;
|
||||
ctx->secs = secs = pebuf + cursor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check and strip the PE wrapper from around the signature and check that the
|
||||
* remnant looks something like PKCS#7.
|
||||
*/
|
||||
static int pefile_strip_sig_wrapper(const void *pebuf,
|
||||
struct pefile_context *ctx)
|
||||
{
|
||||
struct win_certificate wrapper;
|
||||
const u8 *pkcs7;
|
||||
|
||||
if (ctx->sig_len < sizeof(wrapper)) {
|
||||
pr_debug("Signature wrapper too short\n");
|
||||
return -ELIBBAD;
|
||||
}
|
||||
|
||||
memcpy(&wrapper, pebuf + ctx->sig_offset, sizeof(wrapper));
|
||||
pr_debug("sig wrapper = { %x, %x, %x }\n",
|
||||
wrapper.length, wrapper.revision, wrapper.cert_type);
|
||||
|
||||
/* Both pesign and sbsign round up the length of certificate table
|
||||
* (in optional header data directories) to 8 byte alignment.
|
||||
*/
|
||||
if (round_up(wrapper.length, 8) != ctx->sig_len) {
|
||||
pr_debug("Signature wrapper len wrong\n");
|
||||
return -ELIBBAD;
|
||||
}
|
||||
if (wrapper.revision != WIN_CERT_REVISION_2_0) {
|
||||
pr_debug("Signature is not revision 2.0\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
if (wrapper.cert_type != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
|
||||
pr_debug("Signature certificate type is not PKCS\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
/* Looks like actual pkcs signature length is in wrapper->length.
|
||||
* size obtained from data dir entries lists the total size of
|
||||
* certificate table which is also aligned to octawrod boundary.
|
||||
*
|
||||
* So set signature length field appropriately.
|
||||
*/
|
||||
ctx->sig_len = wrapper.length;
|
||||
ctx->sig_offset += sizeof(wrapper);
|
||||
ctx->sig_len -= sizeof(wrapper);
|
||||
if (ctx->sig_len == 0) {
|
||||
pr_debug("Signature data missing\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
|
||||
/* What's left should a PKCS#7 cert */
|
||||
pkcs7 = pebuf + ctx->sig_offset;
|
||||
if (pkcs7[0] == (ASN1_CONS_BIT | ASN1_SEQ)) {
|
||||
if (pkcs7[1] == 0x82 &&
|
||||
pkcs7[2] == (((ctx->sig_len - 4) >> 8) & 0xff) &&
|
||||
pkcs7[3] == ((ctx->sig_len - 4) & 0xff))
|
||||
return 0;
|
||||
if (pkcs7[1] == 0x80)
|
||||
return 0;
|
||||
if (pkcs7[1] > 0x82)
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
pr_debug("Signature data not PKCS#7\n");
|
||||
return -ELIBBAD;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare two sections for canonicalisation.
|
||||
*/
|
||||
static int pefile_compare_shdrs(const void *a, const void *b)
|
||||
{
|
||||
const struct section_header *shdra = a;
|
||||
const struct section_header *shdrb = b;
|
||||
int rc;
|
||||
|
||||
if (shdra->data_addr > shdrb->data_addr)
|
||||
return 1;
|
||||
if (shdrb->data_addr > shdra->data_addr)
|
||||
return -1;
|
||||
|
||||
if (shdra->virtual_address > shdrb->virtual_address)
|
||||
return 1;
|
||||
if (shdrb->virtual_address > shdra->virtual_address)
|
||||
return -1;
|
||||
|
||||
rc = strcmp(shdra->name, shdrb->name);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
if (shdra->virtual_size > shdrb->virtual_size)
|
||||
return 1;
|
||||
if (shdrb->virtual_size > shdra->virtual_size)
|
||||
return -1;
|
||||
|
||||
if (shdra->raw_data_size > shdrb->raw_data_size)
|
||||
return 1;
|
||||
if (shdrb->raw_data_size > shdra->raw_data_size)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the contents of the PE binary into the digest, leaving out the image
|
||||
* checksum and the certificate data block.
|
||||
*/
|
||||
static int pefile_digest_pe_contents(const void *pebuf, unsigned int pelen,
|
||||
struct pefile_context *ctx,
|
||||
struct shash_desc *desc)
|
||||
{
|
||||
unsigned *canon, tmp, loop, i, hashed_bytes;
|
||||
int ret;
|
||||
|
||||
/* Digest the header and data directory, but leave out the image
|
||||
* checksum and the data dirent for the signature.
|
||||
*/
|
||||
ret = crypto_shash_update(desc, pebuf, ctx->image_checksum_offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmp = ctx->image_checksum_offset + sizeof(uint32_t);
|
||||
ret = crypto_shash_update(desc, pebuf + tmp,
|
||||
ctx->cert_dirent_offset - tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmp = ctx->cert_dirent_offset + sizeof(struct data_dirent);
|
||||
ret = crypto_shash_update(desc, pebuf + tmp, ctx->header_size - tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
canon = kcalloc(ctx->n_sections, sizeof(unsigned), GFP_KERNEL);
|
||||
if (!canon)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We have to canonicalise the section table, so we perform an
|
||||
* insertion sort.
|
||||
*/
|
||||
canon[0] = 0;
|
||||
for (loop = 1; loop < ctx->n_sections; loop++) {
|
||||
for (i = 0; i < loop; i++) {
|
||||
if (pefile_compare_shdrs(&ctx->secs[canon[i]],
|
||||
&ctx->secs[loop]) > 0) {
|
||||
memmove(&canon[i + 1], &canon[i],
|
||||
(loop - i) * sizeof(canon[0]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
canon[i] = loop;
|
||||
}
|
||||
|
||||
hashed_bytes = ctx->header_size;
|
||||
for (loop = 0; loop < ctx->n_sections; loop++) {
|
||||
i = canon[loop];
|
||||
if (ctx->secs[i].raw_data_size == 0)
|
||||
continue;
|
||||
ret = crypto_shash_update(desc,
|
||||
pebuf + ctx->secs[i].data_addr,
|
||||
ctx->secs[i].raw_data_size);
|
||||
if (ret < 0) {
|
||||
kfree(canon);
|
||||
return ret;
|
||||
}
|
||||
hashed_bytes += ctx->secs[i].raw_data_size;
|
||||
}
|
||||
kfree(canon);
|
||||
|
||||
if (pelen > hashed_bytes) {
|
||||
tmp = hashed_bytes + ctx->certs_size;
|
||||
ret = crypto_shash_update(desc,
|
||||
pebuf + hashed_bytes,
|
||||
pelen - tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Digest the contents of the PE binary, leaving out the image checksum and the
|
||||
* certificate data block.
|
||||
*/
|
||||
static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
|
||||
struct pefile_context *ctx)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *desc;
|
||||
size_t digest_size, desc_size;
|
||||
void *digest;
|
||||
int ret;
|
||||
|
||||
kenter(",%u", ctx->digest_algo);
|
||||
|
||||
/* Allocate the hashing algorithm we're going to need and find out how
|
||||
* big the hash operational data will be.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(hash_algo_name[ctx->digest_algo], 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
|
||||
|
||||
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
||||
digest_size = crypto_shash_digestsize(tfm);
|
||||
|
||||
if (digest_size != ctx->digest_len) {
|
||||
pr_debug("Digest size mismatch (%zx != %x)\n",
|
||||
digest_size, ctx->digest_len);
|
||||
ret = -EBADMSG;
|
||||
goto error_no_desc;
|
||||
}
|
||||
pr_debug("Digest: desc=%zu size=%zu\n", desc_size, digest_size);
|
||||
|
||||
ret = -ENOMEM;
|
||||
desc = kzalloc(desc_size + digest_size, GFP_KERNEL);
|
||||
if (!desc)
|
||||
goto error_no_desc;
|
||||
|
||||
desc->tfm = tfm;
|
||||
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = pefile_digest_pe_contents(pebuf, pelen, ctx, desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
digest = (void *)desc + desc_size;
|
||||
ret = crypto_shash_final(desc, digest);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
pr_debug("Digest calc = [%*ph]\n", ctx->digest_len, digest);
|
||||
|
||||
/* Check that the PE file digest matches that in the MSCODE part of the
|
||||
* PKCS#7 certificate.
|
||||
*/
|
||||
if (memcmp(digest, ctx->digest, ctx->digest_len) != 0) {
|
||||
pr_debug("Digest mismatch\n");
|
||||
ret = -EKEYREJECTED;
|
||||
} else {
|
||||
pr_debug("The digests match!\n");
|
||||
}
|
||||
|
||||
error:
|
||||
kfree(desc);
|
||||
error_no_desc:
|
||||
crypto_free_shash(tfm);
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* verify_pefile_signature - Verify the signature on a PE binary image
|
||||
* @pebuf: Buffer containing the PE binary image
|
||||
* @pelen: Length of the binary image
|
||||
* @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 inside the PE
|
||||
* binary image intersects keys we already know and trust.
|
||||
*
|
||||
* Returns, in order of descending priority:
|
||||
*
|
||||
* (*) -ELIBBAD if the image cannot be parsed, or:
|
||||
*
|
||||
* (*) -EKEYREJECTED if a signature failed to match for which we have a valid
|
||||
* key, or:
|
||||
*
|
||||
* (*) 0 if at least one signature chain intersects with the keys in the trust
|
||||
* keyring, or:
|
||||
*
|
||||
* (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
|
||||
* chain.
|
||||
*
|
||||
* (*) -ENOKEY if we couldn't find a match for any of the signature chains in
|
||||
* the message.
|
||||
*
|
||||
* May also return -ENOMEM.
|
||||
*/
|
||||
int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||
struct key *trusted_keyring, bool *_trusted)
|
||||
{
|
||||
struct pkcs7_message *pkcs7;
|
||||
struct pefile_context ctx;
|
||||
const void *data;
|
||||
size_t datalen;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ret = pefile_parse_binary(pebuf, pelen, &ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = pefile_strip_sig_wrapper(pebuf, &ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pkcs7 = pkcs7_parse_message(pebuf + ctx.sig_offset, ctx.sig_len);
|
||||
if (IS_ERR(pkcs7))
|
||||
return PTR_ERR(pkcs7);
|
||||
ctx.pkcs7 = pkcs7;
|
||||
|
||||
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)
|
||||
goto error;
|
||||
|
||||
pr_debug("Digest: %u [%*ph]\n",
|
||||
ctx.digest_len, ctx.digest_len, ctx.digest);
|
||||
|
||||
/* Generate the digest and check against the PKCS7 certificate
|
||||
* contents.
|
||||
*/
|
||||
ret = pefile_digest_pe(pebuf, pelen, &ctx);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = pkcs7_verify(pkcs7);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = pkcs7_validate_trust(pkcs7, trusted_keyring, _trusted);
|
||||
|
||||
error:
|
||||
pkcs7_free_message(ctx.pkcs7);
|
||||
return ret;
|
||||
}
|
42
crypto/asymmetric_keys/verify_pefile.h
Normal file
42
crypto/asymmetric_keys/verify_pefile.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* PE Binary parser bits
|
||||
*
|
||||
* Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/verify_pefile.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include <crypto/hash_info.h>
|
||||
|
||||
struct pefile_context {
|
||||
unsigned header_size;
|
||||
unsigned image_checksum_offset;
|
||||
unsigned cert_dirent_offset;
|
||||
unsigned n_data_dirents;
|
||||
unsigned n_sections;
|
||||
unsigned certs_size;
|
||||
unsigned sig_offset;
|
||||
unsigned sig_len;
|
||||
const struct section_header *secs;
|
||||
struct pkcs7_message *pkcs7;
|
||||
|
||||
/* PKCS#7 MS Individual Code Signing content */
|
||||
const void *digest; /* Digest */
|
||||
unsigned digest_len; /* Digest length */
|
||||
enum hash_algo digest_algo; /* Digest algorithm */
|
||||
};
|
||||
|
||||
#define kenter(FMT, ...) \
|
||||
pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
|
||||
#define kleave(FMT, ...) \
|
||||
pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* mscode_parser.c
|
||||
*/
|
||||
extern int mscode_parse(struct pefile_context *ctx);
|
@ -6,7 +6,7 @@ Certificate ::= SEQUENCE {
|
||||
|
||||
TBSCertificate ::= SEQUENCE {
|
||||
version [ 0 ] Version DEFAULT,
|
||||
serialNumber CertificateSerialNumber,
|
||||
serialNumber CertificateSerialNumber ({ x509_note_serial }),
|
||||
signature AlgorithmIdentifier ({ x509_note_pkey_algo }),
|
||||
issuer Name ({ x509_note_issuer }),
|
||||
validity Validity,
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#define pr_fmt(fmt) "X.509: "fmt
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/oid_registry.h>
|
||||
@ -52,6 +53,7 @@ void x509_free_certificate(struct x509_certificate *cert)
|
||||
kfree(cert);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_free_certificate);
|
||||
|
||||
/*
|
||||
* Parse an X.509 certificate
|
||||
@ -97,6 +99,7 @@ error_no_ctx:
|
||||
error_no_cert:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_cert_parse);
|
||||
|
||||
/*
|
||||
* Note an OID when we find one for later processing when we know how
|
||||
@ -210,6 +213,19 @@ int x509_note_signature(void *context, size_t hdrlen,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the certificate serial number
|
||||
*/
|
||||
int x509_note_serial(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
ctx->cert->raw_serial = value;
|
||||
ctx->cert->raw_serial_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note some of the name segments from which we'll fabricate a name.
|
||||
*/
|
||||
@ -322,6 +338,8 @@ int x509_note_issuer(void *context, size_t hdrlen,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
ctx->cert->raw_issuer = value;
|
||||
ctx->cert->raw_issuer_size = vlen;
|
||||
return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
|
||||
}
|
||||
|
||||
@ -330,6 +348,8 @@ int x509_note_subject(void *context, size_t hdrlen,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
ctx->cert->raw_subject = value;
|
||||
ctx->cert->raw_subject_size = vlen;
|
||||
return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,9 @@
|
||||
|
||||
struct x509_certificate {
|
||||
struct x509_certificate *next;
|
||||
struct x509_certificate *signer; /* Certificate that signed this one */
|
||||
struct public_key *pub; /* Public key details */
|
||||
struct public_key_signature sig; /* Signature parameters */
|
||||
char *issuer; /* Name of certificate issuer */
|
||||
char *subject; /* Name of certificate subject */
|
||||
char *fingerprint; /* Key fingerprint as hex */
|
||||
@ -25,7 +27,16 @@ struct x509_certificate {
|
||||
unsigned tbs_size; /* Size of signed data */
|
||||
unsigned raw_sig_size; /* Size of sigature */
|
||||
const void *raw_sig; /* Signature data */
|
||||
struct public_key_signature sig; /* Signature parameters */
|
||||
const void *raw_serial; /* Raw serial number in ASN.1 */
|
||||
unsigned raw_serial_size;
|
||||
unsigned raw_issuer_size;
|
||||
const void *raw_issuer; /* Raw issuer name in ASN.1 */
|
||||
const void *raw_subject; /* Raw subject name in ASN.1 */
|
||||
unsigned raw_subject_size;
|
||||
unsigned index;
|
||||
bool seen; /* Infinite recursion prevention */
|
||||
bool verified;
|
||||
bool trusted;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -18,11 +18,86 @@
|
||||
#include <linux/asn1_decoder.h>
|
||||
#include <keys/asymmetric-subtype.h>
|
||||
#include <keys/asymmetric-parser.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include <crypto/hash.h>
|
||||
#include "asymmetric_keys.h"
|
||||
#include "public_key.h"
|
||||
#include "x509_parser.h"
|
||||
|
||||
static bool use_builtin_keys;
|
||||
static char *ca_keyid;
|
||||
|
||||
#ifndef MODULE
|
||||
static int __init ca_keys_setup(char *str)
|
||||
{
|
||||
if (!str) /* default system keyring */
|
||||
return 1;
|
||||
|
||||
if (strncmp(str, "id:", 3) == 0)
|
||||
ca_keyid = str; /* owner key 'id:xxxxxx' */
|
||||
else if (strcmp(str, "builtin") == 0)
|
||||
use_builtin_keys = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
__setup("ca_keys=", ca_keys_setup);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* x509_request_asymmetric_key - Request a key by X.509 certificate params.
|
||||
* @keyring: The keys to search.
|
||||
* @subject: The name of the subject to whom the key belongs.
|
||||
* @key_id: The subject key ID as a hex string.
|
||||
*
|
||||
* Find a key in the given keyring by subject name and key ID. These might,
|
||||
* for instance, be the issuer name and the authority key ID of an X.509
|
||||
* certificate that needs to be verified.
|
||||
*/
|
||||
struct key *x509_request_asymmetric_key(struct key *keyring,
|
||||
const char *subject,
|
||||
const char *key_id)
|
||||
{
|
||||
key_ref_t key;
|
||||
size_t subject_len = strlen(subject), key_id_len = strlen(key_id);
|
||||
char *id;
|
||||
|
||||
/* Construct an identifier "<subjname>:<keyid>". */
|
||||
id = kmalloc(subject_len + 2 + key_id_len + 1, GFP_KERNEL);
|
||||
if (!id)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(id, subject, subject_len);
|
||||
id[subject_len + 0] = ':';
|
||||
id[subject_len + 1] = ' ';
|
||||
memcpy(id + subject_len + 2, key_id, key_id_len);
|
||||
id[subject_len + 2 + key_id_len] = 0;
|
||||
|
||||
pr_debug("Look up: \"%s\"\n", id);
|
||||
|
||||
key = keyring_search(make_key_ref(keyring, 1),
|
||||
&key_type_asymmetric, id);
|
||||
if (IS_ERR(key))
|
||||
pr_debug("Request for key '%s' err %ld\n", id, PTR_ERR(key));
|
||||
kfree(id);
|
||||
|
||||
if (IS_ERR(key)) {
|
||||
switch (PTR_ERR(key)) {
|
||||
/* Hide some search errors */
|
||||
case -EACCES:
|
||||
case -ENOTDIR:
|
||||
case -EAGAIN:
|
||||
return ERR_PTR(-ENOKEY);
|
||||
default:
|
||||
return ERR_CAST(key);
|
||||
}
|
||||
}
|
||||
|
||||
pr_devel("<==%s() = 0 [%x]\n", __func__,
|
||||
key_serial(key_ref_to_ptr(key)));
|
||||
return key_ref_to_ptr(key);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_request_asymmetric_key);
|
||||
|
||||
/*
|
||||
* Set up the signature parameters in an X.509 certificate. This involves
|
||||
* digesting the signed data and extracting the signature.
|
||||
@ -102,6 +177,38 @@ int x509_check_signature(const struct public_key *pub,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_check_signature);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Return 0 if the new certificate was successfully validated, 1 if we couldn't
|
||||
* find a matching parent certificate in the trusted list and an error if there
|
||||
* is a matching certificate but the signature check fails.
|
||||
*/
|
||||
static int x509_validate_trust(struct x509_certificate *cert,
|
||||
struct key *trust_keyring)
|
||||
{
|
||||
struct key *key;
|
||||
int ret = 1;
|
||||
|
||||
if (!trust_keyring)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (ca_keyid && !asymmetric_keyid_match(cert->authority, ca_keyid))
|
||||
return -EPERM;
|
||||
|
||||
key = x509_request_asymmetric_key(trust_keyring,
|
||||
cert->issuer, cert->authority);
|
||||
if (!IS_ERR(key)) {
|
||||
if (!use_builtin_keys
|
||||
|| test_bit(KEY_FLAG_BUILTIN, &key->flags))
|
||||
ret = x509_check_signature(key->payload.data, cert);
|
||||
key_put(key);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to parse a data blob for a key as an X509 certificate.
|
||||
*/
|
||||
@ -155,9 +262,13 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||
/* Check the signature on the key if it appears to be self-signed */
|
||||
if (!cert->authority ||
|
||||
strcmp(cert->fingerprint, cert->authority) == 0) {
|
||||
ret = x509_check_signature(cert->pub, cert);
|
||||
ret = x509_check_signature(cert->pub, cert); /* self-signed */
|
||||
if (ret < 0)
|
||||
goto error_free_cert;
|
||||
} else if (!prep->trusted) {
|
||||
ret = x509_validate_trust(cert, get_system_trusted_keyring());
|
||||
if (!ret)
|
||||
prep->trusted = 1;
|
||||
}
|
||||
|
||||
/* Propose a description */
|
||||
@ -177,7 +288,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||
__module_get(public_key_subtype.owner);
|
||||
prep->type_data[0] = &public_key_subtype;
|
||||
prep->type_data[1] = cert->fingerprint;
|
||||
prep->payload = cert->pub;
|
||||
prep->payload[0] = cert->pub;
|
||||
prep->description = desc;
|
||||
prep->quotalen = 100;
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
#include <generated/utsrelease.h>
|
||||
|
||||
@ -303,12 +304,17 @@ static int fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf)
|
||||
if (rc != size) {
|
||||
if (rc > 0)
|
||||
rc = -EIO;
|
||||
vfree(buf);
|
||||
return rc;
|
||||
goto fail;
|
||||
}
|
||||
rc = security_kernel_fw_from_file(file, buf, size);
|
||||
if (rc)
|
||||
goto fail;
|
||||
fw_buf->data = buf;
|
||||
fw_buf->size = size;
|
||||
return 0;
|
||||
fail:
|
||||
vfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fw_get_filesystem_firmware(struct device *device,
|
||||
@ -612,6 +618,7 @@ static ssize_t firmware_loading_store(struct device *dev,
|
||||
{
|
||||
struct firmware_priv *fw_priv = to_firmware_priv(dev);
|
||||
struct firmware_buf *fw_buf;
|
||||
ssize_t written = count;
|
||||
int loading = simple_strtol(buf, NULL, 10);
|
||||
int i;
|
||||
|
||||
@ -635,6 +642,8 @@ static ssize_t firmware_loading_store(struct device *dev,
|
||||
break;
|
||||
case 0:
|
||||
if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
|
||||
int rc;
|
||||
|
||||
set_bit(FW_STATUS_DONE, &fw_buf->status);
|
||||
clear_bit(FW_STATUS_LOADING, &fw_buf->status);
|
||||
|
||||
@ -644,10 +653,23 @@ static ssize_t firmware_loading_store(struct device *dev,
|
||||
* see the mapped 'buf->data' once the loading
|
||||
* is completed.
|
||||
* */
|
||||
if (fw_map_pages_buf(fw_buf))
|
||||
rc = fw_map_pages_buf(fw_buf);
|
||||
if (rc)
|
||||
dev_err(dev, "%s: map pages failed\n",
|
||||
__func__);
|
||||
else
|
||||
rc = security_kernel_fw_from_file(NULL,
|
||||
fw_buf->data, fw_buf->size);
|
||||
|
||||
/*
|
||||
* Same logic as fw_load_abort, only the DONE bit
|
||||
* is ignored and we set ABORT only on failure.
|
||||
*/
|
||||
list_del_init(&fw_buf->pending_list);
|
||||
if (rc) {
|
||||
set_bit(FW_STATUS_ABORT, &fw_buf->status);
|
||||
written = rc;
|
||||
}
|
||||
complete_all(&fw_buf->completion);
|
||||
break;
|
||||
}
|
||||
@ -661,7 +683,7 @@ static ssize_t firmware_loading_store(struct device *dev,
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&fw_lock);
|
||||
return count;
|
||||
return written;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
|
||||
|
@ -491,11 +491,10 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
|
||||
int tpm_get_timeouts(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_cmd_t tpm_cmd;
|
||||
struct timeout_t *timeout_cap;
|
||||
unsigned long new_timeout[4];
|
||||
unsigned long old_timeout[4];
|
||||
struct duration_t *duration_cap;
|
||||
ssize_t rc;
|
||||
u32 timeout;
|
||||
unsigned int scale = 1;
|
||||
|
||||
tpm_cmd.header.in = tpm_getcap_header;
|
||||
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
|
||||
@ -529,25 +528,46 @@ int tpm_get_timeouts(struct tpm_chip *chip)
|
||||
!= sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
|
||||
return -EINVAL;
|
||||
|
||||
timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
|
||||
/* Don't overwrite default if value is 0 */
|
||||
timeout = be32_to_cpu(timeout_cap->a);
|
||||
if (timeout && timeout < 1000) {
|
||||
/* timeouts in msec rather usec */
|
||||
scale = 1000;
|
||||
chip->vendor.timeout_adjusted = true;
|
||||
old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
|
||||
old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
|
||||
old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
|
||||
old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
|
||||
memcpy(new_timeout, old_timeout, sizeof(new_timeout));
|
||||
|
||||
/*
|
||||
* Provide ability for vendor overrides of timeout values in case
|
||||
* of misreporting.
|
||||
*/
|
||||
if (chip->ops->update_timeouts != NULL)
|
||||
chip->vendor.timeout_adjusted =
|
||||
chip->ops->update_timeouts(chip, new_timeout);
|
||||
|
||||
if (!chip->vendor.timeout_adjusted) {
|
||||
/* Don't overwrite default if value is 0 */
|
||||
if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
|
||||
int i;
|
||||
|
||||
/* timeouts in msec rather usec */
|
||||
for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
|
||||
new_timeout[i] *= 1000;
|
||||
chip->vendor.timeout_adjusted = true;
|
||||
}
|
||||
}
|
||||
if (timeout)
|
||||
chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale);
|
||||
timeout = be32_to_cpu(timeout_cap->b);
|
||||
if (timeout)
|
||||
chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale);
|
||||
timeout = be32_to_cpu(timeout_cap->c);
|
||||
if (timeout)
|
||||
chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale);
|
||||
timeout = be32_to_cpu(timeout_cap->d);
|
||||
if (timeout)
|
||||
chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
|
||||
|
||||
/* Report adjusted timeouts */
|
||||
if (chip->vendor.timeout_adjusted) {
|
||||
dev_info(chip->dev,
|
||||
HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
|
||||
old_timeout[0], new_timeout[0],
|
||||
old_timeout[1], new_timeout[1],
|
||||
old_timeout[2], new_timeout[2],
|
||||
old_timeout[3], new_timeout[3]);
|
||||
}
|
||||
|
||||
chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
|
||||
chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
|
||||
chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
|
||||
chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
|
||||
|
||||
duration:
|
||||
tpm_cmd.header.in = tpm_getcap_header;
|
||||
@ -991,13 +1011,13 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
|
||||
int err, total = 0, retries = 5;
|
||||
u8 *dest = out;
|
||||
|
||||
if (!out || !num_bytes || max > TPM_MAX_RNG_DATA)
|
||||
return -EINVAL;
|
||||
|
||||
chip = tpm_chip_find_get(chip_num);
|
||||
if (chip == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (!out || !num_bytes || max > TPM_MAX_RNG_DATA)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
tpm_cmd.header.in = tpm_getrandom_header;
|
||||
tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes);
|
||||
@ -1016,6 +1036,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
|
||||
num_bytes -= recd;
|
||||
} while (retries-- && total < max);
|
||||
|
||||
tpm_chip_put(chip);
|
||||
return total ? total : -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_get_random);
|
||||
@ -1095,7 +1116,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
|
||||
goto del_misc;
|
||||
|
||||
if (tpm_add_ppi(&dev->kobj))
|
||||
goto del_misc;
|
||||
goto del_sysfs;
|
||||
|
||||
chip->bios_dir = tpm_bios_log_setup(chip->devname);
|
||||
|
||||
@ -1106,6 +1127,8 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
|
||||
|
||||
return chip;
|
||||
|
||||
del_sysfs:
|
||||
tpm_sysfs_del_device(chip);
|
||||
del_misc:
|
||||
tpm_dev_del_device(chip);
|
||||
put_device:
|
||||
|
@ -235,7 +235,6 @@ static int tpm_bios_measurements_release(struct inode *inode,
|
||||
static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v)
|
||||
{
|
||||
int len = 0;
|
||||
int i;
|
||||
char *eventname;
|
||||
struct tcpa_event *event = v;
|
||||
unsigned char *event_entry =
|
||||
@ -251,8 +250,7 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v)
|
||||
seq_printf(m, "%2d ", event->pcr_index);
|
||||
|
||||
/* 2nd: SHA1 */
|
||||
for (i = 0; i < 20; i++)
|
||||
seq_printf(m, "%02x", event->pcr_value[i]);
|
||||
seq_printf(m, "%20phN", event->pcr_value);
|
||||
|
||||
/* 3rd: event type identifier */
|
||||
seq_printf(m, " %02x", event->event_type);
|
||||
|
@ -714,6 +714,7 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
}
|
||||
|
||||
tpm_get_timeouts(chip);
|
||||
tpm_do_selftest(chip);
|
||||
|
||||
dev_info(chip->dev, "TPM I2C Initialized\n");
|
||||
return 0;
|
||||
|
@ -373,6 +373,36 @@ out_err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct tis_vendor_timeout_override {
|
||||
u32 did_vid;
|
||||
unsigned long timeout_us[4];
|
||||
};
|
||||
|
||||
static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
|
||||
/* Atmel 3204 */
|
||||
{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
|
||||
(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
|
||||
};
|
||||
|
||||
static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
|
||||
unsigned long *timeout_cap)
|
||||
{
|
||||
int i;
|
||||
u32 did_vid;
|
||||
|
||||
did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
|
||||
|
||||
for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
|
||||
if (vendor_timeout_overrides[i].did_vid != did_vid)
|
||||
continue;
|
||||
memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
|
||||
sizeof(vendor_timeout_overrides[i].timeout_us));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Early probing for iTPM with STS_DATA_EXPECT flaw.
|
||||
* Try sending command without itpm flag set and if that
|
||||
@ -437,6 +467,7 @@ static const struct tpm_class_ops tpm_tis = {
|
||||
.recv = tpm_tis_recv,
|
||||
.send = tpm_tis_send,
|
||||
.cancel = tpm_tis_ready,
|
||||
.update_timeouts = tpm_tis_update_timeouts,
|
||||
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_canceled = tpm_tis_req_canceled,
|
||||
|
@ -1216,7 +1216,7 @@ EXPORT_SYMBOL(install_exec_creds);
|
||||
/*
|
||||
* determine how safe it is to execute the proposed program
|
||||
* - the caller must hold ->cred_guard_mutex to protect against
|
||||
* PTRACE_ATTACH
|
||||
* PTRACE_ATTACH or seccomp thread-sync
|
||||
*/
|
||||
static void check_unsafe_exec(struct linux_binprm *bprm)
|
||||
{
|
||||
@ -1234,7 +1234,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
|
||||
* This isn't strictly necessary, but it makes it harder for LSMs to
|
||||
* mess up.
|
||||
*/
|
||||
if (current->no_new_privs)
|
||||
if (task_no_new_privs(current))
|
||||
bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS;
|
||||
|
||||
t = p;
|
||||
@ -1272,7 +1272,7 @@ int prepare_binprm(struct linux_binprm *bprm)
|
||||
bprm->cred->egid = current_egid();
|
||||
|
||||
if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) &&
|
||||
!current->no_new_privs &&
|
||||
!task_no_new_privs(current) &&
|
||||
kuid_has_mapping(bprm->cred->user_ns, inode->i_uid) &&
|
||||
kgid_has_mapping(bprm->cred->user_ns, inode->i_gid)) {
|
||||
/* Set-uid? */
|
||||
|
@ -174,7 +174,9 @@ static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen)
|
||||
|
||||
static struct key_type key_type_id_resolver = {
|
||||
.name = "id_resolver",
|
||||
.instantiate = user_instantiate,
|
||||
.preparse = user_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.revoke = user_revoke,
|
||||
.destroy = user_destroy,
|
||||
@ -282,6 +284,8 @@ static struct key *nfs_idmap_request_key(const char *name, size_t namelen,
|
||||
desc, "", 0, idmap);
|
||||
mutex_unlock(&idmap->idmap_mutex);
|
||||
}
|
||||
if (!IS_ERR(rkey))
|
||||
set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags);
|
||||
|
||||
kfree(desc);
|
||||
return rkey;
|
||||
@ -394,7 +398,9 @@ static const struct rpc_pipe_ops idmap_upcall_ops = {
|
||||
|
||||
static struct key_type key_type_id_resolver_legacy = {
|
||||
.name = "id_legacy",
|
||||
.instantiate = user_instantiate,
|
||||
.preparse = user_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.revoke = user_revoke,
|
||||
.destroy = user_destroy,
|
||||
|
@ -297,15 +297,11 @@ static void render_cap_t(struct seq_file *m, const char *header,
|
||||
seq_puts(m, header);
|
||||
CAP_FOR_EACH_U32(__capi) {
|
||||
seq_printf(m, "%08x",
|
||||
a->cap[(_KERNEL_CAPABILITY_U32S-1) - __capi]);
|
||||
a->cap[CAP_LAST_U32 - __capi]);
|
||||
}
|
||||
seq_putc(m, '\n');
|
||||
}
|
||||
|
||||
/* Remove non-existent capabilities */
|
||||
#define NORM_CAPS(v) (v.cap[CAP_TO_INDEX(CAP_LAST_CAP)] &= \
|
||||
CAP_TO_MASK(CAP_LAST_CAP + 1) - 1)
|
||||
|
||||
static inline void task_cap(struct seq_file *m, struct task_struct *p)
|
||||
{
|
||||
const struct cred *cred;
|
||||
@ -319,11 +315,6 @@ static inline void task_cap(struct seq_file *m, struct task_struct *p)
|
||||
cap_bset = cred->cap_bset;
|
||||
rcu_read_unlock();
|
||||
|
||||
NORM_CAPS(cap_inheritable);
|
||||
NORM_CAPS(cap_permitted);
|
||||
NORM_CAPS(cap_effective);
|
||||
NORM_CAPS(cap_bset);
|
||||
|
||||
render_cap_t(m, "CapInh:\t", &cap_inheritable);
|
||||
render_cap_t(m, "CapPrm:\t", &cap_permitted);
|
||||
render_cap_t(m, "CapEff:\t", &cap_effective);
|
||||
|
36
include/crypto/pkcs7.h
Normal file
36
include/crypto/pkcs7.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* PKCS#7 crypto data parser
|
||||
*
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
struct key;
|
||||
struct pkcs7_message;
|
||||
|
||||
/*
|
||||
* pkcs7_parser.c
|
||||
*/
|
||||
extern struct pkcs7_message *pkcs7_parse_message(const void *data,
|
||||
size_t datalen);
|
||||
extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
|
||||
|
||||
extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
|
||||
const void **_data, size_t *_datalen,
|
||||
bool want_wrapper);
|
||||
|
||||
/*
|
||||
* pkcs7_trust.c
|
||||
*/
|
||||
extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
|
||||
struct key *trust_keyring,
|
||||
bool *_trusted);
|
||||
|
||||
/*
|
||||
* pkcs7_verify.c
|
||||
*/
|
||||
extern int pkcs7_verify(struct pkcs7_message *pkcs7);
|
@ -98,4 +98,8 @@ struct key;
|
||||
extern int verify_signature(const struct key *key,
|
||||
const struct public_key_signature *sig);
|
||||
|
||||
extern struct key *x509_request_asymmetric_key(struct key *keyring,
|
||||
const char *issuer,
|
||||
const char *key_id);
|
||||
|
||||
#endif /* _LINUX_PUBLIC_KEY_H */
|
||||
|
@ -16,7 +16,8 @@
|
||||
|
||||
extern struct key_type key_type_big_key;
|
||||
|
||||
extern int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep);
|
||||
extern int big_key_preparse(struct key_preparsed_payload *prep);
|
||||
extern void big_key_free_preparse(struct key_preparsed_payload *prep);
|
||||
extern void big_key_revoke(struct key *key);
|
||||
extern void big_key_destroy(struct key *key);
|
||||
extern void big_key_describe(const struct key *big_key, struct seq_file *m);
|
||||
|
@ -17,7 +17,15 @@
|
||||
#include <linux/key.h>
|
||||
|
||||
extern struct key *system_trusted_keyring;
|
||||
|
||||
static inline struct key *get_system_trusted_keyring(void)
|
||||
{
|
||||
return system_trusted_keyring;
|
||||
}
|
||||
#else
|
||||
static inline struct key *get_system_trusted_keyring(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _KEYS_SYSTEM_KEYRING_H */
|
||||
|
@ -37,7 +37,8 @@ extern struct key_type key_type_logon;
|
||||
|
||||
struct key_preparsed_payload;
|
||||
|
||||
extern int user_instantiate(struct key *key, struct key_preparsed_payload *prep);
|
||||
extern int user_preparse(struct key_preparsed_payload *prep);
|
||||
extern void user_free_preparse(struct key_preparsed_payload *prep);
|
||||
extern int user_update(struct key *key, struct key_preparsed_payload *prep);
|
||||
extern int user_match(const struct key *key, const void *criterion);
|
||||
extern void user_revoke(struct key *key);
|
||||
|
@ -78,8 +78,11 @@ extern const kernel_cap_t __cap_init_eff_set;
|
||||
# error Fix up hand-coded capability macro initializers
|
||||
#else /* HAND-CODED capability initializers */
|
||||
|
||||
#define CAP_LAST_U32 ((_KERNEL_CAPABILITY_U32S) - 1)
|
||||
#define CAP_LAST_U32_VALID_MASK (CAP_TO_MASK(CAP_LAST_CAP + 1) -1)
|
||||
|
||||
# define CAP_EMPTY_SET ((kernel_cap_t){{ 0, 0 }})
|
||||
# define CAP_FULL_SET ((kernel_cap_t){{ ~0, ~0 }})
|
||||
# define CAP_FULL_SET ((kernel_cap_t){{ ~0, CAP_LAST_U32_VALID_MASK }})
|
||||
# define CAP_FS_SET ((kernel_cap_t){{ CAP_FS_MASK_B0 \
|
||||
| CAP_TO_MASK(CAP_LINUX_IMMUTABLE), \
|
||||
CAP_FS_MASK_B1 } })
|
||||
|
@ -19,6 +19,7 @@ extern int ima_file_check(struct file *file, int mask);
|
||||
extern void ima_file_free(struct file *file);
|
||||
extern int ima_file_mmap(struct file *file, unsigned long prot);
|
||||
extern int ima_module_check(struct file *file);
|
||||
extern int ima_fw_from_file(struct file *file, char *buf, size_t size);
|
||||
|
||||
#else
|
||||
static inline int ima_bprm_check(struct linux_binprm *bprm)
|
||||
@ -46,6 +47,11 @@ static inline int ima_module_check(struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ima_fw_from_file(struct file *file, char *buf, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IMA */
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
|
@ -41,10 +41,11 @@ struct key_construction {
|
||||
struct key_preparsed_payload {
|
||||
char *description; /* Proposed key description (or NULL) */
|
||||
void *type_data[2]; /* Private key-type data */
|
||||
void *payload; /* Proposed payload */
|
||||
void *payload[2]; /* Proposed payload */
|
||||
const void *data; /* Raw data */
|
||||
size_t datalen; /* Raw datalen */
|
||||
size_t quotalen; /* Quota length for proposed payload */
|
||||
time_t expiry; /* Expiry time of key */
|
||||
bool trusted; /* True if key is trusted */
|
||||
};
|
||||
|
||||
@ -159,5 +160,7 @@ static inline int key_negate_and_link(struct key *key,
|
||||
return key_reject_and_link(key, timeout, ENOKEY, keyring, instkey);
|
||||
}
|
||||
|
||||
extern int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep);
|
||||
|
||||
#endif /* CONFIG_KEYS */
|
||||
#endif /* _LINUX_KEY_TYPE_H */
|
||||
|
@ -170,6 +170,8 @@ struct key {
|
||||
#define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */
|
||||
#define KEY_FLAG_TRUSTED 8 /* set if key is trusted */
|
||||
#define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */
|
||||
#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */
|
||||
#define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */
|
||||
|
||||
/* the key type and key description string
|
||||
* - the desc is used to match a key against search criteria
|
||||
|
@ -52,9 +52,15 @@ enum OID {
|
||||
OID_md4, /* 1.2.840.113549.2.4 */
|
||||
OID_md5, /* 1.2.840.113549.2.5 */
|
||||
|
||||
OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */
|
||||
/* Microsoft Authenticode & Software Publishing */
|
||||
OID_msIndirectData, /* 1.3.6.1.4.1.311.2.1.4 */
|
||||
OID_msPeImageDataObjId, /* 1.3.6.1.4.1.311.2.1.15 */
|
||||
OID_msIndividualSPKeyPurpose, /* 1.3.6.1.4.1.311.2.1.21 */
|
||||
OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */
|
||||
|
||||
OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */
|
||||
OID_sha1, /* 1.3.14.3.2.26 */
|
||||
OID_sha256, /* 2.16.840.1.101.3.4.2.1 */
|
||||
|
||||
/* Distinguished Name attribute IDs [RFC 2256] */
|
||||
OID_commonName, /* 2.5.4.3 */
|
||||
|
448
include/linux/pe.h
Normal file
448
include/linux/pe.h
Normal file
@ -0,0 +1,448 @@
|
||||
/*
|
||||
* Copyright 2011 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author(s): Peter Jones <pjones@redhat.com>
|
||||
*/
|
||||
#ifndef __LINUX_PE_H
|
||||
#define __LINUX_PE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define MZ_MAGIC 0x5a4d /* "MZ" */
|
||||
|
||||
struct mz_hdr {
|
||||
uint16_t magic; /* MZ_MAGIC */
|
||||
uint16_t lbsize; /* size of last used block */
|
||||
uint16_t blocks; /* pages in file, 0x3 */
|
||||
uint16_t relocs; /* relocations */
|
||||
uint16_t hdrsize; /* header size in "paragraphs" */
|
||||
uint16_t min_extra_pps; /* .bss */
|
||||
uint16_t max_extra_pps; /* runtime limit for the arena size */
|
||||
uint16_t ss; /* relative stack segment */
|
||||
uint16_t sp; /* initial %sp register */
|
||||
uint16_t checksum; /* word checksum */
|
||||
uint16_t ip; /* initial %ip register */
|
||||
uint16_t cs; /* initial %cs relative to load segment */
|
||||
uint16_t reloc_table_offset; /* offset of the first relocation */
|
||||
uint16_t overlay_num; /* overlay number. set to 0. */
|
||||
uint16_t reserved0[4]; /* reserved */
|
||||
uint16_t oem_id; /* oem identifier */
|
||||
uint16_t oem_info; /* oem specific */
|
||||
uint16_t reserved1[10]; /* reserved */
|
||||
uint32_t peaddr; /* address of pe header */
|
||||
char message[64]; /* message to print */
|
||||
};
|
||||
|
||||
struct mz_reloc {
|
||||
uint16_t offset;
|
||||
uint16_t segment;
|
||||
};
|
||||
|
||||
#define PE_MAGIC 0x00004550 /* "PE\0\0" */
|
||||
#define PE_OPT_MAGIC_PE32 0x010b
|
||||
#define PE_OPT_MAGIC_PE32_ROM 0x0107
|
||||
#define PE_OPT_MAGIC_PE32PLUS 0x020b
|
||||
|
||||
/* machine type */
|
||||
#define IMAGE_FILE_MACHINE_UNKNOWN 0x0000
|
||||
#define IMAGE_FILE_MACHINE_AM33 0x01d3
|
||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
||||
#define IMAGE_FILE_MACHINE_ARM 0x01c0
|
||||
#define IMAGE_FILE_MACHINE_ARMV7 0x01c4
|
||||
#define IMAGE_FILE_MACHINE_EBC 0x0ebc
|
||||
#define IMAGE_FILE_MACHINE_I386 0x014c
|
||||
#define IMAGE_FILE_MACHINE_IA64 0x0200
|
||||
#define IMAGE_FILE_MACHINE_M32R 0x9041
|
||||
#define IMAGE_FILE_MACHINE_MIPS16 0x0266
|
||||
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366
|
||||
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466
|
||||
#define IMAGE_FILE_MACHINE_POWERPC 0x01f0
|
||||
#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
|
||||
#define IMAGE_FILE_MACHINE_R4000 0x0166
|
||||
#define IMAGE_FILE_MACHINE_SH3 0x01a2
|
||||
#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
|
||||
#define IMAGE_FILE_MACHINE_SH3E 0x01a4
|
||||
#define IMAGE_FILE_MACHINE_SH4 0x01a6
|
||||
#define IMAGE_FILE_MACHINE_SH5 0x01a8
|
||||
#define IMAGE_FILE_MACHINE_THUMB 0x01c2
|
||||
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169
|
||||
|
||||
/* flags */
|
||||
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001
|
||||
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
|
||||
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
|
||||
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008
|
||||
#define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010
|
||||
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020
|
||||
#define IMAGE_FILE_16BIT_MACHINE 0x0040
|
||||
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080
|
||||
#define IMAGE_FILE_32BIT_MACHINE 0x0100
|
||||
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
|
||||
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400
|
||||
#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800
|
||||
#define IMAGE_FILE_SYSTEM 0x1000
|
||||
#define IMAGE_FILE_DLL 0x2000
|
||||
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000
|
||||
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
|
||||
|
||||
struct pe_hdr {
|
||||
uint32_t magic; /* PE magic */
|
||||
uint16_t machine; /* machine type */
|
||||
uint16_t sections; /* number of sections */
|
||||
uint32_t timestamp; /* time_t */
|
||||
uint32_t symbol_table; /* symbol table offset */
|
||||
uint32_t symbols; /* number of symbols */
|
||||
uint16_t opt_hdr_size; /* size of optional header */
|
||||
uint16_t flags; /* flags */
|
||||
};
|
||||
|
||||
#define IMAGE_FILE_OPT_ROM_MAGIC 0x107
|
||||
#define IMAGE_FILE_OPT_PE32_MAGIC 0x10b
|
||||
#define IMAGE_FILE_OPT_PE32_PLUS_MAGIC 0x20b
|
||||
|
||||
#define IMAGE_SUBSYSTEM_UNKNOWN 0
|
||||
#define IMAGE_SUBSYSTEM_NATIVE 1
|
||||
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2
|
||||
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3
|
||||
#define IMAGE_SUBSYSTEM_POSIX_CUI 7
|
||||
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9
|
||||
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
|
||||
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
|
||||
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12
|
||||
#define IMAGE_SUBSYSTEM_EFI_ROM_IMAGE 13
|
||||
#define IMAGE_SUBSYSTEM_XBOX 14
|
||||
|
||||
#define IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 0x0040
|
||||
#define IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY 0x0080
|
||||
#define IMAGE_DLL_CHARACTERISTICS_NX_COMPAT 0x0100
|
||||
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200
|
||||
#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400
|
||||
#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800
|
||||
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000
|
||||
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000
|
||||
|
||||
/* the fact that pe32 isn't padded where pe32+ is 64-bit means union won't
|
||||
* work right. vomit. */
|
||||
struct pe32_opt_hdr {
|
||||
/* "standard" header */
|
||||
uint16_t magic; /* file type */
|
||||
uint8_t ld_major; /* linker major version */
|
||||
uint8_t ld_minor; /* linker minor version */
|
||||
uint32_t text_size; /* size of text section(s) */
|
||||
uint32_t data_size; /* size of data section(s) */
|
||||
uint32_t bss_size; /* size of bss section(s) */
|
||||
uint32_t entry_point; /* file offset of entry point */
|
||||
uint32_t code_base; /* relative code addr in ram */
|
||||
uint32_t data_base; /* relative data addr in ram */
|
||||
/* "windows" header */
|
||||
uint32_t image_base; /* preferred load address */
|
||||
uint32_t section_align; /* alignment in bytes */
|
||||
uint32_t file_align; /* file alignment in bytes */
|
||||
uint16_t os_major; /* major OS version */
|
||||
uint16_t os_minor; /* minor OS version */
|
||||
uint16_t image_major; /* major image version */
|
||||
uint16_t image_minor; /* minor image version */
|
||||
uint16_t subsys_major; /* major subsystem version */
|
||||
uint16_t subsys_minor; /* minor subsystem version */
|
||||
uint32_t win32_version; /* reserved, must be 0 */
|
||||
uint32_t image_size; /* image size */
|
||||
uint32_t header_size; /* header size rounded up to
|
||||
file_align */
|
||||
uint32_t csum; /* checksum */
|
||||
uint16_t subsys; /* subsystem */
|
||||
uint16_t dll_flags; /* more flags! */
|
||||
uint32_t stack_size_req;/* amt of stack requested */
|
||||
uint32_t stack_size; /* amt of stack required */
|
||||
uint32_t heap_size_req; /* amt of heap requested */
|
||||
uint32_t heap_size; /* amt of heap required */
|
||||
uint32_t loader_flags; /* reserved, must be 0 */
|
||||
uint32_t data_dirs; /* number of data dir entries */
|
||||
};
|
||||
|
||||
struct pe32plus_opt_hdr {
|
||||
uint16_t magic; /* file type */
|
||||
uint8_t ld_major; /* linker major version */
|
||||
uint8_t ld_minor; /* linker minor version */
|
||||
uint32_t text_size; /* size of text section(s) */
|
||||
uint32_t data_size; /* size of data section(s) */
|
||||
uint32_t bss_size; /* size of bss section(s) */
|
||||
uint32_t entry_point; /* file offset of entry point */
|
||||
uint32_t code_base; /* relative code addr in ram */
|
||||
/* "windows" header */
|
||||
uint64_t image_base; /* preferred load address */
|
||||
uint32_t section_align; /* alignment in bytes */
|
||||
uint32_t file_align; /* file alignment in bytes */
|
||||
uint16_t os_major; /* major OS version */
|
||||
uint16_t os_minor; /* minor OS version */
|
||||
uint16_t image_major; /* major image version */
|
||||
uint16_t image_minor; /* minor image version */
|
||||
uint16_t subsys_major; /* major subsystem version */
|
||||
uint16_t subsys_minor; /* minor subsystem version */
|
||||
uint32_t win32_version; /* reserved, must be 0 */
|
||||
uint32_t image_size; /* image size */
|
||||
uint32_t header_size; /* header size rounded up to
|
||||
file_align */
|
||||
uint32_t csum; /* checksum */
|
||||
uint16_t subsys; /* subsystem */
|
||||
uint16_t dll_flags; /* more flags! */
|
||||
uint64_t stack_size_req;/* amt of stack requested */
|
||||
uint64_t stack_size; /* amt of stack required */
|
||||
uint64_t heap_size_req; /* amt of heap requested */
|
||||
uint64_t heap_size; /* amt of heap required */
|
||||
uint32_t loader_flags; /* reserved, must be 0 */
|
||||
uint32_t data_dirs; /* number of data dir entries */
|
||||
};
|
||||
|
||||
struct data_dirent {
|
||||
uint32_t virtual_address; /* relative to load address */
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct data_directory {
|
||||
struct data_dirent exports; /* .edata */
|
||||
struct data_dirent imports; /* .idata */
|
||||
struct data_dirent resources; /* .rsrc */
|
||||
struct data_dirent exceptions; /* .pdata */
|
||||
struct data_dirent certs; /* certs */
|
||||
struct data_dirent base_relocations; /* .reloc */
|
||||
struct data_dirent debug; /* .debug */
|
||||
struct data_dirent arch; /* reservered */
|
||||
struct data_dirent global_ptr; /* global pointer reg. Size=0 */
|
||||
struct data_dirent tls; /* .tls */
|
||||
struct data_dirent load_config; /* load configuration structure */
|
||||
struct data_dirent bound_imports; /* no idea */
|
||||
struct data_dirent import_addrs; /* import address table */
|
||||
struct data_dirent delay_imports; /* delay-load import table */
|
||||
struct data_dirent clr_runtime_hdr; /* .cor (object only) */
|
||||
struct data_dirent reserved;
|
||||
};
|
||||
|
||||
struct section_header {
|
||||
char name[8]; /* name or "/12\0" string tbl offset */
|
||||
uint32_t virtual_size; /* size of loaded section in ram */
|
||||
uint32_t virtual_address; /* relative virtual address */
|
||||
uint32_t raw_data_size; /* size of the section */
|
||||
uint32_t data_addr; /* file pointer to first page of sec */
|
||||
uint32_t relocs; /* file pointer to relocation entries */
|
||||
uint32_t line_numbers; /* line numbers! */
|
||||
uint16_t num_relocs; /* number of relocations */
|
||||
uint16_t num_lin_numbers; /* srsly. */
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* they actually defined 0x00000000 as well, but I think we'll skip that one. */
|
||||
#define IMAGE_SCN_RESERVED_0 0x00000001
|
||||
#define IMAGE_SCN_RESERVED_1 0x00000002
|
||||
#define IMAGE_SCN_RESERVED_2 0x00000004
|
||||
#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* don't pad - obsolete */
|
||||
#define IMAGE_SCN_RESERVED_3 0x00000010
|
||||
#define IMAGE_SCN_CNT_CODE 0x00000020 /* .text */
|
||||
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* .data */
|
||||
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* .bss */
|
||||
#define IMAGE_SCN_LNK_OTHER 0x00000100 /* reserved */
|
||||
#define IMAGE_SCN_LNK_INFO 0x00000200 /* .drectve comments */
|
||||
#define IMAGE_SCN_RESERVED_4 0x00000400
|
||||
#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* .o only - scn to be rm'd*/
|
||||
#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* .o only - COMDAT data */
|
||||
#define IMAGE_SCN_RESERVED_5 0x00002000 /* spec omits this */
|
||||
#define IMAGE_SCN_RESERVED_6 0x00004000 /* spec omits this */
|
||||
#define IMAGE_SCN_GPREL 0x00008000 /* global pointer referenced data */
|
||||
/* spec lists 0x20000 twice, I suspect they meant 0x10000 for one of them */
|
||||
#define IMAGE_SCN_MEM_PURGEABLE 0x00010000 /* reserved for "future" use */
|
||||
#define IMAGE_SCN_16BIT 0x00020000 /* reserved for "future" use */
|
||||
#define IMAGE_SCN_LOCKED 0x00040000 /* reserved for "future" use */
|
||||
#define IMAGE_SCN_PRELOAD 0x00080000 /* reserved for "future" use */
|
||||
/* and here they just stuck a 1-byte integer in the middle of a bitfield */
|
||||
#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 /* it does what it says on the box */
|
||||
#define IMAGE_SCN_ALIGN_2BYTES 0x00200000
|
||||
#define IMAGE_SCN_ALIGN_4BYTES 0x00300000
|
||||
#define IMAGE_SCN_ALIGN_8BYTES 0x00400000
|
||||
#define IMAGE_SCN_ALIGN_16BYTES 0x00500000
|
||||
#define IMAGE_SCN_ALIGN_32BYTES 0x00600000
|
||||
#define IMAGE_SCN_ALIGN_64BYTES 0x00700000
|
||||
#define IMAGE_SCN_ALIGN_128BYTES 0x00800000
|
||||
#define IMAGE_SCN_ALIGN_256BYTES 0x00900000
|
||||
#define IMAGE_SCN_ALIGN_512BYTES 0x00a00000
|
||||
#define IMAGE_SCN_ALIGN_1024BYTES 0x00b00000
|
||||
#define IMAGE_SCN_ALIGN_2048BYTES 0x00c00000
|
||||
#define IMAGE_SCN_ALIGN_4096BYTES 0x00d00000
|
||||
#define IMAGE_SCN_ALIGN_8192BYTES 0x00e00000
|
||||
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* extended relocations */
|
||||
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 /* scn can be discarded */
|
||||
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* cannot be cached */
|
||||
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* not pageable */
|
||||
#define IMAGE_SCN_MEM_SHARED 0x10000000 /* can be shared */
|
||||
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 /* can be executed as code */
|
||||
#define IMAGE_SCN_MEM_READ 0x40000000 /* readable */
|
||||
#define IMAGE_SCN_MEM_WRITE 0x80000000 /* writeable */
|
||||
|
||||
enum x64_coff_reloc_type {
|
||||
IMAGE_REL_AMD64_ABSOLUTE = 0,
|
||||
IMAGE_REL_AMD64_ADDR64,
|
||||
IMAGE_REL_AMD64_ADDR32,
|
||||
IMAGE_REL_AMD64_ADDR32N,
|
||||
IMAGE_REL_AMD64_REL32,
|
||||
IMAGE_REL_AMD64_REL32_1,
|
||||
IMAGE_REL_AMD64_REL32_2,
|
||||
IMAGE_REL_AMD64_REL32_3,
|
||||
IMAGE_REL_AMD64_REL32_4,
|
||||
IMAGE_REL_AMD64_REL32_5,
|
||||
IMAGE_REL_AMD64_SECTION,
|
||||
IMAGE_REL_AMD64_SECREL,
|
||||
IMAGE_REL_AMD64_SECREL7,
|
||||
IMAGE_REL_AMD64_TOKEN,
|
||||
IMAGE_REL_AMD64_SREL32,
|
||||
IMAGE_REL_AMD64_PAIR,
|
||||
IMAGE_REL_AMD64_SSPAN32,
|
||||
};
|
||||
|
||||
enum arm_coff_reloc_type {
|
||||
IMAGE_REL_ARM_ABSOLUTE,
|
||||
IMAGE_REL_ARM_ADDR32,
|
||||
IMAGE_REL_ARM_ADDR32N,
|
||||
IMAGE_REL_ARM_BRANCH2,
|
||||
IMAGE_REL_ARM_BRANCH1,
|
||||
IMAGE_REL_ARM_SECTION,
|
||||
IMAGE_REL_ARM_SECREL,
|
||||
};
|
||||
|
||||
enum sh_coff_reloc_type {
|
||||
IMAGE_REL_SH3_ABSOLUTE,
|
||||
IMAGE_REL_SH3_DIRECT16,
|
||||
IMAGE_REL_SH3_DIRECT32,
|
||||
IMAGE_REL_SH3_DIRECT8,
|
||||
IMAGE_REL_SH3_DIRECT8_WORD,
|
||||
IMAGE_REL_SH3_DIRECT8_LONG,
|
||||
IMAGE_REL_SH3_DIRECT4,
|
||||
IMAGE_REL_SH3_DIRECT4_WORD,
|
||||
IMAGE_REL_SH3_DIRECT4_LONG,
|
||||
IMAGE_REL_SH3_PCREL8_WORD,
|
||||
IMAGE_REL_SH3_PCREL8_LONG,
|
||||
IMAGE_REL_SH3_PCREL12_WORD,
|
||||
IMAGE_REL_SH3_STARTOF_SECTION,
|
||||
IMAGE_REL_SH3_SIZEOF_SECTION,
|
||||
IMAGE_REL_SH3_SECTION,
|
||||
IMAGE_REL_SH3_SECREL,
|
||||
IMAGE_REL_SH3_DIRECT32_NB,
|
||||
IMAGE_REL_SH3_GPREL4_LONG,
|
||||
IMAGE_REL_SH3_TOKEN,
|
||||
IMAGE_REL_SHM_PCRELPT,
|
||||
IMAGE_REL_SHM_REFLO,
|
||||
IMAGE_REL_SHM_REFHALF,
|
||||
IMAGE_REL_SHM_RELLO,
|
||||
IMAGE_REL_SHM_RELHALF,
|
||||
IMAGE_REL_SHM_PAIR,
|
||||
IMAGE_REL_SHM_NOMODE,
|
||||
};
|
||||
|
||||
enum ppc_coff_reloc_type {
|
||||
IMAGE_REL_PPC_ABSOLUTE,
|
||||
IMAGE_REL_PPC_ADDR64,
|
||||
IMAGE_REL_PPC_ADDR32,
|
||||
IMAGE_REL_PPC_ADDR24,
|
||||
IMAGE_REL_PPC_ADDR16,
|
||||
IMAGE_REL_PPC_ADDR14,
|
||||
IMAGE_REL_PPC_REL24,
|
||||
IMAGE_REL_PPC_REL14,
|
||||
IMAGE_REL_PPC_ADDR32N,
|
||||
IMAGE_REL_PPC_SECREL,
|
||||
IMAGE_REL_PPC_SECTION,
|
||||
IMAGE_REL_PPC_SECREL16,
|
||||
IMAGE_REL_PPC_REFHI,
|
||||
IMAGE_REL_PPC_REFLO,
|
||||
IMAGE_REL_PPC_PAIR,
|
||||
IMAGE_REL_PPC_SECRELLO,
|
||||
IMAGE_REL_PPC_GPREL,
|
||||
IMAGE_REL_PPC_TOKEN,
|
||||
};
|
||||
|
||||
enum x86_coff_reloc_type {
|
||||
IMAGE_REL_I386_ABSOLUTE,
|
||||
IMAGE_REL_I386_DIR16,
|
||||
IMAGE_REL_I386_REL16,
|
||||
IMAGE_REL_I386_DIR32,
|
||||
IMAGE_REL_I386_DIR32NB,
|
||||
IMAGE_REL_I386_SEG12,
|
||||
IMAGE_REL_I386_SECTION,
|
||||
IMAGE_REL_I386_SECREL,
|
||||
IMAGE_REL_I386_TOKEN,
|
||||
IMAGE_REL_I386_SECREL7,
|
||||
IMAGE_REL_I386_REL32,
|
||||
};
|
||||
|
||||
enum ia64_coff_reloc_type {
|
||||
IMAGE_REL_IA64_ABSOLUTE,
|
||||
IMAGE_REL_IA64_IMM14,
|
||||
IMAGE_REL_IA64_IMM22,
|
||||
IMAGE_REL_IA64_IMM64,
|
||||
IMAGE_REL_IA64_DIR32,
|
||||
IMAGE_REL_IA64_DIR64,
|
||||
IMAGE_REL_IA64_PCREL21B,
|
||||
IMAGE_REL_IA64_PCREL21M,
|
||||
IMAGE_REL_IA64_PCREL21F,
|
||||
IMAGE_REL_IA64_GPREL22,
|
||||
IMAGE_REL_IA64_LTOFF22,
|
||||
IMAGE_REL_IA64_SECTION,
|
||||
IMAGE_REL_IA64_SECREL22,
|
||||
IMAGE_REL_IA64_SECREL64I,
|
||||
IMAGE_REL_IA64_SECREL32,
|
||||
IMAGE_REL_IA64_DIR32NB,
|
||||
IMAGE_REL_IA64_SREL14,
|
||||
IMAGE_REL_IA64_SREL22,
|
||||
IMAGE_REL_IA64_SREL32,
|
||||
IMAGE_REL_IA64_UREL32,
|
||||
IMAGE_REL_IA64_PCREL60X,
|
||||
IMAGE_REL_IA64_PCREL60B,
|
||||
IMAGE_REL_IA64_PCREL60F,
|
||||
IMAGE_REL_IA64_PCREL60I,
|
||||
IMAGE_REL_IA64_PCREL60M,
|
||||
IMAGE_REL_IA64_IMMGPREL6,
|
||||
IMAGE_REL_IA64_TOKEN,
|
||||
IMAGE_REL_IA64_GPREL32,
|
||||
IMAGE_REL_IA64_ADDEND,
|
||||
};
|
||||
|
||||
struct coff_reloc {
|
||||
uint32_t virtual_address;
|
||||
uint32_t symbol_table_index;
|
||||
union {
|
||||
enum x64_coff_reloc_type x64_type;
|
||||
enum arm_coff_reloc_type arm_type;
|
||||
enum sh_coff_reloc_type sh_type;
|
||||
enum ppc_coff_reloc_type ppc_type;
|
||||
enum x86_coff_reloc_type x86_type;
|
||||
enum ia64_coff_reloc_type ia64_type;
|
||||
uint16_t data;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Definitions for the contents of the certs data block
|
||||
*/
|
||||
#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002
|
||||
#define WIN_CERT_TYPE_EFI_OKCS115 0x0EF0
|
||||
#define WIN_CERT_TYPE_EFI_GUID 0x0EF1
|
||||
|
||||
#define WIN_CERT_REVISION_1_0 0x0100
|
||||
#define WIN_CERT_REVISION_2_0 0x0200
|
||||
|
||||
struct win_certificate {
|
||||
uint32_t length;
|
||||
uint16_t revision;
|
||||
uint16_t cert_type;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_PE_H */
|
@ -1304,13 +1304,12 @@ struct task_struct {
|
||||
* execve */
|
||||
unsigned in_iowait:1;
|
||||
|
||||
/* task may not gain privileges */
|
||||
unsigned no_new_privs:1;
|
||||
|
||||
/* Revert to default priority/policy when forking */
|
||||
unsigned sched_reset_on_fork:1;
|
||||
unsigned sched_contributes_to_load:1;
|
||||
|
||||
unsigned long atomic_flags; /* Flags needing atomic access. */
|
||||
|
||||
pid_t pid;
|
||||
pid_t tgid;
|
||||
|
||||
@ -1962,6 +1961,19 @@ static inline void memalloc_noio_restore(unsigned int flags)
|
||||
current->flags = (current->flags & ~PF_MEMALLOC_NOIO) | flags;
|
||||
}
|
||||
|
||||
/* Per-process atomic flags. */
|
||||
#define PFA_NO_NEW_PRIVS 0x00000001 /* May not gain new privileges. */
|
||||
|
||||
static inline bool task_no_new_privs(struct task_struct *p)
|
||||
{
|
||||
return test_bit(PFA_NO_NEW_PRIVS, &p->atomic_flags);
|
||||
}
|
||||
|
||||
static inline void task_set_no_new_privs(struct task_struct *p)
|
||||
{
|
||||
set_bit(PFA_NO_NEW_PRIVS, &p->atomic_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* task->jobctl flags
|
||||
*/
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include <uapi/linux/seccomp.h>
|
||||
|
||||
#define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC)
|
||||
|
||||
#ifdef CONFIG_SECCOMP
|
||||
|
||||
#include <linux/thread_info.h>
|
||||
@ -14,11 +16,11 @@ struct seccomp_filter;
|
||||
*
|
||||
* @mode: indicates one of the valid values above for controlled
|
||||
* system calls available to a process.
|
||||
* @filter: The metadata and ruleset for determining what system calls
|
||||
* are allowed for a task.
|
||||
* @filter: must always point to a valid seccomp-filter or NULL as it is
|
||||
* accessed without locking during system call entry.
|
||||
*
|
||||
* @filter must only be accessed from the context of current as there
|
||||
* is no locking.
|
||||
* is no read locking.
|
||||
*/
|
||||
struct seccomp {
|
||||
int mode;
|
||||
|
@ -702,6 +702,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
* @inode points to the inode to use as a reference.
|
||||
* The current task must be the one that nominated @inode.
|
||||
* Return 0 if successful.
|
||||
* @kernel_fw_from_file:
|
||||
* Load firmware from userspace (not called for built-in firmware).
|
||||
* @file contains the file structure pointing to the file containing
|
||||
* the firmware to load. This argument will be NULL if the firmware
|
||||
* was loaded via the uevent-triggered blob-based interface exposed
|
||||
* by CONFIG_FW_LOADER_USER_HELPER.
|
||||
* @buf pointer to buffer containing firmware contents.
|
||||
* @size length of the firmware contents.
|
||||
* Return 0 if permission is granted.
|
||||
* @kernel_module_request:
|
||||
* Ability to trigger the kernel to automatically upcall to userspace for
|
||||
* userspace to load a kernel module with the given name.
|
||||
@ -1565,6 +1574,7 @@ struct security_operations {
|
||||
void (*cred_transfer)(struct cred *new, const struct cred *old);
|
||||
int (*kernel_act_as)(struct cred *new, u32 secid);
|
||||
int (*kernel_create_files_as)(struct cred *new, struct inode *inode);
|
||||
int (*kernel_fw_from_file)(struct file *file, char *buf, size_t size);
|
||||
int (*kernel_module_request)(char *kmod_name);
|
||||
int (*kernel_module_from_file)(struct file *file);
|
||||
int (*task_fix_setuid) (struct cred *new, const struct cred *old,
|
||||
@ -1837,6 +1847,7 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
|
||||
void security_transfer_creds(struct cred *new, const struct cred *old);
|
||||
int security_kernel_act_as(struct cred *new, u32 secid);
|
||||
int security_kernel_create_files_as(struct cred *new, struct inode *inode);
|
||||
int security_kernel_fw_from_file(struct file *file, char *buf, size_t size);
|
||||
int security_kernel_module_request(char *kmod_name);
|
||||
int security_kernel_module_from_file(struct file *file);
|
||||
int security_task_fix_setuid(struct cred *new, const struct cred *old,
|
||||
@ -2363,6 +2374,12 @@ static inline int security_kernel_create_files_as(struct cred *cred,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_kernel_fw_from_file(struct file *file,
|
||||
char *buf, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_kernel_module_request(char *kmod_name)
|
||||
{
|
||||
return 0;
|
||||
|
@ -866,4 +866,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid,
|
||||
asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
|
||||
unsigned long idx1, unsigned long idx2);
|
||||
asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
|
||||
asmlinkage long sys_seccomp(unsigned int op, unsigned int flags,
|
||||
const char __user *uargs);
|
||||
#endif
|
||||
|
@ -39,6 +39,9 @@ struct tpm_class_ops {
|
||||
int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
|
||||
void (*cancel) (struct tpm_chip *chip);
|
||||
u8 (*status) (struct tpm_chip *chip);
|
||||
bool (*update_timeouts)(struct tpm_chip *chip,
|
||||
unsigned long *timeout_cap);
|
||||
|
||||
};
|
||||
|
||||
#if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)
|
||||
|
18
include/linux/verify_pefile.h
Normal file
18
include/linux/verify_pefile.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* Signed PE file verification
|
||||
*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_VERIFY_PEFILE_H
|
||||
#define _LINUX_VERIFY_PEFILE_H
|
||||
|
||||
extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||
struct key *trusted_keyring, bool *_trusted);
|
||||
|
||||
#endif /* _LINUX_VERIFY_PEFILE_H */
|
@ -139,7 +139,7 @@ struct netlbl_lsm_cache {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct netlbl_lsm_secattr_catmap - NetLabel LSM secattr category bitmap
|
||||
* struct netlbl_lsm_catmap - NetLabel LSM secattr category bitmap
|
||||
* @startbit: the value of the lowest order bit in the bitmap
|
||||
* @bitmap: the category bitmap
|
||||
* @next: pointer to the next bitmap "node" or NULL
|
||||
@ -162,10 +162,10 @@ struct netlbl_lsm_cache {
|
||||
#define NETLBL_CATMAP_SIZE (NETLBL_CATMAP_MAPSIZE * \
|
||||
NETLBL_CATMAP_MAPCNT)
|
||||
#define NETLBL_CATMAP_BIT (NETLBL_CATMAP_MAPTYPE)0x01
|
||||
struct netlbl_lsm_secattr_catmap {
|
||||
struct netlbl_lsm_catmap {
|
||||
u32 startbit;
|
||||
NETLBL_CATMAP_MAPTYPE bitmap[NETLBL_CATMAP_MAPCNT];
|
||||
struct netlbl_lsm_secattr_catmap *next;
|
||||
struct netlbl_lsm_catmap *next;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -209,7 +209,7 @@ struct netlbl_lsm_secattr {
|
||||
struct netlbl_lsm_cache *cache;
|
||||
struct {
|
||||
struct {
|
||||
struct netlbl_lsm_secattr_catmap *cat;
|
||||
struct netlbl_lsm_catmap *cat;
|
||||
u32 lvl;
|
||||
} mls;
|
||||
u32 secid;
|
||||
@ -258,7 +258,7 @@ static inline void netlbl_secattr_cache_free(struct netlbl_lsm_cache *cache)
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_secattr_catmap_alloc - Allocate a LSM secattr catmap
|
||||
* netlbl_catmap_alloc - Allocate a LSM secattr catmap
|
||||
* @flags: memory allocation flags
|
||||
*
|
||||
* Description:
|
||||
@ -266,30 +266,28 @@ static inline void netlbl_secattr_cache_free(struct netlbl_lsm_cache *cache)
|
||||
* on failure.
|
||||
*
|
||||
*/
|
||||
static inline struct netlbl_lsm_secattr_catmap *netlbl_secattr_catmap_alloc(
|
||||
gfp_t flags)
|
||||
static inline struct netlbl_lsm_catmap *netlbl_catmap_alloc(gfp_t flags)
|
||||
{
|
||||
return kzalloc(sizeof(struct netlbl_lsm_secattr_catmap), flags);
|
||||
return kzalloc(sizeof(struct netlbl_lsm_catmap), flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_secattr_catmap_free - Free a LSM secattr catmap
|
||||
* netlbl_catmap_free - Free a LSM secattr catmap
|
||||
* @catmap: the category bitmap
|
||||
*
|
||||
* Description:
|
||||
* Free a LSM secattr catmap.
|
||||
*
|
||||
*/
|
||||
static inline void netlbl_secattr_catmap_free(
|
||||
struct netlbl_lsm_secattr_catmap *catmap)
|
||||
static inline void netlbl_catmap_free(struct netlbl_lsm_catmap *catmap)
|
||||
{
|
||||
struct netlbl_lsm_secattr_catmap *iter;
|
||||
struct netlbl_lsm_catmap *iter;
|
||||
|
||||
do {
|
||||
while (catmap) {
|
||||
iter = catmap;
|
||||
catmap = catmap->next;
|
||||
kfree(iter);
|
||||
} while (catmap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -321,7 +319,7 @@ static inline void netlbl_secattr_destroy(struct netlbl_lsm_secattr *secattr)
|
||||
if (secattr->flags & NETLBL_SECATTR_CACHE)
|
||||
netlbl_secattr_cache_free(secattr->cache);
|
||||
if (secattr->flags & NETLBL_SECATTR_MLS_CAT)
|
||||
netlbl_secattr_catmap_free(secattr->attr.mls.cat);
|
||||
netlbl_catmap_free(secattr->attr.mls.cat);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -390,17 +388,22 @@ int netlbl_cfg_cipsov4_map_add(u32 doi,
|
||||
/*
|
||||
* LSM security attribute operations
|
||||
*/
|
||||
int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 offset);
|
||||
int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 offset);
|
||||
int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 bit,
|
||||
gfp_t flags);
|
||||
int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 start,
|
||||
u32 end,
|
||||
gfp_t flags);
|
||||
int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset);
|
||||
int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap, u32 offset);
|
||||
int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap,
|
||||
u32 *offset,
|
||||
unsigned long *bitmap);
|
||||
int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap,
|
||||
u32 bit,
|
||||
gfp_t flags);
|
||||
int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap,
|
||||
u32 start,
|
||||
u32 end,
|
||||
gfp_t flags);
|
||||
int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap,
|
||||
u32 offset,
|
||||
unsigned long bitmap,
|
||||
gfp_t flags);
|
||||
|
||||
/*
|
||||
* LSM protocol operations (NetLabel LSM/kernel API)
|
||||
@ -492,30 +495,39 @@ static inline int netlbl_cfg_cipsov4_map_add(u32 doi,
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
static inline int netlbl_secattr_catmap_walk(
|
||||
struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 offset)
|
||||
static inline int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap,
|
||||
u32 offset)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
static inline int netlbl_secattr_catmap_walk_rng(
|
||||
struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 offset)
|
||||
static inline int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap,
|
||||
u32 offset)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
static inline int netlbl_secattr_catmap_setbit(
|
||||
struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 bit,
|
||||
gfp_t flags)
|
||||
static inline int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap,
|
||||
u32 *offset,
|
||||
unsigned long *bitmap)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int netlbl_secattr_catmap_setrng(
|
||||
struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 start,
|
||||
u32 end,
|
||||
gfp_t flags)
|
||||
static inline int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap,
|
||||
u32 bit,
|
||||
gfp_t flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap,
|
||||
u32 start,
|
||||
u32 end,
|
||||
gfp_t flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap,
|
||||
u32 offset,
|
||||
unsigned long bitmap,
|
||||
gfp_t flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -699,9 +699,11 @@ __SYSCALL(__NR_sched_setattr, sys_sched_setattr)
|
||||
__SYSCALL(__NR_sched_getattr, sys_sched_getattr)
|
||||
#define __NR_renameat2 276
|
||||
__SYSCALL(__NR_renameat2, sys_renameat2)
|
||||
#define __NR_seccomp 277
|
||||
__SYSCALL(__NR_seccomp, sys_seccomp)
|
||||
|
||||
#undef __NR_syscalls
|
||||
#define __NR_syscalls 277
|
||||
#define __NR_syscalls 278
|
||||
|
||||
/*
|
||||
* All syscalls below here should go away really,
|
||||
|
@ -10,6 +10,13 @@
|
||||
#define SECCOMP_MODE_STRICT 1 /* uses hard-coded filter. */
|
||||
#define SECCOMP_MODE_FILTER 2 /* uses user-supplied filter. */
|
||||
|
||||
/* Valid operations for seccomp syscall. */
|
||||
#define SECCOMP_SET_MODE_STRICT 0
|
||||
#define SECCOMP_SET_MODE_FILTER 1
|
||||
|
||||
/* Valid flags for SECCOMP_SET_MODE_FILTER */
|
||||
#define SECCOMP_FILTER_FLAG_TSYNC 1
|
||||
|
||||
/*
|
||||
* All BPF programs must return a 32-bit value.
|
||||
* The bottom 16-bits are for optional return data.
|
||||
|
@ -1677,7 +1677,7 @@ void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap)
|
||||
audit_log_format(ab, " %s=", prefix);
|
||||
CAP_FOR_EACH_U32(i) {
|
||||
audit_log_format(ab, "%08x",
|
||||
cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]);
|
||||
cap->cap[CAP_LAST_U32 - i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,6 +258,10 @@ SYSCALL_DEFINE2(capset, cap_user_header_t, header, const cap_user_data_t, data)
|
||||
i++;
|
||||
}
|
||||
|
||||
effective.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
|
||||
permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
|
||||
inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
@ -315,6 +315,15 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
|
||||
goto free_ti;
|
||||
|
||||
tsk->stack = ti;
|
||||
#ifdef CONFIG_SECCOMP
|
||||
/*
|
||||
* We must handle setting up seccomp filters once we're under
|
||||
* the sighand lock in case orig has changed between now and
|
||||
* then. Until then, filter must be NULL to avoid messing up
|
||||
* the usage counts on the error path calling free_task.
|
||||
*/
|
||||
tsk->seccomp.filter = NULL;
|
||||
#endif
|
||||
|
||||
setup_thread_stack(tsk, orig);
|
||||
clear_user_return_notifier(tsk);
|
||||
@ -1081,6 +1090,39 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void copy_seccomp(struct task_struct *p)
|
||||
{
|
||||
#ifdef CONFIG_SECCOMP
|
||||
/*
|
||||
* Must be called with sighand->lock held, which is common to
|
||||
* all threads in the group. Holding cred_guard_mutex is not
|
||||
* needed because this new task is not yet running and cannot
|
||||
* be racing exec.
|
||||
*/
|
||||
BUG_ON(!spin_is_locked(¤t->sighand->siglock));
|
||||
|
||||
/* Ref-count the new filter user, and assign it. */
|
||||
get_seccomp_filter(current);
|
||||
p->seccomp = current->seccomp;
|
||||
|
||||
/*
|
||||
* Explicitly enable no_new_privs here in case it got set
|
||||
* between the task_struct being duplicated and holding the
|
||||
* sighand lock. The seccomp state and nnp must be in sync.
|
||||
*/
|
||||
if (task_no_new_privs(current))
|
||||
task_set_no_new_privs(p);
|
||||
|
||||
/*
|
||||
* If the parent gained a seccomp mode after copying thread
|
||||
* flags and between before we held the sighand lock, we have
|
||||
* to manually enable the seccomp thread flag here.
|
||||
*/
|
||||
if (p->seccomp.mode != SECCOMP_MODE_DISABLED)
|
||||
set_tsk_thread_flag(p, TIF_SECCOMP);
|
||||
#endif
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(set_tid_address, int __user *, tidptr)
|
||||
{
|
||||
current->clear_child_tid = tidptr;
|
||||
@ -1195,7 +1237,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
goto fork_out;
|
||||
|
||||
ftrace_graph_init_task(p);
|
||||
get_seccomp_filter(p);
|
||||
|
||||
rt_mutex_init_task(p);
|
||||
|
||||
@ -1434,6 +1475,12 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
|
||||
spin_lock(¤t->sighand->siglock);
|
||||
|
||||
/*
|
||||
* Copy seccomp details explicitly here, in case they were changed
|
||||
* before holding sighand lock.
|
||||
*/
|
||||
copy_seccomp(p);
|
||||
|
||||
/*
|
||||
* Process group and session signals need to be delivered to just the
|
||||
* parent before the fork or both the parent and the child after the
|
||||
|
414
kernel/seccomp.c
414
kernel/seccomp.c
@ -18,15 +18,17 @@
|
||||
#include <linux/compat.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
||||
/* #define SECCOMP_DEBUG 1 */
|
||||
|
||||
#ifdef CONFIG_SECCOMP_FILTER
|
||||
#include <asm/syscall.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/pid.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tracehook.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
@ -172,21 +174,24 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
|
||||
*/
|
||||
static u32 seccomp_run_filters(int syscall)
|
||||
{
|
||||
struct seccomp_filter *f;
|
||||
struct seccomp_filter *f = ACCESS_ONCE(current->seccomp.filter);
|
||||
struct seccomp_data sd;
|
||||
u32 ret = SECCOMP_RET_ALLOW;
|
||||
|
||||
/* Ensure unexpected behavior doesn't result in failing open. */
|
||||
if (WARN_ON(current->seccomp.filter == NULL))
|
||||
if (unlikely(WARN_ON(f == NULL)))
|
||||
return SECCOMP_RET_KILL;
|
||||
|
||||
/* Make sure cross-thread synced filter points somewhere sane. */
|
||||
smp_read_barrier_depends();
|
||||
|
||||
populate_seccomp_data(&sd);
|
||||
|
||||
/*
|
||||
* All filters in the list are evaluated and the lowest BPF return
|
||||
* value always takes priority (ignoring the DATA).
|
||||
*/
|
||||
for (f = current->seccomp.filter; f; f = f->prev) {
|
||||
for (; f; f = f->prev) {
|
||||
u32 cur_ret = SK_RUN_FILTER(f->prog, (void *)&sd);
|
||||
|
||||
if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION))
|
||||
@ -194,29 +199,159 @@ static u32 seccomp_run_filters(int syscall)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_SECCOMP_FILTER */
|
||||
|
||||
static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode)
|
||||
{
|
||||
BUG_ON(!spin_is_locked(¤t->sighand->siglock));
|
||||
|
||||
if (current->seccomp.mode && current->seccomp.mode != seccomp_mode)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void seccomp_assign_mode(struct task_struct *task,
|
||||
unsigned long seccomp_mode)
|
||||
{
|
||||
BUG_ON(!spin_is_locked(&task->sighand->siglock));
|
||||
|
||||
task->seccomp.mode = seccomp_mode;
|
||||
/*
|
||||
* Make sure TIF_SECCOMP cannot be set before the mode (and
|
||||
* filter) is set.
|
||||
*/
|
||||
smp_mb__before_atomic();
|
||||
set_tsk_thread_flag(task, TIF_SECCOMP);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECCOMP_FILTER
|
||||
/* Returns 1 if the parent is an ancestor of the child. */
|
||||
static int is_ancestor(struct seccomp_filter *parent,
|
||||
struct seccomp_filter *child)
|
||||
{
|
||||
/* NULL is the root ancestor. */
|
||||
if (parent == NULL)
|
||||
return 1;
|
||||
for (; child; child = child->prev)
|
||||
if (child == parent)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* seccomp_attach_filter: Attaches a seccomp filter to current.
|
||||
* seccomp_can_sync_threads: checks if all threads can be synchronized
|
||||
*
|
||||
* Expects sighand and cred_guard_mutex locks to be held.
|
||||
*
|
||||
* Returns 0 on success, -ve on error, or the pid of a thread which was
|
||||
* either not in the correct seccomp mode or it did not have an ancestral
|
||||
* seccomp filter.
|
||||
*/
|
||||
static inline pid_t seccomp_can_sync_threads(void)
|
||||
{
|
||||
struct task_struct *thread, *caller;
|
||||
|
||||
BUG_ON(!mutex_is_locked(¤t->signal->cred_guard_mutex));
|
||||
BUG_ON(!spin_is_locked(¤t->sighand->siglock));
|
||||
|
||||
/* Validate all threads being eligible for synchronization. */
|
||||
caller = current;
|
||||
for_each_thread(caller, thread) {
|
||||
pid_t failed;
|
||||
|
||||
/* Skip current, since it is initiating the sync. */
|
||||
if (thread == caller)
|
||||
continue;
|
||||
|
||||
if (thread->seccomp.mode == SECCOMP_MODE_DISABLED ||
|
||||
(thread->seccomp.mode == SECCOMP_MODE_FILTER &&
|
||||
is_ancestor(thread->seccomp.filter,
|
||||
caller->seccomp.filter)))
|
||||
continue;
|
||||
|
||||
/* Return the first thread that cannot be synchronized. */
|
||||
failed = task_pid_vnr(thread);
|
||||
/* If the pid cannot be resolved, then return -ESRCH */
|
||||
if (unlikely(WARN_ON(failed == 0)))
|
||||
failed = -ESRCH;
|
||||
return failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* seccomp_sync_threads: sets all threads to use current's filter
|
||||
*
|
||||
* Expects sighand and cred_guard_mutex locks to be held, and for
|
||||
* seccomp_can_sync_threads() to have returned success already
|
||||
* without dropping the locks.
|
||||
*
|
||||
*/
|
||||
static inline void seccomp_sync_threads(void)
|
||||
{
|
||||
struct task_struct *thread, *caller;
|
||||
|
||||
BUG_ON(!mutex_is_locked(¤t->signal->cred_guard_mutex));
|
||||
BUG_ON(!spin_is_locked(¤t->sighand->siglock));
|
||||
|
||||
/* Synchronize all threads. */
|
||||
caller = current;
|
||||
for_each_thread(caller, thread) {
|
||||
/* Skip current, since it needs no changes. */
|
||||
if (thread == caller)
|
||||
continue;
|
||||
|
||||
/* Get a task reference for the new leaf node. */
|
||||
get_seccomp_filter(caller);
|
||||
/*
|
||||
* Drop the task reference to the shared ancestor since
|
||||
* current's path will hold a reference. (This also
|
||||
* allows a put before the assignment.)
|
||||
*/
|
||||
put_seccomp_filter(thread);
|
||||
smp_store_release(&thread->seccomp.filter,
|
||||
caller->seccomp.filter);
|
||||
/*
|
||||
* Opt the other thread into seccomp if needed.
|
||||
* As threads are considered to be trust-realm
|
||||
* equivalent (see ptrace_may_access), it is safe to
|
||||
* allow one thread to transition the other.
|
||||
*/
|
||||
if (thread->seccomp.mode == SECCOMP_MODE_DISABLED) {
|
||||
/*
|
||||
* Don't let an unprivileged task work around
|
||||
* the no_new_privs restriction by creating
|
||||
* a thread that sets it up, enters seccomp,
|
||||
* then dies.
|
||||
*/
|
||||
if (task_no_new_privs(caller))
|
||||
task_set_no_new_privs(thread);
|
||||
|
||||
seccomp_assign_mode(thread, SECCOMP_MODE_FILTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* seccomp_prepare_filter: Prepares a seccomp filter for use.
|
||||
* @fprog: BPF program to install
|
||||
*
|
||||
* Returns 0 on success or an errno on failure.
|
||||
* Returns filter on success or an ERR_PTR on failure.
|
||||
*/
|
||||
static long seccomp_attach_filter(struct sock_fprog *fprog)
|
||||
static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
|
||||
{
|
||||
struct seccomp_filter *filter;
|
||||
unsigned long fp_size = fprog->len * sizeof(struct sock_filter);
|
||||
unsigned long total_insns = fprog->len;
|
||||
unsigned long fp_size;
|
||||
struct sock_filter *fp;
|
||||
int new_len;
|
||||
long ret;
|
||||
|
||||
if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
|
||||
return -EINVAL;
|
||||
|
||||
for (filter = current->seccomp.filter; filter; filter = filter->prev)
|
||||
total_insns += filter->prog->len + 4; /* include a 4 instr penalty */
|
||||
if (total_insns > MAX_INSNS_PER_PATH)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-EINVAL);
|
||||
BUG_ON(INT_MAX / fprog->len < sizeof(struct sock_filter));
|
||||
fp_size = fprog->len * sizeof(struct sock_filter);
|
||||
|
||||
/*
|
||||
* Installing a seccomp filter requires that the task has
|
||||
@ -224,14 +359,14 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
|
||||
* This avoids scenarios where unprivileged tasks can affect the
|
||||
* behavior of privileged children.
|
||||
*/
|
||||
if (!current->no_new_privs &&
|
||||
if (!task_no_new_privs(current) &&
|
||||
security_capable_noaudit(current_cred(), current_user_ns(),
|
||||
CAP_SYS_ADMIN) != 0)
|
||||
return -EACCES;
|
||||
return ERR_PTR(-EACCES);
|
||||
|
||||
fp = kzalloc(fp_size, GFP_KERNEL|__GFP_NOWARN);
|
||||
if (!fp)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Copy the instructions from fprog. */
|
||||
ret = -EFAULT;
|
||||
@ -275,13 +410,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
|
||||
|
||||
sk_filter_select_runtime(filter->prog);
|
||||
|
||||
/*
|
||||
* If there is an existing filter, make it the prev and don't drop its
|
||||
* task reference.
|
||||
*/
|
||||
filter->prev = current->seccomp.filter;
|
||||
current->seccomp.filter = filter;
|
||||
return 0;
|
||||
return filter;
|
||||
|
||||
free_filter_prog:
|
||||
kfree(filter->prog);
|
||||
@ -289,19 +418,20 @@ free_filter:
|
||||
kfree(filter);
|
||||
free_prog:
|
||||
kfree(fp);
|
||||
return ret;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* seccomp_attach_user_filter - attaches a user-supplied sock_fprog
|
||||
* seccomp_prepare_user_filter - prepares a user-supplied sock_fprog
|
||||
* @user_filter: pointer to the user data containing a sock_fprog.
|
||||
*
|
||||
* Returns 0 on success and non-zero otherwise.
|
||||
*/
|
||||
static long seccomp_attach_user_filter(char __user *user_filter)
|
||||
static struct seccomp_filter *
|
||||
seccomp_prepare_user_filter(const char __user *user_filter)
|
||||
{
|
||||
struct sock_fprog fprog;
|
||||
long ret = -EFAULT;
|
||||
struct seccomp_filter *filter = ERR_PTR(-EFAULT);
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
if (is_compat_task()) {
|
||||
@ -314,9 +444,56 @@ static long seccomp_attach_user_filter(char __user *user_filter)
|
||||
#endif
|
||||
if (copy_from_user(&fprog, user_filter, sizeof(fprog)))
|
||||
goto out;
|
||||
ret = seccomp_attach_filter(&fprog);
|
||||
filter = seccomp_prepare_filter(&fprog);
|
||||
out:
|
||||
return ret;
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* seccomp_attach_filter: validate and attach filter
|
||||
* @flags: flags to change filter behavior
|
||||
* @filter: seccomp filter to add to the current process
|
||||
*
|
||||
* Caller must be holding current->sighand->siglock lock.
|
||||
*
|
||||
* Returns 0 on success, -ve on error.
|
||||
*/
|
||||
static long seccomp_attach_filter(unsigned int flags,
|
||||
struct seccomp_filter *filter)
|
||||
{
|
||||
unsigned long total_insns;
|
||||
struct seccomp_filter *walker;
|
||||
|
||||
BUG_ON(!spin_is_locked(¤t->sighand->siglock));
|
||||
|
||||
/* Validate resulting filter length. */
|
||||
total_insns = filter->prog->len;
|
||||
for (walker = current->seccomp.filter; walker; walker = walker->prev)
|
||||
total_insns += walker->prog->len + 4; /* 4 instr penalty */
|
||||
if (total_insns > MAX_INSNS_PER_PATH)
|
||||
return -ENOMEM;
|
||||
|
||||
/* If thread sync has been requested, check that it is possible. */
|
||||
if (flags & SECCOMP_FILTER_FLAG_TSYNC) {
|
||||
int ret;
|
||||
|
||||
ret = seccomp_can_sync_threads();
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is an existing filter, make it the prev and don't drop its
|
||||
* task reference.
|
||||
*/
|
||||
filter->prev = current->seccomp.filter;
|
||||
current->seccomp.filter = filter;
|
||||
|
||||
/* Now that the new filter is in place, synchronize to all threads. */
|
||||
if (flags & SECCOMP_FILTER_FLAG_TSYNC)
|
||||
seccomp_sync_threads();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get_seccomp_filter - increments the reference count of the filter on @tsk */
|
||||
@ -329,6 +506,14 @@ void get_seccomp_filter(struct task_struct *tsk)
|
||||
atomic_inc(&orig->usage);
|
||||
}
|
||||
|
||||
static inline void seccomp_filter_free(struct seccomp_filter *filter)
|
||||
{
|
||||
if (filter) {
|
||||
sk_filter_free(filter->prog);
|
||||
kfree(filter);
|
||||
}
|
||||
}
|
||||
|
||||
/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
|
||||
void put_seccomp_filter(struct task_struct *tsk)
|
||||
{
|
||||
@ -337,8 +522,7 @@ void put_seccomp_filter(struct task_struct *tsk)
|
||||
while (orig && atomic_dec_and_test(&orig->usage)) {
|
||||
struct seccomp_filter *freeme = orig;
|
||||
orig = orig->prev;
|
||||
sk_filter_free(freeme->prog);
|
||||
kfree(freeme);
|
||||
seccomp_filter_free(freeme);
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,12 +566,17 @@ static int mode1_syscalls_32[] = {
|
||||
|
||||
int __secure_computing(int this_syscall)
|
||||
{
|
||||
int mode = current->seccomp.mode;
|
||||
int exit_sig = 0;
|
||||
int *syscall;
|
||||
u32 ret;
|
||||
|
||||
switch (mode) {
|
||||
/*
|
||||
* Make sure that any changes to mode from another thread have
|
||||
* been seen after TIF_SECCOMP was seen.
|
||||
*/
|
||||
rmb();
|
||||
|
||||
switch (current->seccomp.mode) {
|
||||
case SECCOMP_MODE_STRICT:
|
||||
syscall = mode1_syscalls;
|
||||
#ifdef CONFIG_COMPAT
|
||||
@ -473,47 +662,152 @@ long prctl_get_seccomp(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* prctl_set_seccomp: configures current->seccomp.mode
|
||||
* @seccomp_mode: requested mode to use
|
||||
* @filter: optional struct sock_fprog for use with SECCOMP_MODE_FILTER
|
||||
*
|
||||
* This function may be called repeatedly with a @seccomp_mode of
|
||||
* SECCOMP_MODE_FILTER to install additional filters. Every filter
|
||||
* successfully installed will be evaluated (in reverse order) for each system
|
||||
* call the task makes.
|
||||
* seccomp_set_mode_strict: internal function for setting strict seccomp
|
||||
*
|
||||
* Once current->seccomp.mode is non-zero, it may not be changed.
|
||||
*
|
||||
* Returns 0 on success or -EINVAL on failure.
|
||||
*/
|
||||
long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)
|
||||
static long seccomp_set_mode_strict(void)
|
||||
{
|
||||
const unsigned long seccomp_mode = SECCOMP_MODE_STRICT;
|
||||
long ret = -EINVAL;
|
||||
|
||||
if (current->seccomp.mode &&
|
||||
current->seccomp.mode != seccomp_mode)
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
|
||||
if (!seccomp_may_assign_mode(seccomp_mode))
|
||||
goto out;
|
||||
|
||||
#ifdef TIF_NOTSC
|
||||
disable_TSC();
|
||||
#endif
|
||||
seccomp_assign_mode(current, seccomp_mode);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECCOMP_FILTER
|
||||
/**
|
||||
* seccomp_set_mode_filter: internal function for setting seccomp filter
|
||||
* @flags: flags to change filter behavior
|
||||
* @filter: struct sock_fprog containing filter
|
||||
*
|
||||
* This function may be called repeatedly to install additional filters.
|
||||
* Every filter successfully installed will be evaluated (in reverse order)
|
||||
* for each system call the task makes.
|
||||
*
|
||||
* Once current->seccomp.mode is non-zero, it may not be changed.
|
||||
*
|
||||
* Returns 0 on success or -EINVAL on failure.
|
||||
*/
|
||||
static long seccomp_set_mode_filter(unsigned int flags,
|
||||
const char __user *filter)
|
||||
{
|
||||
const unsigned long seccomp_mode = SECCOMP_MODE_FILTER;
|
||||
struct seccomp_filter *prepared = NULL;
|
||||
long ret = -EINVAL;
|
||||
|
||||
/* Validate flags. */
|
||||
if (flags & ~SECCOMP_FILTER_FLAG_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
/* Prepare the new filter before holding any locks. */
|
||||
prepared = seccomp_prepare_user_filter(filter);
|
||||
if (IS_ERR(prepared))
|
||||
return PTR_ERR(prepared);
|
||||
|
||||
/*
|
||||
* Make sure we cannot change seccomp or nnp state via TSYNC
|
||||
* while another thread is in the middle of calling exec.
|
||||
*/
|
||||
if (flags & SECCOMP_FILTER_FLAG_TSYNC &&
|
||||
mutex_lock_killable(¤t->signal->cred_guard_mutex))
|
||||
goto out_free;
|
||||
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
|
||||
if (!seccomp_may_assign_mode(seccomp_mode))
|
||||
goto out;
|
||||
|
||||
ret = seccomp_attach_filter(flags, prepared);
|
||||
if (ret)
|
||||
goto out;
|
||||
/* Do not free the successfully attached filter. */
|
||||
prepared = NULL;
|
||||
|
||||
seccomp_assign_mode(current, seccomp_mode);
|
||||
out:
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
if (flags & SECCOMP_FILTER_FLAG_TSYNC)
|
||||
mutex_unlock(¤t->signal->cred_guard_mutex);
|
||||
out_free:
|
||||
seccomp_filter_free(prepared);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static inline long seccomp_set_mode_filter(unsigned int flags,
|
||||
const char __user *filter)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Common entry point for both prctl and syscall. */
|
||||
static long do_seccomp(unsigned int op, unsigned int flags,
|
||||
const char __user *uargs)
|
||||
{
|
||||
switch (op) {
|
||||
case SECCOMP_SET_MODE_STRICT:
|
||||
if (flags != 0 || uargs != NULL)
|
||||
return -EINVAL;
|
||||
return seccomp_set_mode_strict();
|
||||
case SECCOMP_SET_MODE_FILTER:
|
||||
return seccomp_set_mode_filter(flags, uargs);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(seccomp, unsigned int, op, unsigned int, flags,
|
||||
const char __user *, uargs)
|
||||
{
|
||||
return do_seccomp(op, flags, uargs);
|
||||
}
|
||||
|
||||
/**
|
||||
* prctl_set_seccomp: configures current->seccomp.mode
|
||||
* @seccomp_mode: requested mode to use
|
||||
* @filter: optional struct sock_fprog for use with SECCOMP_MODE_FILTER
|
||||
*
|
||||
* Returns 0 on success or -EINVAL on failure.
|
||||
*/
|
||||
long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)
|
||||
{
|
||||
unsigned int op;
|
||||
char __user *uargs;
|
||||
|
||||
switch (seccomp_mode) {
|
||||
case SECCOMP_MODE_STRICT:
|
||||
ret = 0;
|
||||
#ifdef TIF_NOTSC
|
||||
disable_TSC();
|
||||
#endif
|
||||
op = SECCOMP_SET_MODE_STRICT;
|
||||
/*
|
||||
* Setting strict mode through prctl always ignored filter,
|
||||
* so make sure it is always NULL here to pass the internal
|
||||
* check in do_seccomp().
|
||||
*/
|
||||
uargs = NULL;
|
||||
break;
|
||||
#ifdef CONFIG_SECCOMP_FILTER
|
||||
case SECCOMP_MODE_FILTER:
|
||||
ret = seccomp_attach_user_filter(filter);
|
||||
if (ret)
|
||||
goto out;
|
||||
op = SECCOMP_SET_MODE_FILTER;
|
||||
uargs = filter;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
current->seccomp.mode = seccomp_mode;
|
||||
set_thread_flag(TIF_SECCOMP);
|
||||
out:
|
||||
return ret;
|
||||
/* prctl interface doesn't have flags, so they are always zero. */
|
||||
return do_seccomp(op, 0, uargs);
|
||||
}
|
||||
|
@ -1990,12 +1990,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||
if (arg2 != 1 || arg3 || arg4 || arg5)
|
||||
return -EINVAL;
|
||||
|
||||
current->no_new_privs = 1;
|
||||
task_set_no_new_privs(current);
|
||||
break;
|
||||
case PR_GET_NO_NEW_PRIVS:
|
||||
if (arg2 || arg3 || arg4 || arg5)
|
||||
return -EINVAL;
|
||||
return current->no_new_privs ? 1 : 0;
|
||||
return task_no_new_privs(current) ? 1 : 0;
|
||||
case PR_GET_THP_DISABLE:
|
||||
if (arg2 || arg3 || arg4 || arg5)
|
||||
return -EINVAL;
|
||||
|
@ -213,3 +213,6 @@ cond_syscall(compat_sys_open_by_handle_at);
|
||||
|
||||
/* compare kernel pointers */
|
||||
cond_syscall(sys_kcmp);
|
||||
|
||||
/* operate on Secure Computing state */
|
||||
cond_syscall(sys_seccomp);
|
||||
|
@ -89,6 +89,7 @@ static __init int load_system_certificate_list(void)
|
||||
pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
|
||||
PTR_ERR(key));
|
||||
} else {
|
||||
set_bit(KEY_FLAG_BUILTIN, &key_ref_to_ptr(key)->flags);
|
||||
pr_notice("Loaded X.509 cert '%s'\n",
|
||||
key_ref_to_ptr(key)->description);
|
||||
key_ref_put(key);
|
||||
|
@ -451,7 +451,8 @@ config MPILIB
|
||||
|
||||
config SIGNATURE
|
||||
tristate
|
||||
depends on KEYS && CRYPTO
|
||||
depends on KEYS
|
||||
select CRYPTO
|
||||
select CRYPTO_SHA1
|
||||
select MPILIB
|
||||
help
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <linux/key-type.h>
|
||||
|
||||
#include <keys/ceph-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/ceph/decode.h>
|
||||
#include "crypto.h"
|
||||
|
||||
@ -423,8 +424,7 @@ int ceph_encrypt2(struct ceph_crypto_key *secret, void *dst, size_t *dst_len,
|
||||
}
|
||||
}
|
||||
|
||||
static int ceph_key_instantiate(struct key *key,
|
||||
struct key_preparsed_payload *prep)
|
||||
static int ceph_key_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct ceph_crypto_key *ckey;
|
||||
size_t datalen = prep->datalen;
|
||||
@ -435,10 +435,6 @@ static int ceph_key_instantiate(struct key *key,
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
goto err;
|
||||
|
||||
ret = key_payload_reserve(key, datalen);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = -ENOMEM;
|
||||
ckey = kmalloc(sizeof(*ckey), GFP_KERNEL);
|
||||
if (!ckey)
|
||||
@ -450,7 +446,8 @@ static int ceph_key_instantiate(struct key *key,
|
||||
if (ret < 0)
|
||||
goto err_ckey;
|
||||
|
||||
key->payload.data = ckey;
|
||||
prep->payload[0] = ckey;
|
||||
prep->quotalen = datalen;
|
||||
return 0;
|
||||
|
||||
err_ckey:
|
||||
@ -459,12 +456,15 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ceph_key_match(const struct key *key, const void *description)
|
||||
static void ceph_key_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
return strcmp(key->description, description) == 0;
|
||||
struct ceph_crypto_key *ckey = prep->payload[0];
|
||||
ceph_crypto_key_destroy(ckey);
|
||||
kfree(ckey);
|
||||
}
|
||||
|
||||
static void ceph_key_destroy(struct key *key) {
|
||||
static void ceph_key_destroy(struct key *key)
|
||||
{
|
||||
struct ceph_crypto_key *ckey = key->payload.data;
|
||||
|
||||
ceph_crypto_key_destroy(ckey);
|
||||
@ -473,8 +473,10 @@ static void ceph_key_destroy(struct key *key) {
|
||||
|
||||
struct key_type key_type_ceph = {
|
||||
.name = "ceph",
|
||||
.instantiate = ceph_key_instantiate,
|
||||
.match = ceph_key_match,
|
||||
.preparse = ceph_key_preparse,
|
||||
.free_preparse = ceph_key_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.destroy = ceph_key_destroy,
|
||||
};
|
||||
|
||||
|
@ -46,7 +46,7 @@ const struct cred *dns_resolver_cache;
|
||||
#define DNS_ERRORNO_OPTION "dnserror"
|
||||
|
||||
/*
|
||||
* Instantiate a user defined key for dns_resolver.
|
||||
* Preparse instantiation data for a dns_resolver key.
|
||||
*
|
||||
* The data must be a NUL-terminated string, with the NUL char accounted in
|
||||
* datalen.
|
||||
@ -58,17 +58,15 @@ const struct cred *dns_resolver_cache;
|
||||
* "ip1,ip2,...#foo=bar"
|
||||
*/
|
||||
static int
|
||||
dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
dns_resolver_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct user_key_payload *upayload;
|
||||
unsigned long derrno;
|
||||
int ret;
|
||||
size_t datalen = prep->datalen, result_len = 0;
|
||||
int datalen = prep->datalen, result_len = 0;
|
||||
const char *data = prep->data, *end, *opt;
|
||||
|
||||
kenter("%%%d,%s,'%*.*s',%zu",
|
||||
key->serial, key->description,
|
||||
(int)datalen, (int)datalen, data, datalen);
|
||||
kenter("'%*.*s',%u", datalen, datalen, data, datalen);
|
||||
|
||||
if (datalen <= 1 || !data || data[datalen - 1] != '\0')
|
||||
return -EINVAL;
|
||||
@ -95,8 +93,7 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
opt_len = next_opt - opt;
|
||||
if (!opt_len) {
|
||||
printk(KERN_WARNING
|
||||
"Empty option to dns_resolver key %d\n",
|
||||
key->serial);
|
||||
"Empty option to dns_resolver key\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -125,30 +122,28 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
goto bad_option_value;
|
||||
|
||||
kdebug("dns error no. = %lu", derrno);
|
||||
key->type_data.x[0] = -derrno;
|
||||
prep->type_data[0] = ERR_PTR(-derrno);
|
||||
continue;
|
||||
}
|
||||
|
||||
bad_option_value:
|
||||
printk(KERN_WARNING
|
||||
"Option '%*.*s' to dns_resolver key %d:"
|
||||
"Option '%*.*s' to dns_resolver key:"
|
||||
" bad/missing value\n",
|
||||
opt_nlen, opt_nlen, opt, key->serial);
|
||||
opt_nlen, opt_nlen, opt);
|
||||
return -EINVAL;
|
||||
} while (opt = next_opt + 1, opt < end);
|
||||
}
|
||||
|
||||
/* don't cache the result if we're caching an error saying there's no
|
||||
* result */
|
||||
if (key->type_data.x[0]) {
|
||||
kleave(" = 0 [h_error %ld]", key->type_data.x[0]);
|
||||
if (prep->type_data[0]) {
|
||||
kleave(" = 0 [h_error %ld]", PTR_ERR(prep->type_data[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
kdebug("store result");
|
||||
ret = key_payload_reserve(key, result_len);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
prep->quotalen = result_len;
|
||||
|
||||
upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL);
|
||||
if (!upayload) {
|
||||
@ -159,12 +154,22 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
upayload->datalen = result_len;
|
||||
memcpy(upayload->data, data, result_len);
|
||||
upayload->data[result_len] = '\0';
|
||||
rcu_assign_pointer(key->payload.data, upayload);
|
||||
|
||||
prep->payload[0] = upayload;
|
||||
kleave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up the preparse data
|
||||
*/
|
||||
static void dns_resolver_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
kfree(prep->payload[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* The description is of the form "[<type>:]<domain_name>"
|
||||
*
|
||||
@ -234,7 +239,9 @@ static long dns_resolver_read(const struct key *key,
|
||||
|
||||
struct key_type key_type_dns_resolver = {
|
||||
.name = "dns_resolver",
|
||||
.instantiate = dns_resolver_instantiate,
|
||||
.preparse = dns_resolver_preparse,
|
||||
.free_preparse = dns_resolver_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = dns_resolver_match,
|
||||
.revoke = user_revoke,
|
||||
.destroy = user_destroy,
|
||||
|
@ -129,6 +129,7 @@ int dns_query(const char *type, const char *name, size_t namelen,
|
||||
}
|
||||
|
||||
down_read(&rkey->sem);
|
||||
set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags);
|
||||
rkey->perm |= KEY_USR_VIEW;
|
||||
|
||||
ret = key_validate(rkey);
|
||||
|
@ -890,8 +890,8 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
host_spot = netlbl_secattr_catmap_walk(secattr->attr.mls.cat,
|
||||
host_spot + 1);
|
||||
host_spot = netlbl_catmap_walk(secattr->attr.mls.cat,
|
||||
host_spot + 1);
|
||||
if (host_spot < 0)
|
||||
break;
|
||||
|
||||
@ -973,7 +973,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
|
||||
return -EPERM;
|
||||
break;
|
||||
}
|
||||
ret_val = netlbl_secattr_catmap_setbit(secattr->attr.mls.cat,
|
||||
ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat,
|
||||
host_spot,
|
||||
GFP_ATOMIC);
|
||||
if (ret_val != 0)
|
||||
@ -1039,8 +1039,7 @@ static int cipso_v4_map_cat_enum_hton(const struct cipso_v4_doi *doi_def,
|
||||
u32 cat_iter = 0;
|
||||
|
||||
for (;;) {
|
||||
cat = netlbl_secattr_catmap_walk(secattr->attr.mls.cat,
|
||||
cat + 1);
|
||||
cat = netlbl_catmap_walk(secattr->attr.mls.cat, cat + 1);
|
||||
if (cat < 0)
|
||||
break;
|
||||
if ((cat_iter + 2) > net_cat_len)
|
||||
@ -1075,9 +1074,9 @@ static int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def,
|
||||
u32 iter;
|
||||
|
||||
for (iter = 0; iter < net_cat_len; iter += 2) {
|
||||
ret_val = netlbl_secattr_catmap_setbit(secattr->attr.mls.cat,
|
||||
get_unaligned_be16(&net_cat[iter]),
|
||||
GFP_ATOMIC);
|
||||
ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat,
|
||||
get_unaligned_be16(&net_cat[iter]),
|
||||
GFP_ATOMIC);
|
||||
if (ret_val != 0)
|
||||
return ret_val;
|
||||
}
|
||||
@ -1155,8 +1154,7 @@ static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def,
|
||||
return -ENOSPC;
|
||||
|
||||
for (;;) {
|
||||
iter = netlbl_secattr_catmap_walk(secattr->attr.mls.cat,
|
||||
iter + 1);
|
||||
iter = netlbl_catmap_walk(secattr->attr.mls.cat, iter + 1);
|
||||
if (iter < 0)
|
||||
break;
|
||||
cat_size += (iter == 0 ? 0 : sizeof(u16));
|
||||
@ -1164,8 +1162,7 @@ static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def,
|
||||
return -ENOSPC;
|
||||
array[array_cnt++] = iter;
|
||||
|
||||
iter = netlbl_secattr_catmap_walk_rng(secattr->attr.mls.cat,
|
||||
iter);
|
||||
iter = netlbl_catmap_walkrng(secattr->attr.mls.cat, iter);
|
||||
if (iter < 0)
|
||||
return -EFAULT;
|
||||
cat_size += sizeof(u16);
|
||||
@ -1217,10 +1214,10 @@ static int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def,
|
||||
else
|
||||
cat_low = 0;
|
||||
|
||||
ret_val = netlbl_secattr_catmap_setrng(secattr->attr.mls.cat,
|
||||
cat_low,
|
||||
cat_high,
|
||||
GFP_ATOMIC);
|
||||
ret_val = netlbl_catmap_setrng(&secattr->attr.mls.cat,
|
||||
cat_low,
|
||||
cat_high,
|
||||
GFP_ATOMIC);
|
||||
if (ret_val != 0)
|
||||
return ret_val;
|
||||
}
|
||||
@ -1335,16 +1332,12 @@ static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def,
|
||||
secattr->flags |= NETLBL_SECATTR_MLS_LVL;
|
||||
|
||||
if (tag_len > 4) {
|
||||
secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
|
||||
if (secattr->attr.mls.cat == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def,
|
||||
&tag[4],
|
||||
tag_len - 4,
|
||||
secattr);
|
||||
if (ret_val != 0) {
|
||||
netlbl_secattr_catmap_free(secattr->attr.mls.cat);
|
||||
netlbl_catmap_free(secattr->attr.mls.cat);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
@ -1430,16 +1423,12 @@ static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def,
|
||||
secattr->flags |= NETLBL_SECATTR_MLS_LVL;
|
||||
|
||||
if (tag_len > 4) {
|
||||
secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
|
||||
if (secattr->attr.mls.cat == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret_val = cipso_v4_map_cat_enum_ntoh(doi_def,
|
||||
&tag[4],
|
||||
tag_len - 4,
|
||||
secattr);
|
||||
if (ret_val != 0) {
|
||||
netlbl_secattr_catmap_free(secattr->attr.mls.cat);
|
||||
netlbl_catmap_free(secattr->attr.mls.cat);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
@ -1524,16 +1513,12 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def,
|
||||
secattr->flags |= NETLBL_SECATTR_MLS_LVL;
|
||||
|
||||
if (tag_len > 4) {
|
||||
secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
|
||||
if (secattr->attr.mls.cat == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret_val = cipso_v4_map_cat_rng_ntoh(doi_def,
|
||||
&tag[4],
|
||||
tag_len - 4,
|
||||
secattr);
|
||||
if (ret_val != 0) {
|
||||
netlbl_secattr_catmap_free(secattr->attr.mls.cat);
|
||||
netlbl_catmap_free(secattr->attr.mls.cat);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
@ -405,8 +405,72 @@ out_entry:
|
||||
* Security Attribute Functions
|
||||
*/
|
||||
|
||||
#define _CM_F_NONE 0x00000000
|
||||
#define _CM_F_ALLOC 0x00000001
|
||||
#define _CM_F_WALK 0x00000002
|
||||
|
||||
/**
|
||||
* netlbl_secattr_catmap_walk - Walk a LSM secattr catmap looking for a bit
|
||||
* _netlbl_catmap_getnode - Get a individual node from a catmap
|
||||
* @catmap: pointer to the category bitmap
|
||||
* @offset: the requested offset
|
||||
* @cm_flags: catmap flags, see _CM_F_*
|
||||
* @gfp_flags: memory allocation flags
|
||||
*
|
||||
* Description:
|
||||
* Iterate through the catmap looking for the node associated with @offset.
|
||||
* If the _CM_F_ALLOC flag is set in @cm_flags and there is no associated node,
|
||||
* one will be created and inserted into the catmap. If the _CM_F_WALK flag is
|
||||
* set in @cm_flags and there is no associated node, the next highest node will
|
||||
* be returned. Returns a pointer to the node on success, NULL on failure.
|
||||
*
|
||||
*/
|
||||
static struct netlbl_lsm_catmap *_netlbl_catmap_getnode(
|
||||
struct netlbl_lsm_catmap **catmap,
|
||||
u32 offset,
|
||||
unsigned int cm_flags,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
struct netlbl_lsm_catmap *iter = *catmap;
|
||||
struct netlbl_lsm_catmap *prev = NULL;
|
||||
|
||||
if (iter == NULL)
|
||||
goto catmap_getnode_alloc;
|
||||
if (offset < iter->startbit)
|
||||
goto catmap_getnode_walk;
|
||||
while (iter && offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) {
|
||||
prev = iter;
|
||||
iter = iter->next;
|
||||
}
|
||||
if (iter == NULL || offset < iter->startbit)
|
||||
goto catmap_getnode_walk;
|
||||
|
||||
return iter;
|
||||
|
||||
catmap_getnode_walk:
|
||||
if (cm_flags & _CM_F_WALK)
|
||||
return iter;
|
||||
catmap_getnode_alloc:
|
||||
if (!(cm_flags & _CM_F_ALLOC))
|
||||
return NULL;
|
||||
|
||||
iter = netlbl_catmap_alloc(gfp_flags);
|
||||
if (iter == NULL)
|
||||
return NULL;
|
||||
iter->startbit = offset & ~(NETLBL_CATMAP_SIZE - 1);
|
||||
|
||||
if (prev == NULL) {
|
||||
iter->next = *catmap;
|
||||
*catmap = iter;
|
||||
} else {
|
||||
iter->next = prev->next;
|
||||
prev->next = iter;
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_catmap_walk - Walk a LSM secattr catmap looking for a bit
|
||||
* @catmap: the category bitmap
|
||||
* @offset: the offset to start searching at, in bits
|
||||
*
|
||||
@ -415,54 +479,51 @@ out_entry:
|
||||
* returns the spot of the first set bit or -ENOENT if no bits are set.
|
||||
*
|
||||
*/
|
||||
int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 offset)
|
||||
int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset)
|
||||
{
|
||||
struct netlbl_lsm_secattr_catmap *iter = catmap;
|
||||
u32 node_idx;
|
||||
u32 node_bit;
|
||||
struct netlbl_lsm_catmap *iter = catmap;
|
||||
u32 idx;
|
||||
u32 bit;
|
||||
NETLBL_CATMAP_MAPTYPE bitmap;
|
||||
|
||||
iter = _netlbl_catmap_getnode(&catmap, offset, _CM_F_WALK, 0);
|
||||
if (iter == NULL)
|
||||
return -ENOENT;
|
||||
if (offset > iter->startbit) {
|
||||
while (offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) {
|
||||
iter = iter->next;
|
||||
if (iter == NULL)
|
||||
return -ENOENT;
|
||||
}
|
||||
node_idx = (offset - iter->startbit) / NETLBL_CATMAP_MAPSIZE;
|
||||
node_bit = offset - iter->startbit -
|
||||
(NETLBL_CATMAP_MAPSIZE * node_idx);
|
||||
offset -= iter->startbit;
|
||||
idx = offset / NETLBL_CATMAP_MAPSIZE;
|
||||
bit = offset % NETLBL_CATMAP_MAPSIZE;
|
||||
} else {
|
||||
node_idx = 0;
|
||||
node_bit = 0;
|
||||
idx = 0;
|
||||
bit = 0;
|
||||
}
|
||||
bitmap = iter->bitmap[node_idx] >> node_bit;
|
||||
bitmap = iter->bitmap[idx] >> bit;
|
||||
|
||||
for (;;) {
|
||||
if (bitmap != 0) {
|
||||
while ((bitmap & NETLBL_CATMAP_BIT) == 0) {
|
||||
bitmap >>= 1;
|
||||
node_bit++;
|
||||
bit++;
|
||||
}
|
||||
return iter->startbit +
|
||||
(NETLBL_CATMAP_MAPSIZE * node_idx) + node_bit;
|
||||
(NETLBL_CATMAP_MAPSIZE * idx) + bit;
|
||||
}
|
||||
if (++node_idx >= NETLBL_CATMAP_MAPCNT) {
|
||||
if (++idx >= NETLBL_CATMAP_MAPCNT) {
|
||||
if (iter->next != NULL) {
|
||||
iter = iter->next;
|
||||
node_idx = 0;
|
||||
idx = 0;
|
||||
} else
|
||||
return -ENOENT;
|
||||
}
|
||||
bitmap = iter->bitmap[node_idx];
|
||||
node_bit = 0;
|
||||
bitmap = iter->bitmap[idx];
|
||||
bit = 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_secattr_catmap_walk_rng - Find the end of a string of set bits
|
||||
* netlbl_catmap_walkrng - Find the end of a string of set bits
|
||||
* @catmap: the category bitmap
|
||||
* @offset: the offset to start searching at, in bits
|
||||
*
|
||||
@ -472,57 +533,105 @@ int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap,
|
||||
* the end of the bitmap.
|
||||
*
|
||||
*/
|
||||
int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 offset)
|
||||
int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap, u32 offset)
|
||||
{
|
||||
struct netlbl_lsm_secattr_catmap *iter = catmap;
|
||||
u32 node_idx;
|
||||
u32 node_bit;
|
||||
struct netlbl_lsm_catmap *iter;
|
||||
struct netlbl_lsm_catmap *prev = NULL;
|
||||
u32 idx;
|
||||
u32 bit;
|
||||
NETLBL_CATMAP_MAPTYPE bitmask;
|
||||
NETLBL_CATMAP_MAPTYPE bitmap;
|
||||
|
||||
iter = _netlbl_catmap_getnode(&catmap, offset, _CM_F_WALK, 0);
|
||||
if (iter == NULL)
|
||||
return -ENOENT;
|
||||
if (offset > iter->startbit) {
|
||||
while (offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) {
|
||||
iter = iter->next;
|
||||
if (iter == NULL)
|
||||
return -ENOENT;
|
||||
}
|
||||
node_idx = (offset - iter->startbit) / NETLBL_CATMAP_MAPSIZE;
|
||||
node_bit = offset - iter->startbit -
|
||||
(NETLBL_CATMAP_MAPSIZE * node_idx);
|
||||
offset -= iter->startbit;
|
||||
idx = offset / NETLBL_CATMAP_MAPSIZE;
|
||||
bit = offset % NETLBL_CATMAP_MAPSIZE;
|
||||
} else {
|
||||
node_idx = 0;
|
||||
node_bit = 0;
|
||||
idx = 0;
|
||||
bit = 0;
|
||||
}
|
||||
bitmask = NETLBL_CATMAP_BIT << node_bit;
|
||||
bitmask = NETLBL_CATMAP_BIT << bit;
|
||||
|
||||
for (;;) {
|
||||
bitmap = iter->bitmap[node_idx];
|
||||
bitmap = iter->bitmap[idx];
|
||||
while (bitmask != 0 && (bitmap & bitmask) != 0) {
|
||||
bitmask <<= 1;
|
||||
node_bit++;
|
||||
bit++;
|
||||
}
|
||||
|
||||
if (bitmask != 0)
|
||||
if (prev && idx == 0 && bit == 0)
|
||||
return prev->startbit + NETLBL_CATMAP_SIZE - 1;
|
||||
else if (bitmask != 0)
|
||||
return iter->startbit +
|
||||
(NETLBL_CATMAP_MAPSIZE * node_idx) +
|
||||
node_bit - 1;
|
||||
else if (++node_idx >= NETLBL_CATMAP_MAPCNT) {
|
||||
(NETLBL_CATMAP_MAPSIZE * idx) + bit - 1;
|
||||
else if (++idx >= NETLBL_CATMAP_MAPCNT) {
|
||||
if (iter->next == NULL)
|
||||
return iter->startbit + NETLBL_CATMAP_SIZE - 1;
|
||||
return iter->startbit + NETLBL_CATMAP_SIZE - 1;
|
||||
prev = iter;
|
||||
iter = iter->next;
|
||||
node_idx = 0;
|
||||
idx = 0;
|
||||
}
|
||||
bitmask = NETLBL_CATMAP_BIT;
|
||||
node_bit = 0;
|
||||
bit = 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_secattr_catmap_setbit - Set a bit in a LSM secattr catmap
|
||||
* @catmap: the category bitmap
|
||||
* netlbl_catmap_getlong - Export an unsigned long bitmap
|
||||
* @catmap: pointer to the category bitmap
|
||||
* @offset: pointer to the requested offset
|
||||
* @bitmap: the exported bitmap
|
||||
*
|
||||
* Description:
|
||||
* Export a bitmap with an offset greater than or equal to @offset and return
|
||||
* it in @bitmap. The @offset must be aligned to an unsigned long and will be
|
||||
* updated on return if different from what was requested; if the catmap is
|
||||
* empty at the requested offset and beyond, the @offset is set to (u32)-1.
|
||||
* Returns zero on sucess, negative values on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap,
|
||||
u32 *offset,
|
||||
unsigned long *bitmap)
|
||||
{
|
||||
struct netlbl_lsm_catmap *iter;
|
||||
u32 off = *offset;
|
||||
u32 idx;
|
||||
|
||||
/* only allow aligned offsets */
|
||||
if ((off & (BITS_PER_LONG - 1)) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (off < catmap->startbit) {
|
||||
off = catmap->startbit;
|
||||
*offset = off;
|
||||
}
|
||||
iter = _netlbl_catmap_getnode(&catmap, off, _CM_F_NONE, 0);
|
||||
if (iter == NULL) {
|
||||
*offset = (u32)-1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (off < iter->startbit) {
|
||||
off = iter->startbit;
|
||||
*offset = off;
|
||||
} else
|
||||
off -= iter->startbit;
|
||||
|
||||
idx = off / NETLBL_CATMAP_MAPSIZE;
|
||||
*bitmap = iter->bitmap[idx] >> (off % NETLBL_CATMAP_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_catmap_setbit - Set a bit in a LSM secattr catmap
|
||||
* @catmap: pointer to the category bitmap
|
||||
* @bit: the bit to set
|
||||
* @flags: memory allocation flags
|
||||
*
|
||||
@ -531,36 +640,27 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap,
|
||||
* negative values on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 bit,
|
||||
gfp_t flags)
|
||||
int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap,
|
||||
u32 bit,
|
||||
gfp_t flags)
|
||||
{
|
||||
struct netlbl_lsm_secattr_catmap *iter = catmap;
|
||||
u32 node_bit;
|
||||
u32 node_idx;
|
||||
struct netlbl_lsm_catmap *iter;
|
||||
u32 idx;
|
||||
|
||||
while (iter->next != NULL &&
|
||||
bit >= (iter->startbit + NETLBL_CATMAP_SIZE))
|
||||
iter = iter->next;
|
||||
if (bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) {
|
||||
iter->next = netlbl_secattr_catmap_alloc(flags);
|
||||
if (iter->next == NULL)
|
||||
return -ENOMEM;
|
||||
iter = iter->next;
|
||||
iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1);
|
||||
}
|
||||
iter = _netlbl_catmap_getnode(catmap, bit, _CM_F_ALLOC, flags);
|
||||
if (iter == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* gcc always rounds to zero when doing integer division */
|
||||
node_idx = (bit - iter->startbit) / NETLBL_CATMAP_MAPSIZE;
|
||||
node_bit = bit - iter->startbit - (NETLBL_CATMAP_MAPSIZE * node_idx);
|
||||
iter->bitmap[node_idx] |= NETLBL_CATMAP_BIT << node_bit;
|
||||
bit -= iter->startbit;
|
||||
idx = bit / NETLBL_CATMAP_MAPSIZE;
|
||||
iter->bitmap[idx] |= NETLBL_CATMAP_BIT << (bit % NETLBL_CATMAP_MAPSIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_secattr_catmap_setrng - Set a range of bits in a LSM secattr catmap
|
||||
* @catmap: the category bitmap
|
||||
* netlbl_catmap_setrng - Set a range of bits in a LSM secattr catmap
|
||||
* @catmap: pointer to the category bitmap
|
||||
* @start: the starting bit
|
||||
* @end: the last bit in the string
|
||||
* @flags: memory allocation flags
|
||||
@ -570,36 +670,63 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap,
|
||||
* on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap,
|
||||
u32 start,
|
||||
u32 end,
|
||||
gfp_t flags)
|
||||
int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap,
|
||||
u32 start,
|
||||
u32 end,
|
||||
gfp_t flags)
|
||||
{
|
||||
int ret_val = 0;
|
||||
struct netlbl_lsm_secattr_catmap *iter = catmap;
|
||||
u32 iter_max_spot;
|
||||
u32 spot;
|
||||
int rc = 0;
|
||||
u32 spot = start;
|
||||
|
||||
/* XXX - This could probably be made a bit faster by combining writes
|
||||
* to the catmap instead of setting a single bit each time, but for
|
||||
* right now skipping to the start of the range in the catmap should
|
||||
* be a nice improvement over calling the individual setbit function
|
||||
* repeatedly from a loop. */
|
||||
|
||||
while (iter->next != NULL &&
|
||||
start >= (iter->startbit + NETLBL_CATMAP_SIZE))
|
||||
iter = iter->next;
|
||||
iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE;
|
||||
|
||||
for (spot = start; spot <= end && ret_val == 0; spot++) {
|
||||
if (spot >= iter_max_spot && iter->next != NULL) {
|
||||
iter = iter->next;
|
||||
iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE;
|
||||
}
|
||||
ret_val = netlbl_secattr_catmap_setbit(iter, spot, flags);
|
||||
while (rc == 0 && spot <= end) {
|
||||
if (((spot & (BITS_PER_LONG - 1)) != 0) &&
|
||||
((end - spot) > BITS_PER_LONG)) {
|
||||
rc = netlbl_catmap_setlong(catmap,
|
||||
spot,
|
||||
(unsigned long)-1,
|
||||
flags);
|
||||
spot += BITS_PER_LONG;
|
||||
} else
|
||||
rc = netlbl_catmap_setbit(catmap, spot++, flags);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* netlbl_catmap_setlong - Import an unsigned long bitmap
|
||||
* @catmap: pointer to the category bitmap
|
||||
* @offset: offset to the start of the imported bitmap
|
||||
* @bitmap: the bitmap to import
|
||||
* @flags: memory allocation flags
|
||||
*
|
||||
* Description:
|
||||
* Import the bitmap specified in @bitmap into @catmap, using the offset
|
||||
* in @offset. The offset must be aligned to an unsigned long. Returns zero
|
||||
* on success, negative values on failure.
|
||||
*
|
||||
*/
|
||||
int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap,
|
||||
u32 offset,
|
||||
unsigned long bitmap,
|
||||
gfp_t flags)
|
||||
{
|
||||
struct netlbl_lsm_catmap *iter;
|
||||
u32 idx;
|
||||
|
||||
/* only allow aligned offsets */
|
||||
if ((offset & (BITS_PER_LONG - 1)) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
iter = _netlbl_catmap_getnode(catmap, offset, _CM_F_ALLOC, flags);
|
||||
if (iter == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
offset -= iter->startbit;
|
||||
idx = offset / NETLBL_CATMAP_MAPSIZE;
|
||||
iter->bitmap[idx] |= bitmap << (offset % NETLBL_CATMAP_MAPSIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -26,8 +26,10 @@
|
||||
#include "ar-internal.h"
|
||||
|
||||
static int rxrpc_vet_description_s(const char *);
|
||||
static int rxrpc_instantiate(struct key *, struct key_preparsed_payload *);
|
||||
static int rxrpc_instantiate_s(struct key *, struct key_preparsed_payload *);
|
||||
static int rxrpc_preparse(struct key_preparsed_payload *);
|
||||
static int rxrpc_preparse_s(struct key_preparsed_payload *);
|
||||
static void rxrpc_free_preparse(struct key_preparsed_payload *);
|
||||
static void rxrpc_free_preparse_s(struct key_preparsed_payload *);
|
||||
static void rxrpc_destroy(struct key *);
|
||||
static void rxrpc_destroy_s(struct key *);
|
||||
static void rxrpc_describe(const struct key *, struct seq_file *);
|
||||
@ -39,7 +41,9 @@ static long rxrpc_read(const struct key *, char __user *, size_t);
|
||||
*/
|
||||
struct key_type key_type_rxrpc = {
|
||||
.name = "rxrpc",
|
||||
.instantiate = rxrpc_instantiate,
|
||||
.preparse = rxrpc_preparse,
|
||||
.free_preparse = rxrpc_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.destroy = rxrpc_destroy,
|
||||
.describe = rxrpc_describe,
|
||||
@ -54,7 +58,9 @@ EXPORT_SYMBOL(key_type_rxrpc);
|
||||
struct key_type key_type_rxrpc_s = {
|
||||
.name = "rxrpc_s",
|
||||
.vet_description = rxrpc_vet_description_s,
|
||||
.instantiate = rxrpc_instantiate_s,
|
||||
.preparse = rxrpc_preparse_s,
|
||||
.free_preparse = rxrpc_free_preparse_s,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.destroy = rxrpc_destroy_s,
|
||||
.describe = rxrpc_describe,
|
||||
@ -81,13 +87,13 @@ static int rxrpc_vet_description_s(const char *desc)
|
||||
* parse an RxKAD type XDR format token
|
||||
* - the caller guarantees we have at least 4 words
|
||||
*/
|
||||
static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr,
|
||||
unsigned int toklen)
|
||||
static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep,
|
||||
size_t datalen,
|
||||
const __be32 *xdr, unsigned int toklen)
|
||||
{
|
||||
struct rxrpc_key_token *token, **pptoken;
|
||||
size_t plen;
|
||||
u32 tktlen;
|
||||
int ret;
|
||||
|
||||
_enter(",{%x,%x,%x,%x},%u",
|
||||
ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
|
||||
@ -103,9 +109,7 @@ static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr,
|
||||
return -EKEYREJECTED;
|
||||
|
||||
plen = sizeof(*token) + sizeof(*token->kad) + tktlen;
|
||||
ret = key_payload_reserve(key, key->datalen + plen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
prep->quotalen = datalen + plen;
|
||||
|
||||
plen -= sizeof(*token);
|
||||
token = kzalloc(sizeof(*token), GFP_KERNEL);
|
||||
@ -146,16 +150,16 @@ static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr,
|
||||
token->kad->ticket[6], token->kad->ticket[7]);
|
||||
|
||||
/* count the number of tokens attached */
|
||||
key->type_data.x[0]++;
|
||||
prep->type_data[0] = (void *)((unsigned long)prep->type_data[0] + 1);
|
||||
|
||||
/* attach the data */
|
||||
for (pptoken = (struct rxrpc_key_token **)&key->payload.data;
|
||||
for (pptoken = (struct rxrpc_key_token **)&prep->payload[0];
|
||||
*pptoken;
|
||||
pptoken = &(*pptoken)->next)
|
||||
continue;
|
||||
*pptoken = token;
|
||||
if (token->kad->expiry < key->expiry)
|
||||
key->expiry = token->kad->expiry;
|
||||
if (token->kad->expiry < prep->expiry)
|
||||
prep->expiry = token->kad->expiry;
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
@ -418,8 +422,9 @@ static int rxrpc_krb5_decode_ticket(u8 **_ticket, u16 *_tktlen,
|
||||
* parse an RxK5 type XDR format token
|
||||
* - the caller guarantees we have at least 4 words
|
||||
*/
|
||||
static int rxrpc_instantiate_xdr_rxk5(struct key *key, const __be32 *xdr,
|
||||
unsigned int toklen)
|
||||
static int rxrpc_preparse_xdr_rxk5(struct key_preparsed_payload *prep,
|
||||
size_t datalen,
|
||||
const __be32 *xdr, unsigned int toklen)
|
||||
{
|
||||
struct rxrpc_key_token *token, **pptoken;
|
||||
struct rxk5_key *rxk5;
|
||||
@ -432,9 +437,7 @@ static int rxrpc_instantiate_xdr_rxk5(struct key *key, const __be32 *xdr,
|
||||
|
||||
/* reserve some payload space for this subkey - the length of the token
|
||||
* is a reasonable approximation */
|
||||
ret = key_payload_reserve(key, key->datalen + toklen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
prep->quotalen = datalen + toklen;
|
||||
|
||||
token = kzalloc(sizeof(*token), GFP_KERNEL);
|
||||
if (!token)
|
||||
@ -520,14 +523,14 @@ static int rxrpc_instantiate_xdr_rxk5(struct key *key, const __be32 *xdr,
|
||||
if (toklen != 0)
|
||||
goto inval;
|
||||
|
||||
/* attach the payload to the key */
|
||||
for (pptoken = (struct rxrpc_key_token **)&key->payload.data;
|
||||
/* attach the payload */
|
||||
for (pptoken = (struct rxrpc_key_token **)&prep->payload[0];
|
||||
*pptoken;
|
||||
pptoken = &(*pptoken)->next)
|
||||
continue;
|
||||
*pptoken = token;
|
||||
if (token->kad->expiry < key->expiry)
|
||||
key->expiry = token->kad->expiry;
|
||||
if (token->kad->expiry < prep->expiry)
|
||||
prep->expiry = token->kad->expiry;
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
@ -545,16 +548,17 @@ error:
|
||||
* attempt to parse the data as the XDR format
|
||||
* - the caller guarantees we have more than 7 words
|
||||
*/
|
||||
static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datalen)
|
||||
static int rxrpc_preparse_xdr(struct key_preparsed_payload *prep)
|
||||
{
|
||||
const __be32 *xdr = data, *token;
|
||||
const __be32 *xdr = prep->data, *token;
|
||||
const char *cp;
|
||||
unsigned int len, tmp, loop, ntoken, toklen, sec_ix;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret;
|
||||
|
||||
_enter(",{%x,%x,%x,%x},%zu",
|
||||
ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
|
||||
datalen);
|
||||
prep->datalen);
|
||||
|
||||
if (datalen > AFSTOKEN_LENGTH_MAX)
|
||||
goto not_xdr;
|
||||
@ -635,13 +639,13 @@ static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datal
|
||||
|
||||
switch (sec_ix) {
|
||||
case RXRPC_SECURITY_RXKAD:
|
||||
ret = rxrpc_instantiate_xdr_rxkad(key, xdr, toklen);
|
||||
ret = rxrpc_preparse_xdr_rxkad(prep, datalen, xdr, toklen);
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
break;
|
||||
|
||||
case RXRPC_SECURITY_RXK5:
|
||||
ret = rxrpc_instantiate_xdr_rxk5(key, xdr, toklen);
|
||||
ret = rxrpc_preparse_xdr_rxk5(prep, datalen, xdr, toklen);
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
break;
|
||||
@ -665,8 +669,9 @@ error:
|
||||
}
|
||||
|
||||
/*
|
||||
* instantiate an rxrpc defined key
|
||||
* data should be of the form:
|
||||
* Preparse an rxrpc defined key.
|
||||
*
|
||||
* Data should be of the form:
|
||||
* OFFSET LEN CONTENT
|
||||
* 0 4 key interface version number
|
||||
* 4 2 security index (type)
|
||||
@ -678,7 +683,7 @@ error:
|
||||
*
|
||||
* if no data is provided, then a no-security key is made
|
||||
*/
|
||||
static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
static int rxrpc_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
const struct rxrpc_key_data_v1 *v1;
|
||||
struct rxrpc_key_token *token, **pp;
|
||||
@ -686,7 +691,7 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep
|
||||
u32 kver;
|
||||
int ret;
|
||||
|
||||
_enter("{%x},,%zu", key_serial(key), prep->datalen);
|
||||
_enter("%zu", prep->datalen);
|
||||
|
||||
/* handle a no-security key */
|
||||
if (!prep->data && prep->datalen == 0)
|
||||
@ -694,7 +699,7 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep
|
||||
|
||||
/* determine if the XDR payload format is being used */
|
||||
if (prep->datalen > 7 * 4) {
|
||||
ret = rxrpc_instantiate_xdr(key, prep->data, prep->datalen);
|
||||
ret = rxrpc_preparse_xdr(prep);
|
||||
if (ret != -EPROTO)
|
||||
return ret;
|
||||
}
|
||||
@ -743,9 +748,7 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep
|
||||
goto error;
|
||||
|
||||
plen = sizeof(*token->kad) + v1->ticket_length;
|
||||
ret = key_payload_reserve(key, plen + sizeof(*token));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
prep->quotalen = plen + sizeof(*token);
|
||||
|
||||
ret = -ENOMEM;
|
||||
token = kzalloc(sizeof(*token), GFP_KERNEL);
|
||||
@ -762,15 +765,16 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep
|
||||
memcpy(&token->kad->session_key, &v1->session_key, 8);
|
||||
memcpy(&token->kad->ticket, v1->ticket, v1->ticket_length);
|
||||
|
||||
/* attach the data */
|
||||
key->type_data.x[0]++;
|
||||
/* count the number of tokens attached */
|
||||
prep->type_data[0] = (void *)((unsigned long)prep->type_data[0] + 1);
|
||||
|
||||
pp = (struct rxrpc_key_token **)&key->payload.data;
|
||||
/* attach the data */
|
||||
pp = (struct rxrpc_key_token **)&prep->payload[0];
|
||||
while (*pp)
|
||||
pp = &(*pp)->next;
|
||||
*pp = token;
|
||||
if (token->kad->expiry < key->expiry)
|
||||
key->expiry = token->kad->expiry;
|
||||
if (token->kad->expiry < prep->expiry)
|
||||
prep->expiry = token->kad->expiry;
|
||||
token = NULL;
|
||||
ret = 0;
|
||||
|
||||
@ -781,44 +785,14 @@ error:
|
||||
}
|
||||
|
||||
/*
|
||||
* instantiate a server secret key
|
||||
* data should be a pointer to the 8-byte secret key
|
||||
* Free token list.
|
||||
*/
|
||||
static int rxrpc_instantiate_s(struct key *key,
|
||||
struct key_preparsed_payload *prep)
|
||||
static void rxrpc_free_token_list(struct rxrpc_key_token *token)
|
||||
{
|
||||
struct crypto_blkcipher *ci;
|
||||
struct rxrpc_key_token *next;
|
||||
|
||||
_enter("{%x},,%zu", key_serial(key), prep->datalen);
|
||||
|
||||
if (prep->datalen != 8)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&key->type_data, prep->data, 8);
|
||||
|
||||
ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(ci)) {
|
||||
_leave(" = %ld", PTR_ERR(ci));
|
||||
return PTR_ERR(ci);
|
||||
}
|
||||
|
||||
if (crypto_blkcipher_setkey(ci, prep->data, 8) < 0)
|
||||
BUG();
|
||||
|
||||
key->payload.data = ci;
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a rxrpc key
|
||||
*/
|
||||
static void rxrpc_destroy(struct key *key)
|
||||
{
|
||||
struct rxrpc_key_token *token;
|
||||
|
||||
while ((token = key->payload.data)) {
|
||||
key->payload.data = token->next;
|
||||
for (; token; token = next) {
|
||||
next = token->next;
|
||||
switch (token->security_index) {
|
||||
case RXRPC_SECURITY_RXKAD:
|
||||
kfree(token->kad);
|
||||
@ -837,6 +811,61 @@ static void rxrpc_destroy(struct key *key)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up preparse data.
|
||||
*/
|
||||
static void rxrpc_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
rxrpc_free_token_list(prep->payload[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Preparse a server secret key.
|
||||
*
|
||||
* The data should be the 8-byte secret key.
|
||||
*/
|
||||
static int rxrpc_preparse_s(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct crypto_blkcipher *ci;
|
||||
|
||||
_enter("%zu", prep->datalen);
|
||||
|
||||
if (prep->datalen != 8)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&prep->type_data, prep->data, 8);
|
||||
|
||||
ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(ci)) {
|
||||
_leave(" = %ld", PTR_ERR(ci));
|
||||
return PTR_ERR(ci);
|
||||
}
|
||||
|
||||
if (crypto_blkcipher_setkey(ci, prep->data, 8) < 0)
|
||||
BUG();
|
||||
|
||||
prep->payload[0] = ci;
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up preparse data.
|
||||
*/
|
||||
static void rxrpc_free_preparse_s(struct key_preparsed_payload *prep)
|
||||
{
|
||||
if (prep->payload[0])
|
||||
crypto_free_blkcipher(prep->payload[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a rxrpc key
|
||||
*/
|
||||
static void rxrpc_destroy(struct key *key)
|
||||
{
|
||||
rxrpc_free_token_list(key->payload.data);
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a rxrpc key
|
||||
*/
|
||||
|
@ -2,4 +2,3 @@ hostprogs-y := genheaders
|
||||
HOST_EXTRACFLAGS += -Isecurity/selinux/include
|
||||
|
||||
always := $(hostprogs-y)
|
||||
clean-files := $(hostprogs-y)
|
||||
|
@ -2,4 +2,4 @@ hostprogs-y := mdp
|
||||
HOST_EXTRACFLAGS += -Isecurity/selinux/include
|
||||
|
||||
always := $(hostprogs-y)
|
||||
clean-files := $(hostprogs-y) policy.* file_contexts
|
||||
clean-files := policy.* file_contexts
|
||||
|
@ -621,7 +621,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
|
||||
* There is no exception for unconfined as change_hat is not
|
||||
* available.
|
||||
*/
|
||||
if (current->no_new_privs)
|
||||
if (task_no_new_privs(current))
|
||||
return -EPERM;
|
||||
|
||||
/* released below */
|
||||
@ -776,7 +776,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
|
||||
* no_new_privs is set because this aways results in a reduction
|
||||
* of permissions.
|
||||
*/
|
||||
if (current->no_new_privs && !unconfined(profile)) {
|
||||
if (task_no_new_privs(current) && !unconfined(profile)) {
|
||||
put_cred(cred);
|
||||
return -EPERM;
|
||||
}
|
||||
|
@ -401,6 +401,11 @@ static int cap_kernel_create_files_as(struct cred *new, struct inode *inode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_kernel_fw_from_file(struct file *file, char *buf, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_kernel_module_request(char *kmod_name)
|
||||
{
|
||||
return 0;
|
||||
@ -1015,6 +1020,7 @@ void __init security_fixup_ops(struct security_operations *ops)
|
||||
set_to_cap_if_null(ops, cred_transfer);
|
||||
set_to_cap_if_null(ops, kernel_act_as);
|
||||
set_to_cap_if_null(ops, kernel_create_files_as);
|
||||
set_to_cap_if_null(ops, kernel_fw_from_file);
|
||||
set_to_cap_if_null(ops, kernel_module_request);
|
||||
set_to_cap_if_null(ops, kernel_module_from_file);
|
||||
set_to_cap_if_null(ops, task_fix_setuid);
|
||||
|
@ -421,6 +421,9 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
|
||||
cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable);
|
||||
}
|
||||
|
||||
cpu_caps->permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
|
||||
cpu_caps->inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -822,15 +825,20 @@ int cap_task_setnice(struct task_struct *p, int nice)
|
||||
* Implement PR_CAPBSET_DROP. Attempt to remove the specified capability from
|
||||
* the current task's bounding set. Returns 0 on success, -ve on error.
|
||||
*/
|
||||
static long cap_prctl_drop(struct cred *new, unsigned long cap)
|
||||
static int cap_prctl_drop(unsigned long cap)
|
||||
{
|
||||
struct cred *new;
|
||||
|
||||
if (!ns_capable(current_user_ns(), CAP_SETPCAP))
|
||||
return -EPERM;
|
||||
if (!cap_valid(cap))
|
||||
return -EINVAL;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
cap_lower(new->cap_bset, cap);
|
||||
return 0;
|
||||
return commit_creds(new);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -848,26 +856,17 @@ static long cap_prctl_drop(struct cred *new, unsigned long cap)
|
||||
int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
const struct cred *old = current_cred();
|
||||
struct cred *new;
|
||||
long error = 0;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (option) {
|
||||
case PR_CAPBSET_READ:
|
||||
error = -EINVAL;
|
||||
if (!cap_valid(arg2))
|
||||
goto error;
|
||||
error = !!cap_raised(new->cap_bset, arg2);
|
||||
goto no_change;
|
||||
return -EINVAL;
|
||||
return !!cap_raised(old->cap_bset, arg2);
|
||||
|
||||
case PR_CAPBSET_DROP:
|
||||
error = cap_prctl_drop(new, arg2);
|
||||
if (error < 0)
|
||||
goto error;
|
||||
goto changed;
|
||||
return cap_prctl_drop(arg2);
|
||||
|
||||
/*
|
||||
* The next four prctl's remain to assist with transitioning a
|
||||
@ -889,10 +888,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
* capability-based-privilege environment.
|
||||
*/
|
||||
case PR_SET_SECUREBITS:
|
||||
error = -EPERM;
|
||||
if ((((new->securebits & SECURE_ALL_LOCKS) >> 1)
|
||||
& (new->securebits ^ arg2)) /*[1]*/
|
||||
|| ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
|
||||
if ((((old->securebits & SECURE_ALL_LOCKS) >> 1)
|
||||
& (old->securebits ^ arg2)) /*[1]*/
|
||||
|| ((old->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
|
||||
|| (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
|
||||
|| (cap_capable(current_cred(),
|
||||
current_cred()->user_ns, CAP_SETPCAP,
|
||||
@ -906,46 +904,39 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
*/
|
||||
)
|
||||
/* cannot change a locked bit */
|
||||
goto error;
|
||||
return -EPERM;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
new->securebits = arg2;
|
||||
goto changed;
|
||||
return commit_creds(new);
|
||||
|
||||
case PR_GET_SECUREBITS:
|
||||
error = new->securebits;
|
||||
goto no_change;
|
||||
return old->securebits;
|
||||
|
||||
case PR_GET_KEEPCAPS:
|
||||
if (issecure(SECURE_KEEP_CAPS))
|
||||
error = 1;
|
||||
goto no_change;
|
||||
return !!issecure(SECURE_KEEP_CAPS);
|
||||
|
||||
case PR_SET_KEEPCAPS:
|
||||
error = -EINVAL;
|
||||
if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */
|
||||
goto error;
|
||||
error = -EPERM;
|
||||
return -EINVAL;
|
||||
if (issecure(SECURE_KEEP_CAPS_LOCKED))
|
||||
goto error;
|
||||
return -EPERM;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
if (arg2)
|
||||
new->securebits |= issecure_mask(SECURE_KEEP_CAPS);
|
||||
else
|
||||
new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
|
||||
goto changed;
|
||||
return commit_creds(new);
|
||||
|
||||
default:
|
||||
/* No functionality available - continue with default */
|
||||
error = -ENOSYS;
|
||||
goto error;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/* Functionality provided */
|
||||
changed:
|
||||
return commit_creds(new);
|
||||
|
||||
no_change:
|
||||
error:
|
||||
abort_creds(new);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,9 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/digsig.h>
|
||||
|
||||
@ -24,7 +26,11 @@ static struct key *keyring[INTEGRITY_KEYRING_MAX];
|
||||
static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {
|
||||
"_evm",
|
||||
"_module",
|
||||
#ifndef CONFIG_IMA_TRUSTED_KEYRING
|
||||
"_ima",
|
||||
#else
|
||||
".ima",
|
||||
#endif
|
||||
};
|
||||
|
||||
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
@ -56,3 +62,25 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int integrity_init_keyring(const unsigned int id)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
int err = 0;
|
||||
|
||||
keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
|
||||
KGIDT_INIT(0), cred,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_WRITE | KEY_USR_SEARCH),
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
if (!IS_ERR(keyring[id]))
|
||||
set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags);
|
||||
else {
|
||||
err = PTR_ERR(keyring[id]);
|
||||
pr_info("Can't allocate %s keyring (%d)\n",
|
||||
keyring_name[id], err);
|
||||
keyring[id] = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
@ -123,3 +123,13 @@ config IMA_APPRAISE
|
||||
For more information on integrity appraisal refer to:
|
||||
<http://linux-ima.sourceforge.net>
|
||||
If unsure, say N.
|
||||
|
||||
config IMA_TRUSTED_KEYRING
|
||||
bool "Require all keys on the .ima keyring be signed"
|
||||
depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
|
||||
depends on INTEGRITY_ASYMMETRIC_KEYS
|
||||
select KEYS_DEBUG_PROC_KEYS
|
||||
default y
|
||||
help
|
||||
This option requires that all keys added to the .ima
|
||||
keyring be signed by a key on the system trusted keyring.
|
||||
|
@ -158,7 +158,7 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
|
||||
struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
|
||||
|
||||
/* IMA policy related functions */
|
||||
enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, POST_SETATTR };
|
||||
enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, FIRMWARE_CHECK, POST_SETATTR };
|
||||
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
|
||||
int flags);
|
||||
@ -171,6 +171,7 @@ void ima_delete_rules(void);
|
||||
#define IMA_APPRAISE_ENFORCE 0x01
|
||||
#define IMA_APPRAISE_FIX 0x02
|
||||
#define IMA_APPRAISE_MODULES 0x04
|
||||
#define IMA_APPRAISE_FIRMWARE 0x08
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
|
||||
@ -249,4 +250,16 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_IMA_LSM_RULES */
|
||||
|
||||
#ifdef CONFIG_IMA_TRUSTED_KEYRING
|
||||
static inline int ima_init_keyring(const unsigned int id)
|
||||
{
|
||||
return integrity_init_keyring(id);
|
||||
}
|
||||
#else
|
||||
static inline int ima_init_keyring(const unsigned int id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IMA_TRUSTED_KEYRING */
|
||||
#endif
|
||||
|
@ -75,6 +75,8 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
|
||||
return iint->ima_bprm_status;
|
||||
case MODULE_CHECK:
|
||||
return iint->ima_module_status;
|
||||
case FIRMWARE_CHECK:
|
||||
return iint->ima_firmware_status;
|
||||
case FILE_CHECK:
|
||||
default:
|
||||
return iint->ima_file_status;
|
||||
@ -94,6 +96,9 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,
|
||||
case MODULE_CHECK:
|
||||
iint->ima_module_status = status;
|
||||
break;
|
||||
case FIRMWARE_CHECK:
|
||||
iint->ima_firmware_status = status;
|
||||
break;
|
||||
case FILE_CHECK:
|
||||
default:
|
||||
iint->ima_file_status = status;
|
||||
@ -113,6 +118,9 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
|
||||
case MODULE_CHECK:
|
||||
iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED);
|
||||
break;
|
||||
case FIRMWARE_CHECK:
|
||||
iint->flags |= (IMA_FIRMWARE_APPRAISED | IMA_APPRAISED);
|
||||
break;
|
||||
case FILE_CHECK:
|
||||
default:
|
||||
iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
|
||||
@ -214,7 +222,7 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
|
||||
hash_start = 1;
|
||||
case IMA_XATTR_DIGEST:
|
||||
if (iint->flags & IMA_DIGSIG_REQUIRED) {
|
||||
cause = "IMA signature required";
|
||||
cause = "IMA-signature-required";
|
||||
status = INTEGRITY_FAIL;
|
||||
break;
|
||||
}
|
||||
|
@ -16,6 +16,8 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/scatterlist.h>
|
||||
@ -25,7 +27,45 @@
|
||||
#include <crypto/hash_info.h>
|
||||
#include "ima.h"
|
||||
|
||||
struct ahash_completion {
|
||||
struct completion completion;
|
||||
int err;
|
||||
};
|
||||
|
||||
/* minimum file size for ahash use */
|
||||
static unsigned long ima_ahash_minsize;
|
||||
module_param_named(ahash_minsize, ima_ahash_minsize, ulong, 0644);
|
||||
MODULE_PARM_DESC(ahash_minsize, "Minimum file size for ahash use");
|
||||
|
||||
/* default is 0 - 1 page. */
|
||||
static int ima_maxorder;
|
||||
static unsigned int ima_bufsize = PAGE_SIZE;
|
||||
|
||||
static int param_set_bufsize(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
unsigned long long size;
|
||||
int order;
|
||||
|
||||
size = memparse(val, NULL);
|
||||
order = get_order(size);
|
||||
if (order >= MAX_ORDER)
|
||||
return -EINVAL;
|
||||
ima_maxorder = order;
|
||||
ima_bufsize = PAGE_SIZE << order;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kernel_param_ops param_ops_bufsize = {
|
||||
.set = param_set_bufsize,
|
||||
.get = param_get_uint,
|
||||
};
|
||||
#define param_check_bufsize(name, p) __param_check(name, p, unsigned int)
|
||||
|
||||
module_param_named(ahash_bufsize, ima_bufsize, bufsize, 0644);
|
||||
MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size");
|
||||
|
||||
static struct crypto_shash *ima_shash_tfm;
|
||||
static struct crypto_ahash *ima_ahash_tfm;
|
||||
|
||||
/**
|
||||
* ima_kernel_read - read file content
|
||||
@ -93,9 +133,246 @@ static void ima_free_tfm(struct crypto_shash *tfm)
|
||||
crypto_free_shash(tfm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the MD5/SHA1 file digest
|
||||
/**
|
||||
* ima_alloc_pages() - Allocate contiguous pages.
|
||||
* @max_size: Maximum amount of memory to allocate.
|
||||
* @allocated_size: Returned size of actual allocation.
|
||||
* @last_warn: Should the min_size allocation warn or not.
|
||||
*
|
||||
* Tries to do opportunistic allocation for memory first trying to allocate
|
||||
* max_size amount of memory and then splitting that until zero order is
|
||||
* reached. Allocation is tried without generating allocation warnings unless
|
||||
* last_warn is set. Last_warn set affects only last allocation of zero order.
|
||||
*
|
||||
* By default, ima_maxorder is 0 and it is equivalent to kmalloc(GFP_KERNEL)
|
||||
*
|
||||
* Return pointer to allocated memory, or NULL on failure.
|
||||
*/
|
||||
static void *ima_alloc_pages(loff_t max_size, size_t *allocated_size,
|
||||
int last_warn)
|
||||
{
|
||||
void *ptr;
|
||||
int order = ima_maxorder;
|
||||
gfp_t gfp_mask = __GFP_WAIT | __GFP_NOWARN | __GFP_NORETRY;
|
||||
|
||||
if (order)
|
||||
order = min(get_order(max_size), order);
|
||||
|
||||
for (; order; order--) {
|
||||
ptr = (void *)__get_free_pages(gfp_mask, order);
|
||||
if (ptr) {
|
||||
*allocated_size = PAGE_SIZE << order;
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* order is zero - one page */
|
||||
|
||||
gfp_mask = GFP_KERNEL;
|
||||
|
||||
if (!last_warn)
|
||||
gfp_mask |= __GFP_NOWARN;
|
||||
|
||||
ptr = (void *)__get_free_pages(gfp_mask, 0);
|
||||
if (ptr) {
|
||||
*allocated_size = PAGE_SIZE;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
*allocated_size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_free_pages() - Free pages allocated by ima_alloc_pages().
|
||||
* @ptr: Pointer to allocated pages.
|
||||
* @size: Size of allocated buffer.
|
||||
*/
|
||||
static void ima_free_pages(void *ptr, size_t size)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
free_pages((unsigned long)ptr, get_order(size));
|
||||
}
|
||||
|
||||
static struct crypto_ahash *ima_alloc_atfm(enum hash_algo algo)
|
||||
{
|
||||
struct crypto_ahash *tfm = ima_ahash_tfm;
|
||||
int rc;
|
||||
|
||||
if ((algo != ima_hash_algo && algo < HASH_ALGO__LAST) || !tfm) {
|
||||
tfm = crypto_alloc_ahash(hash_algo_name[algo], 0, 0);
|
||||
if (!IS_ERR(tfm)) {
|
||||
if (algo == ima_hash_algo)
|
||||
ima_ahash_tfm = tfm;
|
||||
} else {
|
||||
rc = PTR_ERR(tfm);
|
||||
pr_err("Can not allocate %s (reason: %d)\n",
|
||||
hash_algo_name[algo], rc);
|
||||
}
|
||||
}
|
||||
return tfm;
|
||||
}
|
||||
|
||||
static void ima_free_atfm(struct crypto_ahash *tfm)
|
||||
{
|
||||
if (tfm != ima_ahash_tfm)
|
||||
crypto_free_ahash(tfm);
|
||||
}
|
||||
|
||||
static void ahash_complete(struct crypto_async_request *req, int err)
|
||||
{
|
||||
struct ahash_completion *res = req->data;
|
||||
|
||||
if (err == -EINPROGRESS)
|
||||
return;
|
||||
res->err = err;
|
||||
complete(&res->completion);
|
||||
}
|
||||
|
||||
static int ahash_wait(int err, struct ahash_completion *res)
|
||||
{
|
||||
switch (err) {
|
||||
case 0:
|
||||
break;
|
||||
case -EINPROGRESS:
|
||||
case -EBUSY:
|
||||
wait_for_completion(&res->completion);
|
||||
reinit_completion(&res->completion);
|
||||
err = res->err;
|
||||
/* fall through */
|
||||
default:
|
||||
pr_crit_ratelimited("ahash calculation failed: err: %d\n", err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ima_calc_file_hash_atfm(struct file *file,
|
||||
struct ima_digest_data *hash,
|
||||
struct crypto_ahash *tfm)
|
||||
{
|
||||
loff_t i_size, offset;
|
||||
char *rbuf[2] = { NULL, };
|
||||
int rc, read = 0, rbuf_len, active = 0, ahash_rc = 0;
|
||||
struct ahash_request *req;
|
||||
struct scatterlist sg[1];
|
||||
struct ahash_completion res;
|
||||
size_t rbuf_size[2];
|
||||
|
||||
hash->length = crypto_ahash_digestsize(tfm);
|
||||
|
||||
req = ahash_request_alloc(tfm, GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
init_completion(&res.completion);
|
||||
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
|
||||
CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
ahash_complete, &res);
|
||||
|
||||
rc = ahash_wait(crypto_ahash_init(req), &res);
|
||||
if (rc)
|
||||
goto out1;
|
||||
|
||||
i_size = i_size_read(file_inode(file));
|
||||
|
||||
if (i_size == 0)
|
||||
goto out2;
|
||||
|
||||
/*
|
||||
* Try to allocate maximum size of memory.
|
||||
* Fail if even a single page cannot be allocated.
|
||||
*/
|
||||
rbuf[0] = ima_alloc_pages(i_size, &rbuf_size[0], 1);
|
||||
if (!rbuf[0]) {
|
||||
rc = -ENOMEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/* Only allocate one buffer if that is enough. */
|
||||
if (i_size > rbuf_size[0]) {
|
||||
/*
|
||||
* Try to allocate secondary buffer. If that fails fallback to
|
||||
* using single buffering. Use previous memory allocation size
|
||||
* as baseline for possible allocation size.
|
||||
*/
|
||||
rbuf[1] = ima_alloc_pages(i_size - rbuf_size[0],
|
||||
&rbuf_size[1], 0);
|
||||
}
|
||||
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
file->f_mode |= FMODE_READ;
|
||||
read = 1;
|
||||
}
|
||||
|
||||
for (offset = 0; offset < i_size; offset += rbuf_len) {
|
||||
if (!rbuf[1] && offset) {
|
||||
/* Not using two buffers, and it is not the first
|
||||
* read/request, wait for the completion of the
|
||||
* previous ahash_update() request.
|
||||
*/
|
||||
rc = ahash_wait(ahash_rc, &res);
|
||||
if (rc)
|
||||
goto out3;
|
||||
}
|
||||
/* read buffer */
|
||||
rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]);
|
||||
rc = ima_kernel_read(file, offset, rbuf[active], rbuf_len);
|
||||
if (rc != rbuf_len)
|
||||
goto out3;
|
||||
|
||||
if (rbuf[1] && offset) {
|
||||
/* Using two buffers, and it is not the first
|
||||
* read/request, wait for the completion of the
|
||||
* previous ahash_update() request.
|
||||
*/
|
||||
rc = ahash_wait(ahash_rc, &res);
|
||||
if (rc)
|
||||
goto out3;
|
||||
}
|
||||
|
||||
sg_init_one(&sg[0], rbuf[active], rbuf_len);
|
||||
ahash_request_set_crypt(req, sg, NULL, rbuf_len);
|
||||
|
||||
ahash_rc = crypto_ahash_update(req);
|
||||
|
||||
if (rbuf[1])
|
||||
active = !active; /* swap buffers, if we use two */
|
||||
}
|
||||
/* wait for the last update request to complete */
|
||||
rc = ahash_wait(ahash_rc, &res);
|
||||
out3:
|
||||
if (read)
|
||||
file->f_mode &= ~FMODE_READ;
|
||||
ima_free_pages(rbuf[0], rbuf_size[0]);
|
||||
ima_free_pages(rbuf[1], rbuf_size[1]);
|
||||
out2:
|
||||
if (!rc) {
|
||||
ahash_request_set_crypt(req, NULL, hash->digest, 0);
|
||||
rc = ahash_wait(crypto_ahash_final(req), &res);
|
||||
}
|
||||
out1:
|
||||
ahash_request_free(req);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ima_calc_file_ahash(struct file *file, struct ima_digest_data *hash)
|
||||
{
|
||||
struct crypto_ahash *tfm;
|
||||
int rc;
|
||||
|
||||
tfm = ima_alloc_atfm(hash->algo);
|
||||
if (IS_ERR(tfm))
|
||||
return PTR_ERR(tfm);
|
||||
|
||||
rc = ima_calc_file_hash_atfm(file, hash, tfm);
|
||||
|
||||
ima_free_atfm(tfm);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ima_calc_file_hash_tfm(struct file *file,
|
||||
struct ima_digest_data *hash,
|
||||
struct crypto_shash *tfm)
|
||||
@ -156,7 +433,7 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
|
||||
static int ima_calc_file_shash(struct file *file, struct ima_digest_data *hash)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
int rc;
|
||||
@ -172,6 +449,35 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_calc_file_hash - calculate file hash
|
||||
*
|
||||
* Asynchronous hash (ahash) allows using HW acceleration for calculating
|
||||
* a hash. ahash performance varies for different data sizes on different
|
||||
* crypto accelerators. shash performance might be better for smaller files.
|
||||
* The 'ima.ahash_minsize' module parameter allows specifying the best
|
||||
* minimum file size for using ahash on the system.
|
||||
*
|
||||
* If the ima.ahash_minsize parameter is not specified, this function uses
|
||||
* shash for the hash calculation. If ahash fails, it falls back to using
|
||||
* shash.
|
||||
*/
|
||||
int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
|
||||
{
|
||||
loff_t i_size;
|
||||
int rc;
|
||||
|
||||
i_size = i_size_read(file_inode(file));
|
||||
|
||||
if (ima_ahash_minsize && i_size >= ima_ahash_minsize) {
|
||||
rc = ima_calc_file_ahash(file, hash);
|
||||
if (!rc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ima_calc_file_shash(file, hash);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the hash of template data
|
||||
*/
|
||||
|
@ -88,8 +88,6 @@ static void ima_rdwr_violation_check(struct file *file)
|
||||
if (!S_ISREG(inode->i_mode) || !ima_initialized)
|
||||
return;
|
||||
|
||||
mutex_lock(&inode->i_mutex); /* file metadata: permissions, xattr */
|
||||
|
||||
if (mode & FMODE_WRITE) {
|
||||
if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) {
|
||||
struct integrity_iint_cache *iint;
|
||||
@ -104,8 +102,6 @@ static void ima_rdwr_violation_check(struct file *file)
|
||||
send_writers = true;
|
||||
}
|
||||
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
if (!send_tomtou && !send_writers)
|
||||
return;
|
||||
|
||||
@ -163,7 +159,7 @@ static int process_measurement(struct file *file, const char *filename,
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct integrity_iint_cache *iint;
|
||||
struct ima_template_desc *template_desc = ima_template_desc_current();
|
||||
struct ima_template_desc *template_desc;
|
||||
char *pathbuf = NULL;
|
||||
const char *pathname = NULL;
|
||||
int rc = -ENOMEM, action, must_appraise, _func;
|
||||
@ -207,6 +203,7 @@ static int process_measurement(struct file *file, const char *filename,
|
||||
goto out_digsig;
|
||||
}
|
||||
|
||||
template_desc = ima_template_desc_current();
|
||||
if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) {
|
||||
if (action & IMA_APPRAISE_SUBMASK)
|
||||
xattr_ptr = &xattr_value;
|
||||
@ -322,14 +319,31 @@ int ima_module_check(struct file *file)
|
||||
return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK);
|
||||
}
|
||||
|
||||
int ima_fw_from_file(struct file *file, char *buf, size_t size)
|
||||
{
|
||||
if (!file) {
|
||||
if ((ima_appraise & IMA_APPRAISE_FIRMWARE) &&
|
||||
(ima_appraise & IMA_APPRAISE_ENFORCE))
|
||||
return -EACCES; /* INTEGRITY_UNKNOWN */
|
||||
return 0;
|
||||
}
|
||||
return process_measurement(file, NULL, MAY_EXEC, FIRMWARE_CHECK);
|
||||
}
|
||||
|
||||
static int __init init_ima(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
hash_setup(CONFIG_IMA_DEFAULT_HASH);
|
||||
error = ima_init();
|
||||
if (!error)
|
||||
ima_initialized = 1;
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
error = ima_init_keyring(INTEGRITY_KEYRING_IMA);
|
||||
if (error)
|
||||
goto out;
|
||||
ima_initialized = 1;
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -84,6 +84,7 @@ static struct ima_rule_entry default_rules[] = {
|
||||
{.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, .uid = GLOBAL_ROOT_UID,
|
||||
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
|
||||
{.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC},
|
||||
{.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC},
|
||||
};
|
||||
|
||||
static struct ima_rule_entry default_appraise_rules[] = {
|
||||
@ -241,6 +242,8 @@ static int get_subaction(struct ima_rule_entry *rule, int func)
|
||||
return IMA_BPRM_APPRAISE;
|
||||
case MODULE_CHECK:
|
||||
return IMA_MODULE_APPRAISE;
|
||||
case FIRMWARE_CHECK:
|
||||
return IMA_FIRMWARE_APPRAISE;
|
||||
case FILE_CHECK:
|
||||
default:
|
||||
return IMA_FILE_APPRAISE;
|
||||
@ -332,7 +335,7 @@ void __init ima_init_policy(void)
|
||||
void ima_update_policy(void)
|
||||
{
|
||||
static const char op[] = "policy_update";
|
||||
const char *cause = "already exists";
|
||||
const char *cause = "already-exists";
|
||||
int result = 1;
|
||||
int audit_info = 0;
|
||||
|
||||
@ -486,6 +489,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
entry->func = FILE_CHECK;
|
||||
else if (strcmp(args[0].from, "MODULE_CHECK") == 0)
|
||||
entry->func = MODULE_CHECK;
|
||||
else if (strcmp(args[0].from, "FIRMWARE_CHECK") == 0)
|
||||
entry->func = FIRMWARE_CHECK;
|
||||
else if ((strcmp(args[0].from, "FILE_MMAP") == 0)
|
||||
|| (strcmp(args[0].from, "MMAP_CHECK") == 0))
|
||||
entry->func = MMAP_CHECK;
|
||||
@ -636,6 +641,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
result = -EINVAL;
|
||||
else if (entry->func == MODULE_CHECK)
|
||||
ima_appraise |= IMA_APPRAISE_MODULES;
|
||||
else if (entry->func == FIRMWARE_CHECK)
|
||||
ima_appraise |= IMA_APPRAISE_FIRMWARE;
|
||||
audit_log_format(ab, "res=%d", !result);
|
||||
audit_log_end(ab);
|
||||
return result;
|
||||
@ -659,7 +666,7 @@ ssize_t ima_parse_add_rule(char *rule)
|
||||
/* Prevent installed policy from changing */
|
||||
if (ima_rules != &ima_default_rules) {
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
|
||||
NULL, op, "already exists",
|
||||
NULL, op, "already-exists",
|
||||
-EACCES, audit_info);
|
||||
return -EACCES;
|
||||
}
|
||||
@ -685,7 +692,7 @@ ssize_t ima_parse_add_rule(char *rule)
|
||||
if (result) {
|
||||
kfree(entry);
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
|
||||
NULL, op, "invalid policy", result,
|
||||
NULL, op, "invalid-policy", result,
|
||||
audit_info);
|
||||
return result;
|
||||
}
|
||||
|
@ -46,10 +46,14 @@
|
||||
#define IMA_BPRM_APPRAISED 0x00002000
|
||||
#define IMA_MODULE_APPRAISE 0x00004000
|
||||
#define IMA_MODULE_APPRAISED 0x00008000
|
||||
#define IMA_FIRMWARE_APPRAISE 0x00010000
|
||||
#define IMA_FIRMWARE_APPRAISED 0x00020000
|
||||
#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
|
||||
IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE)
|
||||
IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE | \
|
||||
IMA_FIRMWARE_APPRAISE)
|
||||
#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
|
||||
IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED)
|
||||
IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED | \
|
||||
IMA_FIRMWARE_APPRAISED)
|
||||
|
||||
enum evm_ima_xattr_type {
|
||||
IMA_XATTR_DIGEST = 0x01,
|
||||
@ -104,6 +108,7 @@ struct integrity_iint_cache {
|
||||
enum integrity_status ima_mmap_status:4;
|
||||
enum integrity_status ima_bprm_status:4;
|
||||
enum integrity_status ima_module_status:4;
|
||||
enum integrity_status ima_firmware_status:4;
|
||||
enum integrity_status evm_status:4;
|
||||
struct ima_digest_data *ima_hash;
|
||||
};
|
||||
@ -124,6 +129,7 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
|
||||
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
const char *digest, int digestlen);
|
||||
|
||||
int integrity_init_keyring(const unsigned int id);
|
||||
#else
|
||||
|
||||
static inline int integrity_digsig_verify(const unsigned int id,
|
||||
@ -133,6 +139,10 @@ static inline int integrity_digsig_verify(const unsigned int id,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int integrity_init_keyring(const unsigned int id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_INTEGRITY_SIGNATURE */
|
||||
|
||||
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
|
||||
|
@ -34,7 +34,9 @@ MODULE_LICENSE("GPL");
|
||||
struct key_type key_type_big_key = {
|
||||
.name = "big_key",
|
||||
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.instantiate = big_key_instantiate,
|
||||
.preparse = big_key_preparse,
|
||||
.free_preparse = big_key_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.revoke = big_key_revoke,
|
||||
.destroy = big_key_destroy,
|
||||
@ -43,11 +45,11 @@ struct key_type key_type_big_key = {
|
||||
};
|
||||
|
||||
/*
|
||||
* Instantiate a big key
|
||||
* Preparse a big key
|
||||
*/
|
||||
int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
int big_key_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct path *path = (struct path *)&key->payload.data2;
|
||||
struct path *path = (struct path *)&prep->payload;
|
||||
struct file *file;
|
||||
ssize_t written;
|
||||
size_t datalen = prep->datalen;
|
||||
@ -58,11 +60,9 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
goto error;
|
||||
|
||||
/* Set an arbitrary quota */
|
||||
ret = key_payload_reserve(key, 16);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
prep->quotalen = 16;
|
||||
|
||||
key->type_data.x[1] = datalen;
|
||||
prep->type_data[1] = (void *)(unsigned long)datalen;
|
||||
|
||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
/* Create a shmem file to store the data in. This will permit the data
|
||||
@ -73,7 +73,7 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
file = shmem_kernel_file_setup("", datalen, 0);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto err_quota;
|
||||
goto error;
|
||||
}
|
||||
|
||||
written = kernel_write(file, prep->data, prep->datalen, 0);
|
||||
@ -93,23 +93,32 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
} else {
|
||||
/* Just store the data in a buffer */
|
||||
void *data = kmalloc(datalen, GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto err_quota;
|
||||
}
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
key->payload.data = memcpy(data, prep->data, prep->datalen);
|
||||
prep->payload[0] = memcpy(data, prep->data, prep->datalen);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_fput:
|
||||
fput(file);
|
||||
err_quota:
|
||||
key_payload_reserve(key, 0);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear preparsement.
|
||||
*/
|
||||
void big_key_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&prep->payload;
|
||||
path_put(path);
|
||||
} else {
|
||||
kfree(prep->payload[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the links from a revoked keyring
|
||||
* - called with the key sem write-locked
|
||||
|
@ -437,6 +437,11 @@ static int __key_instantiate_and_link(struct key *key,
|
||||
/* disable the authorisation key */
|
||||
if (authkey)
|
||||
key_revoke(authkey);
|
||||
|
||||
if (prep->expiry != TIME_T_MAX) {
|
||||
key->expiry = prep->expiry;
|
||||
key_schedule_gc(prep->expiry + key_gc_delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -479,6 +484,7 @@ int key_instantiate_and_link(struct key *key,
|
||||
prep.data = data;
|
||||
prep.datalen = datalen;
|
||||
prep.quotalen = key->type->def_datalen;
|
||||
prep.expiry = TIME_T_MAX;
|
||||
if (key->type->preparse) {
|
||||
ret = key->type->preparse(&prep);
|
||||
if (ret < 0)
|
||||
@ -488,7 +494,7 @@ int key_instantiate_and_link(struct key *key,
|
||||
if (keyring) {
|
||||
ret = __key_link_begin(keyring, &key->index_key, &edit);
|
||||
if (ret < 0)
|
||||
goto error_free_preparse;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
|
||||
@ -496,10 +502,9 @@ int key_instantiate_and_link(struct key *key,
|
||||
if (keyring)
|
||||
__key_link_end(keyring, &key->index_key, edit);
|
||||
|
||||
error_free_preparse:
|
||||
error:
|
||||
if (key->type->preparse)
|
||||
key->type->free_preparse(&prep);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -811,11 +816,12 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
prep.datalen = plen;
|
||||
prep.quotalen = index_key.type->def_datalen;
|
||||
prep.trusted = flags & KEY_ALLOC_TRUSTED;
|
||||
prep.expiry = TIME_T_MAX;
|
||||
if (index_key.type->preparse) {
|
||||
ret = index_key.type->preparse(&prep);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_put_type;
|
||||
goto error_free_prep;
|
||||
}
|
||||
if (!index_key.description)
|
||||
index_key.description = prep.description;
|
||||
@ -941,6 +947,7 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
|
||||
prep.data = payload;
|
||||
prep.datalen = plen;
|
||||
prep.quotalen = key->type->def_datalen;
|
||||
prep.expiry = TIME_T_MAX;
|
||||
if (key->type->preparse) {
|
||||
ret = key->type->preparse(&prep);
|
||||
if (ret < 0)
|
||||
@ -956,9 +963,9 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
|
||||
|
||||
up_write(&key->sem);
|
||||
|
||||
error:
|
||||
if (key->type->preparse)
|
||||
key->type->free_preparse(&prep);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(key_update);
|
||||
@ -1023,6 +1030,38 @@ void key_invalidate(struct key *key)
|
||||
}
|
||||
EXPORT_SYMBOL(key_invalidate);
|
||||
|
||||
/**
|
||||
* generic_key_instantiate - Simple instantiation of a key from preparsed data
|
||||
* @key: The key to be instantiated
|
||||
* @prep: The preparsed data to load.
|
||||
*
|
||||
* Instantiate a key from preparsed data. We assume we can just copy the data
|
||||
* in directly and clear the old pointers.
|
||||
*
|
||||
* This can be pointed to directly by the key type instantiate op pointer.
|
||||
*/
|
||||
int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
ret = key_payload_reserve(key, prep->quotalen);
|
||||
if (ret == 0) {
|
||||
key->type_data.p[0] = prep->type_data[0];
|
||||
key->type_data.p[1] = prep->type_data[1];
|
||||
rcu_assign_keypointer(key, prep->payload[0]);
|
||||
key->payload.data2[1] = prep->payload[1];
|
||||
prep->type_data[0] = NULL;
|
||||
prep->type_data[1] = NULL;
|
||||
prep->payload[0] = NULL;
|
||||
prep->payload[1] = NULL;
|
||||
}
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(generic_key_instantiate);
|
||||
|
||||
/**
|
||||
* register_key_type - Register a type of key.
|
||||
* @ktype: The new key type.
|
||||
|
@ -37,8 +37,6 @@ static int key_get_type_from_user(char *type,
|
||||
return ret;
|
||||
if (ret == 0 || ret >= len)
|
||||
return -EINVAL;
|
||||
if (type[0] == '.')
|
||||
return -EPERM;
|
||||
type[len - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
@ -86,6 +84,10 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
|
||||
if (!*description) {
|
||||
kfree(description);
|
||||
description = NULL;
|
||||
} else if ((description[0] == '.') &&
|
||||
(strncmp(type, "keyring", 7) == 0)) {
|
||||
ret = -EPERM;
|
||||
goto error2;
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,12 +406,25 @@ long keyctl_invalidate_key(key_serial_t id)
|
||||
key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = PTR_ERR(key_ref);
|
||||
|
||||
/* Root is permitted to invalidate certain special keys */
|
||||
if (capable(CAP_SYS_ADMIN)) {
|
||||
key_ref = lookup_user_key(id, 0, 0);
|
||||
if (IS_ERR(key_ref))
|
||||
goto error;
|
||||
if (test_bit(KEY_FLAG_ROOT_CAN_INVAL,
|
||||
&key_ref_to_ptr(key_ref)->flags))
|
||||
goto invalidate;
|
||||
goto error_put;
|
||||
}
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
invalidate:
|
||||
key_invalidate(key_ref_to_ptr(key_ref));
|
||||
ret = 0;
|
||||
|
||||
error_put:
|
||||
key_ref_put(key_ref);
|
||||
error:
|
||||
kleave(" = %ld", ret);
|
||||
|
@ -73,6 +73,8 @@ static inline unsigned keyring_hash(const char *desc)
|
||||
* can be treated as ordinary keys in addition to having their own special
|
||||
* operations.
|
||||
*/
|
||||
static int keyring_preparse(struct key_preparsed_payload *prep);
|
||||
static void keyring_free_preparse(struct key_preparsed_payload *prep);
|
||||
static int keyring_instantiate(struct key *keyring,
|
||||
struct key_preparsed_payload *prep);
|
||||
static void keyring_revoke(struct key *keyring);
|
||||
@ -84,6 +86,8 @@ static long keyring_read(const struct key *keyring,
|
||||
struct key_type key_type_keyring = {
|
||||
.name = "keyring",
|
||||
.def_datalen = 0,
|
||||
.preparse = keyring_preparse,
|
||||
.free_preparse = keyring_free_preparse,
|
||||
.instantiate = keyring_instantiate,
|
||||
.match = user_match,
|
||||
.revoke = keyring_revoke,
|
||||
@ -122,6 +126,21 @@ static void keyring_publish_name(struct key *keyring)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Preparse a keyring payload
|
||||
*/
|
||||
static int keyring_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
return prep->datalen != 0 ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free a preparse of a user defined key payload
|
||||
*/
|
||||
static void keyring_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise a keyring.
|
||||
*
|
||||
@ -130,17 +149,10 @@ static void keyring_publish_name(struct key *keyring)
|
||||
static int keyring_instantiate(struct key *keyring,
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (prep->datalen == 0) {
|
||||
assoc_array_init(&keyring->keys);
|
||||
/* make the keyring available by name if it has one */
|
||||
keyring_publish_name(keyring);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
assoc_array_init(&keyring->keys);
|
||||
/* make the keyring available by name if it has one */
|
||||
keyring_publish_name(keyring);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include "internal.h"
|
||||
#include <keys/user-type.h>
|
||||
|
||||
static int request_key_auth_preparse(struct key_preparsed_payload *);
|
||||
static void request_key_auth_free_preparse(struct key_preparsed_payload *);
|
||||
static int request_key_auth_instantiate(struct key *,
|
||||
struct key_preparsed_payload *);
|
||||
static void request_key_auth_describe(const struct key *, struct seq_file *);
|
||||
@ -33,6 +35,8 @@ static long request_key_auth_read(const struct key *, char __user *, size_t);
|
||||
struct key_type key_type_request_key_auth = {
|
||||
.name = ".request_key_auth",
|
||||
.def_datalen = sizeof(struct request_key_auth),
|
||||
.preparse = request_key_auth_preparse,
|
||||
.free_preparse = request_key_auth_free_preparse,
|
||||
.instantiate = request_key_auth_instantiate,
|
||||
.describe = request_key_auth_describe,
|
||||
.revoke = request_key_auth_revoke,
|
||||
@ -40,6 +44,15 @@ struct key_type key_type_request_key_auth = {
|
||||
.read = request_key_auth_read,
|
||||
};
|
||||
|
||||
int request_key_auth_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void request_key_auth_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Instantiate a request-key authorisation key.
|
||||
*/
|
||||
|
@ -27,7 +27,9 @@ static int logon_vet_description(const char *desc);
|
||||
struct key_type key_type_user = {
|
||||
.name = "user",
|
||||
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.instantiate = user_instantiate,
|
||||
.preparse = user_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.update = user_update,
|
||||
.match = user_match,
|
||||
.revoke = user_revoke,
|
||||
@ -47,7 +49,9 @@ EXPORT_SYMBOL_GPL(key_type_user);
|
||||
struct key_type key_type_logon = {
|
||||
.name = "logon",
|
||||
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.instantiate = user_instantiate,
|
||||
.preparse = user_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.update = user_update,
|
||||
.match = user_match,
|
||||
.revoke = user_revoke,
|
||||
@ -58,38 +62,37 @@ struct key_type key_type_logon = {
|
||||
EXPORT_SYMBOL_GPL(key_type_logon);
|
||||
|
||||
/*
|
||||
* instantiate a user defined key
|
||||
* Preparse a user defined key payload
|
||||
*/
|
||||
int user_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
int user_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct user_key_payload *upayload;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
goto error;
|
||||
return -EINVAL;
|
||||
|
||||
ret = key_payload_reserve(key, datalen);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = -ENOMEM;
|
||||
upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
|
||||
if (!upayload)
|
||||
goto error;
|
||||
return -ENOMEM;
|
||||
|
||||
/* attach the data */
|
||||
prep->quotalen = datalen;
|
||||
prep->payload[0] = upayload;
|
||||
upayload->datalen = datalen;
|
||||
memcpy(upayload->data, prep->data, datalen);
|
||||
rcu_assign_keypointer(key, upayload);
|
||||
ret = 0;
|
||||
|
||||
error:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(user_preparse);
|
||||
|
||||
EXPORT_SYMBOL_GPL(user_instantiate);
|
||||
/*
|
||||
* Free a preparse of a user defined key payload
|
||||
*/
|
||||
void user_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
kfree(prep->payload[0]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(user_free_preparse);
|
||||
|
||||
/*
|
||||
* update a user defined key
|
||||
|
@ -845,6 +845,17 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode)
|
||||
return security_ops->kernel_create_files_as(new, inode);
|
||||
}
|
||||
|
||||
int security_kernel_fw_from_file(struct file *file, char *buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = security_ops->kernel_fw_from_file(file, buf, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
return ima_fw_from_file(file, buf, size);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(security_kernel_fw_from_file);
|
||||
|
||||
int security_kernel_module_request(char *kmod_name)
|
||||
{
|
||||
return security_ops->kernel_module_request(kmod_name);
|
||||
|
@ -161,6 +161,17 @@ static int selinux_peerlbl_enabled(void)
|
||||
return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled());
|
||||
}
|
||||
|
||||
static int selinux_netcache_avc_callback(u32 event)
|
||||
{
|
||||
if (event == AVC_CALLBACK_RESET) {
|
||||
sel_netif_flush();
|
||||
sel_netnode_flush();
|
||||
sel_netport_flush();
|
||||
synchronize_net();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* initialise the security for the init task
|
||||
*/
|
||||
@ -5993,6 +6004,9 @@ static __init int selinux_init(void)
|
||||
if (register_security(&selinux_ops))
|
||||
panic("SELinux: Unable to register with kernel.\n");
|
||||
|
||||
if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
|
||||
panic("SELinux: Unable to register AVC netcache callback\n");
|
||||
|
||||
if (selinux_enforcing)
|
||||
printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n");
|
||||
else
|
||||
|
@ -17,6 +17,8 @@
|
||||
#ifndef _SELINUX_NETIF_H_
|
||||
#define _SELINUX_NETIF_H_
|
||||
|
||||
void sel_netif_flush(void);
|
||||
|
||||
int sel_netif_sid(int ifindex, u32 *sid);
|
||||
|
||||
#endif /* _SELINUX_NETIF_H_ */
|
||||
|
@ -27,6 +27,8 @@
|
||||
#ifndef _SELINUX_NETNODE_H
|
||||
#define _SELINUX_NETNODE_H
|
||||
|
||||
void sel_netnode_flush(void);
|
||||
|
||||
int sel_netnode_sid(void *addr, u16 family, u32 *sid);
|
||||
|
||||
#endif
|
||||
|
@ -26,6 +26,8 @@
|
||||
#ifndef _SELINUX_NETPORT_H
|
||||
#define _SELINUX_NETPORT_H
|
||||
|
||||
void sel_netport_flush(void);
|
||||
|
||||
int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid);
|
||||
|
||||
#endif
|
||||
|
@ -8,6 +8,7 @@
|
||||
#ifndef _SELINUX_SECURITY_H_
|
||||
#define _SELINUX_SECURITY_H_
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/types.h>
|
||||
@ -220,7 +221,7 @@ struct selinux_kernel_status {
|
||||
/*
|
||||
* The version > 0 supports above members.
|
||||
*/
|
||||
} __attribute__((packed));
|
||||
} __packed;
|
||||
|
||||
extern void selinux_status_update_setenforce(int enforcing);
|
||||
extern void selinux_status_update_policyload(int seqno);
|
||||
|
@ -240,7 +240,7 @@ static void sel_netif_kill(int ifindex)
|
||||
* Remove all entries from the network interface table.
|
||||
*
|
||||
*/
|
||||
static void sel_netif_flush(void)
|
||||
void sel_netif_flush(void)
|
||||
{
|
||||
int idx;
|
||||
struct sel_netif *netif;
|
||||
@ -252,15 +252,6 @@ static void sel_netif_flush(void)
|
||||
spin_unlock_bh(&sel_netif_lock);
|
||||
}
|
||||
|
||||
static int sel_netif_avc_callback(u32 event)
|
||||
{
|
||||
if (event == AVC_CALLBACK_RESET) {
|
||||
sel_netif_flush();
|
||||
synchronize_net();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
@ -291,10 +282,6 @@ static __init int sel_netif_init(void)
|
||||
|
||||
register_netdevice_notifier(&sel_netif_netdev_notifier);
|
||||
|
||||
err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET);
|
||||
if (err)
|
||||
panic("avc_add_callback() failed, error %d\n", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user