提交 b793c005 编写于 作者: L Linus Torvalds

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

Pull security subsystem updates from James Morris:
 "Highlights:

   - PKCS#7 support added to support signed kexec, also utilized for
     module signing.  See comments in 3f1e1bea.

     ** NOTE: this requires linking against the OpenSSL library, which
        must be installed, e.g.  the openssl-devel on Fedora **

   - Smack
      - add IPv6 host labeling; ignore labels on kernel threads
      - support smack labeling mounts which use binary mount data

   - SELinux:
      - add ioctl whitelisting (see
        http://kernsec.org/files/lss2015/vanderstoep.pdf)
      - fix mprotect PROT_EXEC regression caused by mm change

   - Seccomp:
      - add ptrace options for suspend/resume"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (57 commits)
  PKCS#7: Add OIDs for sha224, sha284 and sha512 hash algos and use them
  Documentation/Changes: Now need OpenSSL devel packages for module signing
  scripts: add extract-cert and sign-file to .gitignore
  modsign: Handle signing key in source tree
  modsign: Use if_changed rule for extracting cert from module signing key
  Move certificate handling to its own directory
  sign-file: Fix warning about BIO_reset() return value
  PKCS#7: Add MODULE_LICENSE() to test module
  Smack - Fix build error with bringup unconfigured
  sign-file: Document dependency on OpenSSL devel libraries
  PKCS#7: Appropriately restrict authenticated attributes and content type
  KEYS: Add a name for PKEY_ID_PKCS7
  PKCS#7: Improve and export the X.509 ASN.1 time object decoder
  modsign: Use extract-cert to process CONFIG_SYSTEM_TRUSTED_KEYS
  extract-cert: Cope with multiple X.509 certificates in a single file
  sign-file: Generate CMS message as signature instead of PKCS#7
  PKCS#7: Support CMS messages also [RFC5652]
  X.509: Change recorded SKID & AKID to not include Subject or Issuer
  PKCS#7: Check content type and versions
  MAINTAINERS: The keyrings mailing list has moved
  ...
......@@ -97,6 +97,7 @@ GTAGS
# Leavings from module signing
#
extra_certificates
signing_key.pem
signing_key.priv
signing_key.x509
x509.genkey
......
......@@ -43,6 +43,7 @@ o udev 081 # udevd --version
o grub 0.93 # grub --version || grub-install --version
o mcelog 0.6 # mcelog --version
o iptables 1.4.2 # iptables -V
o openssl & libcrypto 1.0.1k # openssl version
Kernel compilation
......@@ -79,6 +80,17 @@ BC
You will need bc to build kernels 3.10 and higher
OpenSSL
-------
Module signing and external certificate handling use the OpenSSL program and
crypto library to do key creation and signature generation.
You will need openssl to build kernels 3.7 and higher if module signing is
enabled. You will also need openssl development packages to build kernels 4.3
and higher.
System utilities
================
......@@ -295,6 +307,10 @@ Binutils
--------
o <ftp://ftp.kernel.org/pub/linux/devel/binutils/>
OpenSSL
-------
o <https://www.openssl.org/>
System utilities
****************
......@@ -392,4 +408,3 @@ o <http://oprofile.sf.net/download/>
NFS-Utils
---------
o <http://nfs.sourceforge.net/>
......@@ -174,6 +174,11 @@ The output directory is often set using "O=..." on the commandline.
The value can be overridden in which case the default value is ignored.
KBUILD_SIGN_PIN
--------------------------------------------------
This variable allows a passphrase or PIN to be passed to the sign-file
utility when signing kernel modules, if the private key requires such.
KBUILD_MODPOST_WARN
--------------------------------------------------
KBUILD_MODPOST_WARN can be set to avoid errors in case of undefined
......
......@@ -89,6 +89,32 @@ This has a number of options available:
their signatures checked without causing a dependency loop.
(4) "File name or PKCS#11 URI of module signing key" (CONFIG_MODULE_SIG_KEY)
Setting this option to something other than its default of
"certs/signing_key.pem" will disable the autogeneration of signing keys
and allow the kernel modules to be signed with a key of your choosing.
The string provided should identify a file containing both a private key
and its corresponding X.509 certificate in PEM form, or — on systems where
the OpenSSL ENGINE_pkcs11 is functional — a PKCS#11 URI as defined by
RFC7512. In the latter case, the PKCS#11 URI should reference both a
certificate and a private key.
If the PEM file containing the private key is encrypted, or if the
PKCS#11 token requries a PIN, this can be provided at build time by
means of the KBUILD_SIGN_PIN variable.
(5) "Additional X.509 keys for default system keyring" (CONFIG_SYSTEM_TRUSTED_KEYS)
This option can be set to the filename of a PEM-encoded file containing
additional certificates which will be included in the system keyring by
default.
Note that enabling module signing adds a dependency on the OpenSSL devel
packages to the kernel build processes for the tool that does the signing.
=======================
GENERATING SIGNING KEYS
=======================
......@@ -100,16 +126,16 @@ it can be deleted or stored securely. The public key gets built into the
kernel so that it can be used to check the signatures as the modules are
loaded.
Under normal conditions, the kernel build will automatically generate a new
keypair using openssl if one does not exist in the files:
Under normal conditions, when CONFIG_MODULE_SIG_KEY is unchanged from its
default, the kernel build will automatically generate a new keypair using
openssl if one does not exist in the file:
signing_key.priv
signing_key.x509
certs/signing_key.pem
during the building of vmlinux (the public part of the key needs to be built
into vmlinux) using parameters in the:
x509.genkey
certs/x509.genkey
file (which is also generated if it does not already exist).
......@@ -135,8 +161,12 @@ kernel sources tree and the openssl command. The following is an example to
generate the public/private key files:
openssl req -new -nodes -utf8 -sha256 -days 36500 -batch -x509 \
-config x509.genkey -outform DER -out signing_key.x509 \
-keyout signing_key.priv
-config x509.genkey -outform PEM -out kernel_key.pem \
-keyout kernel_key.pem
The full pathname for the resulting kernel_key.pem file can then be specified
in the CONFIG_MODULE_SIG_KEY option, and the certificate and key therein will
be used instead of an autogenerated keypair.
=========================
......@@ -152,10 +182,9 @@ in a keyring called ".system_keyring" that can be seen by:
302d2d52 I------ 1 perm 1f010000 0 0 asymmetri Fedora kernel signing key: d69a84e6bce3d216b979e9505b3e3ef9a7118079: X509.RSA a7118079 []
...
Beyond the public key generated specifically for module signing, any file
placed in the kernel source root directory or the kernel build root directory
whose name is suffixed with ".x509" will be assumed to be an X.509 public key
and will be added to the keyring.
Beyond the public key generated specifically for module signing, additional
trusted certificates can be provided in a PEM-encoded file referenced by the
CONFIG_SYSTEM_TRUSTED_KEYS configuration option.
Further, the architecture code may take public keys from a hardware store and
add those in also (e.g. from the UEFI key database).
......@@ -181,7 +210,7 @@ To manually sign a module, use the scripts/sign-file tool available in
the Linux kernel source tree. The script requires 4 arguments:
1. The hash algorithm (e.g., sha256)
2. The private key filename
2. The private key filename or PKCS#11 URI
3. The public key filename
4. The kernel module to be signed
......@@ -194,6 +223,9 @@ The hash algorithm used does not have to match the one configured, but if it
doesn't, you should make sure that hash algorithm is either built into the
kernel or can be loaded without requiring itself.
If the private key requires a passphrase or PIN, it can be provided in the
$KBUILD_SIGN_PIN environment variable.
============================
SIGNED MODULES AND STRIPPING
......
......@@ -28,6 +28,10 @@ Smack kernels use the CIPSO IP option. Some network
configurations are intolerant of IP options and can impede
access to systems that use them as Smack does.
Smack is used in the Tizen operating system. Please
go to http://wiki.tizen.org for information about how
Smack is used in Tizen.
The current git repository for Smack user space is:
git://github.com/smack-team/smack.git
......@@ -108,6 +112,8 @@ in the smackfs filesystem. This pseudo-filesystem is mounted
on /sys/fs/smackfs.
access
Provided for backward compatibility. The access2 interface
is preferred and should be used instead.
This interface reports whether a subject with the specified
Smack label has a particular access to an object with a
specified Smack label. Write a fixed format access rule to
......@@ -136,6 +142,8 @@ change-rule
those in the fourth string. If there is no such rule it will be
created using the access specified in the third and the fourth strings.
cipso
Provided for backward compatibility. The cipso2 interface
is preferred and should be used instead.
This interface allows a specific CIPSO header to be assigned
to a Smack label. The format accepted on write is:
"%24s%4d%4d"["%4d"]...
......@@ -157,7 +165,19 @@ direct
doi
This contains the CIPSO domain of interpretation used in
network packets.
ipv6host
This interface allows specific IPv6 internet addresses to be
treated as single label hosts. Packets are sent to single
label hosts only from processes that have Smack write access
to the host label. All packets received from single label hosts
are given the specified label. The format accepted on write is:
"%h:%h:%h:%h:%h:%h:%h:%h label" or
"%h:%h:%h:%h:%h:%h:%h:%h/%d label".
The "::" address shortcut is not supported.
If label is "-DELETE" a matched entry will be deleted.
load
Provided for backward compatibility. The load2 interface
is preferred and should be used instead.
This interface allows access control rules in addition to
the system defined rules to be specified. The format accepted
on write is:
......@@ -181,6 +201,8 @@ load2
permissions that are not allowed. The string "r-x--" would
specify read and execute access.
load-self
Provided for backward compatibility. The load-self2 interface
is preferred and should be used instead.
This interface allows process specific access rules to be
defined. These rules are only consulted if access would
otherwise be permitted, and are intended to provide additional
......@@ -205,6 +227,8 @@ netlabel
received from single label hosts are given the specified
label. The format accepted on write is:
"%d.%d.%d.%d label" or "%d.%d.%d.%d/%d label".
If the label specified is "-CIPSO" the address is treated
as a host that supports CIPSO headers.
onlycap
This contains labels processes must have for CAP_MAC_ADMIN
and CAP_MAC_OVERRIDE to be effective. If this file is empty
......@@ -232,7 +256,8 @@ unconfined
is dangerous and can ruin the proper labeling of your system.
It should never be used in production.
You can add access rules in /etc/smack/accesses. They take the form:
If you are using the smackload utility
you can add access rules in /etc/smack/accesses. They take the form:
subjectlabel objectlabel access
......
Yama is a Linux Security Module that collects a number of system-wide DAC
security protections that are not handled by the core kernel itself. To
select it at boot time, specify "security=yama" (though this will disable
any other LSM).
Yama is controlled through sysctl in /proc/sys/kernel/yama:
Yama is a Linux Security Module that collects system-wide DAC security
protections that are not handled by the core kernel itself. This is
selectable at build-time with CONFIG_SECURITY_YAMA, and can be controlled
at run-time through sysctls in /proc/sys/kernel/yama:
- ptrace_scope
......
......@@ -2621,6 +2621,15 @@ S: Supported
F: Documentation/filesystems/ceph.txt
F: fs/ceph/
CERTIFICATE HANDLING:
M: David Howells <dhowells@redhat.com>
M: David Woodhouse <dwmw2@infradead.org>
L: keyrings@linux-nfs.org
S: Maintained
F: Documentation/module-signing.txt
F: certs/
F: scripts/extract-cert.c
CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM:
L: linux-usb@vger.kernel.org
S: Orphan
......@@ -5994,7 +6003,7 @@ F: kernel/kexec.c
KEYS/KEYRINGS:
M: David Howells <dhowells@redhat.com>
L: keyrings@linux-nfs.org
L: keyrings@vger.kernel.org
S: Maintained
F: Documentation/security/keys.txt
F: include/linux/key.h
......@@ -6006,7 +6015,7 @@ KEYS-TRUSTED
M: David Safford <safford@us.ibm.com>
M: Mimi Zohar <zohar@linux.vnet.ibm.com>
L: linux-security-module@vger.kernel.org
L: keyrings@linux-nfs.org
L: keyrings@vger.kernel.org
S: Supported
F: Documentation/security/keys-trusted-encrypted.txt
F: include/keys/trusted-type.h
......@@ -6017,7 +6026,7 @@ KEYS-ENCRYPTED
M: Mimi Zohar <zohar@linux.vnet.ibm.com>
M: David Safford <safford@us.ibm.com>
L: linux-security-module@vger.kernel.org
L: keyrings@linux-nfs.org
L: keyrings@vger.kernel.org
S: Supported
F: Documentation/security/keys-trusted-encrypted.txt
F: include/keys/encrypted-type.h
......@@ -9264,6 +9273,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git
S: Supported
F: security/apparmor/
YAMA SECURITY MODULE
M: Kees Cook <keescook@chromium.org>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip
S: Supported
F: security/yama/
SENSABLE PHANTOM
M: Jiri Slaby <jirislaby@gmail.com>
S: Maintained
......
......@@ -875,10 +875,9 @@ INITRD_COMPRESS-$(CONFIG_RD_LZ4) := lz4
# export INITRD_COMPRESS := $(INITRD_COMPRESS-y)
ifdef CONFIG_MODULE_SIG_ALL
MODSECKEY = ./signing_key.priv
MODPUBKEY = ./signing_key.x509
export MODPUBKEY
mod_sign_cmd = perl $(srctree)/scripts/sign-file $(CONFIG_MODULE_SIG_HASH) $(MODSECKEY) $(MODPUBKEY)
$(eval $(call config_filename,MODULE_SIG_KEY))
mod_sign_cmd = scripts/sign-file $(CONFIG_MODULE_SIG_HASH) $(MODULE_SIG_KEY_SRCPREFIX)$(CONFIG_MODULE_SIG_KEY) certs/signing_key.x509
else
mod_sign_cmd = true
endif
......@@ -886,7 +885,7 @@ export mod_sign_cmd
ifeq ($(KBUILD_EXTMOD),)
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/
vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
$(core-y) $(core-m) $(drivers-y) $(drivers-m) \
......@@ -1178,8 +1177,8 @@ MRPROPER_DIRS += include/config usr/include include/generated \
arch/*/include/generated .tmp_objdiff
MRPROPER_FILES += .config .config.old .version .old_version \
Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \
signing_key.priv signing_key.x509 x509.genkey \
extra_certificates signing_key.x509.keyid \
signing_key.pem signing_key.priv signing_key.x509 \
x509.genkey extra_certificates signing_key.x509.keyid \
signing_key.x509.signer vmlinux-gdb.py
# clean - Delete most, but leave enough to build external modules
......
......@@ -320,7 +320,6 @@ CONFIG_KEYS=y
CONFIG_SECURITY=y
CONFIG_SECURITY_NETWORK=y
CONFIG_SECURITY_YAMA=y
CONFIG_SECURITY_YAMA_STACKED=y
CONFIG_DEFAULT_SECURITY_DAC=y
CONFIG_CRYPTO_AUTHENC=y
CONFIG_CRYPTO_HMAC=y
......
......@@ -533,7 +533,9 @@ static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len)
int ret;
ret = verify_pefile_signature(kernel, kernel_len,
system_trusted_keyring, &trusted);
system_trusted_keyring,
VERIFYING_KEXEC_PE_SIGNATURE,
&trusted);
if (ret < 0)
return ret;
if (!trusted)
......
menu "Certificates for signature checking"
config MODULE_SIG_KEY
string "File name or PKCS#11 URI of module signing key"
default "certs/signing_key.pem"
depends on MODULE_SIG
help
Provide the file name of a private key/certificate in PEM format,
or a PKCS#11 URI according to RFC7512. The file should contain, or
the URI should identify, both the certificate and its corresponding
private key.
If this option is unchanged from its default "certs/signing_key.pem",
then the kernel will automatically generate the private key and
certificate as described in Documentation/module-signing.txt
config SYSTEM_TRUSTED_KEYRING
bool "Provide system-wide ring of trusted keys"
depends on KEYS
help
Provide a system keyring to which trusted keys can be added. Keys in
the keyring are considered to be trusted. Keys may be added at will
by the kernel from compiled-in data and from hardware key stores, but
userspace may only add extra keys if those keys can be verified by
keys already in the keyring.
Keys in this keyring are used by module signature checking.
config SYSTEM_TRUSTED_KEYS
string "Additional X.509 keys for default system keyring"
depends on SYSTEM_TRUSTED_KEYRING
help
If set, this option should be the filename of a PEM-formatted file
containing trusted X.509 certificates to be included in the default
system keyring. Any certificate used for module signing is implicitly
also trusted.
NOTE: If you previously provided keys for the system keyring in the
form of DER-encoded *.x509 files in the top-level build directory,
those are no longer used. You will need to set this option instead.
endmenu
#
# Makefile for the linux kernel signature checking certificates.
#
obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o
ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
$(eval $(call config_filename,SYSTEM_TRUSTED_KEYS))
# GCC doesn't include .incbin files in -MD generated dependencies (PR#66871)
$(obj)/system_certificates.o: $(obj)/x509_certificate_list
# Cope with signing_key.x509 existing in $(srctree) not $(objtree)
AFLAGS_system_certificates.o := -I$(srctree)
quiet_cmd_extract_certs = EXTRACT_CERTS $(patsubst "%",%,$(2))
cmd_extract_certs = scripts/extract-cert $(2) $@ || ( rm $@; exit 1)
targets += x509_certificate_list
$(obj)/x509_certificate_list: scripts/extract-cert $(SYSTEM_TRUSTED_KEYS_SRCPREFIX)$(SYSTEM_TRUSTED_KEYS_FILENAME) FORCE
$(call if_changed,extract_certs,$(SYSTEM_TRUSTED_KEYS_SRCPREFIX)$(CONFIG_SYSTEM_TRUSTED_KEYS))
endif
clean-files := x509_certificate_list .x509.list
ifeq ($(CONFIG_MODULE_SIG),y)
###############################################################################
#
# If module signing is requested, say by allyesconfig, but a key has not been
# supplied, then one will need to be generated to make sure the build does not
# fail and that the kernel may be used afterwards.
#
###############################################################################
ifndef CONFIG_MODULE_SIG_HASH
$(error Could not determine digest type to use from kernel config)
endif
# We do it this way rather than having a boolean option for enabling an
# external private key, because 'make randconfig' might enable such a
# boolean option and we unfortunately can't make it depend on !RANDCONFIG.
ifeq ($(CONFIG_MODULE_SIG_KEY),"certs/signing_key.pem")
$(obj)/signing_key.pem: $(obj)/x509.genkey
@echo "###"
@echo "### Now generating an X.509 key pair to be used for signing modules."
@echo "###"
@echo "### If this takes a long time, you might wish to run rngd in the"
@echo "### background to keep the supply of entropy topped up. It"
@echo "### needs to be run as root, and uses a hardware random"
@echo "### number generator if one is available."
@echo "###"
openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_HASH) -days 36500 \
-batch -x509 -config $(obj)/x509.genkey \
-outform PEM -out $(obj)/signing_key.pem \
-keyout $(obj)/signing_key.pem 2>&1
@echo "###"
@echo "### Key pair generated."
@echo "###"
$(obj)/x509.genkey:
@echo Generating X.509 key generation config
@echo >$@ "[ req ]"
@echo >>$@ "default_bits = 4096"
@echo >>$@ "distinguished_name = req_distinguished_name"
@echo >>$@ "prompt = no"
@echo >>$@ "string_mask = utf8only"
@echo >>$@ "x509_extensions = myexts"
@echo >>$@
@echo >>$@ "[ req_distinguished_name ]"
@echo >>$@ "#O = Unspecified company"
@echo >>$@ "CN = Build time autogenerated kernel key"
@echo >>$@ "#emailAddress = unspecified.user@unspecified.company"
@echo >>$@
@echo >>$@ "[ myexts ]"
@echo >>$@ "basicConstraints=critical,CA:FALSE"
@echo >>$@ "keyUsage=digitalSignature"
@echo >>$@ "subjectKeyIdentifier=hash"
@echo >>$@ "authorityKeyIdentifier=keyid"
endif
$(eval $(call config_filename,MODULE_SIG_KEY))
# If CONFIG_MODULE_SIG_KEY isn't a PKCS#11 URI, depend on it
ifeq ($(patsubst pkcs11:%,%,$(firstword $(MODULE_SIG_KEY_FILENAME))),$(firstword $(MODULE_SIG_KEY_FILENAME)))
X509_DEP := $(MODULE_SIG_KEY_SRCPREFIX)$(MODULE_SIG_KEY_FILENAME)
endif
# GCC PR#66871 again.
$(obj)/system_certificates.o: $(obj)/signing_key.x509
targets += signing_key.x509
$(obj)/signing_key.x509: scripts/extract-cert $(X509_DEP) FORCE
$(call if_changed,extract_certs,$(MODULE_SIG_KEY_SRCPREFIX)$(CONFIG_MODULE_SIG_KEY))
endif
......@@ -7,7 +7,10 @@
.globl VMLINUX_SYMBOL(system_certificate_list)
VMLINUX_SYMBOL(system_certificate_list):
__cert_list_start:
.incbin "kernel/x509_certificate_list"
#ifdef CONFIG_MODULE_SIG
.incbin "certs/signing_key.x509"
#endif
.incbin "certs/x509_certificate_list"
__cert_list_end:
.align 8
......
......@@ -16,7 +16,7 @@
#include <linux/err.h>
#include <keys/asymmetric-type.h>
#include <keys/system_keyring.h>
#include "module-internal.h"
#include <crypto/pkcs7.h>
struct key *system_trusted_keyring;
EXPORT_SYMBOL_GPL(system_trusted_keyring);
......@@ -104,3 +104,54 @@ static __init int load_system_certificate_list(void)
return 0;
}
late_initcall(load_system_certificate_list);
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
/**
* Verify a PKCS#7-based signature on system data.
* @data: The data to be verified.
* @len: Size of @data.
* @raw_pkcs7: The PKCS#7 message that is the signature.
* @pkcs7_len: The size of @raw_pkcs7.
* @usage: The use to which the key is being put.
*/
int system_verify_data(const void *data, unsigned long len,
const void *raw_pkcs7, size_t pkcs7_len,
enum key_being_used_for usage)
{
struct pkcs7_message *pkcs7;
bool trusted;
int ret;
pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
if (IS_ERR(pkcs7))
return PTR_ERR(pkcs7);
/* The data should be detached - so we need to supply it. */
if (pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
pr_err("PKCS#7 signature with non-detached data\n");
ret = -EBADMSG;
goto error;
}
ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error;
ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted);
if (ret < 0)
goto error;
if (!trusted) {
pr_err("PKCS#7 signature not signed with a trusted key\n");
ret = -ENOKEY;
}
error:
pkcs7_free_message(pkcs7);
pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
}
EXPORT_SYMBOL_GPL(system_verify_data);
#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
......@@ -1635,5 +1635,6 @@ config CRYPTO_HASH_INFO
source "drivers/crypto/Kconfig"
source crypto/asymmetric_keys/Kconfig
source certs/Kconfig
endif # if CRYPTO
......@@ -15,15 +15,21 @@ obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o
obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
x509_key_parser-y := \
x509-asn1.o \
x509_akid-asn1.o \
x509_rsakey-asn1.o \
x509_cert_parser.o \
x509_public_key.o
$(obj)/x509_cert_parser.o: $(obj)/x509-asn1.h $(obj)/x509_rsakey-asn1.h
$(obj)/x509_cert_parser.o: \
$(obj)/x509-asn1.h \
$(obj)/x509_akid-asn1.h \
$(obj)/x509_rsakey-asn1.h
$(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h
$(obj)/x509_akid-asn1.o: $(obj)/x509_akid-asn1.c $(obj)/x509_akid-asn1.h
$(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_akid-asn1.c x509_akid-asn1.h
clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h
#
......
......@@ -12,6 +12,7 @@
*/
#include <keys/asymmetric-subtype.h>
#include <keys/asymmetric-parser.h>
#include <crypto/public_key.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/slab.h>
......@@ -20,6 +21,16 @@
MODULE_LICENSE("GPL");
const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = {
[VERIFYING_MODULE_SIGNATURE] = "mod sig",
[VERIFYING_FIRMWARE_SIGNATURE] = "firmware sig",
[VERIFYING_KEXEC_PE_SIGNATURE] = "kexec PE sig",
[VERIFYING_KEY_SIGNATURE] = "key sig",
[VERIFYING_KEY_SELF_SIGNATURE] = "key self sig",
[VERIFYING_UNSPECIFIED_SIGNATURE] = "unspec sig",
};
EXPORT_SYMBOL_GPL(key_being_used_for);
static LIST_HEAD(asymmetric_key_parsers);
static DECLARE_RWSEM(asymmetric_key_parsers_sem);
......
......@@ -97,6 +97,15 @@ int mscode_note_digest_algo(void *context, size_t hdrlen,
case OID_sha256:
ctx->digest_algo = HASH_ALGO_SHA256;
break;
case OID_sha384:
ctx->digest_algo = HASH_ALGO_SHA384;
break;
case OID_sha512:
ctx->digest_algo = HASH_ALGO_SHA512;
break;
case OID_sha224:
ctx->digest_algo = HASH_ALGO_SHA224;
break;
case OID__NR:
sprint_oid(value, vlen, buffer, sizeof(buffer));
......
PKCS7ContentInfo ::= SEQUENCE {
contentType ContentType,
contentType ContentType ({ pkcs7_check_content_type }),
content [0] EXPLICIT SignedData OPTIONAL
}
ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID })
SignedData ::= SEQUENCE {
version INTEGER,
version INTEGER ({ pkcs7_note_signeddata_version }),
digestAlgorithms DigestAlgorithmIdentifiers,
contentInfo ContentInfo,
contentInfo ContentInfo ({ pkcs7_note_content }),
certificates CHOICE {
certSet [0] IMPLICIT ExtendedCertificatesAndCertificates,
certSequence [2] IMPLICIT Certificates
......@@ -21,7 +21,7 @@ SignedData ::= SEQUENCE {
}
ContentInfo ::= SEQUENCE {
contentType ContentType,
contentType ContentType ({ pkcs7_note_OID }),
content [0] EXPLICIT Data OPTIONAL
}
......@@ -68,8 +68,8 @@ SignerInfos ::= CHOICE {
}
SignerInfo ::= SEQUENCE {
version INTEGER,
issuerAndSerialNumber IssuerAndSerialNumber,
version INTEGER ({ pkcs7_note_signerinfo_version }),
sid SignerIdentifier, -- CMS variant, not PKCS#7
digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }),
authenticatedAttributes CHOICE {
aaSet [0] IMPLICIT SetOfAuthenticatedAttribute
......@@ -88,6 +88,12 @@ SignerInfo ::= SEQUENCE {
} OPTIONAL
} ({ pkcs7_note_signed_info })
SignerIdentifier ::= CHOICE {
-- RFC5652 sec 5.3
issuerAndSerialNumber IssuerAndSerialNumber,
subjectKeyIdentifier [0] IMPLICIT SubjectKeyIdentifier
}
IssuerAndSerialNumber ::= SEQUENCE {
issuer Name ({ pkcs7_sig_note_issuer }),
serialNumber CertificateSerialNumber ({ pkcs7_sig_note_serial })
......@@ -95,6 +101,8 @@ IssuerAndSerialNumber ::= SEQUENCE {
CertificateSerialNumber ::= INTEGER
SubjectKeyIdentifier ::= OCTET STRING ({ pkcs7_sig_note_skid })
SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute
AuthenticatedAttribute ::= SEQUENCE {
......@@ -103,7 +111,7 @@ AuthenticatedAttribute ::= SEQUENCE {
}
UnauthenticatedAttribute ::= SEQUENCE {
type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
type OBJECT IDENTIFIER,
values SET OF ANY
}
......
......@@ -14,16 +14,26 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/key-type.h>
#include <keys/asymmetric-type.h>
#include <crypto/pkcs7.h>
#include <keys/user-type.h>
#include <keys/system_keyring.h>
#include "pkcs7_parser.h"
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PKCS#7 testing key type");
static unsigned pkcs7_usage;
module_param_named(usage, pkcs7_usage, uint, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(pkcs7_usage,
"Usage to specify when verifying the PKCS#7 message");
/*
* Preparse a PKCS#7 wrapped and validated data blob.
*/
static int pkcs7_preparse(struct key_preparsed_payload *prep)
{
enum key_being_used_for usage = pkcs7_usage;
struct pkcs7_message *pkcs7;
const void *data, *saved_prep_data;
size_t datalen, saved_prep_datalen;
......@@ -32,6 +42,11 @@ static int pkcs7_preparse(struct key_preparsed_payload *prep)
kenter("");
if (usage >= NR__KEY_BEING_USED_FOR) {
pr_err("Invalid usage type %d\n", usage);
return -EINVAL;
}
saved_prep_data = prep->data;
saved_prep_datalen = prep->datalen;
pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen);
......@@ -40,7 +55,7 @@ static int pkcs7_preparse(struct key_preparsed_payload *prep)
goto error;
}
ret = pkcs7_verify(pkcs7);
ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error_free;
......
......@@ -33,6 +33,9 @@ struct pkcs7_parse_context {
unsigned raw_serial_size;
unsigned raw_issuer_size;
const void *raw_issuer;
const void *raw_skid;
unsigned raw_skid_size;
bool expect_skid;
};
/*
......@@ -78,6 +81,30 @@ void pkcs7_free_message(struct pkcs7_message *pkcs7)
}
EXPORT_SYMBOL_GPL(pkcs7_free_message);
/*
* Check authenticatedAttributes are provided or not provided consistently.
*/
static int pkcs7_check_authattrs(struct pkcs7_message *msg)
{
struct pkcs7_signed_info *sinfo;
bool want;
sinfo = msg->signed_infos;
if (sinfo->authattrs) {
want = true;
msg->have_authattrs = true;
}
for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next)
if (!!sinfo->authattrs != want)
goto inconsistent;
return 0;
inconsistent:
pr_warn("Inconsistently supplied authAttrs\n");
return -EINVAL;
}
/**
* pkcs7_parse_message - Parse a PKCS#7 message
* @data: The raw binary ASN.1 encoded message to be parsed
......@@ -110,6 +137,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
goto out;
}
ret = pkcs7_check_authattrs(ctx->msg);
if (ret < 0)
goto out;
msg = ctx->msg;
ctx->msg = NULL;
......@@ -198,6 +229,14 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
case OID_sha256:
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA256;
break;
case OID_sha384:
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA384;
break;
case OID_sha512:
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA512;
break;
case OID_sha224:
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA224;
default:
printk("Unsupported digest algo: %u\n", ctx->last_oid);
return -ENOPKG;
......@@ -225,6 +264,100 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
return 0;
}
/*
* We only support signed data [RFC2315 sec 9].
*/
int pkcs7_check_content_type(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct pkcs7_parse_context *ctx = context;
if (ctx->last_oid != OID_signed_data) {
pr_warn("Only support pkcs7_signedData type\n");
return -EINVAL;
}
return 0;
}
/*
* Note the SignedData version
*/
int pkcs7_note_signeddata_version(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct pkcs7_parse_context *ctx = context;
unsigned version;
if (vlen != 1)
goto unsupported;
ctx->msg->version = version = *(const u8 *)value;
switch (version) {
case 1:
/* PKCS#7 SignedData [RFC2315 sec 9.1]
* CMS ver 1 SignedData [RFC5652 sec 5.1]
*/
break;
case 3:
/* CMS ver 3 SignedData [RFC2315 sec 5.1] */
break;
default:
goto unsupported;
}
return 0;
unsupported:
pr_warn("Unsupported SignedData version\n");
return -EINVAL;
}
/*
* Note the SignerInfo version
*/
int pkcs7_note_signerinfo_version(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct pkcs7_parse_context *ctx = context;
unsigned version;
if (vlen != 1)
goto unsupported;
version = *(const u8 *)value;
switch (version) {
case 1:
/* PKCS#7 SignerInfo [RFC2315 sec 9.2]
* CMS ver 1 SignerInfo [RFC5652 sec 5.3]
*/
if (ctx->msg->version != 1)
goto version_mismatch;
ctx->expect_skid = false;
break;
case 3:
/* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */
if (ctx->msg->version == 1)
goto version_mismatch;
ctx->expect_skid = true;
break;
default:
goto unsupported;
}
return 0;
unsupported:
pr_warn("Unsupported SignerInfo version\n");
return -EINVAL;
version_mismatch:
pr_warn("SignedData-SignerInfo version mismatch\n");
return -EBADMSG;
}
/*
* Extract a certificate and store it in the context.
*/
......@@ -283,6 +416,25 @@ int pkcs7_note_certificate_list(void *context, size_t hdrlen,
return 0;
}
/*
* Note the content type.
*/
int pkcs7_note_content(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct pkcs7_parse_context *ctx = context;
if (ctx->last_oid != OID_data &&
ctx->last_oid != OID_msIndirectData) {
pr_warn("Unsupported data type %d\n", ctx->last_oid);
return -EINVAL;
}
ctx->msg->data_type = ctx->last_oid;
return 0;
}
/*
* Extract the data from the message and store that and its content type OID in
* the context.
......@@ -298,45 +450,119 @@ int pkcs7_note_data(void *context, size_t hdrlen,
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
* 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;
struct pkcs7_signed_info *sinfo = ctx->sinfo;
enum OID content_type;
pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
switch (ctx->last_oid) {
case OID_contentType:
if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set))
goto repeated;
content_type = look_up_OID(value, vlen);
if (content_type != ctx->msg->data_type) {
pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n",
ctx->msg->data_type, sinfo->index,
content_type);
return -EBADMSG;
}
return 0;
case OID_signingTime:
if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set))
goto repeated;
/* Should we check that the signing time is consistent
* with the signer's X.509 cert?
*/
return x509_decode_time(&sinfo->signing_time,
hdrlen, tag, value, vlen);
case OID_messageDigest:
if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set))
goto repeated;
if (tag != ASN1_OTS)
return -EBADMSG;
ctx->sinfo->msgdigest = value;
ctx->sinfo->msgdigest_len = vlen;
sinfo->msgdigest = value;
sinfo->msgdigest_len = vlen;
return 0;
case OID_smimeCapabilites:
if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set))
goto repeated;
if (ctx->msg->data_type != OID_msIndirectData) {
pr_warn("S/MIME Caps only allowed with Authenticode\n");
return -EKEYREJECTED;
}
return 0;
/* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE
* char URLs and cont[1] 8-bit char URLs.
*
* Microsoft StatementType seems to contain a list of OIDs that
* are also used as extendedKeyUsage types in X.509 certs.
*/
case OID_msSpOpusInfo:
if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))
goto repeated;
goto authenticode_check;
case OID_msStatementType:
if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set))
goto repeated;
authenticode_check:
if (ctx->msg->data_type != OID_msIndirectData) {
pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n");
return -EKEYREJECTED;
}
/* I'm not sure how to validate these */
return 0;
default:
return 0;
}
repeated:
/* We permit max one item per AuthenticatedAttribute and no repeats */
pr_warn("Repeated/multivalue AuthAttrs not permitted\n");
return -EKEYREJECTED;
}
/*
* Note the set of auth attributes for digestion purposes [RFC2315 9.3]
* Note the set of auth attributes for digestion purposes [RFC2315 sec 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;
struct pkcs7_signed_info *sinfo = ctx->sinfo;
if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) ||
!test_bit(sinfo_has_message_digest, &sinfo->aa_set) ||
(ctx->msg->data_type == OID_msIndirectData &&
!test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))) {
pr_warn("Missing required AuthAttr\n");
return -EBADMSG;
}
if (ctx->msg->data_type != OID_msIndirectData &&
test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) {
pr_warn("Unexpected Authenticode AuthAttr\n");
return -EBADMSG;
}
/* 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);
sinfo->authattrs = value - (hdrlen - 1);
sinfo->authattrs_len = vlen + (hdrlen - 1);
return 0;
}
......@@ -366,6 +592,22 @@ int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
return 0;
}
/*
* Note the issuing cert's subjectKeyIdentifier
*/
int pkcs7_sig_note_skid(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct pkcs7_parse_context *ctx = context;
pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
ctx->raw_skid = value;
ctx->raw_skid_size = vlen;
return 0;
}
/*
* Note the signature data
*/
......@@ -398,14 +640,27 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen,
struct pkcs7_signed_info *sinfo = ctx->sinfo;
struct asymmetric_key_id *kid;
if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) {
pr_warn("Authenticode requires AuthAttrs\n");
return -EBADMSG;
}
/* Generate cert issuer + serial number key ID */
kid = asymmetric_key_generate_id(ctx->raw_serial,
ctx->raw_serial_size,
ctx->raw_issuer,
ctx->raw_issuer_size);
if (!ctx->expect_skid) {
kid = asymmetric_key_generate_id(ctx->raw_serial,
ctx->raw_serial_size,
ctx->raw_issuer,
ctx->raw_issuer_size);
} else {
kid = asymmetric_key_generate_id(ctx->raw_skid,
ctx->raw_skid_size,
"", 0);
}
if (IS_ERR(kid))
return PTR_ERR(kid);
pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);
sinfo->signing_cert_id = kid;
sinfo->index = ++ctx->sinfo_index;
*ctx->ppsinfo = sinfo;
......
......@@ -21,9 +21,9 @@
struct pkcs7_signed_info {
struct pkcs7_signed_info *next;
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
unsigned index;
bool trusted;
bool unsupported_crypto; /* T if not usable due to missing crypto */
unsigned index;
bool trusted;
bool unsupported_crypto; /* T if not usable due to missing crypto */
/* Message digest - the digest of the Content Data (or NULL) */
const void *msgdigest;
......@@ -32,8 +32,18 @@ struct pkcs7_signed_info {
/* Authenticated Attribute data (or NULL) */
unsigned authattrs_len;
const void *authattrs;
unsigned long aa_set;
#define sinfo_has_content_type 0
#define sinfo_has_signing_time 1
#define sinfo_has_message_digest 2
#define sinfo_has_smime_caps 3
#define sinfo_has_ms_opus_info 4
#define sinfo_has_ms_statement_type 5
time64_t signing_time;
/* Issuing cert serial number and issuer's name */
/* Issuing cert serial number and issuer's name [PKCS#7 or CMS ver 1]
* or issuing cert's SKID [CMS ver 3].
*/
struct asymmetric_key_id *signing_cert_id;
/* Message signature.
......@@ -50,6 +60,8 @@ struct pkcs7_message {
struct x509_certificate *certs; /* Certificate list */
struct x509_certificate *crl; /* Revocation list */
struct pkcs7_signed_info *signed_infos;
u8 version; /* Version of cert (1 -> PKCS#7 or CMS; 3 -> CMS) */
bool have_authattrs; /* T if have authattrs */
/* Content Data (or NULL) */
enum OID data_type; /* Type of Data */
......
......@@ -54,7 +54,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
/* Look to see if this certificate is present in the trusted
* keys.
*/
key = x509_request_asymmetric_key(trust_keyring, x509->id,
key = x509_request_asymmetric_key(trust_keyring,
x509->id, x509->skid,
false);
if (!IS_ERR(key)) {
/* One of the X.509 certificates in the PKCS#7 message
......@@ -85,8 +86,10 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
/* No match - see if the root certificate has a signer amongst the
* trusted keys.
*/
if (last && last->authority) {
key = x509_request_asymmetric_key(trust_keyring, last->authority,
if (last && (last->akid_id || last->akid_skid)) {
key = x509_request_asymmetric_key(trust_keyring,
last->akid_id,
last->akid_skid,
false);
if (!IS_ERR(key)) {
x509 = last;
......@@ -103,6 +106,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
*/
key = x509_request_asymmetric_key(trust_keyring,
sinfo->signing_cert_id,
NULL,
false);
if (!IS_ERR(key)) {
pr_devel("sinfo %u: Direct signer is key %x\n",
......
......@@ -70,9 +70,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
* message digest attribute amongst them which corresponds to the
* digest we just calculated.
*/
if (sinfo->msgdigest) {
if (sinfo->authattrs) {
u8 tag;
if (!sinfo->msgdigest) {
pr_warn("Sig %u: No messageDigest\n", sinfo->index);
ret = -EKEYREJECTED;
goto error;
}
if (sinfo->msgdigest_len != sinfo->sig.digest_size) {
pr_debug("Sig %u: Invalid digest size (%u)\n",
sinfo->index, sinfo->msgdigest_len);
......@@ -170,6 +176,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
struct pkcs7_signed_info *sinfo)
{
struct x509_certificate *x509 = sinfo->signer, *p;
struct asymmetric_key_id *auth;
int ret;
kenter("");
......@@ -187,11 +194,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
goto maybe_missing_crypto_in_x509;
pr_debug("- issuer %s\n", x509->issuer);
if (x509->authority)
pr_debug("- authkeyid %*phN\n",
x509->authority->len, x509->authority->data);
if (!x509->authority ||
if (x509->akid_id)
pr_debug("- authkeyid.id %*phN\n",
x509->akid_id->len, x509->akid_id->data);
if (x509->akid_skid)
pr_debug("- authkeyid.skid %*phN\n",
x509->akid_skid->len, x509->akid_skid->data);
if ((!x509->akid_id && !x509->akid_skid) ||
strcmp(x509->subject, x509->issuer) == 0) {
/* If there's no authority certificate specified, then
* the certificate must be self-signed and is the root
......@@ -215,21 +225,42 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
/* Look through the X.509 certificates in the PKCS#7 message's
* list to see if the next one is there.
*/
pr_debug("- want %*phN\n",
x509->authority->len, x509->authority->data);
for (p = pkcs7->certs; p; p = p->next) {
if (!p->skid)
continue;
pr_debug("- cmp [%u] %*phN\n",
p->index, p->skid->len, p->skid->data);
if (asymmetric_key_id_same(p->skid, x509->authority))
goto found_issuer;
auth = x509->akid_id;
if (auth) {
pr_debug("- want %*phN\n", auth->len, auth->data);
for (p = pkcs7->certs; p; p = p->next) {
pr_debug("- cmp [%u] %*phN\n",
p->index, p->id->len, p->id->data);
if (asymmetric_key_id_same(p->id, auth))
goto found_issuer_check_skid;
}
} else {
auth = x509->akid_skid;
pr_debug("- want %*phN\n", auth->len, auth->data);
for (p = pkcs7->certs; p; p = p->next) {
if (!p->skid)
continue;
pr_debug("- cmp [%u] %*phN\n",
p->index, p->skid->len, p->skid->data);
if (asymmetric_key_id_same(p->skid, auth))
goto found_issuer;
}
}
/* We didn't find the root of this chain */
pr_debug("- top\n");
return 0;
found_issuer_check_skid:
/* We matched issuer + serialNumber, but if there's an
* authKeyId.keyId, that must match the CA subjKeyId also.
*/
if (x509->akid_skid &&
!asymmetric_key_id_same(p->skid, x509->akid_skid)) {
pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n",
sinfo->index, x509->index, p->index);
return -EKEYREJECTED;
}
found_issuer:
pr_debug("- subject %s\n", p->subject);
if (p->seen) {
......@@ -289,6 +320,18 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
pr_devel("Using X.509[%u] for sig %u\n",
sinfo->signer->index, sinfo->index);
/* Check that the PKCS#7 signing time is valid according to the X.509
* certificate. We can't, however, check against the system clock
* since that may not have been set yet and may be wrong.
*/
if (test_bit(sinfo_has_signing_time, &sinfo->aa_set)) {
if (sinfo->signing_time < sinfo->signer->valid_from ||
sinfo->signing_time > sinfo->signer->valid_to) {
pr_warn("Message signed outside of X.509 validity window\n");
return -EKEYREJECTED;
}
}
/* Verify the PKCS#7 binary against the key */
ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig);
if (ret < 0)
......@@ -303,6 +346,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
/**
* pkcs7_verify - Verify a PKCS#7 message
* @pkcs7: The PKCS#7 message to be verified
* @usage: The use to which the key is being put
*
* Verify a PKCS#7 message is internally consistent - that is, the data digest
* matches the digest in the AuthAttrs and any signature in the message or one
......@@ -314,6 +358,9 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
*
* Returns, in order of descending priority:
*
* (*) -EKEYREJECTED if a key was selected that had a usage restriction at
* odds with the specified usage, or:
*
* (*) -EKEYREJECTED if a signature failed to match for which we found an
* appropriate X.509 certificate, or:
*
......@@ -325,7 +372,8 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
* (*) 0 if all the signature chains that don't incur -ENOPKG can be verified
* (note that a signature chain may be of zero length), or:
*/
int pkcs7_verify(struct pkcs7_message *pkcs7)
int pkcs7_verify(struct pkcs7_message *pkcs7,
enum key_being_used_for usage)
{
struct pkcs7_signed_info *sinfo;
struct x509_certificate *x509;
......@@ -334,12 +382,48 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
kenter("");
switch (usage) {
case VERIFYING_MODULE_SIGNATURE:
if (pkcs7->data_type != OID_data) {
pr_warn("Invalid module sig (not pkcs7-data)\n");
return -EKEYREJECTED;
}
if (pkcs7->have_authattrs) {
pr_warn("Invalid module sig (has authattrs)\n");
return -EKEYREJECTED;
}
break;
case VERIFYING_FIRMWARE_SIGNATURE:
if (pkcs7->data_type != OID_data) {
pr_warn("Invalid firmware sig (not pkcs7-data)\n");
return -EKEYREJECTED;
}
if (!pkcs7->have_authattrs) {
pr_warn("Invalid firmware sig (missing authattrs)\n");
return -EKEYREJECTED;
}
break;
case VERIFYING_KEXEC_PE_SIGNATURE:
if (pkcs7->data_type != OID_msIndirectData) {
pr_warn("Invalid kexec sig (not Authenticode)\n");
return -EKEYREJECTED;
}
/* Authattr presence checked in parser */
break;
case VERIFYING_UNSPECIFIED_SIGNATURE:
if (pkcs7->data_type != OID_data) {
pr_warn("Invalid unspecified sig (not pkcs7-data)\n");
return -EKEYREJECTED;
}
break;
default:
return -EINVAL;
}
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] %*phN\n",
n, x509->authority->len, x509->authority->data);
}
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
......@@ -359,3 +443,28 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
return enopkg;
}
EXPORT_SYMBOL_GPL(pkcs7_verify);
/**
* pkcs7_supply_detached_data - Supply the data needed to verify a PKCS#7 message
* @pkcs7: The PKCS#7 message
* @data: The data to be verified
* @datalen: The amount of data
*
* Supply the detached data needed to verify a PKCS#7 message. Note that no
* attempt to retain/pin the data is made. That is left to the caller. The
* data will not be modified by pkcs7_verify() and will not be freed when the
* PKCS#7 message is freed.
*
* Returns -EINVAL if data is already supplied in the message, 0 otherwise.
*/
int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7,
const void *data, size_t datalen)
{
if (pkcs7->data) {
pr_debug("Data already supplied\n");
return -EINVAL;
}
pkcs7->data = data;
pkcs7->data_len = datalen;
return 0;
}
......@@ -39,6 +39,7 @@ EXPORT_SYMBOL_GPL(pkey_algo);
const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = {
[PKEY_ID_PGP] = "PGP",
[PKEY_ID_X509] = "X509",
[PKEY_ID_PKCS7] = "PKCS#7",
};
EXPORT_SYMBOL_GPL(pkey_id_type_name);
......
......@@ -393,6 +393,7 @@ static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
* @pebuf: Buffer containing the PE binary image
* @pelen: Length of the binary image
* @trust_keyring: Signing certificates to use as starting points
* @usage: The use to which the key is being put.
* @_trusted: Set to true if trustworth, false otherwise
*
* Validate that the certificate chain inside the PKCS#7 message inside the PE
......@@ -417,7 +418,9 @@ static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
* May also return -ENOMEM.
*/
int verify_pefile_signature(const void *pebuf, unsigned pelen,
struct key *trusted_keyring, bool *_trusted)
struct key *trusted_keyring,
enum key_being_used_for usage,
bool *_trusted)
{
struct pkcs7_message *pkcs7;
struct pefile_context ctx;
......@@ -462,7 +465,7 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
if (ret < 0)
goto error;
ret = pkcs7_verify(pkcs7);
ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error;
......
-- X.509 AuthorityKeyIdentifier
-- rfc5280 section 4.2.1.1
AuthorityKeyIdentifier ::= SEQUENCE {
keyIdentifier [0] IMPLICIT KeyIdentifier OPTIONAL,
authorityCertIssuer [1] IMPLICIT GeneralNames OPTIONAL,
authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL
}
KeyIdentifier ::= OCTET STRING ({ x509_akid_note_kid })
CertificateSerialNumber ::= INTEGER ({ x509_akid_note_serial })
GeneralNames ::= SEQUENCE OF GeneralName
GeneralName ::= CHOICE {
otherName [0] ANY,
rfc822Name [1] IA5String,
dNSName [2] IA5String,
x400Address [3] ANY,
directoryName [4] Name ({ x509_akid_note_name }),
ediPartyName [5] ANY,
uniformResourceIdentifier [6] IA5String,
iPAddress [7] OCTET STRING,
registeredID [8] OBJECT IDENTIFIER
}
Name ::= SEQUENCE OF RelativeDistinguishedName
RelativeDistinguishedName ::= SET OF AttributeValueAssertion
AttributeValueAssertion ::= SEQUENCE {
attributeType OBJECT IDENTIFIER ({ x509_note_OID }),
attributeValue ANY ({ x509_extract_name_segment })
}
......@@ -18,6 +18,7 @@
#include "public_key.h"
#include "x509_parser.h"
#include "x509-asn1.h"
#include "x509_akid-asn1.h"
#include "x509_rsakey-asn1.h"
struct x509_parse_context {
......@@ -35,6 +36,10 @@ struct x509_parse_context {
u16 o_offset; /* Offset of organizationName (O) */
u16 cn_offset; /* Offset of commonName (CN) */
u16 email_offset; /* Offset of emailAddress */
unsigned raw_akid_size;
const void *raw_akid; /* Raw authorityKeyId in ASN.1 */
const void *akid_raw_issuer; /* Raw directoryName in authorityKeyId */
unsigned akid_raw_issuer_size;
};
/*
......@@ -48,7 +53,8 @@ void x509_free_certificate(struct x509_certificate *cert)
kfree(cert->subject);
kfree(cert->id);
kfree(cert->skid);
kfree(cert->authority);
kfree(cert->akid_id);
kfree(cert->akid_skid);
kfree(cert->sig.digest);
mpi_free(cert->sig.rsa.s);
kfree(cert);
......@@ -85,6 +91,18 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
if (ret < 0)
goto error_decode;
/* Decode the AuthorityKeyIdentifier */
if (ctx->raw_akid) {
pr_devel("AKID: %u %*phN\n",
ctx->raw_akid_size, ctx->raw_akid_size, ctx->raw_akid);
ret = asn1_ber_decoder(&x509_akid_decoder, ctx,
ctx->raw_akid, ctx->raw_akid_size);
if (ret < 0) {
pr_warn("Couldn't decode AuthKeyIdentifier\n");
goto error_decode;
}
}
/* Decode the public key */
ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx,
ctx->key, ctx->key_size);
......@@ -422,7 +440,6 @@ int x509_process_extension(void *context, size_t hdrlen,
struct x509_parse_context *ctx = context;
struct asymmetric_key_id *kid;
const unsigned char *v = value;
int i;
pr_debug("Extension: %u\n", ctx->last_oid);
......@@ -437,9 +454,7 @@ int x509_process_extension(void *context, size_t hdrlen,
ctx->cert->raw_skid_size = vlen;
ctx->cert->raw_skid = v;
kid = asymmetric_key_generate_id(ctx->cert->raw_subject,
ctx->cert->raw_subject_size,
v, vlen);
kid = asymmetric_key_generate_id(v, vlen, "", 0);
if (IS_ERR(kid))
return PTR_ERR(kid);
ctx->cert->skid = kid;
......@@ -449,117 +464,113 @@ int x509_process_extension(void *context, size_t hdrlen,
if (ctx->last_oid == OID_authorityKeyIdentifier) {
/* Get hold of the CA key fingerprint */
if (ctx->cert->authority || vlen < 5)
return -EBADMSG;
/* Authority Key Identifier must be a Constructed SEQUENCE */
if (v[0] != (ASN1_SEQ | (ASN1_CONS << 5)))
return -EBADMSG;
/* Authority Key Identifier is not indefinite length */
if (unlikely(vlen == ASN1_INDEFINITE_LENGTH))
return -EBADMSG;
if (vlen < ASN1_INDEFINITE_LENGTH) {
/* Short Form length */
if (v[1] != vlen - 2 ||
v[2] != SEQ_TAG_KEYID ||
v[3] > vlen - 4)
return -EBADMSG;
vlen = v[3];
v += 4;
} else {
/* Long Form length */
size_t seq_len = 0;
size_t sub = v[1] - ASN1_INDEFINITE_LENGTH;
if (sub > 2)
return -EBADMSG;
/* calculate the length from subsequent octets */
v += 2;
for (i = 0; i < sub; i++) {
seq_len <<= 8;
seq_len |= v[i];
}
if (seq_len != vlen - 2 - sub ||
v[sub] != SEQ_TAG_KEYID ||
v[sub + 1] > vlen - 4 - sub)
return -EBADMSG;
vlen = v[sub + 1];
v += (sub + 2);
}
kid = asymmetric_key_generate_id(ctx->cert->raw_issuer,
ctx->cert->raw_issuer_size,
v, vlen);
if (IS_ERR(kid))
return PTR_ERR(kid);
pr_debug("authkeyid %*phN\n", kid->len, kid->data);
ctx->cert->authority = kid;
ctx->raw_akid = v;
ctx->raw_akid_size = vlen;
return 0;
}
return 0;
}
/*
* Record a certificate time.
/**
* x509_decode_time - Decode an X.509 time ASN.1 object
* @_t: The time to fill in
* @hdrlen: The length of the object header
* @tag: The object tag
* @value: The object value
* @vlen: The size of the object value
*
* Decode an ASN.1 universal time or generalised time field into a struct the
* kernel can handle and check it for validity. The time is decoded thus:
*
* [RFC5280 §4.1.2.5]
* CAs conforming to this profile MUST always encode certificate validity
* dates through the year 2049 as UTCTime; certificate validity dates in
* 2050 or later MUST be encoded as GeneralizedTime. Conforming
* applications MUST be able to process validity dates that are encoded in
* either UTCTime or GeneralizedTime.
*/
static int x509_note_time(struct tm *tm, size_t hdrlen,
unsigned char tag,
const unsigned char *value, size_t vlen)
int x509_decode_time(time64_t *_t, size_t hdrlen,
unsigned char tag,
const unsigned char *value, size_t vlen)
{
static const unsigned char month_lengths[] = { 31, 29, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31 };
const unsigned char *p = value;
unsigned year, mon, day, hour, min, sec, mon_len;
#define dec2bin(X) ((X) - '0')
#define dec2bin(X) ({ unsigned char x = (X) - '0'; if (x > 9) goto invalid_time; x; })
#define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; })
if (tag == ASN1_UNITIM) {
/* UTCTime: YYMMDDHHMMSSZ */
if (vlen != 13)
goto unsupported_time;
tm->tm_year = DD2bin(p);
if (tm->tm_year >= 50)
tm->tm_year += 1900;
year = DD2bin(p);
if (year >= 50)
year += 1900;
else
tm->tm_year += 2000;
year += 2000;
} else if (tag == ASN1_GENTIM) {
/* GenTime: YYYYMMDDHHMMSSZ */
if (vlen != 15)
goto unsupported_time;
tm->tm_year = DD2bin(p) * 100 + DD2bin(p);
year = DD2bin(p) * 100 + DD2bin(p);
if (year >= 1950 && year <= 2049)
goto invalid_time;
} else {
goto unsupported_time;
}
tm->tm_year -= 1900;
tm->tm_mon = DD2bin(p) - 1;
tm->tm_mday = DD2bin(p);
tm->tm_hour = DD2bin(p);
tm->tm_min = DD2bin(p);
tm->tm_sec = DD2bin(p);
mon = DD2bin(p);
day = DD2bin(p);
hour = DD2bin(p);
min = DD2bin(p);
sec = DD2bin(p);
if (*p != 'Z')
goto unsupported_time;
mon_len = month_lengths[mon];
if (mon == 2) {
if (year % 4 == 0) {
mon_len = 29;
if (year % 100 == 0) {
year /= 100;
if (year % 4 != 0)
mon_len = 28;
}
}
}
if (year < 1970 ||
mon < 1 || mon > 12 ||
day < 1 || day > mon_len ||
hour < 0 || hour > 23 ||
min < 0 || min > 59 ||
sec < 0 || sec > 59)
goto invalid_time;
*_t = mktime64(year, mon, day, hour, min, sec);
return 0;
unsupported_time:
pr_debug("Got unsupported time [tag %02x]: '%*.*s'\n",
tag, (int)vlen, (int)vlen, value);
pr_debug("Got unsupported time [tag %02x]: '%*phN'\n",
tag, (int)vlen, value);
return -EBADMSG;
invalid_time:
pr_debug("Got invalid time [tag %02x]: '%*phN'\n",
tag, (int)vlen, value);
return -EBADMSG;
}
EXPORT_SYMBOL_GPL(x509_decode_time);
int x509_note_not_before(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct x509_parse_context *ctx = context;
return x509_note_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen);
return x509_decode_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen);
}
int x509_note_not_after(void *context, size_t hdrlen,
......@@ -567,5 +578,71 @@ int x509_note_not_after(void *context, size_t hdrlen,
const void *value, size_t vlen)
{
struct x509_parse_context *ctx = context;
return x509_note_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
return x509_decode_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
}
/*
* Note a key identifier-based AuthorityKeyIdentifier
*/
int x509_akid_note_kid(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct x509_parse_context *ctx = context;
struct asymmetric_key_id *kid;
pr_debug("AKID: keyid: %*phN\n", (int)vlen, value);
if (ctx->cert->akid_skid)
return 0;
kid = asymmetric_key_generate_id(value, vlen, "", 0);
if (IS_ERR(kid))
return PTR_ERR(kid);
pr_debug("authkeyid %*phN\n", kid->len, kid->data);
ctx->cert->akid_skid = kid;
return 0;
}
/*
* Note a directoryName in an AuthorityKeyIdentifier
*/
int x509_akid_note_name(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct x509_parse_context *ctx = context;
pr_debug("AKID: name: %*phN\n", (int)vlen, value);
ctx->akid_raw_issuer = value;
ctx->akid_raw_issuer_size = vlen;
return 0;
}
/*
* Note a serial number in an AuthorityKeyIdentifier
*/
int x509_akid_note_serial(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct x509_parse_context *ctx = context;
struct asymmetric_key_id *kid;
pr_debug("AKID: serial: %*phN\n", (int)vlen, value);
if (!ctx->akid_raw_issuer || ctx->cert->akid_id)
return 0;
kid = asymmetric_key_generate_id(value,
vlen,
ctx->akid_raw_issuer,
ctx->akid_raw_issuer_size);
if (IS_ERR(kid))
return PTR_ERR(kid);
pr_debug("authkeyid %*phN\n", kid->len, kid->data);
ctx->cert->akid_id = kid;
return 0;
}
......@@ -19,11 +19,12 @@ struct x509_certificate {
struct public_key_signature sig; /* Signature parameters */
char *issuer; /* Name of certificate issuer */
char *subject; /* Name of certificate subject */
struct asymmetric_key_id *id; /* Serial number + issuer */
struct asymmetric_key_id *id; /* Issuer + Serial number */
struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */
struct asymmetric_key_id *authority; /* Authority key identifier (optional) */
struct tm valid_from;
struct tm valid_to;
struct asymmetric_key_id *akid_id; /* CA AuthKeyId matching ->id (optional) */
struct asymmetric_key_id *akid_skid; /* CA AuthKeyId matching ->skid (optional) */
time64_t valid_from;
time64_t valid_to;
const void *tbs; /* Signed data */
unsigned tbs_size; /* Size of signed data */
unsigned raw_sig_size; /* Size of sigature */
......@@ -48,6 +49,9 @@ struct x509_certificate {
*/
extern void x509_free_certificate(struct x509_certificate *cert);
extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
extern int x509_decode_time(time64_t *_t, size_t hdrlen,
unsigned char tag,
const unsigned char *value, size_t vlen);
/*
* x509_public_key.c
......
......@@ -65,23 +65,37 @@ __setup("ca_keys=", ca_keys_setup);
/**
* x509_request_asymmetric_key - Request a key by X.509 certificate params.
* @keyring: The keys to search.
* @kid: The key ID.
* @id: The issuer & serialNumber to look for or NULL.
* @skid: The subjectKeyIdentifier to look for or NULL.
* @partial: Use partial match if true, exact if false.
*
* 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.
* Find a key in the given keyring by identifier. The preferred identifier is
* the issuer + serialNumber and the fallback identifier is the
* subjectKeyIdentifier. If both are given, the lookup is by the former, but
* the latter must also match.
*/
struct key *x509_request_asymmetric_key(struct key *keyring,
const struct asymmetric_key_id *kid,
const struct asymmetric_key_id *id,
const struct asymmetric_key_id *skid,
bool partial)
{
key_ref_t key;
char *id, *p;
struct key *key;
key_ref_t ref;
const char *lookup;
char *req, *p;
int len;
if (id) {
lookup = id->data;
len = id->len;
} else {
lookup = skid->data;
len = skid->len;
}
/* Construct an identifier "id:<keyid>". */
p = id = kmalloc(2 + 1 + kid->len * 2 + 1, GFP_KERNEL);
if (!id)
p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
if (!req)
return ERR_PTR(-ENOMEM);
if (partial) {
......@@ -92,32 +106,48 @@ struct key *x509_request_asymmetric_key(struct key *keyring,
*p++ = 'x';
}
*p++ = ':';
p = bin2hex(p, kid->data, kid->len);
p = bin2hex(p, lookup, len);
*p = 0;
pr_debug("Look up: \"%s\"\n", id);
pr_debug("Look up: \"%s\"\n", req);
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);
ref = keyring_search(make_key_ref(keyring, 1),
&key_type_asymmetric, req);
if (IS_ERR(ref))
pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
kfree(req);
if (IS_ERR(key)) {
switch (PTR_ERR(key)) {
if (IS_ERR(ref)) {
switch (PTR_ERR(ref)) {
/* Hide some search errors */
case -EACCES:
case -ENOTDIR:
case -EAGAIN:
return ERR_PTR(-ENOKEY);
default:
return ERR_CAST(key);
return ERR_CAST(ref);
}
}
key = key_ref_to_ptr(ref);
if (id && skid) {
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
if (!kids->id[1]) {
pr_debug("issuer+serial match, but expected SKID missing\n");
goto reject;
}
if (!asymmetric_key_id_same(skid, kids->id[1])) {
pr_debug("issuer+serial match, but SKID does not\n");
goto reject;
}
}
pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
return key;
pr_devel("<==%s() = 0 [%x]\n", __func__,
key_serial(key_ref_to_ptr(key)));
return key_ref_to_ptr(key);
reject:
key_put(key);
return ERR_PTR(-EKEYREJECTED);
}
EXPORT_SYMBOL_GPL(x509_request_asymmetric_key);
......@@ -227,10 +257,11 @@ static int x509_validate_trust(struct x509_certificate *cert,
if (!trust_keyring)
return -EOPNOTSUPP;
if (ca_keyid && !asymmetric_key_id_partial(cert->authority, ca_keyid))
if (ca_keyid && !asymmetric_key_id_partial(cert->akid_skid, ca_keyid))
return -EPERM;
key = x509_request_asymmetric_key(trust_keyring, cert->authority,
key = x509_request_asymmetric_key(trust_keyring,
cert->akid_id, cert->akid_skid,
false);
if (!IS_ERR(key)) {
if (!use_builtin_keys
......@@ -271,14 +302,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
}
pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pub->pkey_algo]);
pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n",
cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1,
cert->valid_from.tm_mday, cert->valid_from.tm_hour,
cert->valid_from.tm_min, cert->valid_from.tm_sec);
pr_devel("Cert Valid To: %04ld-%02d-%02d %02d:%02d:%02d\n",
cert->valid_to.tm_year + 1900, cert->valid_to.tm_mon + 1,
cert->valid_to.tm_mday, cert->valid_to.tm_hour,
cert->valid_to.tm_min, cert->valid_to.tm_sec);
pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to);
pr_devel("Cert Signature: %s + %s\n",
pkey_algo_name[cert->sig.pkey_algo],
hash_algo_name[cert->sig.pkey_hash_algo]);
......@@ -287,8 +311,9 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
cert->pub->id_type = PKEY_ID_X509;
/* Check the signature on the key if it appears to be self-signed */
if (!cert->authority ||
asymmetric_key_id_same(cert->skid, cert->authority)) {
if ((!cert->akid_skid && !cert->akid_id) ||
asymmetric_key_id_same(cert->skid, cert->akid_skid) ||
asymmetric_key_id_same(cert->id, cert->akid_id)) {
ret = x509_check_signature(cert->pub, cert); /* self-signed */
if (ret < 0)
goto error_free_cert;
......
......@@ -9,6 +9,11 @@
* 2 of the Licence, or (at your option) any later version.
*/
#ifndef _CRYPTO_PKCS7_H
#define _CRYPTO_PKCS7_H
#include <crypto/public_key.h>
struct key;
struct pkcs7_message;
......@@ -33,4 +38,10 @@ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
/*
* pkcs7_verify.c
*/
extern int pkcs7_verify(struct pkcs7_message *pkcs7);
extern int pkcs7_verify(struct pkcs7_message *pkcs7,
enum key_being_used_for usage);
extern int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7,
const void *data, size_t datalen);
#endif /* _CRYPTO_PKCS7_H */
......@@ -33,11 +33,26 @@ extern const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST];
enum pkey_id_type {
PKEY_ID_PGP, /* OpenPGP generated key ID */
PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */
PKEY_ID_PKCS7, /* Signature in PKCS#7 message */
PKEY_ID_TYPE__LAST
};
extern const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST];
/*
* The use to which an asymmetric key is being put.
*/
enum key_being_used_for {
VERIFYING_MODULE_SIGNATURE,
VERIFYING_FIRMWARE_SIGNATURE,
VERIFYING_KEXEC_PE_SIGNATURE,
VERIFYING_KEY_SIGNATURE,
VERIFYING_KEY_SELF_SIGNATURE,
VERIFYING_UNSPECIFIED_SIGNATURE,
NR__KEY_BEING_USED_FOR
};
extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
/*
* Cryptographic data for the public-key subtype of the asymmetric key type.
*
......@@ -101,7 +116,8 @@ extern int verify_signature(const struct key *key,
struct asymmetric_key_id;
extern struct key *x509_request_asymmetric_key(struct key *keyring,
const struct asymmetric_key_id *kid,
const struct asymmetric_key_id *id,
const struct asymmetric_key_id *skid,
bool partial);
#endif /* _LINUX_PUBLIC_KEY_H */
......@@ -15,6 +15,7 @@
#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
#include <linux/key.h>
#include <crypto/public_key.h>
extern struct key *system_trusted_keyring;
static inline struct key *get_system_trusted_keyring(void)
......@@ -28,4 +29,10 @@ static inline struct key *get_system_trusted_keyring(void)
}
#endif
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
extern int system_verify_data(const void *data, unsigned long len,
const void *raw_pkcs7, size_t pkcs7_len,
enum key_being_used_for usage);
#endif
#endif /* _KEYS_SYSTEM_KEYRING_H */
......@@ -45,23 +45,27 @@ enum asn1_opcode {
ASN1_OP_MATCH_JUMP = 0x04,
ASN1_OP_MATCH_JUMP_OR_SKIP = 0x05,
ASN1_OP_MATCH_ANY = 0x08,
ASN1_OP_MATCH_ANY_OR_SKIP = 0x09,
ASN1_OP_MATCH_ANY_ACT = 0x0a,
ASN1_OP_MATCH_ANY_ACT_OR_SKIP = 0x0b,
/* Everything before here matches unconditionally */
ASN1_OP_COND_MATCH_OR_SKIP = 0x11,
ASN1_OP_COND_MATCH_ACT_OR_SKIP = 0x13,
ASN1_OP_COND_MATCH_JUMP_OR_SKIP = 0x15,
ASN1_OP_COND_MATCH_ANY = 0x18,
ASN1_OP_COND_MATCH_ANY_OR_SKIP = 0x19,
ASN1_OP_COND_MATCH_ANY_ACT = 0x1a,
ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP = 0x1b,
/* Everything before here will want a tag from the data */
#define ASN1_OP__MATCHES_TAG ASN1_OP_COND_MATCH_ANY_ACT
#define ASN1_OP__MATCHES_TAG ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP
/* These are here to help fill up space */
ASN1_OP_COND_FAIL = 0x1b,
ASN1_OP_COMPLETE = 0x1c,
ASN1_OP_ACT = 0x1d,
ASN1_OP_RETURN = 0x1e,
ASN1_OP_COND_FAIL = 0x1c,
ASN1_OP_COMPLETE = 0x1d,
ASN1_OP_ACT = 0x1e,
ASN1_OP_MAYBE_ACT = 0x1f,
/* The following eight have bit 0 -> SET, 1 -> OF, 2 -> ACT */
ASN1_OP_END_SEQ = 0x20,
......@@ -76,6 +80,8 @@ enum asn1_opcode {
#define ASN1_OP_END__OF 0x02
#define ASN1_OP_END__ACT 0x04
ASN1_OP_RETURN = 0x28,
ASN1_OP__NR
};
......
......@@ -40,6 +40,11 @@ struct lsm_network_audit {
} fam;
};
struct lsm_ioctlop_audit {
struct path path;
u16 cmd;
};
/* Auxiliary data to use in generating the audit record. */
struct common_audit_data {
char type;
......@@ -53,6 +58,7 @@ struct common_audit_data {
#define LSM_AUDIT_DATA_KMOD 8
#define LSM_AUDIT_DATA_INODE 9
#define LSM_AUDIT_DATA_DENTRY 10
#define LSM_AUDIT_DATA_IOCTL_OP 11
union {
struct path path;
struct dentry *dentry;
......@@ -68,6 +74,7 @@ struct common_audit_data {
} key_struct;
#endif
char *kmod_name;
struct lsm_ioctlop_audit *op;
} u;
/* this union contains LSM specific data */
union {
......
......@@ -1881,8 +1881,10 @@ static inline void security_delete_hooks(struct security_hook_list *hooks,
extern int __init security_module_enable(const char *module);
extern void __init capability_add_hooks(void);
#ifdef CONFIG_SECURITY_YAMA_STACKED
void __init yama_add_hooks(void);
#ifdef CONFIG_SECURITY_YAMA
extern void __init yama_add_hooks(void);
#else
static inline void __init yama_add_hooks(void) { }
#endif
#endif /* ! __LINUX_LSM_HOOKS_H */
......@@ -41,7 +41,7 @@ enum OID {
OID_signed_data, /* 1.2.840.113549.1.7.2 */
/* PKCS#9 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)} */
OID_email_address, /* 1.2.840.113549.1.9.1 */
OID_content_type, /* 1.2.840.113549.1.9.3 */
OID_contentType, /* 1.2.840.113549.1.9.3 */
OID_messageDigest, /* 1.2.840.113549.1.9.4 */
OID_signingTime, /* 1.2.840.113549.1.9.5 */
OID_smimeCapabilites, /* 1.2.840.113549.1.9.15 */
......@@ -54,6 +54,8 @@ enum OID {
/* Microsoft Authenticode & Software Publishing */
OID_msIndirectData, /* 1.3.6.1.4.1.311.2.1.4 */
OID_msStatementType, /* 1.3.6.1.4.1.311.2.1.11 */
OID_msSpOpusInfo, /* 1.3.6.1.4.1.311.2.1.12 */
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 */
......@@ -61,6 +63,9 @@ enum OID {
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 */
OID_sha384, /* 2.16.840.1.101.3.4.2.2 */
OID_sha512, /* 2.16.840.1.101.3.4.2.3 */
OID_sha224, /* 2.16.840.1.101.3.4.2.4 */
/* Distinguished Name attribute IDs [RFC 2256] */
OID_commonName, /* 2.5.4.3 */
......
......@@ -34,6 +34,7 @@
#define PT_TRACE_SECCOMP PT_EVENT_FLAG(PTRACE_EVENT_SECCOMP)
#define PT_EXITKILL (PTRACE_O_EXITKILL << PT_OPT_FLAG_SHIFT)
#define PT_SUSPEND_SECCOMP (PTRACE_O_SUSPEND_SECCOMP << PT_OPT_FLAG_SHIFT)
/* single stepping state bits (used on ARM and PA-RISC) */
#define PT_SINGLESTEP_BIT 31
......
......@@ -78,7 +78,7 @@ static inline long prctl_set_seccomp(unsigned long arg2, char __user *arg3)
static inline int seccomp_mode(struct seccomp *s)
{
return 0;
return SECCOMP_MODE_DISABLED;
}
#endif /* CONFIG_SECCOMP */
......
......@@ -12,7 +12,11 @@
#ifndef _LINUX_VERIFY_PEFILE_H
#define _LINUX_VERIFY_PEFILE_H
#include <crypto/public_key.h>
extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
struct key *trusted_keyring, bool *_trusted);
struct key *trusted_keyring,
enum key_being_used_for usage,
bool *_trusted);
#endif /* _LINUX_VERIFY_PEFILE_H */
......@@ -89,9 +89,11 @@ struct ptrace_peeksiginfo_args {
#define PTRACE_O_TRACESECCOMP (1 << PTRACE_EVENT_SECCOMP)
/* eventless options */
#define PTRACE_O_EXITKILL (1 << 20)
#define PTRACE_O_EXITKILL (1 << 20)
#define PTRACE_O_SUSPEND_SECCOMP (1 << 21)
#define PTRACE_O_MASK (0x000000ff | PTRACE_O_EXITKILL)
#define PTRACE_O_MASK (\
0x000000ff | PTRACE_O_EXITKILL | PTRACE_O_SUSPEND_SECCOMP)
#include <asm/ptrace.h>
......
......@@ -1765,17 +1765,23 @@ config MMAP_ALLOW_UNINITIALIZED
See Documentation/nommu-mmap.txt for more information.
config SYSTEM_TRUSTED_KEYRING
bool "Provide system-wide ring of trusted keys"
depends on KEYS
config SYSTEM_DATA_VERIFICATION
def_bool n
select SYSTEM_TRUSTED_KEYRING
select KEYS
select CRYPTO
select ASYMMETRIC_KEY_TYPE
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
select PUBLIC_KEY_ALGO_RSA
select ASN1
select OID_REGISTRY
select X509_CERTIFICATE_PARSER
select PKCS7_MESSAGE_PARSER
help
Provide a system keyring to which trusted keys can be added. Keys in
the keyring are considered to be trusted. Keys may be added at will
by the kernel from compiled-in data and from hardware key stores, but
userspace may only add extra keys if those keys can be verified by
keys already in the keyring.
Keys in this keyring are used by module signature checking.
Provide PKCS#7 message verification using the contents of the system
trusted keyring to provide public keys. This then can be used for
module verification, kexec image verification and firmware blob
verification.
config PROFILING
bool "Profiling support"
......@@ -1885,20 +1891,16 @@ config MODULE_SRCVERSION_ALL
config MODULE_SIG
bool "Module signature verification"
depends on MODULES
select SYSTEM_TRUSTED_KEYRING
select KEYS
select CRYPTO
select ASYMMETRIC_KEY_TYPE
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
select PUBLIC_KEY_ALGO_RSA
select ASN1
select OID_REGISTRY
select X509_CERTIFICATE_PARSER
select SYSTEM_DATA_VERIFICATION
help
Check modules for valid signatures upon load: the signature
is simply appended to the module. For more information see
Documentation/module-signing.txt.
Note that this option adds the OpenSSL development packages as a
kernel build dependency so that the signing tool can use its crypto
library.
!!!WARNING!!! If you enable this option, you MUST make sure that the
module DOES NOT get stripped after being signed. This includes the
debuginfo strip done by some packagers (such as rpmbuild) and
......
......@@ -45,7 +45,6 @@ ifneq ($(CONFIG_SMP),y)
obj-y += up.o
endif
obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_MODULE_SIG) += module_signing.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
......@@ -112,99 +111,3 @@ $(obj)/config_data.gz: $(KCONFIG_CONFIG) FORCE
targets += config_data.h
$(obj)/config_data.h: $(obj)/config_data.gz FORCE
$(call filechk,ikconfiggz)
###############################################################################
#
# Roll all the X.509 certificates that we can find together and pull them into
# the kernel so that they get loaded into the system trusted keyring during
# boot.
#
# We look in the source root and the build root for all files whose name ends
# in ".x509". Unfortunately, this will generate duplicate filenames, so we
# have make canonicalise the pathnames and then sort them to discard the
# duplicates.
#
###############################################################################
ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
X509_CERTIFICATES-y := $(wildcard *.x509) $(wildcard $(srctree)/*.x509)
X509_CERTIFICATES-$(CONFIG_MODULE_SIG) += $(objtree)/signing_key.x509
X509_CERTIFICATES-raw := $(sort $(foreach CERT,$(X509_CERTIFICATES-y), \
$(or $(realpath $(CERT)),$(CERT))))
X509_CERTIFICATES := $(subst $(realpath $(objtree))/,,$(X509_CERTIFICATES-raw))
ifeq ($(X509_CERTIFICATES),)
$(warning *** No X.509 certificates found ***)
endif
ifneq ($(wildcard $(obj)/.x509.list),)
ifneq ($(shell cat $(obj)/.x509.list),$(X509_CERTIFICATES))
$(warning X.509 certificate list changed to "$(X509_CERTIFICATES)" from "$(shell cat $(obj)/.x509.list)")
$(shell rm $(obj)/.x509.list)
endif
endif
kernel/system_certificates.o: $(obj)/x509_certificate_list
quiet_cmd_x509certs = CERTS $@
cmd_x509certs = cat $(X509_CERTIFICATES) /dev/null >$@ $(foreach X509,$(X509_CERTIFICATES),; $(kecho) " - Including cert $(X509)")
targets += $(obj)/x509_certificate_list
$(obj)/x509_certificate_list: $(X509_CERTIFICATES) $(obj)/.x509.list
$(call if_changed,x509certs)
targets += $(obj)/.x509.list
$(obj)/.x509.list:
@echo $(X509_CERTIFICATES) >$@
endif
clean-files := x509_certificate_list .x509.list
ifeq ($(CONFIG_MODULE_SIG),y)
###############################################################################
#
# If module signing is requested, say by allyesconfig, but a key has not been
# supplied, then one will need to be generated to make sure the build does not
# fail and that the kernel may be used afterwards.
#
###############################################################################
ifndef CONFIG_MODULE_SIG_HASH
$(error Could not determine digest type to use from kernel config)
endif
signing_key.priv signing_key.x509: x509.genkey
@echo "###"
@echo "### Now generating an X.509 key pair to be used for signing modules."
@echo "###"
@echo "### If this takes a long time, you might wish to run rngd in the"
@echo "### background to keep the supply of entropy topped up. It"
@echo "### needs to be run as root, and uses a hardware random"
@echo "### number generator if one is available."
@echo "###"
openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_HASH) -days 36500 \
-batch -x509 -config x509.genkey \
-outform DER -out signing_key.x509 \
-keyout signing_key.priv 2>&1
@echo "###"
@echo "### Key pair generated."
@echo "###"
x509.genkey:
@echo Generating X.509 key generation config
@echo >x509.genkey "[ req ]"
@echo >>x509.genkey "default_bits = 4096"
@echo >>x509.genkey "distinguished_name = req_distinguished_name"
@echo >>x509.genkey "prompt = no"
@echo >>x509.genkey "string_mask = utf8only"
@echo >>x509.genkey "x509_extensions = myexts"
@echo >>x509.genkey
@echo >>x509.genkey "[ req_distinguished_name ]"
@echo >>x509.genkey "#O = Unspecified company"
@echo >>x509.genkey "CN = Build time autogenerated kernel key"
@echo >>x509.genkey "#emailAddress = unspecified.user@unspecified.company"
@echo >>x509.genkey
@echo >>x509.genkey "[ myexts ]"
@echo >>x509.genkey "basicConstraints=critical,CA:FALSE"
@echo >>x509.genkey "keyUsage=digitalSignature"
@echo >>x509.genkey "subjectKeyIdentifier=hash"
@echo >>x509.genkey "authorityKeyIdentifier=keyid"
endif
......@@ -10,11 +10,8 @@
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <crypto/public_key.h>
#include <crypto/hash.h>
#include <keys/asymmetric-type.h>
#include <keys/system_keyring.h>
#include <crypto/public_key.h>
#include "module-internal.h"
/*
......@@ -28,170 +25,22 @@
* - Information block
*/
struct module_signature {
u8 algo; /* Public-key crypto algorithm [enum pkey_algo] */
u8 hash; /* Digest algorithm [enum hash_algo] */
u8 id_type; /* Key identifier type [enum pkey_id_type] */
u8 signer_len; /* Length of signer's name */
u8 key_id_len; /* Length of key identifier */
u8 algo; /* Public-key crypto algorithm [0] */
u8 hash; /* Digest algorithm [0] */
u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */
u8 signer_len; /* Length of signer's name [0] */
u8 key_id_len; /* Length of key identifier [0] */
u8 __pad[3];
__be32 sig_len; /* Length of signature data */
};
/*
* Digest the module contents.
*/
static struct public_key_signature *mod_make_digest(enum hash_algo hash,
const void *mod,
unsigned long modlen)
{
struct public_key_signature *pks;
struct crypto_shash *tfm;
struct shash_desc *desc;
size_t digest_size, desc_size;
int ret;
pr_devel("==>%s()\n", __func__);
/* 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[hash], 0, 0);
if (IS_ERR(tfm))
return (PTR_ERR(tfm) == -ENOENT) ? ERR_PTR(-ENOPKG) : ERR_CAST(tfm);
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
digest_size = crypto_shash_digestsize(tfm);
/* We allocate the hash operational data storage on the end of our
* context data and the digest output buffer on the end of that.
*/
ret = -ENOMEM;
pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL);
if (!pks)
goto error_no_pks;
pks->pkey_hash_algo = hash;
pks->digest = (u8 *)pks + sizeof(*pks) + desc_size;
pks->digest_size = digest_size;
desc = (void *)pks + sizeof(*pks);
desc->tfm = tfm;
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
ret = crypto_shash_init(desc);
if (ret < 0)
goto error;
ret = crypto_shash_finup(desc, mod, modlen, pks->digest);
if (ret < 0)
goto error;
crypto_free_shash(tfm);
pr_devel("<==%s() = ok\n", __func__);
return pks;
error:
kfree(pks);
error_no_pks:
crypto_free_shash(tfm);
pr_devel("<==%s() = %d\n", __func__, ret);
return ERR_PTR(ret);
}
/*
* Extract an MPI array from the signature data. This represents the actual
* signature. Each raw MPI is prefaced by a BE 2-byte value indicating the
* size of the MPI in bytes.
*
* RSA signatures only have one MPI, so currently we only read one.
*/
static int mod_extract_mpi_array(struct public_key_signature *pks,
const void *data, size_t len)
{
size_t nbytes;
MPI mpi;
if (len < 3)
return -EBADMSG;
nbytes = ((const u8 *)data)[0] << 8 | ((const u8 *)data)[1];
data += 2;
len -= 2;
if (len != nbytes)
return -EBADMSG;
mpi = mpi_read_raw_data(data, nbytes);
if (!mpi)
return -ENOMEM;
pks->mpi[0] = mpi;
pks->nr_mpi = 1;
return 0;
}
/*
* Request an asymmetric key.
*/
static struct key *request_asymmetric_key(const char *signer, size_t signer_len,
const u8 *key_id, size_t key_id_len)
{
key_ref_t key;
size_t i;
char *id, *q;
pr_devel("==>%s(,%zu,,%zu)\n", __func__, signer_len, key_id_len);
/* Construct an identifier. */
id = kmalloc(signer_len + 2 + key_id_len * 2 + 1, GFP_KERNEL);
if (!id)
return ERR_PTR(-ENOKEY);
memcpy(id, signer, signer_len);
q = id + signer_len;
*q++ = ':';
*q++ = ' ';
for (i = 0; i < key_id_len; i++) {
*q++ = hex_asc[*key_id >> 4];
*q++ = hex_asc[*key_id++ & 0x0f];
}
*q = 0;
pr_debug("Look up: \"%s\"\n", id);
key = keyring_search(make_key_ref(system_trusted_keyring, 1),
&key_type_asymmetric, id);
if (IS_ERR(key))
pr_warn("Request for unknown module 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);
}
/*
* Verify the signature on a module.
*/
int mod_verify_sig(const void *mod, unsigned long *_modlen)
{
struct public_key_signature *pks;
struct module_signature ms;
struct key *key;
const void *sig;
size_t modlen = *_modlen, sig_len;
int ret;
pr_devel("==>%s(,%zu)\n", __func__, modlen);
......@@ -205,46 +54,24 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
if (sig_len >= modlen)
return -EBADMSG;
modlen -= sig_len;
if ((size_t)ms.signer_len + ms.key_id_len >= modlen)
return -EBADMSG;
modlen -= (size_t)ms.signer_len + ms.key_id_len;
*_modlen = modlen;
sig = mod + modlen;
/* For the moment, only support RSA and X.509 identifiers */
if (ms.algo != PKEY_ALGO_RSA ||
ms.id_type != PKEY_ID_X509)
return -ENOPKG;
if (ms.hash >= PKEY_HASH__LAST ||
!hash_algo_name[ms.hash])
if (ms.id_type != PKEY_ID_PKCS7) {
pr_err("Module is not signed with expected PKCS#7 message\n");
return -ENOPKG;
key = request_asymmetric_key(sig, ms.signer_len,
sig + ms.signer_len, ms.key_id_len);
if (IS_ERR(key))
return PTR_ERR(key);
pks = mod_make_digest(ms.hash, mod, modlen);
if (IS_ERR(pks)) {
ret = PTR_ERR(pks);
goto error_put_key;
}
ret = mod_extract_mpi_array(pks, sig + ms.signer_len + ms.key_id_len,
sig_len);
if (ret < 0)
goto error_free_pks;
ret = verify_signature(key, pks);
pr_devel("verify_signature() = %d\n", ret);
if (ms.algo != 0 ||
ms.hash != 0 ||
ms.signer_len != 0 ||
ms.key_id_len != 0 ||
ms.__pad[0] != 0 ||
ms.__pad[1] != 0 ||
ms.__pad[2] != 0) {
pr_err("PKCS#7 signature info has unexpected non-zero params\n");
return -EBADMSG;
}
error_free_pks:
mpi_free(pks->rsa.s);
kfree(pks);
error_put_key:
key_put(key);
pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
return system_verify_data(mod, modlen, mod + modlen, sig_len,
VERIFYING_MODULE_SIGNATURE);
}
......@@ -556,6 +556,19 @@ static int ptrace_setoptions(struct task_struct *child, unsigned long data)
if (data & ~(unsigned long)PTRACE_O_MASK)
return -EINVAL;
if (unlikely(data & PTRACE_O_SUSPEND_SECCOMP)) {
if (!config_enabled(CONFIG_CHECKPOINT_RESTORE) ||
!config_enabled(CONFIG_SECCOMP))
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (seccomp_mode(&current->seccomp) != SECCOMP_MODE_DISABLED ||
current->ptrace & PT_SUSPEND_SECCOMP)
return -EPERM;
}
/* Avoid intermediate state when all opts are cleared */
flags = child->ptrace;
flags &= ~(PTRACE_O_MASK << PT_OPT_FLAG_SHIFT);
......
......@@ -175,17 +175,16 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
*/
static u32 seccomp_run_filters(struct seccomp_data *sd)
{
struct seccomp_filter *f = ACCESS_ONCE(current->seccomp.filter);
struct seccomp_data sd_local;
u32 ret = SECCOMP_RET_ALLOW;
/* Make sure cross-thread synced filter points somewhere sane. */
struct seccomp_filter *f =
lockless_dereference(current->seccomp.filter);
/* Ensure unexpected behavior doesn't result in failing open. */
if (unlikely(WARN_ON(f == NULL)))
return SECCOMP_RET_KILL;
/* Make sure cross-thread synced filter points somewhere sane. */
smp_read_barrier_depends();
if (!sd) {
populate_seccomp_data(&sd_local);
sd = &sd_local;
......@@ -549,7 +548,11 @@ void secure_computing_strict(int this_syscall)
{
int mode = current->seccomp.mode;
if (mode == 0)
if (config_enabled(CONFIG_CHECKPOINT_RESTORE) &&
unlikely(current->ptrace & PT_SUSPEND_SECCOMP))
return;
if (mode == SECCOMP_MODE_DISABLED)
return;
else if (mode == SECCOMP_MODE_STRICT)
__secure_computing_strict(this_syscall);
......@@ -650,6 +653,10 @@ u32 seccomp_phase1(struct seccomp_data *sd)
int this_syscall = sd ? sd->nr :
syscall_get_nr(current, task_pt_regs(current));
if (config_enabled(CONFIG_CHECKPOINT_RESTORE) &&
unlikely(current->ptrace & PT_SUSPEND_SECCOMP))
return SECCOMP_PHASE1_OK;
switch (mode) {
case SECCOMP_MODE_STRICT:
__secure_computing_strict(this_syscall); /* may call do_exit */
......
......@@ -24,15 +24,20 @@ static const unsigned char asn1_op_lengths[ASN1_OP__NR] = {
[ASN1_OP_MATCH_JUMP] = 1 + 1 + 1,
[ASN1_OP_MATCH_JUMP_OR_SKIP] = 1 + 1 + 1,
[ASN1_OP_MATCH_ANY] = 1,
[ASN1_OP_MATCH_ANY_OR_SKIP] = 1,
[ASN1_OP_MATCH_ANY_ACT] = 1 + 1,
[ASN1_OP_MATCH_ANY_ACT_OR_SKIP] = 1 + 1,
[ASN1_OP_COND_MATCH_OR_SKIP] = 1 + 1,
[ASN1_OP_COND_MATCH_ACT_OR_SKIP] = 1 + 1 + 1,
[ASN1_OP_COND_MATCH_JUMP_OR_SKIP] = 1 + 1 + 1,
[ASN1_OP_COND_MATCH_ANY] = 1,
[ASN1_OP_COND_MATCH_ANY_OR_SKIP] = 1,
[ASN1_OP_COND_MATCH_ANY_ACT] = 1 + 1,
[ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP] = 1 + 1,
[ASN1_OP_COND_FAIL] = 1,
[ASN1_OP_COMPLETE] = 1,
[ASN1_OP_ACT] = 1 + 1,
[ASN1_OP_MAYBE_ACT] = 1 + 1,
[ASN1_OP_RETURN] = 1,
[ASN1_OP_END_SEQ] = 1,
[ASN1_OP_END_SEQ_OF] = 1 + 1,
......@@ -177,6 +182,7 @@ int asn1_ber_decoder(const struct asn1_decoder *decoder,
unsigned char flags = 0;
#define FLAG_INDEFINITE_LENGTH 0x01
#define FLAG_MATCHED 0x02
#define FLAG_LAST_MATCHED 0x04 /* Last tag matched */
#define FLAG_CONS 0x20 /* Corresponds to CONS bit in the opcode tag
* - ie. whether or not we are going to parse
* a compound type.
......@@ -208,9 +214,9 @@ int asn1_ber_decoder(const struct asn1_decoder *decoder,
unsigned char tmp;
/* Skip conditional matches if possible */
if ((op & ASN1_OP_MATCH__COND &&
flags & FLAG_MATCHED) ||
dp == datalen) {
if ((op & ASN1_OP_MATCH__COND && flags & FLAG_MATCHED) ||
(op & ASN1_OP_MATCH__SKIP && dp == datalen)) {
flags &= ~FLAG_LAST_MATCHED;
pc += asn1_op_lengths[op];
goto next_op;
}
......@@ -302,7 +308,9 @@ int asn1_ber_decoder(const struct asn1_decoder *decoder,
/* Decide how to handle the operation */
switch (op) {
case ASN1_OP_MATCH_ANY_ACT:
case ASN1_OP_MATCH_ANY_ACT_OR_SKIP:
case ASN1_OP_COND_MATCH_ANY_ACT:
case ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP:
ret = actions[machine[pc + 1]](context, hdr, tag, data + dp, len);
if (ret < 0)
return ret;
......@@ -319,8 +327,10 @@ int asn1_ber_decoder(const struct asn1_decoder *decoder,
case ASN1_OP_MATCH:
case ASN1_OP_MATCH_OR_SKIP:
case ASN1_OP_MATCH_ANY:
case ASN1_OP_MATCH_ANY_OR_SKIP:
case ASN1_OP_COND_MATCH_OR_SKIP:
case ASN1_OP_COND_MATCH_ANY:
case ASN1_OP_COND_MATCH_ANY_OR_SKIP:
skip_data:
if (!(flags & FLAG_CONS)) {
if (flags & FLAG_INDEFINITE_LENGTH) {
......@@ -422,8 +432,15 @@ int asn1_ber_decoder(const struct asn1_decoder *decoder,
pc += asn1_op_lengths[op];
goto next_op;
case ASN1_OP_MAYBE_ACT:
if (!(flags & FLAG_LAST_MATCHED)) {
pc += asn1_op_lengths[op];
goto next_op;
}
case ASN1_OP_ACT:
ret = actions[machine[pc + 1]](context, hdr, tag, data + tdp, len);
if (ret < 0)
return ret;
pc += asn1_op_lengths[op];
goto next_op;
......@@ -431,6 +448,7 @@ int asn1_ber_decoder(const struct asn1_decoder *decoder,
if (unlikely(jsp <= 0))
goto jump_stack_underflow;
pc = jump_stack[--jsp];
flags |= FLAG_MATCHED | FLAG_LAST_MATCHED;
goto next_op;
default:
......@@ -438,7 +456,8 @@ int asn1_ber_decoder(const struct asn1_decoder *decoder,
}
/* Shouldn't reach here */
pr_err("ASN.1 decoder error: Found reserved opcode (%u)\n", op);
pr_err("ASN.1 decoder error: Found reserved opcode (%u) pc=%zu\n",
op, pc);
return -EBADMSG;
data_overrun_error:
......
......@@ -10,3 +10,5 @@ recordmcount
docproc
sortextable
asn1_compiler
extract-cert
sign-file
......@@ -303,3 +303,54 @@ why = \
echo-why = $(call escsq, $(strip $(why)))
endif
###############################################################################
#
# When a Kconfig string contains a filename, it is suitable for
# passing to shell commands. It is surrounded by double-quotes, and
# any double-quotes or backslashes within it are escaped by
# backslashes.
#
# This is no use for dependencies or $(wildcard). We need to strip the
# surrounding quotes and the escaping from quotes and backslashes, and
# we *do* need to escape any spaces in the string. So, for example:
#
# Usage: $(eval $(call config_filename,FOO))
#
# Defines FOO_FILENAME based on the contents of the CONFIG_FOO option,
# transformed as described above to be suitable for use within the
# makefile.
#
# Also, if the filename is a relative filename and exists in the source
# tree but not the build tree, define FOO_SRCPREFIX as $(srctree)/ to
# be prefixed to *both* command invocation and dependencies.
#
# Note: We also print the filenames in the quiet_cmd_foo text, and
# perhaps ought to have a version specially escaped for that purpose.
# But it's only cosmetic, and $(patsubst "%",%,$(CONFIG_FOO)) is good
# enough. It'll strip the quotes in the common case where there's no
# space and it's a simple filename, and it'll retain the quotes when
# there's a space. There are some esoteric cases in which it'll print
# the wrong thing, but we don't really care. The actual dependencies
# and commands *do* get it right, with various combinations of single
# and double quotes, backslashes and spaces in the filenames.
#
###############################################################################
#
space_escape := %%%SPACE%%%
#
define config_filename
ifneq ($$(CONFIG_$(1)),"")
$(1)_FILENAME := $$(subst \\,\,$$(subst \$$(quote),$$(quote),$$(subst $$(space_escape),\$$(space),$$(patsubst "%",%,$$(subst $$(space),$$(space_escape),$$(CONFIG_$(1)))))))
ifneq ($$(patsubst /%,%,$$(firstword $$($(1)_FILENAME))),$$(firstword $$($(1)_FILENAME)))
else
ifeq ($$(wildcard $$($(1)_FILENAME)),)
ifneq ($$(wildcard $$(srctree)/$$($(1)_FILENAME)),)
$(1)_SRCPREFIX := $(srctree)/
endif
endif
endif
endif
endef
#
###############################################################################
......@@ -16,9 +16,13 @@ hostprogs-$(CONFIG_VT) += conmakehash
hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
hostprogs-$(CONFIG_ASN1) += asn1_compiler
hostprogs-$(CONFIG_MODULE_SIG) += sign-file
hostprogs-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert
HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include
HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
HOSTLOADLIBES_sign-file = -lcrypto
HOSTLOADLIBES_extract-cert = -lcrypto
always := $(hostprogs-y) $(hostprogs-m)
......
......@@ -22,7 +22,7 @@ quiet_cmd_modules_install = INSTALL $@
mkdir -p $(2) ; \
cp $@ $(2) ; \
$(mod_strip_cmd) $(2)/$(notdir $@) ; \
$(mod_sign_cmd) $(2)/$(notdir $@) $(patsubst %,|| true,$(KBUILD_EXTMOD)) ; \
$(mod_sign_cmd) $(2)/$(notdir $@) $(patsubst %,|| true,$(KBUILD_EXTMOD)) && \
$(mod_compress_cmd) $(2)/$(notdir $@)
# Modules built outside the kernel source tree go into extra by default
......
......@@ -13,6 +13,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
......@@ -293,8 +294,8 @@ static const char *const directives[NR__DIRECTIVES] = {
struct action {
struct action *next;
char *name;
unsigned char index;
char name[];
};
static struct action *action_list;
......@@ -305,15 +306,17 @@ struct token {
enum token_type token_type : 8;
unsigned char size;
struct action *action;
const char *value;
char *content;
struct type *type;
};
static struct token *token_list;
static unsigned nr_tokens;
static _Bool verbose;
static bool verbose_opt;
static bool debug_opt;
#define debug(fmt, ...) do { if (verbose) printf(fmt, ## __VA_ARGS__); } while (0)
#define verbose(fmt, ...) do { if (verbose_opt) printf(fmt, ## __VA_ARGS__); } while (0)
#define debug(fmt, ...) do { if (debug_opt) printf(fmt, ## __VA_ARGS__); } while (0)
static int directive_compare(const void *_key, const void *_pdir)
{
......@@ -325,11 +328,9 @@ static int directive_compare(const void *_key, const void *_pdir)
dlen = strlen(dir);
clen = (dlen < token->size) ? dlen : token->size;
//debug("cmp(%*.*s,%s) = ",
// (int)token->size, (int)token->size, token->value,
// dir);
//debug("cmp(%s,%s) = ", token->content, dir);
val = memcmp(token->value, dir, clen);
val = memcmp(token->content, dir, clen);
if (val != 0) {
//debug("%d [cmp]\n", val);
return val;
......@@ -349,7 +350,7 @@ static int directive_compare(const void *_key, const void *_pdir)
static void tokenise(char *buffer, char *end)
{
struct token *tokens;
char *line, *nl, *p, *q;
char *line, *nl, *start, *p, *q;
unsigned tix, lineno;
/* Assume we're going to have half as many tokens as we have
......@@ -408,11 +409,11 @@ static void tokenise(char *buffer, char *end)
break;
tokens[tix].line = lineno;
tokens[tix].value = p;
start = p;
/* Handle string tokens */
if (isalpha(*p)) {
const char **dir;
const char **dir, *start = p;
/* Can be a directive, type name or element
* name. Find the end of the name.
......@@ -423,10 +424,18 @@ static void tokenise(char *buffer, char *end)
tokens[tix].size = q - p;
p = q;
tokens[tix].content = malloc(tokens[tix].size + 1);
if (!tokens[tix].content) {
perror(NULL);
exit(1);
}
memcpy(tokens[tix].content, start, tokens[tix].size);
tokens[tix].content[tokens[tix].size] = 0;
/* If it begins with a lowercase letter then
* it's an element name
*/
if (islower(tokens[tix].value[0])) {
if (islower(tokens[tix].content[0])) {
tokens[tix++].token_type = TOKEN_ELEMENT_NAME;
continue;
}
......@@ -455,6 +464,13 @@ static void tokenise(char *buffer, char *end)
q++;
tokens[tix].size = q - p;
p = q;
tokens[tix].content = malloc(tokens[tix].size + 1);
if (!tokens[tix].content) {
perror(NULL);
exit(1);
}
memcpy(tokens[tix].content, start, tokens[tix].size);
tokens[tix].content[tokens[tix].size] = 0;
tokens[tix++].token_type = TOKEN_NUMBER;
continue;
}
......@@ -463,6 +479,7 @@ static void tokenise(char *buffer, char *end)
if (memcmp(p, "::=", 3) == 0) {
p += 3;
tokens[tix].size = 3;
tokens[tix].content = "::=";
tokens[tix++].token_type = TOKEN_ASSIGNMENT;
continue;
}
......@@ -472,12 +489,14 @@ static void tokenise(char *buffer, char *end)
if (memcmp(p, "({", 2) == 0) {
p += 2;
tokens[tix].size = 2;
tokens[tix].content = "({";
tokens[tix++].token_type = TOKEN_OPEN_ACTION;
continue;
}
if (memcmp(p, "})", 2) == 0) {
p += 2;
tokens[tix].size = 2;
tokens[tix].content = "})";
tokens[tix++].token_type = TOKEN_CLOSE_ACTION;
continue;
}
......@@ -488,22 +507,27 @@ static void tokenise(char *buffer, char *end)
switch (*p) {
case '{':
p += 1;
tokens[tix].content = "{";
tokens[tix++].token_type = TOKEN_OPEN_CURLY;
continue;
case '}':
p += 1;
tokens[tix].content = "}";
tokens[tix++].token_type = TOKEN_CLOSE_CURLY;
continue;
case '[':
p += 1;
tokens[tix].content = "[";
tokens[tix++].token_type = TOKEN_OPEN_SQUARE;
continue;
case ']':
p += 1;
tokens[tix].content = "]";
tokens[tix++].token_type = TOKEN_CLOSE_SQUARE;
continue;
case ',':
p += 1;
tokens[tix].content = ",";
tokens[tix++].token_type = TOKEN_COMMA;
continue;
default:
......@@ -518,22 +542,20 @@ static void tokenise(char *buffer, char *end)
}
nr_tokens = tix;
debug("Extracted %u tokens\n", nr_tokens);
verbose("Extracted %u tokens\n", nr_tokens);
#if 0
{
int n;
for (n = 0; n < nr_tokens; n++)
debug("Token %3u: '%*.*s'\n",
n,
(int)token_list[n].size, (int)token_list[n].size,
token_list[n].value);
debug("Token %3u: '%s'\n", n, token_list[n].content);
}
#endif
}
static void build_type_list(void);
static void parse(void);
static void dump_elements(void);
static void render(FILE *out, FILE *hdr);
/*
......@@ -548,16 +570,27 @@ int main(int argc, char **argv)
char *kbuild_verbose;
int fd;
kbuild_verbose = getenv("KBUILD_VERBOSE");
if (kbuild_verbose)
verbose_opt = atoi(kbuild_verbose);
while (argc > 4) {
if (strcmp(argv[1], "-v") == 0)
verbose_opt = true;
else if (strcmp(argv[1], "-d") == 0)
debug_opt = true;
else
break;
memmove(&argv[1], &argv[2], (argc - 2) * sizeof(char *));
argc--;
}
if (argc != 4) {
fprintf(stderr, "Format: %s <grammar-file> <c-file> <hdr-file>\n",
fprintf(stderr, "Format: %s [-v] [-d] <grammar-file> <c-file> <hdr-file>\n",
argv[0]);
exit(2);
}
kbuild_verbose = getenv("KBUILD_VERBOSE");
if (kbuild_verbose)
verbose = atoi(kbuild_verbose);
filename = argv[1];
outputname = argv[2];
headername = argv[3];
......@@ -608,6 +641,7 @@ int main(int argc, char **argv)
tokenise(buffer, buffer + readlen);
build_type_list();
parse();
dump_elements();
out = fopen(outputname, "w");
if (!out) {
......@@ -666,7 +700,7 @@ struct element {
unsigned flags;
#define ELEMENT_IMPLICIT 0x0001
#define ELEMENT_EXPLICIT 0x0002
#define ELEMENT_MARKED 0x0004
#define ELEMENT_TAG_SPECIFIED 0x0004
#define ELEMENT_RENDERED 0x0008
#define ELEMENT_SKIPPABLE 0x0010
#define ELEMENT_CONDITIONAL 0x0020
......@@ -693,7 +727,7 @@ static int type_index_compare(const void *_a, const void *_b)
if ((*a)->name->size != (*b)->name->size)
return (*a)->name->size - (*b)->name->size;
else
return memcmp((*a)->name->value, (*b)->name->value,
return memcmp((*a)->name->content, (*b)->name->content,
(*a)->name->size);
}
......@@ -706,7 +740,7 @@ static int type_finder(const void *_key, const void *_ti)
if (token->size != type->name->size)
return token->size - type->name->size;
else
return memcmp(token->value, type->name->value,
return memcmp(token->content, type->name->content,
token->size);
}
......@@ -756,14 +790,11 @@ static void build_type_list(void)
qsort(type_index, nr, sizeof(type_index[0]), type_index_compare);
debug("Extracted %u types\n", nr_types);
verbose("Extracted %u types\n", nr_types);
#if 0
for (n = 0; n < nr_types; n++) {
struct type *type = type_index[n];
debug("- %*.*s\n",
(int)type->name->size,
(int)type->name->size,
type->name->value);
debug("- %*.*s\n", type->name->content);
}
#endif
}
......@@ -793,15 +824,14 @@ static void parse(void)
type->element->type_def = type;
if (cursor != type[1].name) {
fprintf(stderr, "%s:%d: Parse error at token '%*.*s'\n",
filename, cursor->line,
(int)cursor->size, (int)cursor->size, cursor->value);
fprintf(stderr, "%s:%d: Parse error at token '%s'\n",
filename, cursor->line, cursor->content);
exit(1);
}
} while (type++, !(type->flags & TYPE_STOP_MARKER));
debug("Extracted %u actions\n", nr_actions);
verbose("Extracted %u actions\n", nr_actions);
}
static struct element *element_list;
......@@ -862,33 +892,31 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
cursor++;
break;
default:
fprintf(stderr, "%s:%d: Unrecognised tag class token '%*.*s'\n",
filename, cursor->line,
(int)cursor->size, (int)cursor->size, cursor->value);
fprintf(stderr, "%s:%d: Unrecognised tag class token '%s'\n",
filename, cursor->line, cursor->content);
exit(1);
}
if (cursor >= end)
goto overrun_error;
if (cursor->token_type != TOKEN_NUMBER) {
fprintf(stderr, "%s:%d: Missing tag number '%*.*s'\n",
filename, cursor->line,
(int)cursor->size, (int)cursor->size, cursor->value);
fprintf(stderr, "%s:%d: Missing tag number '%s'\n",
filename, cursor->line, cursor->content);
exit(1);
}
element->tag &= ~0x1f;
element->tag |= strtoul(cursor->value, &p, 10);
if (p - cursor->value != cursor->size)
element->tag |= strtoul(cursor->content, &p, 10);
element->flags |= ELEMENT_TAG_SPECIFIED;
if (p - cursor->content != cursor->size)
abort();
cursor++;
if (cursor >= end)
goto overrun_error;
if (cursor->token_type != TOKEN_CLOSE_SQUARE) {
fprintf(stderr, "%s:%d: Missing closing square bracket '%*.*s'\n",
filename, cursor->line,
(int)cursor->size, (int)cursor->size, cursor->value);
fprintf(stderr, "%s:%d: Missing closing square bracket '%s'\n",
filename, cursor->line, cursor->content);
exit(1);
}
cursor++;
......@@ -988,9 +1016,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
ref = bsearch(cursor, type_index, nr_types, sizeof(type_index[0]),
type_finder);
if (!ref) {
fprintf(stderr, "%s:%d: Type '%*.*s' undefined\n",
filename, cursor->line,
(int)cursor->size, (int)cursor->size, cursor->value);
fprintf(stderr, "%s:%d: Type '%s' undefined\n",
filename, cursor->line, cursor->content);
exit(1);
}
cursor->type = *ref;
......@@ -1039,9 +1066,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
break;
default:
fprintf(stderr, "%s:%d: Token '%*.*s' does not introduce a type\n",
filename, cursor->line,
(int)cursor->size, (int)cursor->size, cursor->value);
fprintf(stderr, "%s:%d: Token '%s' does not introduce a type\n",
filename, cursor->line, cursor->content);
exit(1);
}
......@@ -1058,20 +1084,18 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
if (cursor >= end)
goto overrun_error;
if (cursor->token_type != TOKEN_ELEMENT_NAME) {
fprintf(stderr, "%s:%d: Token '%*.*s' is not an action function name\n",
filename, cursor->line,
(int)cursor->size, (int)cursor->size, cursor->value);
fprintf(stderr, "%s:%d: Token '%s' is not an action function name\n",
filename, cursor->line, cursor->content);
exit(1);
}
action = malloc(sizeof(struct action) + cursor->size + 1);
action = malloc(sizeof(struct action));
if (!action) {
perror(NULL);
exit(1);
}
action->index = 0;
memcpy(action->name, cursor->value, cursor->size);
action->name[cursor->size] = 0;
action->name = cursor->content;
for (ppaction = &action_list;
*ppaction;
......@@ -1101,9 +1125,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
if (cursor >= end)
goto overrun_error;
if (cursor->token_type != TOKEN_CLOSE_ACTION) {
fprintf(stderr, "%s:%d: Missing close action, got '%*.*s'\n",
filename, cursor->line,
(int)cursor->size, (int)cursor->size, cursor->value);
fprintf(stderr, "%s:%d: Missing close action, got '%s'\n",
filename, cursor->line, cursor->content);
exit(1);
}
cursor++;
......@@ -1113,9 +1136,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end,
return top;
parse_error:
fprintf(stderr, "%s:%d: Unexpected token '%*.*s'\n",
filename, cursor->line,
(int)cursor->size, (int)cursor->size, cursor->value);
fprintf(stderr, "%s:%d: Unexpected token '%s'\n",
filename, cursor->line, cursor->content);
exit(1);
overrun_error:
......@@ -1133,9 +1155,8 @@ static struct element *parse_compound(struct token **_cursor, struct token *end,
struct token *cursor = *_cursor, *name;
if (cursor->token_type != TOKEN_OPEN_CURLY) {
fprintf(stderr, "%s:%d: Expected compound to start with brace not '%*.*s'\n",
filename, cursor->line,
(int)cursor->size, (int)cursor->size, cursor->value);
fprintf(stderr, "%s:%d: Expected compound to start with brace not '%s'\n",
filename, cursor->line, cursor->content);
exit(1);
}
cursor++;
......@@ -1176,9 +1197,8 @@ static struct element *parse_compound(struct token **_cursor, struct token *end,
children->flags &= ~ELEMENT_CONDITIONAL;
if (cursor->token_type != TOKEN_CLOSE_CURLY) {
fprintf(stderr, "%s:%d: Expected compound closure, got '%*.*s'\n",
filename, cursor->line,
(int)cursor->size, (int)cursor->size, cursor->value);
fprintf(stderr, "%s:%d: Expected compound closure, got '%s'\n",
filename, cursor->line, cursor->content);
exit(1);
}
cursor++;
......@@ -1191,6 +1211,52 @@ static struct element *parse_compound(struct token **_cursor, struct token *end,
exit(1);
}
static void dump_element(const struct element *e, int level)
{
const struct element *c;
const struct type *t = e->type_def;
const char *name = e->name ? e->name->content : ".";
const char *tname = t && t->name ? t->name->content : ".";
char tag[32];
if (e->class == 0 && e->method == 0 && e->tag == 0)
strcpy(tag, "<...>");
else if (e->class == ASN1_UNIV)
sprintf(tag, "%s %s %s",
asn1_classes[e->class],
asn1_methods[e->method],
asn1_universal_tags[e->tag]);
else
sprintf(tag, "%s %s %u",
asn1_classes[e->class],
asn1_methods[e->method],
e->tag);
printf("%c%c%c%c%c %c %*s[*] \e[33m%s\e[m %s %s \e[35m%s\e[m\n",
e->flags & ELEMENT_IMPLICIT ? 'I' : '-',
e->flags & ELEMENT_EXPLICIT ? 'E' : '-',
e->flags & ELEMENT_TAG_SPECIFIED ? 'T' : '-',
e->flags & ELEMENT_SKIPPABLE ? 'S' : '-',
e->flags & ELEMENT_CONDITIONAL ? 'C' : '-',
"-tTqQcaro"[e->compound],
level, "",
tag,
tname,
name,
e->action ? e->action->name : "");
if (e->compound == TYPE_REF)
dump_element(e->type->type->element, level + 3);
else
for (c = e->children; c; c = c->next)
dump_element(c, level + 3);
}
static void dump_elements(void)
{
if (debug_opt)
dump_element(type_list[0].element, 0);
}
static void render_element(FILE *out, struct element *e, struct element *tag);
static void render_out_of_line_list(FILE *out);
......@@ -1292,7 +1358,7 @@ static void render(FILE *out, FILE *hdr)
}
/* We do two passes - the first one calculates all the offsets */
debug("Pass 1\n");
verbose("Pass 1\n");
nr_entries = 0;
root = &type_list[0];
render_element(NULL, root->element, NULL);
......@@ -1303,7 +1369,7 @@ static void render(FILE *out, FILE *hdr)
e->flags &= ~ELEMENT_RENDERED;
/* And then we actually render */
debug("Pass 2\n");
verbose("Pass 2\n");
fprintf(out, "\n");
fprintf(out, "static const unsigned char %s_machine[] = {\n",
grammar_name);
......@@ -1376,7 +1442,7 @@ static void render_out_of_line_list(FILE *out)
*/
static void render_element(FILE *out, struct element *e, struct element *tag)
{
struct element *ec;
struct element *ec, *x;
const char *cond, *act;
int entry, skippable = 0, outofline = 0;
......@@ -1389,9 +1455,7 @@ static void render_element(FILE *out, struct element *e, struct element *tag)
outofline = 1;
if (e->type_def && out) {
render_more(out, "\t// %*.*s\n",
(int)e->type_def->name->size, (int)e->type_def->name->size,
e->type_def->name->value);
render_more(out, "\t// %s\n", e->type_def->name->content);
}
/* Render the operation */
......@@ -1400,11 +1464,10 @@ static void render_element(FILE *out, struct element *e, struct element *tag)
act = e->action ? "_ACT" : "";
switch (e->compound) {
case ANY:
render_opcode(out, "ASN1_OP_%sMATCH_ANY%s,", cond, act);
render_opcode(out, "ASN1_OP_%sMATCH_ANY%s%s,",
cond, act, skippable ? "_OR_SKIP" : "");
if (e->name)
render_more(out, "\t\t// %*.*s",
(int)e->name->size, (int)e->name->size,
e->name->value);
render_more(out, "\t\t// %s", e->name->content);
render_more(out, "\n");
goto dont_render_tag;
......@@ -1435,15 +1498,15 @@ static void render_element(FILE *out, struct element *e, struct element *tag)
break;
}
if (e->name)
render_more(out, "\t\t// %*.*s",
(int)e->name->size, (int)e->name->size,
e->name->value);
x = tag ?: e;
if (x->name)
render_more(out, "\t\t// %s", x->name->content);
render_more(out, "\n");
/* Render the tag */
if (!tag)
if (!tag || !(tag->flags & ELEMENT_TAG_SPECIFIED))
tag = e;
if (tag->class == ASN1_UNIV &&
tag->tag != 14 &&
tag->tag != 15 &&
......@@ -1465,7 +1528,8 @@ static void render_element(FILE *out, struct element *e, struct element *tag)
case TYPE_REF:
render_element(out, e->type->type->element, tag);
if (e->action)
render_opcode(out, "ASN1_OP_ACT,\n");
render_opcode(out, "ASN1_OP_%sACT,\n",
skippable ? "MAYBE_" : "");
break;
case SEQUENCE:
......@@ -1474,10 +1538,8 @@ static void render_element(FILE *out, struct element *e, struct element *tag)
* skipability */
render_opcode(out, "_jump_target(%u),", e->entry_index);
if (e->type_def && e->type_def->name)
render_more(out, "\t\t// --> %*.*s",
(int)e->type_def->name->size,
(int)e->type_def->name->size,
e->type_def->name->value);
render_more(out, "\t\t// --> %s",
e->type_def->name->content);
render_more(out, "\n");
if (!(e->flags & ELEMENT_RENDERED)) {
e->flags |= ELEMENT_RENDERED;
......@@ -1502,10 +1564,8 @@ static void render_element(FILE *out, struct element *e, struct element *tag)
* skipability */
render_opcode(out, "_jump_target(%u),", e->entry_index);
if (e->type_def && e->type_def->name)
render_more(out, "\t\t// --> %*.*s",
(int)e->type_def->name->size,
(int)e->type_def->name->size,
e->type_def->name->value);
render_more(out, "\t\t// --> %s",
e->type_def->name->content);
render_more(out, "\n");
if (!(e->flags & ELEMENT_RENDERED)) {
e->flags |= ELEMENT_RENDERED;
......@@ -1539,7 +1599,7 @@ static void render_element(FILE *out, struct element *e, struct element *tag)
case CHOICE:
for (ec = e->children; ec; ec = ec->next)
render_element(out, ec, NULL);
render_element(out, ec, ec);
if (!skippable)
render_opcode(out, "ASN1_OP_COND_FAIL,\n");
if (e->action)
......
/* Extract X.509 certificate in DER form from PKCS#11 or PEM.
*
* Copyright © 2014 Red Hat, Inc. All Rights Reserved.
* Copyright © 2015 Intel Corporation.
*
* Authors: David Howells <dhowells@redhat.com>
* David Woodhouse <dwmw2@infradead.org>
*
* 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 _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <getopt.h>
#include <err.h>
#include <arpa/inet.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/pkcs7.h>
#include <openssl/err.h>
#include <openssl/engine.h>
#define PKEY_ID_PKCS7 2
static __attribute__((noreturn))
void format(void)
{
fprintf(stderr,
"Usage: scripts/extract-cert <source> <dest>\n");
exit(2);
}
static void display_openssl_errors(int l)
{
const char *file;
char buf[120];
int e, line;
if (ERR_peek_error() == 0)
return;
fprintf(stderr, "At main.c:%d:\n", l);
while ((e = ERR_get_error_line(&file, &line))) {
ERR_error_string(e, buf);
fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line);
}
}
static void drain_openssl_errors(void)
{
const char *file;
int line;
if (ERR_peek_error() == 0)
return;
while (ERR_get_error_line(&file, &line)) {}
}
#define ERR(cond, fmt, ...) \
do { \
bool __cond = (cond); \
display_openssl_errors(__LINE__); \
if (__cond) { \
err(1, fmt, ## __VA_ARGS__); \
} \
} while(0)
static const char *key_pass;
static BIO *wb;
static char *cert_dst;
int kbuild_verbose;
static void write_cert(X509 *x509)
{
char buf[200];
if (!wb) {
wb = BIO_new_file(cert_dst, "wb");
ERR(!wb, "%s", cert_dst);
}
X509_NAME_oneline(X509_get_subject_name(x509), buf, sizeof(buf));
ERR(!i2d_X509_bio(wb, x509), cert_dst);
if (kbuild_verbose)
fprintf(stderr, "Extracted cert: %s\n", buf);
}
int main(int argc, char **argv)
{
char *cert_src;
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
ERR_clear_error();
kbuild_verbose = atoi(getenv("KBUILD_VERBOSE")?:"0");
key_pass = getenv("KBUILD_SIGN_PIN");
if (argc != 3)
format();
cert_src = argv[1];
cert_dst = argv[2];
if (!cert_src[0]) {
/* Invoked with no input; create empty file */
FILE *f = fopen(cert_dst, "wb");
ERR(!f, "%s", cert_dst);
fclose(f);
exit(0);
} else if (!strncmp(cert_src, "pkcs11:", 7)) {
ENGINE *e;
struct {
const char *cert_id;
X509 *cert;
} parms;
parms.cert_id = cert_src;
parms.cert = NULL;
ENGINE_load_builtin_engines();
drain_openssl_errors();
e = ENGINE_by_id("pkcs11");
ERR(!e, "Load PKCS#11 ENGINE");
if (ENGINE_init(e))
drain_openssl_errors();
else
ERR(1, "ENGINE_init");
if (key_pass)
ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN");
ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &parms, NULL, 1);
ERR(!parms.cert, "Get X.509 from PKCS#11");
write_cert(parms.cert);
} else {
BIO *b;
X509 *x509;
b = BIO_new_file(cert_src, "rb");
ERR(!b, "%s", cert_src);
while (1) {
x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
if (wb && !x509) {
unsigned long err = ERR_peek_last_error();
if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
ERR_clear_error();
break;
}
}
ERR(!x509, "%s", cert_src);
write_cert(x509);
}
}
BIO_free(wb);
return 0;
}
......@@ -98,6 +98,7 @@ int main(int argc, char *argv[])
/* types, roles, and allows */
fprintf(fout, "type base_t;\n");
fprintf(fout, "role base_r;\n");
fprintf(fout, "role base_r types { base_t };\n");
for (i = 0; secclass_map[i].name; i++)
fprintf(fout, "allow base_t base_t:%s *;\n",
......
#!/usr/bin/perl -w
#
# Sign a module file using the given key.
#
my $USAGE =
"Usage: scripts/sign-file [-v] <hash algo> <key> <x509> <module> [<dest>]\n" .
" scripts/sign-file [-v] -s <raw sig> <hash algo> <x509> <module> [<dest>]\n";
use strict;
use FileHandle;
use IPC::Open2;
use Getopt::Std;
my %opts;
getopts('vs:', \%opts) or die $USAGE;
my $verbose = $opts{'v'};
my $signature_file = $opts{'s'};
die $USAGE if ($#ARGV > 4);
die $USAGE if (!$signature_file && $#ARGV < 3 || $signature_file && $#ARGV < 2);
my $dgst = shift @ARGV;
my $private_key;
if (!$signature_file) {
$private_key = shift @ARGV;
}
my $x509 = shift @ARGV;
my $module = shift @ARGV;
my ($dest, $keep_orig);
if (@ARGV) {
$dest = $ARGV[0];
$keep_orig = 1;
} else {
$dest = $module . "~";
}
die "Can't read private key\n" if (!$signature_file && !-r $private_key);
die "Can't read signature file\n" if ($signature_file && !-r $signature_file);
die "Can't read X.509 certificate\n" unless (-r $x509);
die "Can't read module\n" unless (-r $module);
#
# Function to read the contents of a file into a variable.
#
sub read_file($)
{
my ($file) = @_;
my $contents;
my $len;
open(FD, "<$file") || die $file;
binmode FD;
my @st = stat(FD);
die $file if (!@st);
$len = read(FD, $contents, $st[7]) || die $file;
close(FD) || die $file;
die "$file: Wanted length ", $st[7], ", got ", $len, "\n"
if ($len != $st[7]);
return $contents;
}
###############################################################################
#
# First of all, we have to parse the X.509 certificate to find certain details
# about it.
#
# We read the DER-encoded X509 certificate and parse it to extract the Subject
# name and Subject Key Identifier. Theis provides the data we need to build
# the certificate identifier.
#
# The signer's name part of the identifier is fabricated from the commonName,
# the organizationName or the emailAddress components of the X.509 subject
# name.
#
# The subject key ID is used to select which of that signer's certificates
# we're intending to use to sign the module.
#
###############################################################################
my $x509_certificate = read_file($x509);
my $UNIV = 0 << 6;
my $APPL = 1 << 6;
my $CONT = 2 << 6;
my $PRIV = 3 << 6;
my $CONS = 0x20;
my $BOOLEAN = 0x01;
my $INTEGER = 0x02;
my $BIT_STRING = 0x03;
my $OCTET_STRING = 0x04;
my $NULL = 0x05;
my $OBJ_ID = 0x06;
my $UTF8String = 0x0c;
my $SEQUENCE = 0x10;
my $SET = 0x11;
my $UTCTime = 0x17;
my $GeneralizedTime = 0x18;
my %OIDs = (
pack("CCC", 85, 4, 3) => "commonName",
pack("CCC", 85, 4, 6) => "countryName",
pack("CCC", 85, 4, 10) => "organizationName",
pack("CCC", 85, 4, 11) => "organizationUnitName",
pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 1) => "rsaEncryption",
pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 5) => "sha1WithRSAEncryption",
pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 9, 1) => "emailAddress",
pack("CCC", 85, 29, 35) => "authorityKeyIdentifier",
pack("CCC", 85, 29, 14) => "subjectKeyIdentifier",
pack("CCC", 85, 29, 19) => "basicConstraints"
);
###############################################################################
#
# Extract an ASN.1 element from a string and return information about it.
#
###############################################################################
sub asn1_extract($$@)
{
my ($cursor, $expected_tag, $optional) = @_;
return [ -1 ]
if ($cursor->[1] == 0 && $optional);
die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (elem ", $cursor->[1], ")\n"
if ($cursor->[1] < 2);
my ($tag, $len) = unpack("CC", substr(${$cursor->[2]}, $cursor->[0], 2));
if ($expected_tag != -1 && $tag != $expected_tag) {
return [ -1 ]
if ($optional);
die $x509, ": ", $cursor->[0], ": ASN.1 unexpected tag (", $tag,
" not ", $expected_tag, ")\n";
}
$cursor->[0] += 2;
$cursor->[1] -= 2;
die $x509, ": ", $cursor->[0], ": ASN.1 long tag\n"
if (($tag & 0x1f) == 0x1f);
die $x509, ": ", $cursor->[0], ": ASN.1 indefinite length\n"
if ($len == 0x80);
if ($len > 0x80) {
my $l = $len - 0x80;
die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (len len $l)\n"
if ($cursor->[1] < $l);
if ($l == 0x1) {
$len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1));
} elsif ($l == 0x2) {
$len = unpack("n", substr(${$cursor->[2]}, $cursor->[0], 2));
} elsif ($l == 0x3) {
$len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1)) << 16;
$len = unpack("n", substr(${$cursor->[2]}, $cursor->[0] + 1, 2));
} elsif ($l == 0x4) {
$len = unpack("N", substr(${$cursor->[2]}, $cursor->[0], 4));
} else {
die $x509, ": ", $cursor->[0], ": ASN.1 element too long (", $l, ")\n";
}
$cursor->[0] += $l;
$cursor->[1] -= $l;
}
die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (", $len, ")\n"
if ($cursor->[1] < $len);
my $ret = [ $tag, [ $cursor->[0], $len, $cursor->[2] ] ];
$cursor->[0] += $len;
$cursor->[1] -= $len;
return $ret;
}
###############################################################################
#
# Retrieve the data referred to by a cursor
#
###############################################################################
sub asn1_retrieve($)
{
my ($cursor) = @_;
my ($offset, $len, $data) = @$cursor;
return substr($$data, $offset, $len);
}
###############################################################################
#
# Roughly parse the X.509 certificate
#
###############################################################################
my $cursor = [ 0, length($x509_certificate), \$x509_certificate ];
my $cert = asn1_extract($cursor, $UNIV | $CONS | $SEQUENCE);
my $tbs = asn1_extract($cert->[1], $UNIV | $CONS | $SEQUENCE);
my $version = asn1_extract($tbs->[1], $CONT | $CONS | 0, 1);
my $serial_number = asn1_extract($tbs->[1], $UNIV | $INTEGER);
my $sig_type = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
my $issuer = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
my $validity = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
my $subject = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
my $key = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
my $issuer_uid = asn1_extract($tbs->[1], $CONT | $CONS | 1, 1);
my $subject_uid = asn1_extract($tbs->[1], $CONT | $CONS | 2, 1);
my $extension_list = asn1_extract($tbs->[1], $CONT | $CONS | 3, 1);
my $subject_key_id = ();
my $authority_key_id = ();
#
# Parse the extension list
#
if ($extension_list->[0] != -1) {
my $extensions = asn1_extract($extension_list->[1], $UNIV | $CONS | $SEQUENCE);
while ($extensions->[1]->[1] > 0) {
my $ext = asn1_extract($extensions->[1], $UNIV | $CONS | $SEQUENCE);
my $x_oid = asn1_extract($ext->[1], $UNIV | $OBJ_ID);
my $x_crit = asn1_extract($ext->[1], $UNIV | $BOOLEAN, 1);
my $x_val = asn1_extract($ext->[1], $UNIV | $OCTET_STRING);
my $raw_oid = asn1_retrieve($x_oid->[1]);
next if (!exists($OIDs{$raw_oid}));
my $x_type = $OIDs{$raw_oid};
my $raw_value = asn1_retrieve($x_val->[1]);
if ($x_type eq "subjectKeyIdentifier") {
my $vcursor = [ 0, length($raw_value), \$raw_value ];
$subject_key_id = asn1_extract($vcursor, $UNIV | $OCTET_STRING);
}
}
}
###############################################################################
#
# Determine what we're going to use as the signer's name. In order of
# preference, take one of: commonName, organizationName or emailAddress.
#
###############################################################################
my $org = "";
my $cn = "";
my $email = "";
while ($subject->[1]->[1] > 0) {
my $rdn = asn1_extract($subject->[1], $UNIV | $CONS | $SET);
my $attr = asn1_extract($rdn->[1], $UNIV | $CONS | $SEQUENCE);
my $n_oid = asn1_extract($attr->[1], $UNIV | $OBJ_ID);
my $n_val = asn1_extract($attr->[1], -1);
my $raw_oid = asn1_retrieve($n_oid->[1]);
next if (!exists($OIDs{$raw_oid}));
my $n_type = $OIDs{$raw_oid};
my $raw_value = asn1_retrieve($n_val->[1]);
if ($n_type eq "organizationName") {
$org = $raw_value;
} elsif ($n_type eq "commonName") {
$cn = $raw_value;
} elsif ($n_type eq "emailAddress") {
$email = $raw_value;
}
}
my $signers_name = $email;
if ($org && $cn) {
# Don't use the organizationName if the commonName repeats it
if (length($org) <= length($cn) &&
substr($cn, 0, length($org)) eq $org) {
$signers_name = $cn;
goto got_id_name;
}
# Or a signifcant chunk of it
if (length($org) >= 7 &&
length($cn) >= 7 &&
substr($cn, 0, 7) eq substr($org, 0, 7)) {
$signers_name = $cn;
goto got_id_name;
}
$signers_name = $org . ": " . $cn;
} elsif ($org) {
$signers_name = $org;
} elsif ($cn) {
$signers_name = $cn;
}
got_id_name:
die $x509, ": ", "X.509: Couldn't find the Subject Key Identifier extension\n"
if (!$subject_key_id);
my $key_identifier = asn1_retrieve($subject_key_id->[1]);
###############################################################################
#
# Create and attach the module signature
#
###############################################################################
#
# Signature parameters
#
my $algo = 1; # Public-key crypto algorithm: RSA
my $hash = 0; # Digest algorithm
my $id_type = 1; # Identifier type: X.509
#
# Digest the data
#
my $prologue;
if ($dgst eq "sha1") {
$prologue = pack("C*",
0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
0x2B, 0x0E, 0x03, 0x02, 0x1A,
0x05, 0x00, 0x04, 0x14);
$hash = 2;
} elsif ($dgst eq "sha224") {
$prologue = pack("C*",
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
0x05, 0x00, 0x04, 0x1C);
$hash = 7;
} elsif ($dgst eq "sha256") {
$prologue = pack("C*",
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
0x05, 0x00, 0x04, 0x20);
$hash = 4;
} elsif ($dgst eq "sha384") {
$prologue = pack("C*",
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
0x05, 0x00, 0x04, 0x30);
$hash = 5;
} elsif ($dgst eq "sha512") {
$prologue = pack("C*",
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
0x05, 0x00, 0x04, 0x40);
$hash = 6;
} else {
die "Unknown hash algorithm: $dgst\n";
}
my $signature;
if ($signature_file) {
$signature = read_file($signature_file);
} else {
#
# Generate the digest and read from openssl's stdout
#
my $digest;
$digest = readpipe("openssl dgst -$dgst -binary $module") || die "openssl dgst";
#
# Generate the binary signature, which will be just the integer that
# comprises the signature with no metadata attached.
#
my $pid;
$pid = open2(*read_from, *write_to,
"openssl rsautl -sign -inkey $private_key -keyform PEM") ||
die "openssl rsautl";
binmode write_to;
print write_to $prologue . $digest || die "pipe to openssl rsautl";
close(write_to) || die "pipe to openssl rsautl";
binmode read_from;
read(read_from, $signature, 4096) || die "pipe from openssl rsautl";
close(read_from) || die "pipe from openssl rsautl";
waitpid($pid, 0) || die;
die "openssl rsautl died: $?" if ($? >> 8);
}
$signature = pack("n", length($signature)) . $signature,
#
# Build the signed binary
#
my $unsigned_module = read_file($module);
my $magic_number = "~Module signature appended~\n";
my $info = pack("CCCCCxxxN",
$algo, $hash, $id_type,
length($signers_name),
length($key_identifier),
length($signature));
if ($verbose) {
print "Size of unsigned module: ", length($unsigned_module), "\n";
print "Size of signer's name : ", length($signers_name), "\n";
print "Size of key identifier : ", length($key_identifier), "\n";
print "Size of signature : ", length($signature), "\n";
print "Size of information : ", length($info), "\n";
print "Size of magic number : ", length($magic_number), "\n";
print "Signer's name : '", $signers_name, "'\n";
print "Digest : $dgst\n";
}
open(FD, ">$dest") || die $dest;
binmode FD;
print FD
$unsigned_module,
$signers_name,
$key_identifier,
$signature,
$info,
$magic_number
;
close FD || die $dest;
if (!$keep_orig) {
rename($dest, $module) || die $module;
}
/* Sign a module file using the given key.
*
* 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 _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <getopt.h>
#include <err.h>
#include <arpa/inet.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/cms.h>
#include <openssl/err.h>
#include <openssl/engine.h>
struct module_signature {
uint8_t algo; /* Public-key crypto algorithm [0] */
uint8_t hash; /* Digest algorithm [0] */
uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */
uint8_t signer_len; /* Length of signer's name [0] */
uint8_t key_id_len; /* Length of key identifier [0] */
uint8_t __pad[3];
uint32_t sig_len; /* Length of signature data */
};
#define PKEY_ID_PKCS7 2
static char magic_number[] = "~Module signature appended~\n";
static __attribute__((noreturn))
void format(void)
{
fprintf(stderr,
"Usage: scripts/sign-file [-dp] <hash algo> <key> <x509> <module> [<dest>]\n");
exit(2);
}
static void display_openssl_errors(int l)
{
const char *file;
char buf[120];
int e, line;
if (ERR_peek_error() == 0)
return;
fprintf(stderr, "At main.c:%d:\n", l);
while ((e = ERR_get_error_line(&file, &line))) {
ERR_error_string(e, buf);
fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line);
}
}
static void drain_openssl_errors(void)
{
const char *file;
int line;
if (ERR_peek_error() == 0)
return;
while (ERR_get_error_line(&file, &line)) {}
}
#define ERR(cond, fmt, ...) \
do { \
bool __cond = (cond); \
display_openssl_errors(__LINE__); \
if (__cond) { \
err(1, fmt, ## __VA_ARGS__); \
} \
} while(0)
static const char *key_pass;
static int pem_pw_cb(char *buf, int len, int w, void *v)
{
int pwlen;
if (!key_pass)
return -1;
pwlen = strlen(key_pass);
if (pwlen >= len)
return -1;
strcpy(buf, key_pass);
/* If it's wrong, don't keep trying it. */
key_pass = NULL;
return pwlen;
}
int main(int argc, char **argv)
{
struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 };
char *hash_algo = NULL;
char *private_key_name, *x509_name, *module_name, *dest_name;
bool save_cms = false, replace_orig;
bool sign_only = false;
unsigned char buf[4096];
unsigned long module_size, cms_size;
unsigned int use_keyid = 0, use_signed_attrs = CMS_NOATTR;
const EVP_MD *digest_algo;
EVP_PKEY *private_key;
CMS_ContentInfo *cms;
X509 *x509;
BIO *b, *bd = NULL, *bm;
int opt, n;
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
ERR_clear_error();
key_pass = getenv("KBUILD_SIGN_PIN");
do {
opt = getopt(argc, argv, "dpk");
switch (opt) {
case 'p': save_cms = true; break;
case 'd': sign_only = true; save_cms = true; break;
case 'k': use_keyid = CMS_USE_KEYID; break;
case -1: break;
default: format();
}
} while (opt != -1);
argc -= optind;
argv += optind;
if (argc < 4 || argc > 5)
format();
hash_algo = argv[0];
private_key_name = argv[1];
x509_name = argv[2];
module_name = argv[3];
if (argc == 5) {
dest_name = argv[4];
replace_orig = false;
} else {
ERR(asprintf(&dest_name, "%s.~signed~", module_name) < 0,
"asprintf");
replace_orig = true;
}
/* Read the private key and the X.509 cert the PKCS#7 message
* will point to.
*/
if (!strncmp(private_key_name, "pkcs11:", 7)) {
ENGINE *e;
ENGINE_load_builtin_engines();
drain_openssl_errors();
e = ENGINE_by_id("pkcs11");
ERR(!e, "Load PKCS#11 ENGINE");
if (ENGINE_init(e))
drain_openssl_errors();
else
ERR(1, "ENGINE_init");
if (key_pass)
ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN");
private_key = ENGINE_load_private_key(e, private_key_name, NULL,
NULL);
ERR(!private_key, "%s", private_key_name);
} else {
b = BIO_new_file(private_key_name, "rb");
ERR(!b, "%s", private_key_name);
private_key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb, NULL);
ERR(!private_key, "%s", private_key_name);
BIO_free(b);
}
b = BIO_new_file(x509_name, "rb");
ERR(!b, "%s", x509_name);
x509 = d2i_X509_bio(b, NULL); /* Binary encoded X.509 */
if (!x509) {
ERR(BIO_reset(b) != 1, "%s", x509_name);
x509 = PEM_read_bio_X509(b, NULL, NULL, NULL); /* PEM encoded X.509 */
if (x509)
drain_openssl_errors();
}
BIO_free(b);
ERR(!x509, "%s", x509_name);
/* Open the destination file now so that we can shovel the module data
* across as we read it.
*/
if (!sign_only) {
bd = BIO_new_file(dest_name, "wb");
ERR(!bd, "%s", dest_name);
}
/* Digest the module data. */
OpenSSL_add_all_digests();
display_openssl_errors(__LINE__);
digest_algo = EVP_get_digestbyname(hash_algo);
ERR(!digest_algo, "EVP_get_digestbyname");
bm = BIO_new_file(module_name, "rb");
ERR(!bm, "%s", module_name);
/* Load the CMS message from the digest buffer. */
cms = CMS_sign(NULL, NULL, NULL, NULL,
CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED | CMS_STREAM);
ERR(!cms, "CMS_sign");
ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo,
CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
use_keyid | use_signed_attrs),
"CMS_sign_add_signer");
ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) < 0,
"CMS_final");
if (save_cms) {
char *cms_name;
ERR(asprintf(&cms_name, "%s.p7s", module_name) < 0, "asprintf");
b = BIO_new_file(cms_name, "wb");
ERR(!b, "%s", cms_name);
ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0, "%s", cms_name);
BIO_free(b);
}
if (sign_only)
return 0;
/* Append the marker and the PKCS#7 message to the destination file */
ERR(BIO_reset(bm) < 0, "%s", module_name);
while ((n = BIO_read(bm, buf, sizeof(buf))),
n > 0) {
ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name);
}
ERR(n < 0, "%s", module_name);
module_size = BIO_number_written(bd);
ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name);
cms_size = BIO_number_written(bd) - module_size;
sig_info.sig_len = htonl(cms_size);
ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name);
ERR(BIO_write(bd, magic_number, sizeof(magic_number) - 1) < 0, "%s", dest_name);
ERR(BIO_free(bd) < 0, "%s", dest_name);
/* Finally, if we're signing in place, replace the original. */
if (replace_orig)
ERR(rename(dest_name, module_name) < 0, "%s", dest_name);
return 0;
}
......@@ -132,7 +132,6 @@ choice
default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
default DEFAULT_SECURITY_YAMA if SECURITY_YAMA
default DEFAULT_SECURITY_DAC
help
......@@ -151,9 +150,6 @@ choice
config DEFAULT_SECURITY_APPARMOR
bool "AppArmor" if SECURITY_APPARMOR=y
config DEFAULT_SECURITY_YAMA
bool "Yama" if SECURITY_YAMA=y
config DEFAULT_SECURITY_DAC
bool "Unix Discretionary Access Controls"
......@@ -165,7 +161,6 @@ config DEFAULT_SECURITY
default "smack" if DEFAULT_SECURITY_SMACK
default "tomoyo" if DEFAULT_SECURITY_TOMOYO
default "apparmor" if DEFAULT_SECURITY_APPARMOR
default "yama" if DEFAULT_SECURITY_YAMA
default "" if DEFAULT_SECURITY_DAC
endmenu
......
......@@ -245,6 +245,21 @@ static void dump_common_audit_data(struct audit_buffer *ab,
}
break;
}
case LSM_AUDIT_DATA_IOCTL_OP: {
struct inode *inode;
audit_log_d_path(ab, " path=", &a->u.op->path);
inode = a->u.op->path.dentry->d_inode;
if (inode) {
audit_log_format(ab, " dev=");
audit_log_untrustedstring(ab, inode->i_sb->s_id);
audit_log_format(ab, " ino=%lu", inode->i_ino);
}
audit_log_format(ab, " ioctlcmd=%hx", a->u.op->cmd);
break;
}
case LSM_AUDIT_DATA_DENTRY: {
struct inode *inode;
......
......@@ -56,18 +56,13 @@ int __init security_init(void)
pr_info("Security Framework initialized\n");
/*
* Always load the capability module.
* Load minor LSMs, with the capability module always first.
*/
capability_add_hooks();
#ifdef CONFIG_SECURITY_YAMA_STACKED
/*
* If Yama is configured for stacking load it next.
*/
yama_add_hooks();
#endif
/*
* Load the chosen module if there is one.
* This will also find yama if it is stacking
* Load all the remaining security modules.
*/
do_security_initcalls();
......
......@@ -22,6 +22,7 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/percpu.h>
#include <linux/list.h>
#include <net/sock.h>
#include <linux/un.h>
#include <net/af_unix.h>
......@@ -48,6 +49,7 @@ struct avc_entry {
u32 tsid;
u16 tclass;
struct av_decision avd;
struct avc_xperms_node *xp_node;
};
struct avc_node {
......@@ -56,6 +58,16 @@ struct avc_node {
struct rcu_head rhead;
};
struct avc_xperms_decision_node {
struct extended_perms_decision xpd;
struct list_head xpd_list; /* list of extended_perms_decision */
};
struct avc_xperms_node {
struct extended_perms xp;
struct list_head xpd_head; /* list head of extended_perms_decision */
};
struct avc_cache {
struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */
spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
......@@ -80,6 +92,9 @@ DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 };
static struct avc_cache avc_cache;
static struct avc_callback_node *avc_callbacks;
static struct kmem_cache *avc_node_cachep;
static struct kmem_cache *avc_xperms_data_cachep;
static struct kmem_cache *avc_xperms_decision_cachep;
static struct kmem_cache *avc_xperms_cachep;
static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
{
......@@ -101,6 +116,7 @@ static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
return;
}
BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map));
perms = secclass_map[tclass-1].perms;
audit_log_format(ab, " {");
......@@ -149,7 +165,7 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla
kfree(scontext);
}
BUG_ON(tclass >= ARRAY_SIZE(secclass_map));
BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map));
audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name);
}
......@@ -170,7 +186,17 @@ void __init avc_init(void)
atomic_set(&avc_cache.lru_hint, 0);
avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
0, SLAB_PANIC, NULL);
0, SLAB_PANIC, NULL);
avc_xperms_cachep = kmem_cache_create("avc_xperms_node",
sizeof(struct avc_xperms_node),
0, SLAB_PANIC, NULL);
avc_xperms_decision_cachep = kmem_cache_create(
"avc_xperms_decision_node",
sizeof(struct avc_xperms_decision_node),
0, SLAB_PANIC, NULL);
avc_xperms_data_cachep = kmem_cache_create("avc_xperms_data",
sizeof(struct extended_perms_data),
0, SLAB_PANIC, NULL);
audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n");
}
......@@ -205,9 +231,261 @@ int avc_get_hash_stats(char *page)
slots_used, AVC_CACHE_SLOTS, max_chain_len);
}
/*
* using a linked list for extended_perms_decision lookup because the list is
* always small. i.e. less than 5, typically 1
*/
static struct extended_perms_decision *avc_xperms_decision_lookup(u8 driver,
struct avc_xperms_node *xp_node)
{
struct avc_xperms_decision_node *xpd_node;
list_for_each_entry(xpd_node, &xp_node->xpd_head, xpd_list) {
if (xpd_node->xpd.driver == driver)
return &xpd_node->xpd;
}
return NULL;
}
static inline unsigned int
avc_xperms_has_perm(struct extended_perms_decision *xpd,
u8 perm, u8 which)
{
unsigned int rc = 0;
if ((which == XPERMS_ALLOWED) &&
(xpd->used & XPERMS_ALLOWED))
rc = security_xperm_test(xpd->allowed->p, perm);
else if ((which == XPERMS_AUDITALLOW) &&
(xpd->used & XPERMS_AUDITALLOW))
rc = security_xperm_test(xpd->auditallow->p, perm);
else if ((which == XPERMS_DONTAUDIT) &&
(xpd->used & XPERMS_DONTAUDIT))
rc = security_xperm_test(xpd->dontaudit->p, perm);
return rc;
}
static void avc_xperms_allow_perm(struct avc_xperms_node *xp_node,
u8 driver, u8 perm)
{
struct extended_perms_decision *xpd;
security_xperm_set(xp_node->xp.drivers.p, driver);
xpd = avc_xperms_decision_lookup(driver, xp_node);
if (xpd && xpd->allowed)
security_xperm_set(xpd->allowed->p, perm);
}
static void avc_xperms_decision_free(struct avc_xperms_decision_node *xpd_node)
{
struct extended_perms_decision *xpd;
xpd = &xpd_node->xpd;
if (xpd->allowed)
kmem_cache_free(avc_xperms_data_cachep, xpd->allowed);
if (xpd->auditallow)
kmem_cache_free(avc_xperms_data_cachep, xpd->auditallow);
if (xpd->dontaudit)
kmem_cache_free(avc_xperms_data_cachep, xpd->dontaudit);
kmem_cache_free(avc_xperms_decision_cachep, xpd_node);
}
static void avc_xperms_free(struct avc_xperms_node *xp_node)
{
struct avc_xperms_decision_node *xpd_node, *tmp;
if (!xp_node)
return;
list_for_each_entry_safe(xpd_node, tmp, &xp_node->xpd_head, xpd_list) {
list_del(&xpd_node->xpd_list);
avc_xperms_decision_free(xpd_node);
}
kmem_cache_free(avc_xperms_cachep, xp_node);
}
static void avc_copy_xperms_decision(struct extended_perms_decision *dest,
struct extended_perms_decision *src)
{
dest->driver = src->driver;
dest->used = src->used;
if (dest->used & XPERMS_ALLOWED)
memcpy(dest->allowed->p, src->allowed->p,
sizeof(src->allowed->p));
if (dest->used & XPERMS_AUDITALLOW)
memcpy(dest->auditallow->p, src->auditallow->p,
sizeof(src->auditallow->p));
if (dest->used & XPERMS_DONTAUDIT)
memcpy(dest->dontaudit->p, src->dontaudit->p,
sizeof(src->dontaudit->p));
}
/*
* similar to avc_copy_xperms_decision, but only copy decision
* information relevant to this perm
*/
static inline void avc_quick_copy_xperms_decision(u8 perm,
struct extended_perms_decision *dest,
struct extended_perms_decision *src)
{
/*
* compute index of the u32 of the 256 bits (8 u32s) that contain this
* command permission
*/
u8 i = perm >> 5;
dest->used = src->used;
if (dest->used & XPERMS_ALLOWED)
dest->allowed->p[i] = src->allowed->p[i];
if (dest->used & XPERMS_AUDITALLOW)
dest->auditallow->p[i] = src->auditallow->p[i];
if (dest->used & XPERMS_DONTAUDIT)
dest->dontaudit->p[i] = src->dontaudit->p[i];
}
static struct avc_xperms_decision_node
*avc_xperms_decision_alloc(u8 which)
{
struct avc_xperms_decision_node *xpd_node;
struct extended_perms_decision *xpd;
xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep,
GFP_ATOMIC | __GFP_NOMEMALLOC);
if (!xpd_node)
return NULL;
xpd = &xpd_node->xpd;
if (which & XPERMS_ALLOWED) {
xpd->allowed = kmem_cache_zalloc(avc_xperms_data_cachep,
GFP_ATOMIC | __GFP_NOMEMALLOC);
if (!xpd->allowed)
goto error;
}
if (which & XPERMS_AUDITALLOW) {
xpd->auditallow = kmem_cache_zalloc(avc_xperms_data_cachep,
GFP_ATOMIC | __GFP_NOMEMALLOC);
if (!xpd->auditallow)
goto error;
}
if (which & XPERMS_DONTAUDIT) {
xpd->dontaudit = kmem_cache_zalloc(avc_xperms_data_cachep,
GFP_ATOMIC | __GFP_NOMEMALLOC);
if (!xpd->dontaudit)
goto error;
}
return xpd_node;
error:
avc_xperms_decision_free(xpd_node);
return NULL;
}
static int avc_add_xperms_decision(struct avc_node *node,
struct extended_perms_decision *src)
{
struct avc_xperms_decision_node *dest_xpd;
node->ae.xp_node->xp.len++;
dest_xpd = avc_xperms_decision_alloc(src->used);
if (!dest_xpd)
return -ENOMEM;
avc_copy_xperms_decision(&dest_xpd->xpd, src);
list_add(&dest_xpd->xpd_list, &node->ae.xp_node->xpd_head);
return 0;
}
static struct avc_xperms_node *avc_xperms_alloc(void)
{
struct avc_xperms_node *xp_node;
xp_node = kmem_cache_zalloc(avc_xperms_cachep,
GFP_ATOMIC|__GFP_NOMEMALLOC);
if (!xp_node)
return xp_node;
INIT_LIST_HEAD(&xp_node->xpd_head);
return xp_node;
}
static int avc_xperms_populate(struct avc_node *node,
struct avc_xperms_node *src)
{
struct avc_xperms_node *dest;
struct avc_xperms_decision_node *dest_xpd;
struct avc_xperms_decision_node *src_xpd;
if (src->xp.len == 0)
return 0;
dest = avc_xperms_alloc();
if (!dest)
return -ENOMEM;
memcpy(dest->xp.drivers.p, src->xp.drivers.p, sizeof(dest->xp.drivers.p));
dest->xp.len = src->xp.len;
/* for each source xpd allocate a destination xpd and copy */
list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) {
dest_xpd = avc_xperms_decision_alloc(src_xpd->xpd.used);
if (!dest_xpd)
goto error;
avc_copy_xperms_decision(&dest_xpd->xpd, &src_xpd->xpd);
list_add(&dest_xpd->xpd_list, &dest->xpd_head);
}
node->ae.xp_node = dest;
return 0;
error:
avc_xperms_free(dest);
return -ENOMEM;
}
static inline u32 avc_xperms_audit_required(u32 requested,
struct av_decision *avd,
struct extended_perms_decision *xpd,
u8 perm,
int result,
u32 *deniedp)
{
u32 denied, audited;
denied = requested & ~avd->allowed;
if (unlikely(denied)) {
audited = denied & avd->auditdeny;
if (audited && xpd) {
if (avc_xperms_has_perm(xpd, perm, XPERMS_DONTAUDIT))
audited &= ~requested;
}
} else if (result) {
audited = denied = requested;
} else {
audited = requested & avd->auditallow;
if (audited && xpd) {
if (!avc_xperms_has_perm(xpd, perm, XPERMS_AUDITALLOW))
audited &= ~requested;
}
}
*deniedp = denied;
return audited;
}
static inline int avc_xperms_audit(u32 ssid, u32 tsid, u16 tclass,
u32 requested, struct av_decision *avd,
struct extended_perms_decision *xpd,
u8 perm, int result,
struct common_audit_data *ad)
{
u32 audited, denied;
audited = avc_xperms_audit_required(
requested, avd, xpd, perm, result, &denied);
if (likely(!audited))
return 0;
return slow_avc_audit(ssid, tsid, tclass, requested,
audited, denied, result, ad, 0);
}
static void avc_node_free(struct rcu_head *rhead)
{
struct avc_node *node = container_of(rhead, struct avc_node, rhead);
avc_xperms_free(node->ae.xp_node);
kmem_cache_free(avc_node_cachep, node);
avc_cache_stats_incr(frees);
}
......@@ -221,6 +499,7 @@ static void avc_node_delete(struct avc_node *node)
static void avc_node_kill(struct avc_node *node)
{
avc_xperms_free(node->ae.xp_node);
kmem_cache_free(avc_node_cachep, node);
avc_cache_stats_incr(frees);
atomic_dec(&avc_cache.active_nodes);
......@@ -367,6 +646,7 @@ static int avc_latest_notif_update(int seqno, int is_insert)
* @tsid: target security identifier
* @tclass: target security class
* @avd: resulting av decision
* @xp_node: resulting extended permissions
*
* Insert an AVC entry for the SID pair
* (@ssid, @tsid) and class @tclass.
......@@ -378,7 +658,9 @@ static int avc_latest_notif_update(int seqno, int is_insert)
* the access vectors into a cache entry, returns
* avc_node inserted. Otherwise, this function returns NULL.
*/
static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass,
struct av_decision *avd,
struct avc_xperms_node *xp_node)
{
struct avc_node *pos, *node = NULL;
int hvalue;
......@@ -391,10 +673,15 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_dec
if (node) {
struct hlist_head *head;
spinlock_t *lock;
int rc = 0;
hvalue = avc_hash(ssid, tsid, tclass);
avc_node_populate(node, ssid, tsid, tclass, avd);
rc = avc_xperms_populate(node, xp_node);
if (rc) {
kmem_cache_free(avc_node_cachep, node);
return NULL;
}
head = &avc_cache.slots[hvalue];
lock = &avc_cache.slots_lock[hvalue];
......@@ -523,14 +810,17 @@ int __init avc_add_callback(int (*callback)(u32 event), u32 events)
* @perms : Permission mask bits
* @ssid,@tsid,@tclass : identifier of an AVC entry
* @seqno : sequence number when decision was made
* @xpd: extended_perms_decision to be added to the node
*
* if a valid AVC entry doesn't exist,this function returns -ENOENT.
* if kmalloc() called internal returns NULL, this function returns -ENOMEM.
* otherwise, this function updates the AVC entry. The original AVC-entry object
* will release later by RCU.
*/
static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
u32 seqno)
static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
u32 tsid, u16 tclass, u32 seqno,
struct extended_perms_decision *xpd,
u32 flags)
{
int hvalue, rc = 0;
unsigned long flag;
......@@ -574,9 +864,19 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd);
if (orig->ae.xp_node) {
rc = avc_xperms_populate(node, orig->ae.xp_node);
if (rc) {
kmem_cache_free(avc_node_cachep, node);
goto out_unlock;
}
}
switch (event) {
case AVC_CALLBACK_GRANT:
node->ae.avd.allowed |= perms;
if (node->ae.xp_node && (flags & AVC_EXTENDED_PERMS))
avc_xperms_allow_perm(node->ae.xp_node, driver, xperm);
break;
case AVC_CALLBACK_TRY_REVOKE:
case AVC_CALLBACK_REVOKE:
......@@ -594,6 +894,9 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
case AVC_CALLBACK_AUDITDENY_DISABLE:
node->ae.avd.auditdeny &= ~perms;
break;
case AVC_CALLBACK_ADD_XPERMS:
avc_add_xperms_decision(node, xpd);
break;
}
avc_node_replace(node, orig);
out_unlock:
......@@ -665,18 +968,20 @@ int avc_ss_reset(u32 seqno)
* results in a bigger stack frame.
*/
static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid,
u16 tclass, struct av_decision *avd)
u16 tclass, struct av_decision *avd,
struct avc_xperms_node *xp_node)
{
rcu_read_unlock();
security_compute_av(ssid, tsid, tclass, avd);
INIT_LIST_HEAD(&xp_node->xpd_head);
security_compute_av(ssid, tsid, tclass, avd, &xp_node->xp);
rcu_read_lock();
return avc_insert(ssid, tsid, tclass, avd);
return avc_insert(ssid, tsid, tclass, avd, xp_node);
}
static noinline int avc_denied(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned flags,
struct av_decision *avd)
u16 tclass, u32 requested,
u8 driver, u8 xperm, unsigned flags,
struct av_decision *avd)
{
if (flags & AVC_STRICT)
return -EACCES;
......@@ -684,11 +989,91 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE))
return -EACCES;
avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
tsid, tclass, avd->seqno);
avc_update_node(AVC_CALLBACK_GRANT, requested, driver, xperm, ssid,
tsid, tclass, avd->seqno, NULL, flags);
return 0;
}
/*
* The avc extended permissions logic adds an additional 256 bits of
* permissions to an avc node when extended permissions for that node are
* specified in the avtab. If the additional 256 permissions is not adequate,
* as-is the case with ioctls, then multiple may be chained together and the
* driver field is used to specify which set contains the permission.
*/
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
u8 driver, u8 xperm, struct common_audit_data *ad)
{
struct avc_node *node;
struct av_decision avd;
u32 denied;
struct extended_perms_decision local_xpd;
struct extended_perms_decision *xpd = NULL;
struct extended_perms_data allowed;
struct extended_perms_data auditallow;
struct extended_perms_data dontaudit;
struct avc_xperms_node local_xp_node;
struct avc_xperms_node *xp_node;
int rc = 0, rc2;
xp_node = &local_xp_node;
BUG_ON(!requested);
rcu_read_lock();
node = avc_lookup(ssid, tsid, tclass);
if (unlikely(!node)) {
node = avc_compute_av(ssid, tsid, tclass, &avd, xp_node);
} else {
memcpy(&avd, &node->ae.avd, sizeof(avd));
xp_node = node->ae.xp_node;
}
/* if extended permissions are not defined, only consider av_decision */
if (!xp_node || !xp_node->xp.len)
goto decision;
local_xpd.allowed = &allowed;
local_xpd.auditallow = &auditallow;
local_xpd.dontaudit = &dontaudit;
xpd = avc_xperms_decision_lookup(driver, xp_node);
if (unlikely(!xpd)) {
/*
* Compute the extended_perms_decision only if the driver
* is flagged
*/
if (!security_xperm_test(xp_node->xp.drivers.p, driver)) {
avd.allowed &= ~requested;
goto decision;
}
rcu_read_unlock();
security_compute_xperms_decision(ssid, tsid, tclass, driver,
&local_xpd);
rcu_read_lock();
avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, driver, xperm,
ssid, tsid, tclass, avd.seqno, &local_xpd, 0);
} else {
avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd);
}
xpd = &local_xpd;
if (!avc_xperms_has_perm(xpd, xperm, XPERMS_ALLOWED))
avd.allowed &= ~requested;
decision:
denied = requested & ~(avd.allowed);
if (unlikely(denied))
rc = avc_denied(ssid, tsid, tclass, requested, driver, xperm,
AVC_EXTENDED_PERMS, &avd);
rcu_read_unlock();
rc2 = avc_xperms_audit(ssid, tsid, tclass, requested,
&avd, xpd, xperm, rc, ad);
if (rc2)
return rc2;
return rc;
}
/**
* avc_has_perm_noaudit - Check permissions but perform no auditing.
......@@ -716,6 +1101,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
struct av_decision *avd)
{
struct avc_node *node;
struct avc_xperms_node xp_node;
int rc = 0;
u32 denied;
......@@ -725,13 +1111,13 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
node = avc_lookup(ssid, tsid, tclass);
if (unlikely(!node))
node = avc_compute_av(ssid, tsid, tclass, avd);
node = avc_compute_av(ssid, tsid, tclass, avd, &xp_node);
else
memcpy(avd, &node->ae.avd, sizeof(*avd));
denied = requested & ~(avd->allowed);
if (unlikely(denied))
rc = avc_denied(ssid, tsid, tclass, requested, flags, avd);
rc = avc_denied(ssid, tsid, tclass, requested, 0, 0, flags, avd);
rcu_read_unlock();
return rc;
......
此差异已折叠。
......@@ -143,6 +143,7 @@ static inline int avc_audit(u32 ssid, u32 tsid,
}
#define AVC_STRICT 1 /* Ignore permissive mode. */
#define AVC_EXTENDED_PERMS 2 /* update extended permissions */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned flags,
......@@ -156,6 +157,10 @@ int avc_has_perm_flags(u32 ssid, u32 tsid,
struct common_audit_data *auditdata,
int flags);
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
u8 driver, u8 perm, struct common_audit_data *ad);
u32 avc_policy_seqno(void);
#define AVC_CALLBACK_GRANT 1
......@@ -166,6 +171,7 @@ u32 avc_policy_seqno(void);
#define AVC_CALLBACK_AUDITALLOW_DISABLE 32
#define AVC_CALLBACK_AUDITDENY_ENABLE 64
#define AVC_CALLBACK_AUDITDENY_DISABLE 128
#define AVC_CALLBACK_ADD_XPERMS 256
int avc_add_callback(int (*callback)(u32 event), u32 events);
......
......@@ -35,13 +35,14 @@
#define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27
#define POLICYDB_VERSION_DEFAULT_TYPE 28
#define POLICYDB_VERSION_CONSTRAINT_NAMES 29
#define POLICYDB_VERSION_XPERMS_IOCTL 30
/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
#else
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_CONSTRAINT_NAMES
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_XPERMS_IOCTL
#endif
/* Mask for just the mount related flags */
......@@ -109,11 +110,38 @@ struct av_decision {
u32 flags;
};
#define XPERMS_ALLOWED 1
#define XPERMS_AUDITALLOW 2
#define XPERMS_DONTAUDIT 4
#define security_xperm_set(perms, x) (perms[x >> 5] |= 1 << (x & 0x1f))
#define security_xperm_test(perms, x) (1 & (perms[x >> 5] >> (x & 0x1f)))
struct extended_perms_data {
u32 p[8];
};
struct extended_perms_decision {
u8 used;
u8 driver;
struct extended_perms_data *allowed;
struct extended_perms_data *auditallow;
struct extended_perms_data *dontaudit;
};
struct extended_perms {
u16 len; /* length associated decision chain */
struct extended_perms_data drivers; /* flag drivers that are used */
};
/* definitions of av_decision.flags */
#define AVD_FLAGS_PERMISSIVE 0x0001
void security_compute_av(u32 ssid, u32 tsid,
u16 tclass, struct av_decision *avd);
u16 tclass, struct av_decision *avd,
struct extended_perms *xperms);
void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 tclass,
u8 driver, struct extended_perms_decision *xpermd);
void security_compute_av_user(u32 ssid, u32 tsid,
u16 tclass, struct av_decision *avd);
......
此差异已折叠。
......@@ -23,6 +23,7 @@
#ifndef _SS_AVTAB_H_
#define _SS_AVTAB_H_
#include "security.h"
#include <linux/flex_array.h>
struct avtab_key {
......@@ -37,13 +38,43 @@ struct avtab_key {
#define AVTAB_MEMBER 0x0020
#define AVTAB_CHANGE 0x0040
#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
/* extended permissions */
#define AVTAB_XPERMS_ALLOWED 0x0100
#define AVTAB_XPERMS_AUDITALLOW 0x0200
#define AVTAB_XPERMS_DONTAUDIT 0x0400
#define AVTAB_XPERMS (AVTAB_XPERMS_ALLOWED | \
AVTAB_XPERMS_AUDITALLOW | \
AVTAB_XPERMS_DONTAUDIT)
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
u16 specified; /* what field is specified */
};
/*
* For operations that require more than the 32 permissions provided by the avc
* extended permissions may be used to provide 256 bits of permissions.
*/
struct avtab_extended_perms {
/* These are not flags. All 256 values may be used */
#define AVTAB_XPERMS_IOCTLFUNCTION 0x01
#define AVTAB_XPERMS_IOCTLDRIVER 0x02
/* extension of the avtab_key specified */
u8 specified; /* ioctl, netfilter, ... */
/*
* if 256 bits is not adequate as is often the case with ioctls, then
* multiple extended perms may be used and the driver field
* specifies which permissions are included.
*/
u8 driver;
/* 256 bits of permissions */
struct extended_perms_data perms;
};
struct avtab_datum {
u32 data; /* access vector or type value */
union {
u32 data; /* access vector or type value */
struct avtab_extended_perms *xperms;
} u;
};
struct avtab_node {
......
此差异已折叠。
......@@ -73,8 +73,10 @@ int cond_read_list(struct policydb *p, void *fp);
int cond_write_bool(void *key, void *datum, void *ptr);
int cond_write_list(struct policydb *p, struct cond_node *list, void *fp);
void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd);
void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
struct av_decision *avd, struct extended_perms *xperms);
void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
struct extended_perms_decision *xpermd);
int evaluate_cond_node(struct policydb *p, struct cond_node *node);
#endif /* _CONDITIONAL_H_ */
......@@ -148,6 +148,11 @@ static struct policydb_compat_info policydb_compat[] = {
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
},
{
.version = POLICYDB_VERSION_XPERMS_IOCTL,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
},
};
static struct policydb_compat_info *policydb_lookup_compat(int version)
......
此差异已折叠。
......@@ -11,5 +11,11 @@
extern struct policydb policydb;
void services_compute_xperms_drivers(struct extended_perms *xperms,
struct avtab_node *node);
void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
struct avtab_node *node);
#endif /* _SS_SERVICES_H_ */
此差异已折叠。
......@@ -639,6 +639,12 @@ int smack_privileged(int cap)
struct smack_known *skp = smk_of_current();
struct smack_onlycap *sop;
/*
* All kernel tasks are privileged
*/
if (unlikely(current->flags & PF_KTHREAD))
return 1;
if (!capable(cap))
return 0;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册