forked from Minki/linux
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
...
This commit is contained in:
commit
b793c005ce
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
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
|
||||
|
||||
|
21
MAINTAINERS
21
MAINTAINERS
@ -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
|
||||
|
13
Makefile
13
Makefile
@ -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)
|
||||
|
42
certs/Kconfig
Normal file
42
certs/Kconfig
Normal file
@ -0,0 +1,42 @@
|
||||
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
|
94
certs/Makefile
Normal file
94
certs/Makefile
Normal file
@ -0,0 +1,94 @@
|
||||
#
|
||||
# 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 @@ dodgy_cert:
|
||||
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));
|
||||
|
@ -1,14 +1,14 @@
|
||||
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->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->authority ||
|
||||
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 @@ error_no_desc:
|
||||
* @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 @@ error_no_desc:
|
||||
* 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;
|
||||
|
||||
|
35
crypto/asymmetric_keys/x509_akid.asn1
Normal file
35
crypto/asymmetric_keys/x509_akid.asn1
Normal file
@ -0,0 +1,35 @@
|
||||
-- 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);
|
||||
}
|
||||
}
|
||||
|
||||
pr_devel("<==%s() = 0 [%x]\n", __func__,
|
||||
key_serial(key_ref_to_ptr(key)));
|
||||
return key_ref_to_ptr(key);
|
||||
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;
|
||||
|
||||
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>
|
||||
|
||||
|
40
init/Kconfig
40
init/Kconfig
@ -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)
|
||||
if (ms.id_type != PKEY_ID_PKCS7) {
|
||||
pr_err("Module is not signed with expected PKCS#7 message\n");
|
||||
return -ENOPKG;
|
||||
|
||||
if (ms.hash >= PKEY_HASH__LAST ||
|
||||
!hash_algo_name[ms.hash])
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
ret = verify_signature(key, pks);
|
||||
pr_devel("verify_signature() = %d\n", ret);
|
||||
|
||||
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(¤t->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 @@ next_op:
|
||||
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 @@ next_op:
|
||||
/* 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 @@ next_op:
|
||||
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 @@ next_op:
|
||||
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 @@ next_op:
|
||||
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 @@ next_op:
|
||||
}
|
||||
|
||||
/* 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:
|
||||
|
2
scripts/.gitignore
vendored
2
scripts/.gitignore
vendored
@ -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 @@ overrun_error:
|
||||
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 @@ dont_render_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 @@ dont_render_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 @@ dont_render_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 @@ dont_render_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)
|
||||
|
166
scripts/extract-cert.c
Normal file
166
scripts/extract-cert.c
Normal file
@ -0,0 +1,166 @@
|
||||
/* 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",
|
||||
|
@ -1,421 +0,0 @@
|
||||
#!/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;
|
||||
}
|
260
scripts/sign-file.c
Executable file
260
scripts/sign-file.c
Executable file
@ -0,0 +1,260 @@
|
||||
/* 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 @@ out:
|
||||
* @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;
|
||||
|
@ -254,10 +254,21 @@ static void inode_free_security(struct inode *inode)
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct superblock_security_struct *sbsec = inode->i_sb->s_security;
|
||||
|
||||
spin_lock(&sbsec->isec_lock);
|
||||
if (!list_empty(&isec->list))
|
||||
/*
|
||||
* As not all inode security structures are in a list, we check for
|
||||
* empty list outside of the lock to make sure that we won't waste
|
||||
* time taking a lock doing nothing.
|
||||
*
|
||||
* The list_del_init() function can be safely called more than once.
|
||||
* It should not be possible for this function to be called with
|
||||
* concurrent list_add(), but for better safety against future changes
|
||||
* in the code, we use list_empty_careful() here.
|
||||
*/
|
||||
if (!list_empty_careful(&isec->list)) {
|
||||
spin_lock(&sbsec->isec_lock);
|
||||
list_del_init(&isec->list);
|
||||
spin_unlock(&sbsec->isec_lock);
|
||||
spin_unlock(&sbsec->isec_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* The inode may still be referenced in a path walk and
|
||||
@ -1698,6 +1709,32 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the label for an inode that might be unioned.
|
||||
*/
|
||||
static int selinux_determine_inode_label(const struct inode *dir,
|
||||
const struct qstr *name,
|
||||
u16 tclass,
|
||||
u32 *_new_isid)
|
||||
{
|
||||
const struct superblock_security_struct *sbsec = dir->i_sb->s_security;
|
||||
const struct inode_security_struct *dsec = dir->i_security;
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
|
||||
if ((sbsec->flags & SE_SBINITIALIZED) &&
|
||||
(sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) {
|
||||
*_new_isid = sbsec->mntpoint_sid;
|
||||
} else if ((sbsec->flags & SBLABEL_MNT) &&
|
||||
tsec->create_sid) {
|
||||
*_new_isid = tsec->create_sid;
|
||||
} else {
|
||||
return security_transition_sid(tsec->sid, dsec->sid, tclass,
|
||||
name, _new_isid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether a task can create a file. */
|
||||
static int may_create(struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
@ -1714,7 +1751,6 @@ static int may_create(struct inode *dir,
|
||||
sbsec = dir->i_sb->s_security;
|
||||
|
||||
sid = tsec->sid;
|
||||
newsid = tsec->create_sid;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_DENTRY;
|
||||
ad.u.dentry = dentry;
|
||||
@ -1725,12 +1761,10 @@ static int may_create(struct inode *dir,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {
|
||||
rc = security_transition_sid(sid, dsec->sid, tclass,
|
||||
&dentry->d_name, &newsid);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
rc = selinux_determine_inode_label(dir, &dentry->d_name, tclass,
|
||||
&newsid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad);
|
||||
if (rc)
|
||||
@ -2704,32 +2738,14 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
|
||||
struct qstr *name, void **ctx,
|
||||
u32 *ctxlen)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
struct task_security_struct *tsec;
|
||||
struct inode_security_struct *dsec;
|
||||
struct superblock_security_struct *sbsec;
|
||||
struct inode *dir = d_backing_inode(dentry->d_parent);
|
||||
u32 newsid;
|
||||
int rc;
|
||||
|
||||
tsec = cred->security;
|
||||
dsec = dir->i_security;
|
||||
sbsec = dir->i_sb->s_security;
|
||||
|
||||
if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
|
||||
newsid = tsec->create_sid;
|
||||
} else {
|
||||
rc = security_transition_sid(tsec->sid, dsec->sid,
|
||||
inode_mode_to_security_class(mode),
|
||||
name,
|
||||
&newsid);
|
||||
if (rc) {
|
||||
printk(KERN_WARNING
|
||||
"%s: security_transition_sid failed, rc=%d\n",
|
||||
__func__, -rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
rc = selinux_determine_inode_label(d_inode(dentry->d_parent), name,
|
||||
inode_mode_to_security_class(mode),
|
||||
&newsid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return security_sid_to_context(newsid, (char **)ctx, ctxlen);
|
||||
}
|
||||
@ -2752,22 +2768,12 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
|
||||
sid = tsec->sid;
|
||||
newsid = tsec->create_sid;
|
||||
|
||||
if ((sbsec->flags & SE_SBINITIALIZED) &&
|
||||
(sbsec->behavior == SECURITY_FS_USE_MNTPOINT))
|
||||
newsid = sbsec->mntpoint_sid;
|
||||
else if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {
|
||||
rc = security_transition_sid(sid, dsec->sid,
|
||||
inode_mode_to_security_class(inode->i_mode),
|
||||
qstr, &newsid);
|
||||
if (rc) {
|
||||
printk(KERN_WARNING "%s: "
|
||||
"security_transition_sid failed, rc=%d (dev=%s "
|
||||
"ino=%ld)\n",
|
||||
__func__,
|
||||
-rc, inode->i_sb->s_id, inode->i_ino);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
rc = selinux_determine_inode_label(
|
||||
dir, qstr,
|
||||
inode_mode_to_security_class(inode->i_mode),
|
||||
&newsid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Possibly defer initialization to selinux_complete_init. */
|
||||
if (sbsec->flags & SE_SBINITIALIZED) {
|
||||
@ -3228,6 +3234,46 @@ static void selinux_file_free_security(struct file *file)
|
||||
file_free_security(file);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a task has the ioctl permission and cmd
|
||||
* operation to an inode.
|
||||
*/
|
||||
int ioctl_has_perm(const struct cred *cred, struct file *file,
|
||||
u32 requested, u16 cmd)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct lsm_ioctlop_audit ioctl;
|
||||
u32 ssid = cred_sid(cred);
|
||||
int rc;
|
||||
u8 driver = cmd >> 8;
|
||||
u8 xperm = cmd & 0xff;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IOCTL_OP;
|
||||
ad.u.op = &ioctl;
|
||||
ad.u.op->cmd = cmd;
|
||||
ad.u.op->path = file->f_path;
|
||||
|
||||
if (ssid != fsec->sid) {
|
||||
rc = avc_has_perm(ssid, fsec->sid,
|
||||
SECCLASS_FD,
|
||||
FD__USE,
|
||||
&ad);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (unlikely(IS_PRIVATE(inode)))
|
||||
return 0;
|
||||
|
||||
rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
|
||||
requested, driver, xperm, &ad);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int selinux_file_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
@ -3270,7 +3316,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
|
||||
* to the file's ioctl() function.
|
||||
*/
|
||||
default:
|
||||
error = file_has_perm(cred, file, FILE__IOCTL);
|
||||
error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
@ -4520,6 +4566,7 @@ static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority
|
||||
|
||||
sksec->peer_sid = SECINITSID_UNLABELED;
|
||||
sksec->sid = SECINITSID_UNLABELED;
|
||||
sksec->sclass = SECCLASS_SOCKET;
|
||||
selinux_netlbl_sk_security_reset(sksec);
|
||||
sk->sk_security = sksec;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "policydb.h"
|
||||
|
||||
static struct kmem_cache *avtab_node_cachep;
|
||||
static struct kmem_cache *avtab_xperms_cachep;
|
||||
|
||||
/* Based on MurmurHash3, written by Austin Appleby and placed in the
|
||||
* public domain.
|
||||
@ -70,11 +71,24 @@ avtab_insert_node(struct avtab *h, int hvalue,
|
||||
struct avtab_key *key, struct avtab_datum *datum)
|
||||
{
|
||||
struct avtab_node *newnode;
|
||||
struct avtab_extended_perms *xperms;
|
||||
newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
|
||||
if (newnode == NULL)
|
||||
return NULL;
|
||||
newnode->key = *key;
|
||||
newnode->datum = *datum;
|
||||
|
||||
if (key->specified & AVTAB_XPERMS) {
|
||||
xperms = kmem_cache_zalloc(avtab_xperms_cachep, GFP_KERNEL);
|
||||
if (xperms == NULL) {
|
||||
kmem_cache_free(avtab_node_cachep, newnode);
|
||||
return NULL;
|
||||
}
|
||||
*xperms = *(datum->u.xperms);
|
||||
newnode->datum.u.xperms = xperms;
|
||||
} else {
|
||||
newnode->datum.u.data = datum->u.data;
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
newnode->next = prev->next;
|
||||
prev->next = newnode;
|
||||
@ -107,8 +121,12 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
|
||||
if (key->source_type == cur->key.source_type &&
|
||||
key->target_type == cur->key.target_type &&
|
||||
key->target_class == cur->key.target_class &&
|
||||
(specified & cur->key.specified))
|
||||
(specified & cur->key.specified)) {
|
||||
/* extended perms may not be unique */
|
||||
if (specified & AVTAB_XPERMS)
|
||||
break;
|
||||
return -EEXIST;
|
||||
}
|
||||
if (key->source_type < cur->key.source_type)
|
||||
break;
|
||||
if (key->source_type == cur->key.source_type &&
|
||||
@ -271,6 +289,9 @@ void avtab_destroy(struct avtab *h)
|
||||
while (cur) {
|
||||
temp = cur;
|
||||
cur = cur->next;
|
||||
if (temp->key.specified & AVTAB_XPERMS)
|
||||
kmem_cache_free(avtab_xperms_cachep,
|
||||
temp->datum.u.xperms);
|
||||
kmem_cache_free(avtab_node_cachep, temp);
|
||||
}
|
||||
}
|
||||
@ -359,7 +380,10 @@ static uint16_t spec_order[] = {
|
||||
AVTAB_AUDITALLOW,
|
||||
AVTAB_TRANSITION,
|
||||
AVTAB_CHANGE,
|
||||
AVTAB_MEMBER
|
||||
AVTAB_MEMBER,
|
||||
AVTAB_XPERMS_ALLOWED,
|
||||
AVTAB_XPERMS_AUDITALLOW,
|
||||
AVTAB_XPERMS_DONTAUDIT
|
||||
};
|
||||
|
||||
int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
|
||||
@ -369,10 +393,11 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
|
||||
{
|
||||
__le16 buf16[4];
|
||||
u16 enabled;
|
||||
__le32 buf32[7];
|
||||
u32 items, items2, val, vers = pol->policyvers;
|
||||
struct avtab_key key;
|
||||
struct avtab_datum datum;
|
||||
struct avtab_extended_perms xperms;
|
||||
__le32 buf32[ARRAY_SIZE(xperms.perms.p)];
|
||||
int i, rc;
|
||||
unsigned set;
|
||||
|
||||
@ -429,11 +454,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
|
||||
printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (val & AVTAB_XPERMS) {
|
||||
printk(KERN_ERR "SELinux: avtab: entry has extended permissions\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
|
||||
if (val & spec_order[i]) {
|
||||
key.specified = spec_order[i] | enabled;
|
||||
datum.data = le32_to_cpu(buf32[items++]);
|
||||
datum.u.data = le32_to_cpu(buf32[items++]);
|
||||
rc = insertf(a, &key, &datum, p);
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -476,14 +505,42 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = next_entry(buf32, fp, sizeof(u32));
|
||||
if (rc) {
|
||||
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
|
||||
return rc;
|
||||
if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) &&
|
||||
(key.specified & AVTAB_XPERMS)) {
|
||||
printk(KERN_ERR "SELinux: avtab: policy version %u does not "
|
||||
"support extended permissions rules and one "
|
||||
"was specified\n", vers);
|
||||
return -EINVAL;
|
||||
} else if (key.specified & AVTAB_XPERMS) {
|
||||
memset(&xperms, 0, sizeof(struct avtab_extended_perms));
|
||||
rc = next_entry(&xperms.specified, fp, sizeof(u8));
|
||||
if (rc) {
|
||||
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
|
||||
return rc;
|
||||
}
|
||||
rc = next_entry(&xperms.driver, fp, sizeof(u8));
|
||||
if (rc) {
|
||||
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
|
||||
return rc;
|
||||
}
|
||||
rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(xperms.perms.p));
|
||||
if (rc) {
|
||||
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
|
||||
return rc;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(xperms.perms.p); i++)
|
||||
xperms.perms.p[i] = le32_to_cpu(buf32[i]);
|
||||
datum.u.xperms = &xperms;
|
||||
} else {
|
||||
rc = next_entry(buf32, fp, sizeof(u32));
|
||||
if (rc) {
|
||||
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
|
||||
return rc;
|
||||
}
|
||||
datum.u.data = le32_to_cpu(*buf32);
|
||||
}
|
||||
datum.data = le32_to_cpu(*buf32);
|
||||
if ((key.specified & AVTAB_TYPE) &&
|
||||
!policydb_type_isvalid(pol, datum.data)) {
|
||||
!policydb_type_isvalid(pol, datum.u.data)) {
|
||||
printk(KERN_ERR "SELinux: avtab: invalid type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -543,8 +600,9 @@ bad:
|
||||
int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
|
||||
{
|
||||
__le16 buf16[4];
|
||||
__le32 buf32[1];
|
||||
__le32 buf32[ARRAY_SIZE(cur->datum.u.xperms->perms.p)];
|
||||
int rc;
|
||||
unsigned int i;
|
||||
|
||||
buf16[0] = cpu_to_le16(cur->key.source_type);
|
||||
buf16[1] = cpu_to_le16(cur->key.target_type);
|
||||
@ -553,8 +611,22 @@ int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
|
||||
rc = put_entry(buf16, sizeof(u16), 4, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
buf32[0] = cpu_to_le32(cur->datum.data);
|
||||
rc = put_entry(buf32, sizeof(u32), 1, fp);
|
||||
|
||||
if (cur->key.specified & AVTAB_XPERMS) {
|
||||
rc = put_entry(&cur->datum.u.xperms->specified, sizeof(u8), 1, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = put_entry(&cur->datum.u.xperms->driver, sizeof(u8), 1, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
for (i = 0; i < ARRAY_SIZE(cur->datum.u.xperms->perms.p); i++)
|
||||
buf32[i] = cpu_to_le32(cur->datum.u.xperms->perms.p[i]);
|
||||
rc = put_entry(buf32, sizeof(u32),
|
||||
ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp);
|
||||
} else {
|
||||
buf32[0] = cpu_to_le32(cur->datum.u.data);
|
||||
rc = put_entry(buf32, sizeof(u32), 1, fp);
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
return 0;
|
||||
@ -588,9 +660,13 @@ void avtab_cache_init(void)
|
||||
avtab_node_cachep = kmem_cache_create("avtab_node",
|
||||
sizeof(struct avtab_node),
|
||||
0, SLAB_PANIC, NULL);
|
||||
avtab_xperms_cachep = kmem_cache_create("avtab_extended_perms",
|
||||
sizeof(struct avtab_extended_perms),
|
||||
0, SLAB_PANIC, NULL);
|
||||
}
|
||||
|
||||
void avtab_cache_destroy(void)
|
||||
{
|
||||
kmem_cache_destroy(avtab_node_cachep);
|
||||
kmem_cache_destroy(avtab_xperms_cachep);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "security.h"
|
||||
#include "conditional.h"
|
||||
#include "services.h"
|
||||
|
||||
/*
|
||||
* cond_evaluate_expr evaluates a conditional expr
|
||||
@ -612,21 +613,39 @@ int cond_write_list(struct policydb *p, struct cond_node *list, void *fp)
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* Determine whether additional permissions are granted by the conditional
|
||||
* av table, and if so, add them to the result
|
||||
*/
|
||||
void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd)
|
||||
|
||||
void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
|
||||
struct extended_perms_decision *xpermd)
|
||||
{
|
||||
struct avtab_node *node;
|
||||
|
||||
if (!ctab || !key || !avd)
|
||||
if (!ctab || !key || !xpermd)
|
||||
return;
|
||||
|
||||
for (node = avtab_search_node(ctab, key); node;
|
||||
node = avtab_search_node_next(node, key->specified)) {
|
||||
if (node->key.specified & AVTAB_ENABLED)
|
||||
services_compute_xperms_decision(xpermd, node);
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
/* Determine whether additional permissions are granted by the conditional
|
||||
* av table, and if so, add them to the result
|
||||
*/
|
||||
void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
|
||||
struct av_decision *avd, struct extended_perms *xperms)
|
||||
{
|
||||
struct avtab_node *node;
|
||||
|
||||
if (!ctab || !key || !avd || !xperms)
|
||||
return;
|
||||
|
||||
for (node = avtab_search_node(ctab, key); node;
|
||||
node = avtab_search_node_next(node, key->specified)) {
|
||||
if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) ==
|
||||
(node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
|
||||
avd->allowed |= node->datum.data;
|
||||
avd->allowed |= node->datum.u.data;
|
||||
if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) ==
|
||||
(node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
|
||||
/* Since a '0' in an auditdeny mask represents a
|
||||
@ -634,10 +653,13 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi
|
||||
* the '&' operand to ensure that all '0's in the mask
|
||||
* are retained (much unlike the allow and auditallow cases).
|
||||
*/
|
||||
avd->auditdeny &= node->datum.data;
|
||||
avd->auditdeny &= node->datum.u.data;
|
||||
if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
|
||||
(node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
|
||||
avd->auditallow |= node->datum.data;
|
||||
avd->auditallow |= node->datum.u.data;
|
||||
if ((node->key.specified & AVTAB_ENABLED) &&
|
||||
(node->key.specified & AVTAB_XPERMS))
|
||||
services_compute_xperms_drivers(xperms, node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -93,9 +93,10 @@ static int context_struct_to_string(struct context *context, char **scontext,
|
||||
u32 *scontext_len);
|
||||
|
||||
static void context_struct_compute_av(struct context *scontext,
|
||||
struct context *tcontext,
|
||||
u16 tclass,
|
||||
struct av_decision *avd);
|
||||
struct context *tcontext,
|
||||
u16 tclass,
|
||||
struct av_decision *avd,
|
||||
struct extended_perms *xperms);
|
||||
|
||||
struct selinux_mapping {
|
||||
u16 value; /* policy value */
|
||||
@ -565,7 +566,8 @@ static void type_attribute_bounds_av(struct context *scontext,
|
||||
context_struct_compute_av(&lo_scontext,
|
||||
tcontext,
|
||||
tclass,
|
||||
&lo_avd);
|
||||
&lo_avd,
|
||||
NULL);
|
||||
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
||||
return; /* no masked permission */
|
||||
masked = ~lo_avd.allowed & avd->allowed;
|
||||
@ -580,7 +582,8 @@ static void type_attribute_bounds_av(struct context *scontext,
|
||||
context_struct_compute_av(scontext,
|
||||
&lo_tcontext,
|
||||
tclass,
|
||||
&lo_avd);
|
||||
&lo_avd,
|
||||
NULL);
|
||||
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
||||
return; /* no masked permission */
|
||||
masked = ~lo_avd.allowed & avd->allowed;
|
||||
@ -596,7 +599,8 @@ static void type_attribute_bounds_av(struct context *scontext,
|
||||
context_struct_compute_av(&lo_scontext,
|
||||
&lo_tcontext,
|
||||
tclass,
|
||||
&lo_avd);
|
||||
&lo_avd,
|
||||
NULL);
|
||||
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
||||
return; /* no masked permission */
|
||||
masked = ~lo_avd.allowed & avd->allowed;
|
||||
@ -613,13 +617,39 @@ static void type_attribute_bounds_av(struct context *scontext,
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute access vectors based on a context structure pair for
|
||||
* the permissions in a particular class.
|
||||
* flag which drivers have permissions
|
||||
* only looking for ioctl based extended permssions
|
||||
*/
|
||||
void services_compute_xperms_drivers(
|
||||
struct extended_perms *xperms,
|
||||
struct avtab_node *node)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
|
||||
/* if one or more driver has all permissions allowed */
|
||||
for (i = 0; i < ARRAY_SIZE(xperms->drivers.p); i++)
|
||||
xperms->drivers.p[i] |= node->datum.u.xperms->perms.p[i];
|
||||
} else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
|
||||
/* if allowing permissions within a driver */
|
||||
security_xperm_set(xperms->drivers.p,
|
||||
node->datum.u.xperms->driver);
|
||||
}
|
||||
|
||||
/* If no ioctl commands are allowed, ignore auditallow and auditdeny */
|
||||
if (node->key.specified & AVTAB_XPERMS_ALLOWED)
|
||||
xperms->len = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute access vectors and extended permissions based on a context
|
||||
* structure pair for the permissions in a particular class.
|
||||
*/
|
||||
static void context_struct_compute_av(struct context *scontext,
|
||||
struct context *tcontext,
|
||||
u16 tclass,
|
||||
struct av_decision *avd)
|
||||
struct context *tcontext,
|
||||
u16 tclass,
|
||||
struct av_decision *avd,
|
||||
struct extended_perms *xperms)
|
||||
{
|
||||
struct constraint_node *constraint;
|
||||
struct role_allow *ra;
|
||||
@ -633,6 +663,10 @@ static void context_struct_compute_av(struct context *scontext,
|
||||
avd->allowed = 0;
|
||||
avd->auditallow = 0;
|
||||
avd->auditdeny = 0xffffffff;
|
||||
if (xperms) {
|
||||
memset(&xperms->drivers, 0, sizeof(xperms->drivers));
|
||||
xperms->len = 0;
|
||||
}
|
||||
|
||||
if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
|
||||
if (printk_ratelimit())
|
||||
@ -647,7 +681,7 @@ static void context_struct_compute_av(struct context *scontext,
|
||||
* this permission check, then use it.
|
||||
*/
|
||||
avkey.target_class = tclass;
|
||||
avkey.specified = AVTAB_AV;
|
||||
avkey.specified = AVTAB_AV | AVTAB_XPERMS;
|
||||
sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1);
|
||||
BUG_ON(!sattr);
|
||||
tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1);
|
||||
@ -660,15 +694,18 @@ static void context_struct_compute_av(struct context *scontext,
|
||||
node;
|
||||
node = avtab_search_node_next(node, avkey.specified)) {
|
||||
if (node->key.specified == AVTAB_ALLOWED)
|
||||
avd->allowed |= node->datum.data;
|
||||
avd->allowed |= node->datum.u.data;
|
||||
else if (node->key.specified == AVTAB_AUDITALLOW)
|
||||
avd->auditallow |= node->datum.data;
|
||||
avd->auditallow |= node->datum.u.data;
|
||||
else if (node->key.specified == AVTAB_AUDITDENY)
|
||||
avd->auditdeny &= node->datum.data;
|
||||
avd->auditdeny &= node->datum.u.data;
|
||||
else if (xperms && (node->key.specified & AVTAB_XPERMS))
|
||||
services_compute_xperms_drivers(xperms, node);
|
||||
}
|
||||
|
||||
/* Check conditional av table for additional permissions */
|
||||
cond_compute_av(&policydb.te_cond_avtab, &avkey, avd);
|
||||
cond_compute_av(&policydb.te_cond_avtab, &avkey,
|
||||
avd, xperms);
|
||||
|
||||
}
|
||||
}
|
||||
@ -899,6 +936,139 @@ static void avd_init(struct av_decision *avd)
|
||||
avd->flags = 0;
|
||||
}
|
||||
|
||||
void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
|
||||
struct avtab_node *node)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
|
||||
if (xpermd->driver != node->datum.u.xperms->driver)
|
||||
return;
|
||||
} else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
|
||||
if (!security_xperm_test(node->datum.u.xperms->perms.p,
|
||||
xpermd->driver))
|
||||
return;
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (node->key.specified == AVTAB_XPERMS_ALLOWED) {
|
||||
xpermd->used |= XPERMS_ALLOWED;
|
||||
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
|
||||
memset(xpermd->allowed->p, 0xff,
|
||||
sizeof(xpermd->allowed->p));
|
||||
}
|
||||
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
|
||||
for (i = 0; i < ARRAY_SIZE(xpermd->allowed->p); i++)
|
||||
xpermd->allowed->p[i] |=
|
||||
node->datum.u.xperms->perms.p[i];
|
||||
}
|
||||
} else if (node->key.specified == AVTAB_XPERMS_AUDITALLOW) {
|
||||
xpermd->used |= XPERMS_AUDITALLOW;
|
||||
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
|
||||
memset(xpermd->auditallow->p, 0xff,
|
||||
sizeof(xpermd->auditallow->p));
|
||||
}
|
||||
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
|
||||
for (i = 0; i < ARRAY_SIZE(xpermd->auditallow->p); i++)
|
||||
xpermd->auditallow->p[i] |=
|
||||
node->datum.u.xperms->perms.p[i];
|
||||
}
|
||||
} else if (node->key.specified == AVTAB_XPERMS_DONTAUDIT) {
|
||||
xpermd->used |= XPERMS_DONTAUDIT;
|
||||
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
|
||||
memset(xpermd->dontaudit->p, 0xff,
|
||||
sizeof(xpermd->dontaudit->p));
|
||||
}
|
||||
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
|
||||
for (i = 0; i < ARRAY_SIZE(xpermd->dontaudit->p); i++)
|
||||
xpermd->dontaudit->p[i] |=
|
||||
node->datum.u.xperms->perms.p[i];
|
||||
}
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
void security_compute_xperms_decision(u32 ssid,
|
||||
u32 tsid,
|
||||
u16 orig_tclass,
|
||||
u8 driver,
|
||||
struct extended_perms_decision *xpermd)
|
||||
{
|
||||
u16 tclass;
|
||||
struct context *scontext, *tcontext;
|
||||
struct avtab_key avkey;
|
||||
struct avtab_node *node;
|
||||
struct ebitmap *sattr, *tattr;
|
||||
struct ebitmap_node *snode, *tnode;
|
||||
unsigned int i, j;
|
||||
|
||||
xpermd->driver = driver;
|
||||
xpermd->used = 0;
|
||||
memset(xpermd->allowed->p, 0, sizeof(xpermd->allowed->p));
|
||||
memset(xpermd->auditallow->p, 0, sizeof(xpermd->auditallow->p));
|
||||
memset(xpermd->dontaudit->p, 0, sizeof(xpermd->dontaudit->p));
|
||||
|
||||
read_lock(&policy_rwlock);
|
||||
if (!ss_initialized)
|
||||
goto allow;
|
||||
|
||||
scontext = sidtab_search(&sidtab, ssid);
|
||||
if (!scontext) {
|
||||
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
||||
__func__, ssid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tcontext = sidtab_search(&sidtab, tsid);
|
||||
if (!tcontext) {
|
||||
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
||||
__func__, tsid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tclass = unmap_class(orig_tclass);
|
||||
if (unlikely(orig_tclass && !tclass)) {
|
||||
if (policydb.allow_unknown)
|
||||
goto allow;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
|
||||
pr_warn_ratelimited("SELinux: Invalid class %hu\n", tclass);
|
||||
goto out;
|
||||
}
|
||||
|
||||
avkey.target_class = tclass;
|
||||
avkey.specified = AVTAB_XPERMS;
|
||||
sattr = flex_array_get(policydb.type_attr_map_array,
|
||||
scontext->type - 1);
|
||||
BUG_ON(!sattr);
|
||||
tattr = flex_array_get(policydb.type_attr_map_array,
|
||||
tcontext->type - 1);
|
||||
BUG_ON(!tattr);
|
||||
ebitmap_for_each_positive_bit(sattr, snode, i) {
|
||||
ebitmap_for_each_positive_bit(tattr, tnode, j) {
|
||||
avkey.source_type = i + 1;
|
||||
avkey.target_type = j + 1;
|
||||
for (node = avtab_search_node(&policydb.te_avtab, &avkey);
|
||||
node;
|
||||
node = avtab_search_node_next(node, avkey.specified))
|
||||
services_compute_xperms_decision(xpermd, node);
|
||||
|
||||
cond_compute_xperms(&policydb.te_cond_avtab,
|
||||
&avkey, xpermd);
|
||||
}
|
||||
}
|
||||
out:
|
||||
read_unlock(&policy_rwlock);
|
||||
return;
|
||||
allow:
|
||||
memset(xpermd->allowed->p, 0xff, sizeof(xpermd->allowed->p));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_compute_av - Compute access vector decisions.
|
||||
@ -906,6 +1076,7 @@ static void avd_init(struct av_decision *avd)
|
||||
* @tsid: target security identifier
|
||||
* @tclass: target security class
|
||||
* @avd: access vector decisions
|
||||
* @xperms: extended permissions
|
||||
*
|
||||
* Compute a set of access vector decisions based on the
|
||||
* SID pair (@ssid, @tsid) for the permissions in @tclass.
|
||||
@ -913,13 +1084,15 @@ static void avd_init(struct av_decision *avd)
|
||||
void security_compute_av(u32 ssid,
|
||||
u32 tsid,
|
||||
u16 orig_tclass,
|
||||
struct av_decision *avd)
|
||||
struct av_decision *avd,
|
||||
struct extended_perms *xperms)
|
||||
{
|
||||
u16 tclass;
|
||||
struct context *scontext = NULL, *tcontext = NULL;
|
||||
|
||||
read_lock(&policy_rwlock);
|
||||
avd_init(avd);
|
||||
xperms->len = 0;
|
||||
if (!ss_initialized)
|
||||
goto allow;
|
||||
|
||||
@ -947,7 +1120,7 @@ void security_compute_av(u32 ssid,
|
||||
goto allow;
|
||||
goto out;
|
||||
}
|
||||
context_struct_compute_av(scontext, tcontext, tclass, avd);
|
||||
context_struct_compute_av(scontext, tcontext, tclass, avd, xperms);
|
||||
map_decision(orig_tclass, avd, policydb.allow_unknown);
|
||||
out:
|
||||
read_unlock(&policy_rwlock);
|
||||
@ -993,7 +1166,7 @@ void security_compute_av_user(u32 ssid,
|
||||
goto out;
|
||||
}
|
||||
|
||||
context_struct_compute_av(scontext, tcontext, tclass, avd);
|
||||
context_struct_compute_av(scontext, tcontext, tclass, avd, NULL);
|
||||
out:
|
||||
read_unlock(&policy_rwlock);
|
||||
return;
|
||||
@ -1515,7 +1688,7 @@ static int security_compute_sid(u32 ssid,
|
||||
|
||||
if (avdatum) {
|
||||
/* Use the type from the type transition/member/change rule. */
|
||||
newcontext.type = avdatum->data;
|
||||
newcontext.type = avdatum->u.data;
|
||||
}
|
||||
|
||||
/* if we have a objname this is a file trans check so check those rules */
|
||||
|
@ -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_ */
|
||||
|
||||
|
@ -17,11 +17,26 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/lsm_hooks.h>
|
||||
#include <linux/in.h>
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
#include <linux/in6.h>
|
||||
#endif /* CONFIG_IPV6 */
|
||||
#include <net/netlabel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/lsm_audit.h>
|
||||
|
||||
/*
|
||||
* Use IPv6 port labeling if IPv6 is enabled and secmarks
|
||||
* are not being used.
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
|
||||
#define SMACK_IPV6_PORT_LABELING 1
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6) && defined(CONFIG_SECURITY_SMACK_NETFILTER)
|
||||
#define SMACK_IPV6_SECMARK_LABELING 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Smack labels were limited to 23 characters for a long time.
|
||||
*/
|
||||
@ -118,15 +133,30 @@ struct smack_rule {
|
||||
};
|
||||
|
||||
/*
|
||||
* An entry in the table identifying hosts.
|
||||
* An entry in the table identifying IPv4 hosts.
|
||||
*/
|
||||
struct smk_netlbladdr {
|
||||
struct smk_net4addr {
|
||||
struct list_head list;
|
||||
struct sockaddr_in smk_host; /* network address */
|
||||
struct in_addr smk_host; /* network address */
|
||||
struct in_addr smk_mask; /* network mask */
|
||||
int smk_masks; /* mask size */
|
||||
struct smack_known *smk_label; /* label */
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
/*
|
||||
* An entry in the table identifying IPv6 hosts.
|
||||
*/
|
||||
struct smk_net6addr {
|
||||
struct list_head list;
|
||||
struct in6_addr smk_host; /* network address */
|
||||
struct in6_addr smk_mask; /* network mask */
|
||||
int smk_masks; /* mask size */
|
||||
struct smack_known *smk_label; /* label */
|
||||
};
|
||||
#endif /* CONFIG_IPV6 */
|
||||
|
||||
#ifdef SMACK_IPV6_PORT_LABELING
|
||||
/*
|
||||
* An entry in the table identifying ports.
|
||||
*/
|
||||
@ -137,12 +167,31 @@ struct smk_port_label {
|
||||
struct smack_known *smk_in; /* inbound label */
|
||||
struct smack_known *smk_out; /* outgoing label */
|
||||
};
|
||||
#endif /* SMACK_IPV6_PORT_LABELING */
|
||||
|
||||
struct smack_onlycap {
|
||||
struct list_head list;
|
||||
struct smack_known *smk_label;
|
||||
};
|
||||
|
||||
/* Super block security struct flags for mount options */
|
||||
#define FSDEFAULT_MNT 0x01
|
||||
#define FSFLOOR_MNT 0x02
|
||||
#define FSHAT_MNT 0x04
|
||||
#define FSROOT_MNT 0x08
|
||||
#define FSTRANS_MNT 0x10
|
||||
|
||||
#define NUM_SMK_MNT_OPTS 5
|
||||
|
||||
enum {
|
||||
Opt_error = -1,
|
||||
Opt_fsdefault = 1,
|
||||
Opt_fsfloor = 2,
|
||||
Opt_fshat = 3,
|
||||
Opt_fsroot = 4,
|
||||
Opt_fstransmute = 5,
|
||||
};
|
||||
|
||||
/*
|
||||
* Mount options
|
||||
*/
|
||||
@ -152,6 +201,7 @@ struct smack_onlycap {
|
||||
#define SMK_FSROOT "smackfsroot="
|
||||
#define SMK_FSTRANS "smackfstransmute="
|
||||
|
||||
#define SMACK_DELETE_OPTION "-DELETE"
|
||||
#define SMACK_CIPSO_OPTION "-CIPSO"
|
||||
|
||||
/*
|
||||
@ -234,10 +284,6 @@ struct smk_audit_info {
|
||||
struct smack_audit_data sad;
|
||||
#endif
|
||||
};
|
||||
/*
|
||||
* These functions are in smack_lsm.c
|
||||
*/
|
||||
struct inode_smack *new_inode_smack(struct smack_known *);
|
||||
|
||||
/*
|
||||
* These functions are in smack_access.c
|
||||
@ -267,7 +313,6 @@ extern struct smack_known *smack_syslog_label;
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
extern struct smack_known *smack_unconfined;
|
||||
#endif
|
||||
extern struct smack_known smack_cipso_option;
|
||||
extern int smack_ptrace_rule;
|
||||
|
||||
extern struct smack_known smack_known_floor;
|
||||
@ -279,7 +324,10 @@ extern struct smack_known smack_known_web;
|
||||
|
||||
extern struct mutex smack_known_lock;
|
||||
extern struct list_head smack_known_list;
|
||||
extern struct list_head smk_netlbladdr_list;
|
||||
extern struct list_head smk_net4addr_list;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
extern struct list_head smk_net6addr_list;
|
||||
#endif /* CONFIG_IPV6 */
|
||||
|
||||
extern struct mutex smack_onlycap_lock;
|
||||
extern struct list_head smack_onlycap_list;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include <linux/msg.h>
|
||||
#include <linux/shm.h>
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/parser.h>
|
||||
#include "smack.h"
|
||||
|
||||
#define TRANS_TRUE "TRUE"
|
||||
@ -50,12 +51,21 @@
|
||||
#define SMK_RECEIVING 1
|
||||
#define SMK_SENDING 2
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
|
||||
#ifdef SMACK_IPV6_PORT_LABELING
|
||||
LIST_HEAD(smk_ipv6_port_list);
|
||||
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
|
||||
#endif
|
||||
static struct kmem_cache *smack_inode_cache;
|
||||
int smack_enabled;
|
||||
|
||||
static const match_table_t smk_mount_tokens = {
|
||||
{Opt_fsdefault, SMK_FSDEFAULT "%s"},
|
||||
{Opt_fsfloor, SMK_FSFLOOR "%s"},
|
||||
{Opt_fshat, SMK_FSHAT "%s"},
|
||||
{Opt_fsroot, SMK_FSROOT "%s"},
|
||||
{Opt_fstransmute, SMK_FSTRANS "%s"},
|
||||
{Opt_error, NULL},
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
static char *smk_bu_mess[] = {
|
||||
"Bringup Error", /* Unused */
|
||||
@ -281,7 +291,7 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip,
|
||||
*
|
||||
* Returns the new blob or NULL if there's no memory available
|
||||
*/
|
||||
struct inode_smack *new_inode_smack(struct smack_known *skp)
|
||||
static struct inode_smack *new_inode_smack(struct smack_known *skp)
|
||||
{
|
||||
struct inode_smack *isp;
|
||||
|
||||
@ -577,76 +587,197 @@ static int smack_sb_copy_data(char *orig, char *smackopts)
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_sb_kern_mount - Smack specific mount processing
|
||||
* smack_parse_opts_str - parse Smack specific mount options
|
||||
* @options: mount options string
|
||||
* @opts: where to store converted mount opts
|
||||
*
|
||||
* Returns 0 on success or -ENOMEM on error.
|
||||
*
|
||||
* converts Smack specific mount options to generic security option format
|
||||
*/
|
||||
static int smack_parse_opts_str(char *options,
|
||||
struct security_mnt_opts *opts)
|
||||
{
|
||||
char *p;
|
||||
char *fsdefault = NULL;
|
||||
char *fsfloor = NULL;
|
||||
char *fshat = NULL;
|
||||
char *fsroot = NULL;
|
||||
char *fstransmute = NULL;
|
||||
int rc = -ENOMEM;
|
||||
int num_mnt_opts = 0;
|
||||
int token;
|
||||
|
||||
opts->num_mnt_opts = 0;
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, smk_mount_tokens, args);
|
||||
|
||||
switch (token) {
|
||||
case Opt_fsdefault:
|
||||
if (fsdefault)
|
||||
goto out_opt_err;
|
||||
fsdefault = match_strdup(&args[0]);
|
||||
if (!fsdefault)
|
||||
goto out_err;
|
||||
break;
|
||||
case Opt_fsfloor:
|
||||
if (fsfloor)
|
||||
goto out_opt_err;
|
||||
fsfloor = match_strdup(&args[0]);
|
||||
if (!fsfloor)
|
||||
goto out_err;
|
||||
break;
|
||||
case Opt_fshat:
|
||||
if (fshat)
|
||||
goto out_opt_err;
|
||||
fshat = match_strdup(&args[0]);
|
||||
if (!fshat)
|
||||
goto out_err;
|
||||
break;
|
||||
case Opt_fsroot:
|
||||
if (fsroot)
|
||||
goto out_opt_err;
|
||||
fsroot = match_strdup(&args[0]);
|
||||
if (!fsroot)
|
||||
goto out_err;
|
||||
break;
|
||||
case Opt_fstransmute:
|
||||
if (fstransmute)
|
||||
goto out_opt_err;
|
||||
fstransmute = match_strdup(&args[0]);
|
||||
if (!fstransmute)
|
||||
goto out_err;
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
pr_warn("Smack: unknown mount option\n");
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_ATOMIC);
|
||||
if (!opts->mnt_opts)
|
||||
goto out_err;
|
||||
|
||||
opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
|
||||
GFP_ATOMIC);
|
||||
if (!opts->mnt_opts_flags) {
|
||||
kfree(opts->mnt_opts);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (fsdefault) {
|
||||
opts->mnt_opts[num_mnt_opts] = fsdefault;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSDEFAULT_MNT;
|
||||
}
|
||||
if (fsfloor) {
|
||||
opts->mnt_opts[num_mnt_opts] = fsfloor;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSFLOOR_MNT;
|
||||
}
|
||||
if (fshat) {
|
||||
opts->mnt_opts[num_mnt_opts] = fshat;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSHAT_MNT;
|
||||
}
|
||||
if (fsroot) {
|
||||
opts->mnt_opts[num_mnt_opts] = fsroot;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSROOT_MNT;
|
||||
}
|
||||
if (fstransmute) {
|
||||
opts->mnt_opts[num_mnt_opts] = fstransmute;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSTRANS_MNT;
|
||||
}
|
||||
|
||||
opts->num_mnt_opts = num_mnt_opts;
|
||||
return 0;
|
||||
|
||||
out_opt_err:
|
||||
rc = -EINVAL;
|
||||
pr_warn("Smack: duplicate mount options\n");
|
||||
|
||||
out_err:
|
||||
kfree(fsdefault);
|
||||
kfree(fsfloor);
|
||||
kfree(fshat);
|
||||
kfree(fsroot);
|
||||
kfree(fstransmute);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_set_mnt_opts - set Smack specific mount options
|
||||
* @sb: the file system superblock
|
||||
* @flags: the mount flags
|
||||
* @data: the smack mount options
|
||||
* @opts: Smack mount options
|
||||
* @kern_flags: mount option from kernel space or user space
|
||||
* @set_kern_flags: where to store converted mount opts
|
||||
*
|
||||
* Returns 0 on success, an error code on failure
|
||||
*
|
||||
* Allow filesystems with binary mount data to explicitly set Smack mount
|
||||
* labels.
|
||||
*/
|
||||
static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
static int smack_set_mnt_opts(struct super_block *sb,
|
||||
struct security_mnt_opts *opts,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags)
|
||||
{
|
||||
struct dentry *root = sb->s_root;
|
||||
struct inode *inode = d_backing_inode(root);
|
||||
struct superblock_smack *sp = sb->s_security;
|
||||
struct inode_smack *isp;
|
||||
struct smack_known *skp;
|
||||
char *op;
|
||||
char *commap;
|
||||
int i;
|
||||
int num_opts = opts->num_mnt_opts;
|
||||
int transmute = 0;
|
||||
int specified = 0;
|
||||
|
||||
if (sp->smk_initialized)
|
||||
return 0;
|
||||
|
||||
sp->smk_initialized = 1;
|
||||
|
||||
for (op = data; op != NULL; op = commap) {
|
||||
commap = strchr(op, ',');
|
||||
if (commap != NULL)
|
||||
*commap++ = '\0';
|
||||
|
||||
if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) {
|
||||
op += strlen(SMK_FSHAT);
|
||||
skp = smk_import_entry(op, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_hat = skp;
|
||||
specified = 1;
|
||||
|
||||
} else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) {
|
||||
op += strlen(SMK_FSFLOOR);
|
||||
skp = smk_import_entry(op, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_floor = skp;
|
||||
specified = 1;
|
||||
|
||||
} else if (strncmp(op, SMK_FSDEFAULT,
|
||||
strlen(SMK_FSDEFAULT)) == 0) {
|
||||
op += strlen(SMK_FSDEFAULT);
|
||||
skp = smk_import_entry(op, 0);
|
||||
for (i = 0; i < num_opts; i++) {
|
||||
switch (opts->mnt_opts_flags[i]) {
|
||||
case FSDEFAULT_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_default = skp;
|
||||
specified = 1;
|
||||
|
||||
} else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) {
|
||||
op += strlen(SMK_FSROOT);
|
||||
skp = smk_import_entry(op, 0);
|
||||
break;
|
||||
case FSFLOOR_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_floor = skp;
|
||||
break;
|
||||
case FSHAT_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_hat = skp;
|
||||
break;
|
||||
case FSROOT_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_root = skp;
|
||||
specified = 1;
|
||||
|
||||
} else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) {
|
||||
op += strlen(SMK_FSTRANS);
|
||||
skp = smk_import_entry(op, 0);
|
||||
break;
|
||||
case FSTRANS_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_root = skp;
|
||||
transmute = 1;
|
||||
specified = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -654,7 +785,7 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
/*
|
||||
* Unprivileged mounts don't get to specify Smack values.
|
||||
*/
|
||||
if (specified)
|
||||
if (num_opts)
|
||||
return -EPERM;
|
||||
/*
|
||||
* Unprivileged mounts get root and default from the caller.
|
||||
@ -663,6 +794,7 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
sp->smk_root = skp;
|
||||
sp->smk_default = skp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the root inode.
|
||||
*/
|
||||
@ -681,6 +813,37 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_sb_kern_mount - Smack specific mount processing
|
||||
* @sb: the file system superblock
|
||||
* @flags: the mount flags
|
||||
* @data: the smack mount options
|
||||
*
|
||||
* Returns 0 on success, an error code on failure
|
||||
*/
|
||||
static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
{
|
||||
int rc = 0;
|
||||
char *options = data;
|
||||
struct security_mnt_opts opts;
|
||||
|
||||
security_init_mnt_opts(&opts);
|
||||
|
||||
if (!options)
|
||||
goto out;
|
||||
|
||||
rc = smack_parse_opts_str(options, &opts);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
|
||||
out:
|
||||
rc = smack_set_mnt_opts(sb, &opts, 0, NULL);
|
||||
|
||||
out_err:
|
||||
security_free_mnt_opts(&opts);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_sb_statfs - Smack check on statfs
|
||||
* @dentry: identifies the file system in question
|
||||
@ -2113,7 +2276,7 @@ static void smack_sk_free_security(struct sock *sk)
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_host_label - check host based restrictions
|
||||
* smack_ipv4host_label - check host based restrictions
|
||||
* @sip: the object end
|
||||
*
|
||||
* looks for host based access restrictions
|
||||
@ -2124,30 +2287,96 @@ static void smack_sk_free_security(struct sock *sk)
|
||||
*
|
||||
* Returns the label of the far end or NULL if it's not special.
|
||||
*/
|
||||
static struct smack_known *smack_host_label(struct sockaddr_in *sip)
|
||||
static struct smack_known *smack_ipv4host_label(struct sockaddr_in *sip)
|
||||
{
|
||||
struct smk_netlbladdr *snp;
|
||||
struct smk_net4addr *snp;
|
||||
struct in_addr *siap = &sip->sin_addr;
|
||||
|
||||
if (siap->s_addr == 0)
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list)
|
||||
list_for_each_entry_rcu(snp, &smk_net4addr_list, list)
|
||||
/*
|
||||
* we break after finding the first match because
|
||||
* the list is sorted from longest to shortest mask
|
||||
* so we have found the most specific match
|
||||
*/
|
||||
if (snp->smk_host.s_addr ==
|
||||
(siap->s_addr & snp->smk_mask.s_addr))
|
||||
return snp->smk_label;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
/*
|
||||
* smk_ipv6_localhost - Check for local ipv6 host address
|
||||
* @sip: the address
|
||||
*
|
||||
* Returns boolean true if this is the localhost address
|
||||
*/
|
||||
static bool smk_ipv6_localhost(struct sockaddr_in6 *sip)
|
||||
{
|
||||
__be16 *be16p = (__be16 *)&sip->sin6_addr;
|
||||
__be32 *be32p = (__be32 *)&sip->sin6_addr;
|
||||
|
||||
if (be32p[0] == 0 && be32p[1] == 0 && be32p[2] == 0 && be16p[6] == 0 &&
|
||||
ntohs(be16p[7]) == 1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_ipv6host_label - check host based restrictions
|
||||
* @sip: the object end
|
||||
*
|
||||
* looks for host based access restrictions
|
||||
*
|
||||
* This version will only be appropriate for really small sets of single label
|
||||
* hosts. The caller is responsible for ensuring that the RCU read lock is
|
||||
* taken before calling this function.
|
||||
*
|
||||
* Returns the label of the far end or NULL if it's not special.
|
||||
*/
|
||||
static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
|
||||
{
|
||||
struct smk_net6addr *snp;
|
||||
struct in6_addr *sap = &sip->sin6_addr;
|
||||
int i;
|
||||
int found = 0;
|
||||
|
||||
/*
|
||||
* It's local. Don't look for a host label.
|
||||
*/
|
||||
if (smk_ipv6_localhost(sip))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
|
||||
/*
|
||||
* we break after finding the first match because
|
||||
* the list is sorted from longest to shortest mask
|
||||
* so we have found the most specific match
|
||||
*/
|
||||
if ((&snp->smk_host.sin_addr)->s_addr ==
|
||||
(siap->s_addr & (&snp->smk_mask)->s_addr)) {
|
||||
/* we have found the special CIPSO option */
|
||||
if (snp->smk_label == &smack_cipso_option)
|
||||
return NULL;
|
||||
return snp->smk_label;
|
||||
for (found = 1, i = 0; i < 8; i++) {
|
||||
/*
|
||||
* If the label is NULL the entry has
|
||||
* been renounced. Ignore it.
|
||||
*/
|
||||
if (snp->smk_label == NULL)
|
||||
continue;
|
||||
if ((sap->s6_addr16[i] & snp->smk_mask.s6_addr16[i]) !=
|
||||
snp->smk_host.s6_addr16[i]) {
|
||||
found = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
return snp->smk_label;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_IPV6 */
|
||||
|
||||
/**
|
||||
* smack_netlabel - Set the secattr on a socket
|
||||
@ -2211,7 +2440,7 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
|
||||
struct smk_audit_info ad;
|
||||
|
||||
rcu_read_lock();
|
||||
hkp = smack_host_label(sap);
|
||||
hkp = smack_ipv4host_label(sap);
|
||||
if (hkp != NULL) {
|
||||
#ifdef CONFIG_AUDIT
|
||||
struct lsm_network_audit net;
|
||||
@ -2236,7 +2465,42 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
|
||||
return smack_netlabel(sk, sk_lbl);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
/**
|
||||
* smk_ipv6_check - check Smack access
|
||||
* @subject: subject Smack label
|
||||
* @object: object Smack label
|
||||
* @address: address
|
||||
* @act: the action being taken
|
||||
*
|
||||
* Check an IPv6 access
|
||||
*/
|
||||
static int smk_ipv6_check(struct smack_known *subject,
|
||||
struct smack_known *object,
|
||||
struct sockaddr_in6 *address, int act)
|
||||
{
|
||||
#ifdef CONFIG_AUDIT
|
||||
struct lsm_network_audit net;
|
||||
#endif
|
||||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
|
||||
ad.a.u.net->family = PF_INET6;
|
||||
ad.a.u.net->dport = ntohs(address->sin6_port);
|
||||
if (act == SMK_RECEIVING)
|
||||
ad.a.u.net->v6info.saddr = address->sin6_addr;
|
||||
else
|
||||
ad.a.u.net->v6info.daddr = address->sin6_addr;
|
||||
#endif
|
||||
rc = smk_access(subject, object, MAY_WRITE, &ad);
|
||||
rc = smk_bu_note("IPv6 check", subject, object, MAY_WRITE, rc);
|
||||
return rc;
|
||||
}
|
||||
#endif /* CONFIG_IPV6 */
|
||||
|
||||
#ifdef SMACK_IPV6_PORT_LABELING
|
||||
/**
|
||||
* smk_ipv6_port_label - Smack port access table management
|
||||
* @sock: socket
|
||||
@ -2320,48 +2584,43 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
|
||||
static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
|
||||
int act)
|
||||
{
|
||||
__be16 *bep;
|
||||
__be32 *be32p;
|
||||
struct smk_port_label *spp;
|
||||
struct socket_smack *ssp = sk->sk_security;
|
||||
struct smack_known *skp;
|
||||
unsigned short port = 0;
|
||||
struct smack_known *skp = NULL;
|
||||
unsigned short port;
|
||||
struct smack_known *object;
|
||||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
#ifdef CONFIG_AUDIT
|
||||
struct lsm_network_audit net;
|
||||
#endif
|
||||
|
||||
if (act == SMK_RECEIVING) {
|
||||
skp = smack_net_ambient;
|
||||
skp = smack_ipv6host_label(address);
|
||||
object = ssp->smk_in;
|
||||
} else {
|
||||
skp = ssp->smk_out;
|
||||
object = smack_net_ambient;
|
||||
object = smack_ipv6host_label(address);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the IP address and port from the address.
|
||||
* The other end is a single label host.
|
||||
*/
|
||||
port = ntohs(address->sin6_port);
|
||||
bep = (__be16 *)(&address->sin6_addr);
|
||||
be32p = (__be32 *)(&address->sin6_addr);
|
||||
if (skp != NULL && object != NULL)
|
||||
return smk_ipv6_check(skp, object, address, act);
|
||||
if (skp == NULL)
|
||||
skp = smack_net_ambient;
|
||||
if (object == NULL)
|
||||
object = smack_net_ambient;
|
||||
|
||||
/*
|
||||
* It's remote, so port lookup does no good.
|
||||
*/
|
||||
if (be32p[0] || be32p[1] || be32p[2] || bep[6] || ntohs(bep[7]) != 1)
|
||||
goto auditout;
|
||||
if (!smk_ipv6_localhost(address))
|
||||
return smk_ipv6_check(skp, object, address, act);
|
||||
|
||||
/*
|
||||
* It's local so the send check has to have passed.
|
||||
*/
|
||||
if (act == SMK_RECEIVING) {
|
||||
skp = &smack_known_web;
|
||||
goto auditout;
|
||||
}
|
||||
if (act == SMK_RECEIVING)
|
||||
return 0;
|
||||
|
||||
port = ntohs(address->sin6_port);
|
||||
list_for_each_entry(spp, &smk_ipv6_port_list, list) {
|
||||
if (spp->smk_port != port)
|
||||
continue;
|
||||
@ -2371,22 +2630,9 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
|
||||
break;
|
||||
}
|
||||
|
||||
auditout:
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
|
||||
ad.a.u.net->family = sk->sk_family;
|
||||
ad.a.u.net->dport = port;
|
||||
if (act == SMK_RECEIVING)
|
||||
ad.a.u.net->v6info.saddr = address->sin6_addr;
|
||||
else
|
||||
ad.a.u.net->v6info.daddr = address->sin6_addr;
|
||||
#endif
|
||||
rc = smk_access(skp, object, MAY_WRITE, &ad);
|
||||
rc = smk_bu_note("IPv6 port check", skp, object, MAY_WRITE, rc);
|
||||
return rc;
|
||||
return smk_ipv6_check(skp, object, address, act);
|
||||
}
|
||||
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
|
||||
#endif /* SMACK_IPV6_PORT_LABELING */
|
||||
|
||||
/**
|
||||
* smack_inode_setsecurity - set smack xattrs
|
||||
@ -2447,10 +2693,10 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
|
||||
} else
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
|
||||
#ifdef SMACK_IPV6_PORT_LABELING
|
||||
if (sock->sk->sk_family == PF_INET6)
|
||||
smk_ipv6_port_label(sock, NULL);
|
||||
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2492,7 +2738,7 @@ static int smack_socket_post_create(struct socket *sock, int family,
|
||||
return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SECURITY_SMACK_NETFILTER
|
||||
#ifdef SMACK_IPV6_PORT_LABELING
|
||||
/**
|
||||
* smack_socket_bind - record port binding information.
|
||||
* @sock: the socket
|
||||
@ -2506,14 +2752,11 @@ static int smack_socket_post_create(struct socket *sock, int family,
|
||||
static int smack_socket_bind(struct socket *sock, struct sockaddr *address,
|
||||
int addrlen)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
if (sock->sk != NULL && sock->sk->sk_family == PF_INET6)
|
||||
smk_ipv6_port_label(sock, address);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* !CONFIG_SECURITY_SMACK_NETFILTER */
|
||||
#endif /* SMACK_IPV6_PORT_LABELING */
|
||||
|
||||
/**
|
||||
* smack_socket_connect - connect access check
|
||||
@ -2529,6 +2772,13 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
|
||||
int addrlen)
|
||||
{
|
||||
int rc = 0;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
struct sockaddr_in6 *sip = (struct sockaddr_in6 *)sap;
|
||||
#endif
|
||||
#ifdef SMACK_IPV6_SECMARK_LABELING
|
||||
struct smack_known *rsp;
|
||||
struct socket_smack *ssp = sock->sk->sk_security;
|
||||
#endif
|
||||
|
||||
if (sock->sk == NULL)
|
||||
return 0;
|
||||
@ -2542,10 +2792,15 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
|
||||
case PF_INET6:
|
||||
if (addrlen < sizeof(struct sockaddr_in6))
|
||||
return -EINVAL;
|
||||
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
|
||||
rc = smk_ipv6_port_check(sock->sk, (struct sockaddr_in6 *)sap,
|
||||
#ifdef SMACK_IPV6_SECMARK_LABELING
|
||||
rsp = smack_ipv6host_label(sip);
|
||||
if (rsp != NULL)
|
||||
rc = smk_ipv6_check(ssp->smk_out, rsp, sip,
|
||||
SMK_CONNECTING);
|
||||
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
|
||||
#endif
|
||||
#ifdef SMACK_IPV6_PORT_LABELING
|
||||
rc = smk_ipv6_port_check(sock->sk, sip, SMK_CONNECTING);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
@ -3431,9 +3686,13 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
int size)
|
||||
{
|
||||
struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name;
|
||||
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name;
|
||||
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
|
||||
#endif
|
||||
#ifdef SMACK_IPV6_SECMARK_LABELING
|
||||
struct socket_smack *ssp = sock->sk->sk_security;
|
||||
struct smack_known *rsp;
|
||||
#endif
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
@ -3447,9 +3706,15 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
rc = smack_netlabel_send(sock->sk, sip);
|
||||
break;
|
||||
case AF_INET6:
|
||||
#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
|
||||
#ifdef SMACK_IPV6_SECMARK_LABELING
|
||||
rsp = smack_ipv6host_label(sap);
|
||||
if (rsp != NULL)
|
||||
rc = smk_ipv6_check(ssp->smk_out, rsp, sap,
|
||||
SMK_CONNECTING);
|
||||
#endif
|
||||
#ifdef SMACK_IPV6_PORT_LABELING
|
||||
rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING);
|
||||
#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
@ -3663,10 +3928,12 @@ access_check:
|
||||
proto = smk_skb_to_addr_ipv6(skb, &sadd);
|
||||
if (proto != IPPROTO_UDP && proto != IPPROTO_TCP)
|
||||
break;
|
||||
#ifdef CONFIG_SECURITY_SMACK_NETFILTER
|
||||
#ifdef SMACK_IPV6_SECMARK_LABELING
|
||||
if (skb && skb->secmark != 0)
|
||||
skp = smack_from_secid(skb->secmark);
|
||||
else
|
||||
skp = smack_ipv6host_label(&sadd);
|
||||
if (skp == NULL)
|
||||
skp = smack_net_ambient;
|
||||
#ifdef CONFIG_AUDIT
|
||||
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
|
||||
@ -3677,9 +3944,10 @@ access_check:
|
||||
rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
|
||||
rc = smk_bu_note("IPv6 delivery", skp, ssp->smk_in,
|
||||
MAY_WRITE, rc);
|
||||
#else /* CONFIG_SECURITY_SMACK_NETFILTER */
|
||||
#endif /* SMACK_IPV6_SECMARK_LABELING */
|
||||
#ifdef SMACK_IPV6_PORT_LABELING
|
||||
rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING);
|
||||
#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
|
||||
#endif /* SMACK_IPV6_PORT_LABELING */
|
||||
break;
|
||||
#endif /* CONFIG_IPV6 */
|
||||
}
|
||||
@ -3777,13 +4045,11 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
|
||||
}
|
||||
netlbl_secattr_destroy(&secattr);
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
case PF_INET6:
|
||||
#ifdef CONFIG_SECURITY_SMACK_NETFILTER
|
||||
#ifdef SMACK_IPV6_SECMARK_LABELING
|
||||
s = skb->secmark;
|
||||
#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
|
||||
#endif
|
||||
break;
|
||||
#endif /* CONFIG_IPV6 */
|
||||
}
|
||||
*secid = s;
|
||||
if (s == 0)
|
||||
@ -3906,7 +4172,7 @@ access_check:
|
||||
hdr = ip_hdr(skb);
|
||||
addr.sin_addr.s_addr = hdr->saddr;
|
||||
rcu_read_lock();
|
||||
hskp = smack_host_label(&addr);
|
||||
hskp = smack_ipv4host_label(&addr);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (hskp == NULL)
|
||||
@ -4254,7 +4520,7 @@ static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct security_hook_list smack_hooks[] = {
|
||||
static struct security_hook_list smack_hooks[] = {
|
||||
LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
|
||||
LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
|
||||
LSM_HOOK_INIT(syslog, smack_syslog),
|
||||
@ -4264,6 +4530,8 @@ struct security_hook_list smack_hooks[] = {
|
||||
LSM_HOOK_INIT(sb_copy_data, smack_sb_copy_data),
|
||||
LSM_HOOK_INIT(sb_kern_mount, smack_sb_kern_mount),
|
||||
LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
|
||||
LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),
|
||||
LSM_HOOK_INIT(sb_parse_opts_str, smack_parse_opts_str),
|
||||
|
||||
LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
|
||||
LSM_HOOK_INIT(bprm_committing_creds, smack_bprm_committing_creds),
|
||||
@ -4356,9 +4624,9 @@ struct security_hook_list smack_hooks[] = {
|
||||
LSM_HOOK_INIT(unix_may_send, smack_unix_may_send),
|
||||
|
||||
LSM_HOOK_INIT(socket_post_create, smack_socket_post_create),
|
||||
#ifndef CONFIG_SECURITY_SMACK_NETFILTER
|
||||
#ifdef SMACK_IPV6_PORT_LABELING
|
||||
LSM_HOOK_INIT(socket_bind, smack_socket_bind),
|
||||
#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
|
||||
#endif
|
||||
LSM_HOOK_INIT(socket_connect, smack_socket_connect),
|
||||
LSM_HOOK_INIT(socket_sendmsg, smack_socket_sendmsg),
|
||||
LSM_HOOK_INIT(socket_sock_rcv_skb, smack_socket_sock_rcv_skb),
|
||||
@ -4453,7 +4721,16 @@ static __init int smack_init(void)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "Smack: Initializing.\n");
|
||||
pr_info("Smack: Initializing.\n");
|
||||
#ifdef CONFIG_SECURITY_SMACK_NETFILTER
|
||||
pr_info("Smack: Netfilter enabled.\n");
|
||||
#endif
|
||||
#ifdef SMACK_IPV6_PORT_LABELING
|
||||
pr_info("Smack: IPv6 port labeling enabled.\n");
|
||||
#endif
|
||||
#ifdef SMACK_IPV6_SECMARK_LABELING
|
||||
pr_info("Smack: IPv6 Netfilter enabled.\n");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set the security state for the initial task.
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <linux/magic.h>
|
||||
#include "smack.h"
|
||||
|
||||
#define BEBITS (sizeof(__be32) * 8)
|
||||
/*
|
||||
* smackfs pseudo filesystem.
|
||||
*/
|
||||
@ -40,7 +41,7 @@ enum smk_inos {
|
||||
SMK_DOI = 5, /* CIPSO DOI */
|
||||
SMK_DIRECT = 6, /* CIPSO level indicating direct label */
|
||||
SMK_AMBIENT = 7, /* internet ambient label */
|
||||
SMK_NETLBLADDR = 8, /* single label hosts */
|
||||
SMK_NET4ADDR = 8, /* single label hosts */
|
||||
SMK_ONLYCAP = 9, /* the only "capable" label */
|
||||
SMK_LOGGING = 10, /* logging */
|
||||
SMK_LOAD_SELF = 11, /* task specific rules */
|
||||
@ -57,6 +58,9 @@ enum smk_inos {
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
SMK_UNCONFINED = 22, /* define an unconfined label */
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
SMK_NET6ADDR = 23, /* single label IPv6 hosts */
|
||||
#endif /* CONFIG_IPV6 */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -64,7 +68,10 @@ enum smk_inos {
|
||||
*/
|
||||
static DEFINE_MUTEX(smack_cipso_lock);
|
||||
static DEFINE_MUTEX(smack_ambient_lock);
|
||||
static DEFINE_MUTEX(smk_netlbladdr_lock);
|
||||
static DEFINE_MUTEX(smk_net4addr_lock);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
static DEFINE_MUTEX(smk_net6addr_lock);
|
||||
#endif /* CONFIG_IPV6 */
|
||||
|
||||
/*
|
||||
* This is the "ambient" label for network traffic.
|
||||
@ -118,7 +125,10 @@ int smack_ptrace_rule = SMACK_PTRACE_DEFAULT;
|
||||
* can write to the specified label.
|
||||
*/
|
||||
|
||||
LIST_HEAD(smk_netlbladdr_list);
|
||||
LIST_HEAD(smk_net4addr_list);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
LIST_HEAD(smk_net6addr_list);
|
||||
#endif /* CONFIG_IPV6 */
|
||||
|
||||
/*
|
||||
* Rule lists are maintained for each label.
|
||||
@ -129,7 +139,7 @@ struct smack_master_list {
|
||||
struct smack_rule *smk_rule;
|
||||
};
|
||||
|
||||
LIST_HEAD(smack_rule_list);
|
||||
static LIST_HEAD(smack_rule_list);
|
||||
|
||||
struct smack_parsed_rule {
|
||||
struct smack_known *smk_subject;
|
||||
@ -140,11 +150,6 @@ struct smack_parsed_rule {
|
||||
|
||||
static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
|
||||
|
||||
struct smack_known smack_cipso_option = {
|
||||
.smk_known = SMACK_CIPSO_OPTION,
|
||||
.smk_secid = 0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Values for parsing cipso rules
|
||||
* SMK_DIGITLEN: Length of a digit field in a rule.
|
||||
@ -1047,92 +1052,90 @@ static const struct file_operations smk_cipso2_ops = {
|
||||
* Seq_file read operations for /smack/netlabel
|
||||
*/
|
||||
|
||||
static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos)
|
||||
static void *net4addr_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
return smk_seq_start(s, pos, &smk_netlbladdr_list);
|
||||
return smk_seq_start(s, pos, &smk_net4addr_list);
|
||||
}
|
||||
|
||||
static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
static void *net4addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
return smk_seq_next(s, v, pos, &smk_netlbladdr_list);
|
||||
return smk_seq_next(s, v, pos, &smk_net4addr_list);
|
||||
}
|
||||
#define BEBITS (sizeof(__be32) * 8)
|
||||
|
||||
/*
|
||||
* Print host/label pairs
|
||||
*/
|
||||
static int netlbladdr_seq_show(struct seq_file *s, void *v)
|
||||
static int net4addr_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct list_head *list = v;
|
||||
struct smk_netlbladdr *skp =
|
||||
list_entry_rcu(list, struct smk_netlbladdr, list);
|
||||
unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr;
|
||||
int maskn;
|
||||
u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr);
|
||||
struct smk_net4addr *skp =
|
||||
list_entry_rcu(list, struct smk_net4addr, list);
|
||||
char *kp = SMACK_CIPSO_OPTION;
|
||||
|
||||
for (maskn = 0; temp_mask; temp_mask <<= 1, maskn++);
|
||||
|
||||
seq_printf(s, "%u.%u.%u.%u/%d %s\n",
|
||||
hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label->smk_known);
|
||||
if (skp->smk_label != NULL)
|
||||
kp = skp->smk_label->smk_known;
|
||||
seq_printf(s, "%pI4/%d %s\n", &skp->smk_host.s_addr,
|
||||
skp->smk_masks, kp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations netlbladdr_seq_ops = {
|
||||
.start = netlbladdr_seq_start,
|
||||
.next = netlbladdr_seq_next,
|
||||
.show = netlbladdr_seq_show,
|
||||
static const struct seq_operations net4addr_seq_ops = {
|
||||
.start = net4addr_seq_start,
|
||||
.next = net4addr_seq_next,
|
||||
.show = net4addr_seq_show,
|
||||
.stop = smk_seq_stop,
|
||||
};
|
||||
|
||||
/**
|
||||
* smk_open_netlbladdr - open() for /smack/netlabel
|
||||
* smk_open_net4addr - open() for /smack/netlabel
|
||||
* @inode: inode structure representing file
|
||||
* @file: "netlabel" file pointer
|
||||
*
|
||||
* Connect our netlbladdr_seq_* operations with /smack/netlabel
|
||||
* Connect our net4addr_seq_* operations with /smack/netlabel
|
||||
* file_operations
|
||||
*/
|
||||
static int smk_open_netlbladdr(struct inode *inode, struct file *file)
|
||||
static int smk_open_net4addr(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &netlbladdr_seq_ops);
|
||||
return seq_open(file, &net4addr_seq_ops);
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_netlbladdr_insert
|
||||
* smk_net4addr_insert
|
||||
* @new : netlabel to insert
|
||||
*
|
||||
* This helper insert netlabel in the smack_netlbladdrs list
|
||||
* This helper insert netlabel in the smack_net4addrs list
|
||||
* sorted by netmask length (longest to smallest)
|
||||
* locked by &smk_netlbladdr_lock in smk_write_netlbladdr
|
||||
* locked by &smk_net4addr_lock in smk_write_net4addr
|
||||
*
|
||||
*/
|
||||
static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
|
||||
static void smk_net4addr_insert(struct smk_net4addr *new)
|
||||
{
|
||||
struct smk_netlbladdr *m, *m_next;
|
||||
struct smk_net4addr *m;
|
||||
struct smk_net4addr *m_next;
|
||||
|
||||
if (list_empty(&smk_netlbladdr_list)) {
|
||||
list_add_rcu(&new->list, &smk_netlbladdr_list);
|
||||
if (list_empty(&smk_net4addr_list)) {
|
||||
list_add_rcu(&new->list, &smk_net4addr_list);
|
||||
return;
|
||||
}
|
||||
|
||||
m = list_entry_rcu(smk_netlbladdr_list.next,
|
||||
struct smk_netlbladdr, list);
|
||||
m = list_entry_rcu(smk_net4addr_list.next,
|
||||
struct smk_net4addr, list);
|
||||
|
||||
/* the comparison '>' is a bit hacky, but works */
|
||||
if (new->smk_mask.s_addr > m->smk_mask.s_addr) {
|
||||
list_add_rcu(&new->list, &smk_netlbladdr_list);
|
||||
if (new->smk_masks > m->smk_masks) {
|
||||
list_add_rcu(&new->list, &smk_net4addr_list);
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(m, &smk_netlbladdr_list, list) {
|
||||
if (list_is_last(&m->list, &smk_netlbladdr_list)) {
|
||||
list_for_each_entry_rcu(m, &smk_net4addr_list, list) {
|
||||
if (list_is_last(&m->list, &smk_net4addr_list)) {
|
||||
list_add_rcu(&new->list, &m->list);
|
||||
return;
|
||||
}
|
||||
m_next = list_entry_rcu(m->list.next,
|
||||
struct smk_netlbladdr, list);
|
||||
if (new->smk_mask.s_addr > m_next->smk_mask.s_addr) {
|
||||
struct smk_net4addr, list);
|
||||
if (new->smk_masks > m_next->smk_masks) {
|
||||
list_add_rcu(&new->list, &m->list);
|
||||
return;
|
||||
}
|
||||
@ -1141,28 +1144,29 @@ static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
|
||||
|
||||
|
||||
/**
|
||||
* smk_write_netlbladdr - write() for /smack/netlabel
|
||||
* smk_write_net4addr - write() for /smack/netlabel
|
||||
* @file: file pointer, not actually used
|
||||
* @buf: where to get the data from
|
||||
* @count: bytes sent
|
||||
* @ppos: where to start
|
||||
*
|
||||
* Accepts only one netlbladdr per write call.
|
||||
* Accepts only one net4addr per write call.
|
||||
* Returns number of bytes written or error code, as appropriate
|
||||
*/
|
||||
static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
|
||||
static ssize_t smk_write_net4addr(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct smk_netlbladdr *snp;
|
||||
struct smk_net4addr *snp;
|
||||
struct sockaddr_in newname;
|
||||
char *smack;
|
||||
struct smack_known *skp;
|
||||
struct smack_known *skp = NULL;
|
||||
char *data;
|
||||
char *host = (char *)&newname.sin_addr.s_addr;
|
||||
int rc;
|
||||
struct netlbl_audit audit_info;
|
||||
struct in_addr mask;
|
||||
unsigned int m;
|
||||
unsigned int masks;
|
||||
int found;
|
||||
u32 mask_bits = (1<<31);
|
||||
__be32 nsa;
|
||||
@ -1200,7 +1204,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
|
||||
data[count] = '\0';
|
||||
|
||||
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%u %s",
|
||||
&host[0], &host[1], &host[2], &host[3], &m, smack);
|
||||
&host[0], &host[1], &host[2], &host[3], &masks, smack);
|
||||
if (rc != 6) {
|
||||
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s",
|
||||
&host[0], &host[1], &host[2], &host[3], smack);
|
||||
@ -1209,8 +1213,9 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
|
||||
goto free_out;
|
||||
}
|
||||
m = BEBITS;
|
||||
masks = 32;
|
||||
}
|
||||
if (m > BEBITS) {
|
||||
if (masks > BEBITS) {
|
||||
rc = -EINVAL;
|
||||
goto free_out;
|
||||
}
|
||||
@ -1225,16 +1230,16 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
|
||||
goto free_out;
|
||||
}
|
||||
} else {
|
||||
/* check known options */
|
||||
if (strcmp(smack, smack_cipso_option.smk_known) == 0)
|
||||
skp = &smack_cipso_option;
|
||||
else {
|
||||
/*
|
||||
* Only the -CIPSO option is supported for IPv4
|
||||
*/
|
||||
if (strcmp(smack, SMACK_CIPSO_OPTION) != 0) {
|
||||
rc = -EINVAL;
|
||||
goto free_out;
|
||||
}
|
||||
}
|
||||
|
||||
for (temp_mask = 0; m > 0; m--) {
|
||||
for (m = masks, temp_mask = 0; m > 0; m--) {
|
||||
temp_mask |= mask_bits;
|
||||
mask_bits >>= 1;
|
||||
}
|
||||
@ -1245,14 +1250,13 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
|
||||
* Only allow one writer at a time. Writes should be
|
||||
* quite rare and small in any case.
|
||||
*/
|
||||
mutex_lock(&smk_netlbladdr_lock);
|
||||
mutex_lock(&smk_net4addr_lock);
|
||||
|
||||
nsa = newname.sin_addr.s_addr;
|
||||
/* try to find if the prefix is already in the list */
|
||||
found = 0;
|
||||
list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) {
|
||||
if (snp->smk_host.sin_addr.s_addr == nsa &&
|
||||
snp->smk_mask.s_addr == mask.s_addr) {
|
||||
list_for_each_entry_rcu(snp, &smk_net4addr_list, list) {
|
||||
if (snp->smk_host.s_addr == nsa && snp->smk_masks == masks) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
@ -1265,17 +1269,20 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
|
||||
rc = -ENOMEM;
|
||||
else {
|
||||
rc = 0;
|
||||
snp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr;
|
||||
snp->smk_host.s_addr = newname.sin_addr.s_addr;
|
||||
snp->smk_mask.s_addr = mask.s_addr;
|
||||
snp->smk_label = skp;
|
||||
smk_netlbladdr_insert(snp);
|
||||
snp->smk_masks = masks;
|
||||
smk_net4addr_insert(snp);
|
||||
}
|
||||
} else {
|
||||
/* we delete the unlabeled entry, only if the previous label
|
||||
* wasn't the special CIPSO option */
|
||||
if (snp->smk_label != &smack_cipso_option)
|
||||
/*
|
||||
* Delete the unlabeled entry, only if the previous label
|
||||
* wasn't the special CIPSO option
|
||||
*/
|
||||
if (snp->smk_label != NULL)
|
||||
rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
|
||||
&snp->smk_host.sin_addr, &snp->smk_mask,
|
||||
&snp->smk_host, &snp->smk_mask,
|
||||
PF_INET, &audit_info);
|
||||
else
|
||||
rc = 0;
|
||||
@ -1287,15 +1294,15 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
|
||||
* this host so that incoming packets get labeled.
|
||||
* but only if we didn't get the special CIPSO option
|
||||
*/
|
||||
if (rc == 0 && skp != &smack_cipso_option)
|
||||
if (rc == 0 && skp != NULL)
|
||||
rc = netlbl_cfg_unlbl_static_add(&init_net, NULL,
|
||||
&snp->smk_host.sin_addr, &snp->smk_mask, PF_INET,
|
||||
&snp->smk_host, &snp->smk_mask, PF_INET,
|
||||
snp->smk_label->smk_secid, &audit_info);
|
||||
|
||||
if (rc == 0)
|
||||
rc = count;
|
||||
|
||||
mutex_unlock(&smk_netlbladdr_lock);
|
||||
mutex_unlock(&smk_net4addr_lock);
|
||||
|
||||
free_out:
|
||||
kfree(smack);
|
||||
@ -1305,14 +1312,279 @@ free_data_out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations smk_netlbladdr_ops = {
|
||||
.open = smk_open_netlbladdr,
|
||||
static const struct file_operations smk_net4addr_ops = {
|
||||
.open = smk_open_net4addr,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.write = smk_write_netlbladdr,
|
||||
.write = smk_write_net4addr,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
/*
|
||||
* Seq_file read operations for /smack/netlabel6
|
||||
*/
|
||||
|
||||
static void *net6addr_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
return smk_seq_start(s, pos, &smk_net6addr_list);
|
||||
}
|
||||
|
||||
static void *net6addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
return smk_seq_next(s, v, pos, &smk_net6addr_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print host/label pairs
|
||||
*/
|
||||
static int net6addr_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct list_head *list = v;
|
||||
struct smk_net6addr *skp =
|
||||
list_entry(list, struct smk_net6addr, list);
|
||||
|
||||
if (skp->smk_label != NULL)
|
||||
seq_printf(s, "%pI6/%d %s\n", &skp->smk_host, skp->smk_masks,
|
||||
skp->smk_label->smk_known);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations net6addr_seq_ops = {
|
||||
.start = net6addr_seq_start,
|
||||
.next = net6addr_seq_next,
|
||||
.show = net6addr_seq_show,
|
||||
.stop = smk_seq_stop,
|
||||
};
|
||||
|
||||
/**
|
||||
* smk_open_net6addr - open() for /smack/netlabel
|
||||
* @inode: inode structure representing file
|
||||
* @file: "netlabel" file pointer
|
||||
*
|
||||
* Connect our net6addr_seq_* operations with /smack/netlabel
|
||||
* file_operations
|
||||
*/
|
||||
static int smk_open_net6addr(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &net6addr_seq_ops);
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_net6addr_insert
|
||||
* @new : entry to insert
|
||||
*
|
||||
* This inserts an entry in the smack_net6addrs list
|
||||
* sorted by netmask length (longest to smallest)
|
||||
* locked by &smk_net6addr_lock in smk_write_net6addr
|
||||
*
|
||||
*/
|
||||
static void smk_net6addr_insert(struct smk_net6addr *new)
|
||||
{
|
||||
struct smk_net6addr *m_next;
|
||||
struct smk_net6addr *m;
|
||||
|
||||
if (list_empty(&smk_net6addr_list)) {
|
||||
list_add_rcu(&new->list, &smk_net6addr_list);
|
||||
return;
|
||||
}
|
||||
|
||||
m = list_entry_rcu(smk_net6addr_list.next,
|
||||
struct smk_net6addr, list);
|
||||
|
||||
if (new->smk_masks > m->smk_masks) {
|
||||
list_add_rcu(&new->list, &smk_net6addr_list);
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(m, &smk_net6addr_list, list) {
|
||||
if (list_is_last(&m->list, &smk_net6addr_list)) {
|
||||
list_add_rcu(&new->list, &m->list);
|
||||
return;
|
||||
}
|
||||
m_next = list_entry_rcu(m->list.next,
|
||||
struct smk_net6addr, list);
|
||||
if (new->smk_masks > m_next->smk_masks) {
|
||||
list_add_rcu(&new->list, &m->list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* smk_write_net6addr - write() for /smack/netlabel
|
||||
* @file: file pointer, not actually used
|
||||
* @buf: where to get the data from
|
||||
* @count: bytes sent
|
||||
* @ppos: where to start
|
||||
*
|
||||
* Accepts only one net6addr per write call.
|
||||
* Returns number of bytes written or error code, as appropriate
|
||||
*/
|
||||
static ssize_t smk_write_net6addr(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct smk_net6addr *snp;
|
||||
struct in6_addr newname;
|
||||
struct in6_addr fullmask;
|
||||
struct smack_known *skp = NULL;
|
||||
char *smack;
|
||||
char *data;
|
||||
int rc = 0;
|
||||
int found = 0;
|
||||
int i;
|
||||
unsigned int scanned[8];
|
||||
unsigned int m;
|
||||
unsigned int mask = 128;
|
||||
|
||||
/*
|
||||
* Must have privilege.
|
||||
* No partial writes.
|
||||
* Enough data must be present.
|
||||
* "<addr/mask, as a:b:c:d:e:f:g:h/e><space><label>"
|
||||
* "<addr, as a:b:c:d:e:f:g:h><space><label>"
|
||||
*/
|
||||
if (!smack_privileged(CAP_MAC_ADMIN))
|
||||
return -EPERM;
|
||||
if (*ppos != 0)
|
||||
return -EINVAL;
|
||||
if (count < SMK_NETLBLADDRMIN)
|
||||
return -EINVAL;
|
||||
|
||||
data = kzalloc(count + 1, GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(data, buf, count) != 0) {
|
||||
rc = -EFAULT;
|
||||
goto free_data_out;
|
||||
}
|
||||
|
||||
smack = kzalloc(count + 1, GFP_KERNEL);
|
||||
if (smack == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto free_data_out;
|
||||
}
|
||||
|
||||
data[count] = '\0';
|
||||
|
||||
i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x/%u %s",
|
||||
&scanned[0], &scanned[1], &scanned[2], &scanned[3],
|
||||
&scanned[4], &scanned[5], &scanned[6], &scanned[7],
|
||||
&mask, smack);
|
||||
if (i != 10) {
|
||||
i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x %s",
|
||||
&scanned[0], &scanned[1], &scanned[2],
|
||||
&scanned[3], &scanned[4], &scanned[5],
|
||||
&scanned[6], &scanned[7], smack);
|
||||
if (i != 9) {
|
||||
rc = -EINVAL;
|
||||
goto free_out;
|
||||
}
|
||||
}
|
||||
if (mask > 128) {
|
||||
rc = -EINVAL;
|
||||
goto free_out;
|
||||
}
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (scanned[i] > 0xffff) {
|
||||
rc = -EINVAL;
|
||||
goto free_out;
|
||||
}
|
||||
newname.s6_addr16[i] = htons(scanned[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* If smack begins with '-', it is an option, don't import it
|
||||
*/
|
||||
if (smack[0] != '-') {
|
||||
skp = smk_import_entry(smack, 0);
|
||||
if (skp == NULL) {
|
||||
rc = -EINVAL;
|
||||
goto free_out;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Only -DELETE is supported for IPv6
|
||||
*/
|
||||
if (strcmp(smack, SMACK_DELETE_OPTION) != 0) {
|
||||
rc = -EINVAL;
|
||||
goto free_out;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0, m = mask; i < 8; i++) {
|
||||
if (m >= 16) {
|
||||
fullmask.s6_addr16[i] = 0xffff;
|
||||
m -= 16;
|
||||
} else if (m > 0) {
|
||||
fullmask.s6_addr16[i] = (1 << m) - 1;
|
||||
m = 0;
|
||||
} else
|
||||
fullmask.s6_addr16[i] = 0;
|
||||
newname.s6_addr16[i] &= fullmask.s6_addr16[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* Only allow one writer at a time. Writes should be
|
||||
* quite rare and small in any case.
|
||||
*/
|
||||
mutex_lock(&smk_net6addr_lock);
|
||||
/*
|
||||
* Try to find the prefix in the list
|
||||
*/
|
||||
list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
|
||||
if (mask != snp->smk_masks)
|
||||
continue;
|
||||
for (found = 1, i = 0; i < 8; i++) {
|
||||
if (newname.s6_addr16[i] !=
|
||||
snp->smk_host.s6_addr16[i]) {
|
||||
found = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found == 1)
|
||||
break;
|
||||
}
|
||||
if (found == 0) {
|
||||
snp = kzalloc(sizeof(*snp), GFP_KERNEL);
|
||||
if (snp == NULL)
|
||||
rc = -ENOMEM;
|
||||
else {
|
||||
snp->smk_host = newname;
|
||||
snp->smk_mask = fullmask;
|
||||
snp->smk_masks = mask;
|
||||
snp->smk_label = skp;
|
||||
smk_net6addr_insert(snp);
|
||||
}
|
||||
} else {
|
||||
snp->smk_label = skp;
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
rc = count;
|
||||
|
||||
mutex_unlock(&smk_net6addr_lock);
|
||||
|
||||
free_out:
|
||||
kfree(smack);
|
||||
free_data_out:
|
||||
kfree(data);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations smk_net6addr_ops = {
|
||||
.open = smk_open_net6addr,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.write = smk_write_net6addr,
|
||||
.release = seq_release,
|
||||
};
|
||||
#endif /* CONFIG_IPV6 */
|
||||
|
||||
/**
|
||||
* smk_read_doi - read() for /smack/doi
|
||||
* @filp: file pointer, not actually used
|
||||
@ -2320,11 +2592,7 @@ static const struct file_operations smk_revoke_subj_ops = {
|
||||
*/
|
||||
static int smk_init_sysfs(void)
|
||||
{
|
||||
int err;
|
||||
err = sysfs_create_mount_point(fs_kobj, "smackfs");
|
||||
if (err)
|
||||
return err;
|
||||
return 0;
|
||||
return sysfs_create_mount_point(fs_kobj, "smackfs");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2519,8 +2787,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
|
||||
"direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
|
||||
[SMK_AMBIENT] = {
|
||||
"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
|
||||
[SMK_NETLBLADDR] = {
|
||||
"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},
|
||||
[SMK_NET4ADDR] = {
|
||||
"netlabel", &smk_net4addr_ops, S_IRUGO|S_IWUSR},
|
||||
[SMK_ONLYCAP] = {
|
||||
"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
|
||||
[SMK_LOGGING] = {
|
||||
@ -2552,6 +2820,10 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
|
||||
[SMK_UNCONFINED] = {
|
||||
"unconfined", &smk_unconfined_ops, S_IRUGO|S_IWUSR},
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
[SMK_NET6ADDR] = {
|
||||
"ipv6host", &smk_net6addr_ops, S_IRUGO|S_IWUSR},
|
||||
#endif /* CONFIG_IPV6 */
|
||||
/* last one */
|
||||
{""}
|
||||
};
|
||||
|
@ -6,14 +6,7 @@ config SECURITY_YAMA
|
||||
This selects Yama, which extends DAC support with additional
|
||||
system-wide security settings beyond regular Linux discretionary
|
||||
access controls. Currently available is ptrace scope restriction.
|
||||
Like capabilities, this security module stacks with other LSMs.
|
||||
Further information can be found in Documentation/security/Yama.txt.
|
||||
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_YAMA_STACKED
|
||||
bool "Yama stacked with other LSMs"
|
||||
depends on SECURITY_YAMA
|
||||
default n
|
||||
help
|
||||
When Yama is built into the kernel, force it to stack with the
|
||||
selected primary LSM.
|
||||
|
@ -353,11 +353,6 @@ static struct security_hook_list yama_hooks[] = {
|
||||
LSM_HOOK_INIT(task_free, yama_task_free),
|
||||
};
|
||||
|
||||
void __init yama_add_hooks(void)
|
||||
{
|
||||
security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static int yama_dointvec_minmax(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
@ -396,26 +391,18 @@ static struct ctl_table yama_sysctl_table[] = {
|
||||
},
|
||||
{ }
|
||||
};
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
||||
static __init int yama_init(void)
|
||||
static void __init yama_init_sysctl(void)
|
||||
{
|
||||
#ifndef CONFIG_SECURITY_YAMA_STACKED
|
||||
/*
|
||||
* If yama is being stacked this is already taken care of.
|
||||
*/
|
||||
if (!security_module_enable("yama"))
|
||||
return 0;
|
||||
yama_add_hooks();
|
||||
#endif
|
||||
pr_info("Yama: becoming mindful.\n");
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table))
|
||||
panic("Yama: sysctl registration failed.\n");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline void yama_init_sysctl(void) { }
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
||||
security_initcall(yama_init);
|
||||
void __init yama_add_hooks(void)
|
||||
{
|
||||
pr_info("Yama: becoming mindful.\n");
|
||||
security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks));
|
||||
yama_init_sysctl();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user