diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-14 13:39:34 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-14 13:39:34 -0700 |
commit | d25282d1c9b9bc4cda7f9d3c0205108e99aa7a9d (patch) | |
tree | f414482d768b015a609924293b779b4ad0b8f764 | |
parent | b6eea87fc6850d3531a64a27d2323a4498cd4e43 (diff) | |
parent | dbadc17683e6c673a69b236c0f041b931cc55c42 (diff) | |
download | op-kernel-dev-d25282d1c9b9bc4cda7f9d3c0205108e99aa7a9d.zip op-kernel-dev-d25282d1c9b9bc4cda7f9d3c0205108e99aa7a9d.tar.gz |
Merge branch 'modules-next' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux
Pull module signing support from Rusty Russell:
"module signing is the highlight, but it's an all-over David Howells frenzy..."
Hmm "Magrathea: Glacier signing key". Somebody has been reading too much HHGTTG.
* 'modules-next' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux: (37 commits)
X.509: Fix indefinite length element skip error handling
X.509: Convert some printk calls to pr_devel
asymmetric keys: fix printk format warning
MODSIGN: Fix 32-bit overflow in X.509 certificate validity date checking
MODSIGN: Make mrproper should remove generated files.
MODSIGN: Use utf8 strings in signer's name in autogenerated X.509 certs
MODSIGN: Use the same digest for the autogen key sig as for the module sig
MODSIGN: Sign modules during the build process
MODSIGN: Provide a script for generating a key ID from an X.509 cert
MODSIGN: Implement module signature checking
MODSIGN: Provide module signing public keys to the kernel
MODSIGN: Automatically generate module signing keys if missing
MODSIGN: Provide Kconfig options
MODSIGN: Provide gitignore and make clean rules for extra files
MODSIGN: Add FIPS policy
module: signature checking hook
X.509: Add a crypto key parser for binary (DER) X.509 certificates
MPILIB: Provide a function to read raw data into an MPI
X.509: Add an ASN.1 decoder
X.509: Add simple ASN.1 grammar compiler
...
128 files changed, 6799 insertions, 594 deletions
@@ -14,6 +14,10 @@ *.o.* *.a *.s +*.ko.unsigned +*.ko.stripped +*.ko.stripped.dig +*.ko.stripped.sig *.ko *.so *.so.dbg @@ -84,3 +88,13 @@ GTAGS *.orig *~ \#*# + +# +# Leavings from module signing +# +extra_certificates +signing_key.priv +signing_key.x509 +signing_key.x509.keyid +signing_key.x509.signer +x509.genkey diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt new file mode 100644 index 0000000..b767590 --- /dev/null +++ b/Documentation/crypto/asymmetric-keys.txt @@ -0,0 +1,312 @@ + ============================================= + ASYMMETRIC / PUBLIC-KEY CRYPTOGRAPHY KEY TYPE + ============================================= + +Contents: + + - Overview. + - Key identification. + - Accessing asymmetric keys. + - Signature verification. + - Asymmetric key subtypes. + - Instantiation data parsers. + + +======== +OVERVIEW +======== + +The "asymmetric" key type is designed to be a container for the keys used in +public-key cryptography, without imposing any particular restrictions on the +form or mechanism of the cryptography or form of the key. + +The asymmetric key is given a subtype that defines what sort of data is +associated with the key and provides operations to describe and destroy it. +However, no requirement is made that the key data actually be stored in the +key. + +A completely in-kernel key retention and operation subtype can be defined, but +it would also be possible to provide access to cryptographic hardware (such as +a TPM) that might be used to both retain the relevant key and perform +operations using that key. In such a case, the asymmetric key would then +merely be an interface to the TPM driver. + +Also provided is the concept of a data parser. Data parsers are responsible +for extracting information from the blobs of data passed to the instantiation +function. The first data parser that recognises the blob gets to set the +subtype of the key and define the operations that can be done on that key. + +A data parser may interpret the data blob as containing the bits representing a +key, or it may interpret it as a reference to a key held somewhere else in the +system (for example, a TPM). + + +================== +KEY IDENTIFICATION +================== + +If a key is added with an empty name, the instantiation data parsers are given +the opportunity to pre-parse a key and to determine the description the key +should be given from the content of the key. + +This can then be used to refer to the key, either by complete match or by +partial match. The key type may also use other criteria to refer to a key. + +The asymmetric key type's match function can then perform a wider range of +comparisons than just the straightforward comparison of the description with +the criterion string: + + (1) If the criterion string is of the form "id:<hexdigits>" then the match + function will examine a key's fingerprint to see if the hex digits given + after the "id:" match the tail. For instance: + + keyctl search @s asymmetric id:5acc2142 + + will match a key with fingerprint: + + 1A00 2040 7601 7889 DE11 882C 3823 04AD 5ACC 2142 + + (2) If the criterion string is of the form "<subtype>:<hexdigits>" then the + match will match the ID as in (1), but with the added restriction that + only keys of the specified subtype (e.g. tpm) will be matched. For + instance: + + keyctl search @s asymmetric tpm:5acc2142 + +Looking in /proc/keys, the last 8 hex digits of the key fingerprint are +displayed, along with the subtype: + + 1a39e171 I----- 1 perm 3f010000 0 0 asymmetri modsign.0: DSA 5acc2142 [] + + +========================= +ACCESSING ASYMMETRIC KEYS +========================= + +For general access to asymmetric keys from within the kernel, the following +inclusion is required: + + #include <crypto/public_key.h> + +This gives access to functions for dealing with asymmetric / public keys. +Three enums are defined there for representing public-key cryptography +algorithms: + + enum pkey_algo + +digest algorithms used by those: + + enum pkey_hash_algo + +and key identifier representations: + + enum pkey_id_type + +Note that the key type representation types are required because key +identifiers from different standards aren't necessarily compatible. For +instance, PGP generates key identifiers by hashing the key data plus some +PGP-specific metadata, whereas X.509 has arbitrary certificate identifiers. + +The operations defined upon a key are: + + (1) Signature verification. + +Other operations are possible (such as encryption) with the same key data +required for verification, but not currently supported, and others +(eg. decryption and signature generation) require extra key data. + + +SIGNATURE VERIFICATION +---------------------- + +An operation is provided to perform cryptographic signature verification, using +an asymmetric key to provide or to provide access to the public key. + + int verify_signature(const struct key *key, + const struct public_key_signature *sig); + +The caller must have already obtained the key from some source and can then use +it to check the signature. The caller must have parsed the signature and +transferred the relevant bits to the structure pointed to by sig. + + struct public_key_signature { + u8 *digest; + u8 digest_size; + enum pkey_hash_algo pkey_hash_algo : 8; + u8 nr_mpi; + union { + MPI mpi[2]; + ... + }; + }; + +The algorithm used must be noted in sig->pkey_hash_algo, and all the MPIs that +make up the actual signature must be stored in sig->mpi[] and the count of MPIs +placed in sig->nr_mpi. + +In addition, the data must have been digested by the caller and the resulting +hash must be pointed to by sig->digest and the size of the hash be placed in +sig->digest_size. + +The function will return 0 upon success or -EKEYREJECTED if the signature +doesn't match. + +The function may also return -ENOTSUPP if an unsupported public-key algorithm +or public-key/hash algorithm combination is specified or the key doesn't +support the operation; -EBADMSG or -ERANGE if some of the parameters have weird +data; or -ENOMEM if an allocation can't be performed. -EINVAL can be returned +if the key argument is the wrong type or is incompletely set up. + + +======================= +ASYMMETRIC KEY SUBTYPES +======================= + +Asymmetric keys have a subtype that defines the set of operations that can be +performed on that key and that determines what data is attached as the key +payload. The payload format is entirely at the whim of the subtype. + +The subtype is selected by the key data parser and the parser must initialise +the data required for it. The asymmetric key retains a reference on the +subtype module. + +The subtype definition structure can be found in: + + #include <keys/asymmetric-subtype.h> + +and looks like the following: + + struct asymmetric_key_subtype { + struct module *owner; + const char *name; + + void (*describe)(const struct key *key, struct seq_file *m); + void (*destroy)(void *payload); + int (*verify_signature)(const struct key *key, + const struct public_key_signature *sig); + }; + +Asymmetric keys point to this with their type_data[0] member. + +The owner and name fields should be set to the owning module and the name of +the subtype. Currently, the name is only used for print statements. + +There are a number of operations defined by the subtype: + + (1) describe(). + + Mandatory. This allows the subtype to display something in /proc/keys + against the key. For instance the name of the public key algorithm type + could be displayed. The key type will display the tail of the key + identity string after this. + + (2) destroy(). + + Mandatory. This should free the memory associated with the key. The + asymmetric key will look after freeing the fingerprint and releasing the + reference on the subtype module. + + (3) verify_signature(). + + Optional. These are the entry points for the key usage operations. + Currently there is only the one defined. If not set, the caller will be + given -ENOTSUPP. The subtype may do anything it likes to implement an + operation, including offloading to hardware. + + +========================== +INSTANTIATION DATA PARSERS +========================== + +The asymmetric key type doesn't generally want to store or to deal with a raw +blob of data that holds the key data. It would have to parse it and error +check it each time it wanted to use it. Further, the contents of the blob may +have various checks that can be performed on it (eg. self-signatures, validity +dates) and may contain useful data about the key (identifiers, capabilities). + +Also, the blob may represent a pointer to some hardware containing the key +rather than the key itself. + +Examples of blob formats for which parsers could be implemented include: + + - OpenPGP packet stream [RFC 4880]. + - X.509 ASN.1 stream. + - Pointer to TPM key. + - Pointer to UEFI key. + +During key instantiation each parser in the list is tried until one doesn't +return -EBADMSG. + +The parser definition structure can be found in: + + #include <keys/asymmetric-parser.h> + +and looks like the following: + + struct asymmetric_key_parser { + struct module *owner; + const char *name; + + int (*parse)(struct key_preparsed_payload *prep); + }; + +The owner and name fields should be set to the owning module and the name of +the parser. + +There is currently only a single operation defined by the parser, and it is +mandatory: + + (1) parse(). + + This is called to preparse the key from the key creation and update paths. + In particular, it is called during the key creation _before_ a key is + allocated, and as such, is permitted to provide the key's description in + the case that the caller declines to do so. + + The caller passes a pointer to the following struct with all of the fields + cleared, except for data, datalen and quotalen [see + Documentation/security/keys.txt]. + + struct key_preparsed_payload { + char *description; + void *type_data[2]; + void *payload; + const void *data; + size_t datalen; + size_t quotalen; + }; + + The instantiation data is in a blob pointed to by data and is datalen in + size. The parse() function is not permitted to change these two values at + all, and shouldn't change any of the other values _unless_ they are + recognise the blob format and will not return -EBADMSG to indicate it is + not theirs. + + If the parser is happy with the blob, it should propose a description for + the key and attach it to ->description, ->type_data[0] should be set to + point to the subtype to be used, ->payload should be set to point to the + initialised data for that subtype, ->type_data[1] should point to a hex + fingerprint and quotalen should be updated to indicate how much quota this + key should account for. + + When clearing up, the data attached to ->type_data[1] and ->description + will be kfree()'d and the data attached to ->payload will be passed to the + subtype's ->destroy() method to be disposed of. A module reference for + the subtype pointed to by ->type_data[0] will be put. + + + If the data format is not recognised, -EBADMSG should be returned. If it + is recognised, but the key cannot for some reason be set up, some other + negative error code should be returned. On success, 0 should be returned. + + The key's fingerprint string may be partially matched upon. For a + public-key algorithm such as RSA and DSA this will likely be a printable + hex version of the key's fingerprint. + +Functions are provided to register and unregister parsers: + + int register_asymmetric_key_parser(struct asymmetric_key_parser *parser); + void unregister_asymmetric_key_parser(struct asymmetric_key_parser *subtype); + +Parsers may not have the same name. The names are otherwise only used for +displaying in debugging messages. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e2ed336..9776f06 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1593,6 +1593,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted. log everything. Information is printed at KERN_DEBUG so loglevel=8 may also need to be specified. + module.sig_enforce + [KNL] When CONFIG_MODULE_SIG is set, this means that + modules without (valid) signatures will fail to load. + Note that if CONFIG_MODULE_SIG_ENFORCE is set, that + is always true, so this option does nothing. + mousedev.tap_time= [MOUSE] Maximum time between finger touching and leaving touchpad surface for touch to be considered diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index aa0dbd7..7d9ca92 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -412,6 +412,10 @@ The main syscalls are: to the keyring. In this case, an error will be generated if the process does not have permission to write to the keyring. + If the key type supports it, if the description is NULL or an empty + string, the key type will try and generate a description from the content + of the payload. + The payload is optional, and the pointer can be NULL if not required by the type. The payload is plen in size, and plen can be zero for an empty payload. @@ -1114,12 +1118,53 @@ The structure has a number of fields, some of which are mandatory: it should return 0. - (*) int (*instantiate)(struct key *key, const void *data, size_t datalen); + (*) int (*preparse)(struct key_preparsed_payload *prep); + + This optional method permits the key type to attempt to parse payload + before a key is created (add key) or the key semaphore is taken (update or + instantiate key). The structure pointed to by prep looks like: + + struct key_preparsed_payload { + char *description; + void *type_data[2]; + void *payload; + const void *data; + size_t datalen; + size_t quotalen; + }; + + Before calling the method, the caller will fill in data and datalen with + the payload blob parameters; quotalen will be filled in with the default + quota size from the key type and the rest will be cleared. + + If a description can be proposed from the payload contents, that should be + attached as a string to the description field. This will be used for the + key description if the caller of add_key() passes NULL or "". + + The method can attach anything it likes to type_data[] and payload. These + are merely passed along to the instantiate() or update() operations. + + The method should return 0 if success ful or a negative error code + otherwise. + + + (*) void (*free_preparse)(struct key_preparsed_payload *prep); + + This method is only required if the preparse() method is provided, + otherwise it is unused. It cleans up anything attached to the + description, type_data and payload fields of the key_preparsed_payload + struct as filled in by the preparse() method. + + + (*) int (*instantiate)(struct key *key, struct key_preparsed_payload *prep); This method is called to attach a payload to a key during construction. The payload attached need not bear any relation to the data passed to this function. + The prep->data and prep->datalen fields will define the original payload + blob. If preparse() was supplied then other fields may be filled in also. + If the amount of data attached to the key differs from the size in keytype->def_datalen, then key_payload_reserve() should be called. @@ -1135,6 +1180,9 @@ The structure has a number of fields, some of which are mandatory: If this type of key can be updated, then this method should be provided. It is called to update a key's payload from the blob of data provided. + The prep->data and prep->datalen fields will define the original payload + blob. If preparse() was supplied then other fields may be filled in also. + key_payload_reserve() should be called if the data length might change before any changes are actually made. Note that if this succeeds, the type is committed to changing the key because it's already been altered, so all @@ -997,7 +997,10 @@ CLEAN_DIRS += $(MODVERDIR) MRPROPER_DIRS += include/config usr/include include/generated \ arch/*/include/generated MRPROPER_FILES += .config .config.old .version .old_version $(version_h) \ - Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS + 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.x509.signer # clean - Delete most, but leave enough to build external modules # @@ -1241,6 +1244,7 @@ clean: $(clean-dirs) $(call cmd,rmfiles) @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \ \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ + -o -name '*.ko.*' \ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \ -o -name '*.symtypes' -o -name 'modules.order' \ -o -name modules.builtin -o -name '.tmp_*.o.*' \ diff --git a/arch/Kconfig b/arch/Kconfig index a79a1ad..366ec06 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -322,4 +322,23 @@ config HAVE_IRQ_TIME_ACCOUNTING config HAVE_ARCH_TRANSPARENT_HUGEPAGE bool +config HAVE_MOD_ARCH_SPECIFIC + bool + help + The arch uses struct mod_arch_specific to store data. Many arches + just need a simple module loader without arch specific data - those + should not enable this. + +config MODULES_USE_ELF_RELA + bool + help + Modules only use ELF RELA relocations. Modules with ELF REL + relocations will give an error. + +config MODULES_USE_ELF_REL + bool + help + Modules only use ELF REL relocations. Modules with ELF RELA + relocations will give an error. + source "kernel/gcov/Kconfig" diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 7a08cfb..5dd7f5d 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -22,6 +22,8 @@ config ALPHA select GENERIC_STRNLEN_USER select GENERIC_KERNEL_THREAD select GENERIC_KERNEL_EXECVE + select HAVE_MOD_ARCH_SPECIFIC + select MODULES_USE_ELF_RELA help The Alpha is a 64-bit general-purpose processor designed and marketed by the Digital Equipment Corporation of blessed memory, diff --git a/arch/alpha/include/asm/module.h b/arch/alpha/include/asm/module.h index 7b63743..9cd13b5 100644 --- a/arch/alpha/include/asm/module.h +++ b/arch/alpha/include/asm/module.h @@ -1,19 +1,13 @@ #ifndef _ALPHA_MODULE_H #define _ALPHA_MODULE_H +#include <asm-generic/module.h> + struct mod_arch_specific { unsigned int gotsecindex; }; -#define Elf_Sym Elf64_Sym -#define Elf_Shdr Elf64_Shdr -#define Elf_Ehdr Elf64_Ehdr -#define Elf_Phdr Elf64_Phdr -#define Elf_Dyn Elf64_Dyn -#define Elf_Rel Elf64_Rel -#define Elf_Rela Elf64_Rela - #define ARCH_SHF_SMALL SHF_ALPHA_GPREL #ifdef MODULE diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 261fdd0..73067ef 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -53,6 +53,8 @@ config ARM select PERF_USE_VMALLOC select RTC_LIB select SYS_SUPPORTS_APM_EMULATION + select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND + select MODULES_USE_ELF_REL help The ARM series is a line of low-power-consumption RISC chip designs licensed by ARM Ltd and targeted at embedded applications and diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h index 6c6809f..0d3a28d 100644 --- a/arch/arm/include/asm/module.h +++ b/arch/arm/include/asm/module.h @@ -1,9 +1,7 @@ #ifndef _ASM_ARM_MODULE_H #define _ASM_ARM_MODULE_H -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr +#include <asm-generic/module.h> struct unwind_table; @@ -16,13 +14,11 @@ enum { ARM_SEC_DEVEXIT, ARM_SEC_MAX, }; -#endif struct mod_arch_specific { -#ifdef CONFIG_ARM_UNWIND struct unwind_table *unwind[ARM_SEC_MAX]; -#endif }; +#endif /* * Add the ARM architecture version to the version magic string diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index 5ade51c..06e73bf 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -15,6 +15,8 @@ config AVR32 select ARCH_WANT_IPC_PARSE_VERSION select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_CLOCKEVENTS + select HAVE_MOD_ARCH_SPECIFIC + select MODULES_USE_ELF_RELA help AVR32 is a high-performance 32-bit RISC microprocessor core, designed for cost-sensitive embedded applications, with particular diff --git a/arch/avr32/include/asm/module.h b/arch/avr32/include/asm/module.h index 4514445..3f083d3 100644 --- a/arch/avr32/include/asm/module.h +++ b/arch/avr32/include/asm/module.h @@ -1,6 +1,8 @@ #ifndef __ASM_AVR32_MODULE_H #define __ASM_AVR32_MODULE_H +#include <asm-generic/module.h> + struct mod_arch_syminfo { unsigned long got_offset; int got_initialized; @@ -17,10 +19,6 @@ struct mod_arch_specific { struct mod_arch_syminfo *syminfo; }; -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr - #define MODULE_PROC_FAMILY "AVR32v1" #define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index ccd9193..b6f3ad5 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -43,6 +43,8 @@ config BLACKFIN select HAVE_NMI_WATCHDOG if NMI_WATCHDOG select GENERIC_SMP_IDLE_THREAD select ARCH_USES_GETTIMEOFFSET if !GENERIC_CLOCKEVENTS + select HAVE_MOD_ARCH_SPECIFIC + select MODULES_USE_ELF_RELA config GENERIC_CSUM def_bool y diff --git a/arch/blackfin/include/asm/module.h b/arch/blackfin/include/asm/module.h index ed5689b..231a149 100644 --- a/arch/blackfin/include/asm/module.h +++ b/arch/blackfin/include/asm/module.h @@ -7,9 +7,7 @@ #ifndef _ASM_BFIN_MODULE_H #define _ASM_BFIN_MODULE_H -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr +#include <asm-generic/module.h> struct mod_arch_specific { Elf_Shdr *text_l1; diff --git a/arch/c6x/Kconfig b/arch/c6x/Kconfig index 45268b5..aee1b56 100644 --- a/arch/c6x/Kconfig +++ b/arch/c6x/Kconfig @@ -18,6 +18,7 @@ config C6X select OF_EARLY_FLATTREE select GENERIC_CLOCKEVENTS select GENERIC_KERNEL_THREAD + select MODULES_USE_ELF_RELA config MMU def_bool n diff --git a/arch/c6x/include/asm/module.h b/arch/c6x/include/asm/module.h index a453f97..5c7269c 100644 --- a/arch/c6x/include/asm/module.h +++ b/arch/c6x/include/asm/module.h @@ -13,17 +13,7 @@ #ifndef _ASM_C6X_MODULE_H #define _ASM_C6X_MODULE_H -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr -#define Elf_Addr Elf32_Addr -#define Elf_Word Elf32_Word - -/* - * This file contains the C6x architecture specific module code. - */ -struct mod_arch_specific { -}; +#include <asm-generic/module.h> struct loaded_sections { unsigned int new_vaddr; diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index a118163..a672444 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -48,6 +48,7 @@ config CRIS select GENERIC_IOMAP select GENERIC_SMP_IDLE_THREAD if ETRAX_ARCH_V32 select GENERIC_CMOS_UPDATE + select MODULES_USE_ELF_RELA config HZ int diff --git a/arch/cris/include/asm/Kbuild b/arch/cris/include/asm/Kbuild index ff1bf7f..6d43a95 100644 --- a/arch/cris/include/asm/Kbuild +++ b/arch/cris/include/asm/Kbuild @@ -10,3 +10,4 @@ header-y += sync_serial.h generic-y += clkdev.h generic-y += exec.h +generic-y += module.h diff --git a/arch/cris/include/asm/module.h b/arch/cris/include/asm/module.h deleted file mode 100644 index 7ee7231..0000000 --- a/arch/cris/include/asm/module.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _ASM_CRIS_MODULE_H -#define _ASM_CRIS_MODULE_H -/* cris is simple */ -struct mod_arch_specific { }; - -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr -#endif /* _ASM_CRIS_MODULE_H */ diff --git a/arch/frv/include/asm/module.h b/arch/frv/include/asm/module.h index 3d5c636..a8848f0 100644 --- a/arch/frv/include/asm/module.h +++ b/arch/frv/include/asm/module.h @@ -11,13 +11,7 @@ #ifndef _ASM_MODULE_H #define _ASM_MODULE_H -struct mod_arch_specific -{ -}; - -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr +#include <asm-generic/module.h> /* * Include the architecture version. diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 90462eb..98fabd1 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -7,6 +7,7 @@ config H8300 select ARCH_WANT_IPC_PARSE_VERSION select GENERIC_IRQ_SHOW select GENERIC_CPU_DEVICES + select MODULES_USE_ELF_RELA config SYMBOL_PREFIX string diff --git a/arch/h8300/include/asm/Kbuild b/arch/h8300/include/asm/Kbuild index fccd81e..50bbf38 100644 --- a/arch/h8300/include/asm/Kbuild +++ b/arch/h8300/include/asm/Kbuild @@ -2,3 +2,4 @@ include include/asm-generic/Kbuild.asm generic-y += clkdev.h generic-y += exec.h +generic-y += module.h diff --git a/arch/h8300/include/asm/module.h b/arch/h8300/include/asm/module.h deleted file mode 100644 index 8e46724..0000000 --- a/arch/h8300/include/asm/module.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _ASM_H8300_MODULE_H -#define _ASM_H8300_MODULE_H -/* - * This file contains the H8/300 architecture specific module code. - */ -struct mod_arch_specific { }; -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr - -#endif /* _ASM_H8/300_MODULE_H */ diff --git a/arch/hexagon/Kconfig b/arch/hexagon/Kconfig index b2fdfb70..0744f7d 100644 --- a/arch/hexagon/Kconfig +++ b/arch/hexagon/Kconfig @@ -30,6 +30,7 @@ config HEXAGON select KTIME_SCALAR select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS_BROADCAST + select MODULES_USE_ELF_RELA ---help--- Qualcomm Hexagon is a processor architecture designed for high performance and low power across a wide variety of applications. diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index 4c10e60..3279646 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -40,6 +40,8 @@ config IA64 select ARCH_THREAD_INFO_ALLOCATOR select ARCH_CLOCKSOURCE_DATA select GENERIC_TIME_VSYSCALL_OLD + select HAVE_MOD_ARCH_SPECIFIC + select MODULES_USE_ELF_RELA default y help The Itanium Processor Family is Intel's 64-bit successor to diff --git a/arch/ia64/include/asm/module.h b/arch/ia64/include/asm/module.h index 908eaef..dfba22a 100644 --- a/arch/ia64/include/asm/module.h +++ b/arch/ia64/include/asm/module.h @@ -1,6 +1,8 @@ #ifndef _ASM_IA64_MODULE_H #define _ASM_IA64_MODULE_H +#include <asm-generic/module.h> + /* * IA-64-specific support for kernel module loader. * @@ -29,10 +31,6 @@ struct mod_arch_specific { unsigned int next_got_entry; /* index of next available got entry */ }; -#define Elf_Shdr Elf64_Shdr -#define Elf_Sym Elf64_Sym -#define Elf_Ehdr Elf64_Ehdr - #define MODULE_PROC_FAMILY "ia64" #define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY \ "gcc-" __stringify(__GNUC__) "." __stringify(__GNUC_MINOR__) diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index e875fc3..f807721 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -14,6 +14,7 @@ config M32R select GENERIC_IRQ_SHOW select GENERIC_ATOMIC64 select ARCH_USES_GETTIMEOFFSET + select MODULES_USE_ELF_RELA config SBUS bool diff --git a/arch/m32r/include/asm/Kbuild b/arch/m32r/include/asm/Kbuild index fccd81e..50bbf38 100644 --- a/arch/m32r/include/asm/Kbuild +++ b/arch/m32r/include/asm/Kbuild @@ -2,3 +2,4 @@ include include/asm-generic/Kbuild.asm generic-y += clkdev.h generic-y += exec.h +generic-y += module.h diff --git a/arch/m32r/include/asm/module.h b/arch/m32r/include/asm/module.h deleted file mode 100644 index eb73ee0..0000000 --- a/arch/m32r/include/asm/module.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _ASM_M32R_MODULE_H -#define _ASM_M32R_MODULE_H - -struct mod_arch_specific { }; - -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr - -#endif /* _ASM_M32R_MODULE_H */ diff --git a/arch/m32r/kernel/module.c b/arch/m32r/kernel/module.c index 3071fe8..38233b6 100644 --- a/arch/m32r/kernel/module.c +++ b/arch/m32r/kernel/module.c @@ -201,18 +201,3 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, } return 0; } - -int apply_relocate(Elf32_Shdr *sechdrs, - const char *strtab, - unsigned int symindex, - unsigned int relsec, - struct module *me) -{ -#if 0 - printk(KERN_ERR "module %s: REL RELOCATION unsupported\n", - me->name); - return -ENOEXEC; -#endif - return 0; - -} diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index 76fd6e2..e7c1614 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -16,6 +16,9 @@ config M68K select ARCH_WANT_IPC_PARSE_VERSION select ARCH_USES_GETTIMEOFFSET if MMU && !COLDFIRE select GENERIC_KERNEL_THREAD + select HAVE_MOD_ARCH_SPECIFIC + select MODULES_USE_ELF_REL + select MODULES_USE_ELF_RELA config RWSEM_GENERIC_SPINLOCK bool diff --git a/arch/m68k/include/asm/module.h b/arch/m68k/include/asm/module.h index edffe66..8b58fce 100644 --- a/arch/m68k/include/asm/module.h +++ b/arch/m68k/include/asm/module.h @@ -1,6 +1,8 @@ #ifndef _ASM_M68K_MODULE_H #define _ASM_M68K_MODULE_H +#include <asm-generic/module.h> + enum m68k_fixup_type { m68k_fixup_memoffset, m68k_fixup_vnode_shift, @@ -36,8 +38,4 @@ struct module; extern void module_fixup(struct module *mod, struct m68k_fixup_info *start, struct m68k_fixup_info *end); -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr - #endif /* _ASM_M68K_MODULE_H */ diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index 53fd94a..4cba743 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -25,6 +25,7 @@ config MICROBLAZE select GENERIC_CPU_DEVICES select GENERIC_ATOMIC64 select GENERIC_CLOCKEVENTS + select MODULES_USE_ELF_RELA config SWAP def_bool n diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 35453ea..aae16cd 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -37,6 +37,9 @@ config MIPS select BUILDTIME_EXTABLE_SORT select GENERIC_CLOCKEVENTS select GENERIC_CMOS_UPDATE + select HAVE_MOD_ARCH_SPECIFIC + select MODULES_USE_ELF_REL + select MODULES_USE_ELF_RELA if 64BIT menu "Machine selection" diff --git a/arch/mips/include/asm/module.h b/arch/mips/include/asm/module.h index dca8bce..26137da 100644 --- a/arch/mips/include/asm/module.h +++ b/arch/mips/include/asm/module.h @@ -35,11 +35,14 @@ typedef struct { } Elf64_Mips_Rela; #ifdef CONFIG_32BIT - #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Ehdr Elf32_Ehdr #define Elf_Addr Elf32_Addr +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELF_R_SYM(X) ELF32_R_SYM(X) #define Elf_Mips_Rel Elf32_Rel #define Elf_Mips_Rela Elf32_Rela @@ -50,11 +53,14 @@ typedef struct { #endif #ifdef CONFIG_64BIT - #define Elf_Shdr Elf64_Shdr #define Elf_Sym Elf64_Sym #define Elf_Ehdr Elf64_Ehdr #define Elf_Addr Elf64_Addr +#define Elf_Rel Elf64_Rel +#define Elf_Rela Elf64_Rela +#define ELF_R_TYPE(X) ELF64_R_TYPE(X) +#define ELF_R_SYM(X) ELF64_R_SYM(X) #define Elf_Mips_Rel Elf64_Mips_Rel #define Elf_Mips_Rela Elf64_Mips_Rela diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index d6c2a74..414c269 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_SYNC_R4K) += sync-r4k.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_MODULES) += mips_ksyms.o module.o +obj-$(CONFIG_MODULES_USE_ELF_RELA) += module-rela.o obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o diff --git a/arch/mips/kernel/module-rela.c b/arch/mips/kernel/module-rela.c new file mode 100644 index 0000000..61d6002 --- /dev/null +++ b/arch/mips/kernel/module-rela.c @@ -0,0 +1,145 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) 2001 Rusty Russell. + * Copyright (C) 2003, 2004 Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2005 Thiemo Seufer + */ + +#include <linux/elf.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/moduleloader.h> + +extern int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v); + +static int apply_r_mips_32_rela(struct module *me, u32 *location, Elf_Addr v) +{ + *location = v; + + return 0; +} + +static int apply_r_mips_26_rela(struct module *me, u32 *location, Elf_Addr v) +{ + if (v % 4) { + pr_err("module %s: dangerous R_MIPS_26 RELArelocation\n", + me->name); + return -ENOEXEC; + } + + if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { + printk(KERN_ERR + "module %s: relocation overflow\n", + me->name); + return -ENOEXEC; + } + + *location = (*location & ~0x03ffffff) | ((v >> 2) & 0x03ffffff); + + return 0; +} + +static int apply_r_mips_hi16_rela(struct module *me, u32 *location, Elf_Addr v) +{ + *location = (*location & 0xffff0000) | + ((((long long) v + 0x8000LL) >> 16) & 0xffff); + + return 0; +} + +static int apply_r_mips_lo16_rela(struct module *me, u32 *location, Elf_Addr v) +{ + *location = (*location & 0xffff0000) | (v & 0xffff); + + return 0; +} + +static int apply_r_mips_64_rela(struct module *me, u32 *location, Elf_Addr v) +{ + *(Elf_Addr *)location = v; + + return 0; +} + +static int apply_r_mips_higher_rela(struct module *me, u32 *location, + Elf_Addr v) +{ + *location = (*location & 0xffff0000) | + ((((long long) v + 0x80008000LL) >> 32) & 0xffff); + + return 0; +} + +static int apply_r_mips_highest_rela(struct module *me, u32 *location, + Elf_Addr v) +{ + *location = (*location & 0xffff0000) | + ((((long long) v + 0x800080008000LL) >> 48) & 0xffff); + + return 0; +} + +static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, + Elf_Addr v) = { + [R_MIPS_NONE] = apply_r_mips_none, + [R_MIPS_32] = apply_r_mips_32_rela, + [R_MIPS_26] = apply_r_mips_26_rela, + [R_MIPS_HI16] = apply_r_mips_hi16_rela, + [R_MIPS_LO16] = apply_r_mips_lo16_rela, + [R_MIPS_64] = apply_r_mips_64_rela, + [R_MIPS_HIGHER] = apply_r_mips_higher_rela, + [R_MIPS_HIGHEST] = apply_r_mips_highest_rela +}; + +int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, + unsigned int symindex, unsigned int relsec, + struct module *me) +{ + Elf_Mips_Rela *rel = (void *) sechdrs[relsec].sh_addr; + Elf_Sym *sym; + u32 *location; + unsigned int i; + Elf_Addr v; + int res; + + pr_debug("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + /* This is the symbol it is referring to */ + sym = (Elf_Sym *)sechdrs[symindex].sh_addr + + ELF_MIPS_R_SYM(rel[i]); + if (IS_ERR_VALUE(sym->st_value)) { + /* Ignore unresolved weak symbol */ + if (ELF_ST_BIND(sym->st_info) == STB_WEAK) + continue; + printk(KERN_WARNING "%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + + v = sym->st_value + rel[i].r_addend; + + res = reloc_handlers_rela[ELF_MIPS_R_TYPE(rel[i])](me, location, v); + if (res) + return res; + } + + return 0; +} diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c index 4f8c3cb..07ff581 100644 --- a/arch/mips/kernel/module.c +++ b/arch/mips/kernel/module.c @@ -51,7 +51,7 @@ void *module_alloc(unsigned long size) } #endif -static int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v) +int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v) { return 0; } @@ -63,13 +63,6 @@ static int apply_r_mips_32_rel(struct module *me, u32 *location, Elf_Addr v) return 0; } -static int apply_r_mips_32_rela(struct module *me, u32 *location, Elf_Addr v) -{ - *location = v; - - return 0; -} - static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v) { if (v % 4) { @@ -91,26 +84,6 @@ static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v) return 0; } -static int apply_r_mips_26_rela(struct module *me, u32 *location, Elf_Addr v) -{ - if (v % 4) { - pr_err("module %s: dangerous R_MIPS_26 RELArelocation\n", - me->name); - return -ENOEXEC; - } - - if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { - printk(KERN_ERR - "module %s: relocation overflow\n", - me->name); - return -ENOEXEC; - } - - *location = (*location & ~0x03ffffff) | ((v >> 2) & 0x03ffffff); - - return 0; -} - static int apply_r_mips_hi16_rel(struct module *me, u32 *location, Elf_Addr v) { struct mips_hi16 *n; @@ -132,14 +105,6 @@ static int apply_r_mips_hi16_rel(struct module *me, u32 *location, Elf_Addr v) return 0; } -static int apply_r_mips_hi16_rela(struct module *me, u32 *location, Elf_Addr v) -{ - *location = (*location & 0xffff0000) | - ((((long long) v + 0x8000LL) >> 16) & 0xffff); - - return 0; -} - static void free_relocation_chain(struct mips_hi16 *l) { struct mips_hi16 *next; @@ -217,38 +182,6 @@ out_danger: return -ENOEXEC; } -static int apply_r_mips_lo16_rela(struct module *me, u32 *location, Elf_Addr v) -{ - *location = (*location & 0xffff0000) | (v & 0xffff); - - return 0; -} - -static int apply_r_mips_64_rela(struct module *me, u32 *location, Elf_Addr v) -{ - *(Elf_Addr *)location = v; - - return 0; -} - -static int apply_r_mips_higher_rela(struct module *me, u32 *location, - Elf_Addr v) -{ - *location = (*location & 0xffff0000) | - ((((long long) v + 0x80008000LL) >> 32) & 0xffff); - - return 0; -} - -static int apply_r_mips_highest_rela(struct module *me, u32 *location, - Elf_Addr v) -{ - *location = (*location & 0xffff0000) | - ((((long long) v + 0x800080008000LL) >> 48) & 0xffff); - - return 0; -} - static int (*reloc_handlers_rel[]) (struct module *me, u32 *location, Elf_Addr v) = { [R_MIPS_NONE] = apply_r_mips_none, @@ -258,18 +191,6 @@ static int (*reloc_handlers_rel[]) (struct module *me, u32 *location, [R_MIPS_LO16] = apply_r_mips_lo16_rel }; -static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, - Elf_Addr v) = { - [R_MIPS_NONE] = apply_r_mips_none, - [R_MIPS_32] = apply_r_mips_32_rela, - [R_MIPS_26] = apply_r_mips_26_rela, - [R_MIPS_HI16] = apply_r_mips_hi16_rela, - [R_MIPS_LO16] = apply_r_mips_lo16_rela, - [R_MIPS_64] = apply_r_mips_64_rela, - [R_MIPS_HIGHER] = apply_r_mips_higher_rela, - [R_MIPS_HIGHEST] = apply_r_mips_highest_rela -}; - int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) @@ -324,46 +245,6 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, return 0; } -int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, - unsigned int symindex, unsigned int relsec, - struct module *me) -{ - Elf_Mips_Rela *rel = (void *) sechdrs[relsec].sh_addr; - Elf_Sym *sym; - u32 *location; - unsigned int i; - Elf_Addr v; - int res; - - pr_debug("Applying relocate section %u to %u\n", relsec, - sechdrs[relsec].sh_info); - - for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { - /* This is where to make the change */ - location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr - + rel[i].r_offset; - /* This is the symbol it is referring to */ - sym = (Elf_Sym *)sechdrs[symindex].sh_addr - + ELF_MIPS_R_SYM(rel[i]); - if (IS_ERR_VALUE(sym->st_value)) { - /* Ignore unresolved weak symbol */ - if (ELF_ST_BIND(sym->st_info) == STB_WEAK) - continue; - printk(KERN_WARNING "%s: Unknown symbol %s\n", - me->name, strtab + sym->st_name); - return -ENOENT; - } - - v = sym->st_value + rel[i].r_addend; - - res = reloc_handlers_rela[ELF_MIPS_R_TYPE(rel[i])](me, location, v); - if (res) - return res; - } - - return 0; -} - /* Given an address, look for it in the module exception tables. */ const struct exception_table_entry *search_module_dbetables(unsigned long addr) { diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig index ddbdc33..04669fa 100644 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig @@ -9,6 +9,7 @@ config MN10300 select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER select GENERIC_CLOCKEVENTS select GENERIC_KERNEL_THREAD + select MODULES_USE_ELF_RELA config AM33_2 def_bool n diff --git a/arch/mn10300/include/asm/module.h b/arch/mn10300/include/asm/module.h index 5d7057d..6571103 100644 --- a/arch/mn10300/include/asm/module.h +++ b/arch/mn10300/include/asm/module.h @@ -12,12 +12,7 @@ #ifndef _ASM_MODULE_H #define _ASM_MODULE_H -struct mod_arch_specific { -}; - -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr +#include <asm-generic/module.h> /* * Include the MN10300 architecture version. diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index 49765b5..05f2ba4 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -21,6 +21,7 @@ config OPENRISC select GENERIC_CLOCKEVENTS select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER + select MODULES_USE_ELF_RELA config MMU def_bool y diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index b87438b..11def45 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -20,6 +20,8 @@ config PARISC select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_SMP_IDLE_THREAD select GENERIC_STRNCPY_FROM_USER + select HAVE_MOD_ARCH_SPECIFIC + select MODULES_USE_ELF_RELA help The PA-RISC microprocessor is designed by Hewlett-Packard and used diff --git a/arch/parisc/include/asm/module.h b/arch/parisc/include/asm/module.h index 1f41234..bab37e9 100644 --- a/arch/parisc/include/asm/module.h +++ b/arch/parisc/include/asm/module.h @@ -1,21 +1,11 @@ #ifndef _ASM_PARISC_MODULE_H #define _ASM_PARISC_MODULE_H + +#include <asm-generic/module.h> + /* * This file contains the parisc architecture specific module code. */ -#ifdef CONFIG_64BIT -#define Elf_Shdr Elf64_Shdr -#define Elf_Sym Elf64_Sym -#define Elf_Ehdr Elf64_Ehdr -#define Elf_Addr Elf64_Addr -#define Elf_Rela Elf64_Rela -#else -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr -#define Elf_Addr Elf32_Addr -#define Elf_Rela Elf32_Rela -#endif struct unwind_table; diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 969f3d9..a902a5c 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -142,6 +142,8 @@ config PPC select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER select GENERIC_KERNEL_THREAD + select HAVE_MOD_ARCH_SPECIFIC + select MODULES_USE_ELF_RELA config EARLY_PRINTK bool diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h index 0192a4e..c1df590 100644 --- a/arch/powerpc/include/asm/module.h +++ b/arch/powerpc/include/asm/module.h @@ -11,6 +11,7 @@ #include <linux/list.h> #include <asm/bug.h> +#include <asm-generic/module.h> #ifndef __powerpc64__ @@ -60,16 +61,10 @@ struct mod_arch_specific { */ #ifdef __powerpc64__ -# define Elf_Shdr Elf64_Shdr -# define Elf_Sym Elf64_Sym -# define Elf_Ehdr Elf64_Ehdr # ifdef MODULE asm(".section .stubs,\"ax\",@nobits; .align 3; .previous"); # endif #else -# define Elf_Shdr Elf32_Shdr -# define Elf_Sym Elf32_Sym -# define Elf_Ehdr Elf32_Ehdr # ifdef MODULE asm(".section .plt,\"ax\",@nobits; .align 3; .previous"); asm(".section .init.plt,\"ax\",@nobits; .align 3; .previous"); diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index e5dac12..3f3d9ca 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -136,6 +136,8 @@ config S390 select KTIME_SCALAR if 32BIT select HAVE_ARCH_SECCOMP_FILTER select GENERIC_KERNEL_THREAD + select HAVE_MOD_ARCH_SPECIFIC + select MODULES_USE_ELF_RELA config SCHED_OMIT_FRAME_POINTER def_bool y diff --git a/arch/s390/include/asm/module.h b/arch/s390/include/asm/module.h index f0b6b26..df1f861 100644 --- a/arch/s390/include/asm/module.h +++ b/arch/s390/include/asm/module.h @@ -1,5 +1,8 @@ #ifndef _ASM_S390_MODULE_H #define _ASM_S390_MODULE_H + +#include <asm-generic/module.h> + /* * This file contains the s390 architecture specific module code. */ @@ -28,19 +31,4 @@ struct mod_arch_specific struct mod_arch_syminfo *syminfo; }; -#ifdef CONFIG_64BIT -#define ElfW(x) Elf64_ ## x -#define ELFW(x) ELF64_ ## x -#else -#define ElfW(x) Elf32_ ## x -#define ELFW(x) ELF32_ ## x -#endif - -#define Elf_Addr ElfW(Addr) -#define Elf_Rela ElfW(Rela) -#define Elf_Shdr ElfW(Shdr) -#define Elf_Sym ElfW(Sym) -#define Elf_Ehdr ElfW(Ehdr) -#define ELF_R_SYM ELFW(R_SYM) -#define ELF_R_TYPE ELFW(R_TYPE) #endif /* _ASM_S390_MODULE_H */ diff --git a/arch/score/Kconfig b/arch/score/Kconfig index 461c237..4f93a43 100644 --- a/arch/score/Kconfig +++ b/arch/score/Kconfig @@ -11,6 +11,8 @@ config SCORE select ARCH_DISCARD_MEMBLOCK select GENERIC_CPU_DEVICES select GENERIC_CLOCKEVENTS + select HAVE_MOD_ARCH_SPECIFIC + select MODULES_USE_ELF_REL choice prompt "System type" diff --git a/arch/score/include/asm/module.h b/arch/score/include/asm/module.h index f0b5dc0..abf395b 100644 --- a/arch/score/include/asm/module.h +++ b/arch/score/include/asm/module.h @@ -3,6 +3,7 @@ #include <linux/list.h> #include <asm/uaccess.h> +#include <asm-generic/module.h> struct mod_arch_specific { /* Data Bus Error exception tables */ @@ -13,11 +14,6 @@ struct mod_arch_specific { typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */ -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr -#define Elf_Addr Elf32_Addr - /* Given an address, look for it in the exception tables. */ #ifdef CONFIG_MODULES const struct exception_table_entry *search_module_dbetables(unsigned long addr); diff --git a/arch/score/kernel/module.c b/arch/score/kernel/module.c index 469e3b6..1378d99b 100644 --- a/arch/score/kernel/module.c +++ b/arch/score/kernel/module.c @@ -125,16 +125,6 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, return 0; } -int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, - unsigned int symindex, unsigned int relsec, - struct module *me) -{ - /* Non-standard return value... most other arch's return -ENOEXEC - * for an unsupported relocation variant - */ - return 0; -} - /* Given an address, look for it in the module exception tables. */ const struct exception_table_entry *search_module_dbetables(unsigned long addr) { diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 3b3e27a..babc2b8 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -38,6 +38,8 @@ config SUPERH select GENERIC_CMOS_UPDATE if SH_SH03 || SH_DREAMCAST select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER + select HAVE_MOD_ARCH_SPECIFIC if DWARF_UNWINDER + select MODULES_USE_ELF_RELA help The SuperH is a RISC processor targeted for use in embedded systems and consumer electronics; it was also used in the Sega Dreamcast diff --git a/arch/sh/include/asm/module.h b/arch/sh/include/asm/module.h index b7927de..81300d8b 100644 --- a/arch/sh/include/asm/module.h +++ b/arch/sh/include/asm/module.h @@ -1,21 +1,13 @@ #ifndef _ASM_SH_MODULE_H #define _ASM_SH_MODULE_H -struct mod_arch_specific { +#include <asm-generic/module.h> + #ifdef CONFIG_DWARF_UNWINDER +struct mod_arch_specific { struct list_head fde_list; struct list_head cie_list; -#endif }; - -#ifdef CONFIG_64BIT -#define Elf_Shdr Elf64_Shdr -#define Elf_Sym Elf64_Sym -#define Elf_Ehdr Elf64_Ehdr -#else -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr #endif #ifdef CONFIG_CPU_LITTLE_ENDIAN diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 91c780c..b6b442b 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -39,6 +39,7 @@ config SPARC select GENERIC_CLOCKEVENTS select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER + select MODULES_USE_ELF_RELA config SPARC32 def_bool !64BIT diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild index 10d54e5..645a58d 100644 --- a/arch/sparc/include/asm/Kbuild +++ b/arch/sparc/include/asm/Kbuild @@ -7,4 +7,5 @@ generic-y += exec.h generic-y += local64.h generic-y += irq_regs.h generic-y += local.h +generic-y += module.h generic-y += word-at-a-time.h diff --git a/arch/sparc/include/asm/module.h b/arch/sparc/include/asm/module.h deleted file mode 100644 index ff8e02d..0000000 --- a/arch/sparc/include/asm/module.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef __SPARC_MODULE_H -#define __SPARC_MODULE_H -struct mod_arch_specific { }; - -/* - * Use some preprocessor magic to define the correct symbol - * for sparc32 and sparc64. - * Elf_Addr becomes Elf32_Addr for sparc32 and Elf64_Addr for sparc64 - */ -#define ___ELF(a, b, c) a##b##c -#define __ELF(a, b, c) ___ELF(a, b, c) -#define _Elf(t) __ELF(Elf, CONFIG_BITS, t) -#define _ELF(t) __ELF(ELF, CONFIG_BITS, t) - -#define Elf_Shdr _Elf(_Shdr) -#define Elf_Sym _Elf(_Sym) -#define Elf_Ehdr _Elf(_Ehdr) -#define Elf_Rela _Elf(_Rela) -#define Elf_Addr _Elf(_Addr) - -#define ELF_R_SYM _ELF(_R_SYM) -#define ELF_R_TYPE _ELF(_R_TYPE) - -#endif /* __SPARC_MODULE_H */ diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index dc46490..875d008 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -20,6 +20,7 @@ config TILE select SYS_HYPERVISOR select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_CLOCKEVENTS + select MODULES_USE_ELF_RELA # FIXME: investigate whether we need/want these options. # select HAVE_IOREMAP_PROT diff --git a/arch/unicore32/Kconfig b/arch/unicore32/Kconfig index 35ee2bf..e5c5473 100644 --- a/arch/unicore32/Kconfig +++ b/arch/unicore32/Kconfig @@ -15,6 +15,7 @@ config UNICORE32 select GENERIC_IRQ_SHOW select ARCH_WANT_FRAME_POINTERS select GENERIC_IOMAP + select MODULES_USE_ELF_REL help UniCore-32 is 32-bit Instruction Set Architecture, including a series of low-power-consumption RISC chip diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 70071b1..46c3bff 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -110,6 +110,8 @@ config X86 select HAVE_IRQ_TIME_ACCOUNTING select GENERIC_KERNEL_THREAD select GENERIC_KERNEL_EXECVE + select MODULES_USE_ELF_REL if X86_32 + select MODULES_USE_ELF_RELA if X86_64 config INSTRUCTION_DECODER def_bool y diff --git a/arch/x86/um/Kconfig b/arch/x86/um/Kconfig index 9fa950d..0761175 100644 --- a/arch/x86/um/Kconfig +++ b/arch/x86/um/Kconfig @@ -24,9 +24,11 @@ config X86_32 def_bool !64BIT select HAVE_AOUT select ARCH_WANT_IPC_PARSE_VERSION + select MODULES_USE_ELF_REL config X86_64 def_bool 64BIT + select MODULES_USE_ELF_RELA config RWSEM_XCHGADD_ALGORITHM def_bool X86_XADD && 64BIT diff --git a/arch/xtensa/include/asm/module.h b/arch/xtensa/include/asm/module.h index d9b34be..488b40c 100644 --- a/arch/xtensa/include/asm/module.h +++ b/arch/xtensa/include/asm/module.h @@ -13,15 +13,8 @@ #ifndef _XTENSA_MODULE_H #define _XTENSA_MODULE_H -struct mod_arch_specific -{ - /* No special elements, yet. */ -}; - #define MODULE_ARCH_VERMAGIC "xtensa-" __stringify(XCHAL_CORE_ID) " " -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr +#include <asm-generic/module.h> #endif /* _XTENSA_MODULE_H */ diff --git a/crypto/Kconfig b/crypto/Kconfig index 50402dc..6563366 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1216,5 +1216,6 @@ config CRYPTO_USER_API_SKCIPHER key cipher algorithms. source "drivers/crypto/Kconfig" +source crypto/asymmetric_keys/Kconfig endif # if CRYPTO diff --git a/crypto/Makefile b/crypto/Makefile index a301ad2..8cf61ff 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -97,3 +97,4 @@ obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o # obj-$(CONFIG_XOR_BLOCKS) += xor.o obj-$(CONFIG_ASYNC_CORE) += async_tx/ +obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/ diff --git a/crypto/asymmetric_keys/.gitignore b/crypto/asymmetric_keys/.gitignore new file mode 100644 index 0000000..ee328374 --- /dev/null +++ b/crypto/asymmetric_keys/.gitignore @@ -0,0 +1 @@ +*-asn1.[ch] diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig new file mode 100644 index 0000000..6d2c2ea --- /dev/null +++ b/crypto/asymmetric_keys/Kconfig @@ -0,0 +1,38 @@ +menuconfig ASYMMETRIC_KEY_TYPE + tristate "Asymmetric (public-key cryptographic) key type" + depends on KEYS + help + This option provides support for a key type that holds the data for + the asymmetric keys used for public key cryptographic operations such + as encryption, decryption, signature generation and signature + verification. + +if ASYMMETRIC_KEY_TYPE + +config ASYMMETRIC_PUBLIC_KEY_SUBTYPE + tristate "Asymmetric public-key crypto algorithm subtype" + select MPILIB + help + This option provides support for asymmetric public key type handling. + If signature generation and/or verification are to be used, + appropriate hash algorithms (such as SHA-1) must be available. + ENOPKG will be reported if the requisite algorithm is unavailable. + +config PUBLIC_KEY_ALGO_RSA + tristate "RSA public-key algorithm" + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select MPILIB_EXTRA + help + This option enables support for the RSA algorithm (PKCS#1, RFC3447). + +config X509_CERTIFICATE_PARSER + tristate "X.509 certificate parser" + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select ASN1 + select OID_REGISTRY + help + This option procides support for parsing X.509 format blobs for key + data and provides the ability to instantiate a crypto key from a + public key packet found inside the certificate. + +endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile new file mode 100644 index 0000000..0727204 --- /dev/null +++ b/crypto/asymmetric_keys/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for asymmetric cryptographic keys +# + +obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o + +asymmetric_keys-y := asymmetric_type.o signature.o + +obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o +obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o + +# +# X.509 Certificate handling +# +obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o +x509_key_parser-y := \ + x509-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-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-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_rsakey-asn1.c x509_rsakey-asn1.h diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h new file mode 100644 index 0000000..515b634 --- /dev/null +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -0,0 +1,15 @@ +/* Internal definitions for asymmetric key type + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +static inline const char *asymmetric_key_id(const struct key *key) +{ + return key->type_data.p[1]; +} diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c new file mode 100644 index 0000000..cf80765 --- /dev/null +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -0,0 +1,274 @@ +/* Asymmetric public-key cryptography key type + * + * See Documentation/security/asymmetric-keys.txt + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <keys/asymmetric-subtype.h> +#include <keys/asymmetric-parser.h> +#include <linux/seq_file.h> +#include <linux/module.h> +#include <linux/slab.h> +#include "asymmetric_keys.h" + +MODULE_LICENSE("GPL"); + +static LIST_HEAD(asymmetric_key_parsers); +static DECLARE_RWSEM(asymmetric_key_parsers_sem); + +/* + * Match asymmetric keys on (part of) their name + * We have some shorthand methods for matching keys. We allow: + * + * "<desc>" - request a key by description + * "id:<id>" - request a key matching the ID + * "<subtype>:<id>" - request a key of a subtype + */ +static int asymmetric_key_match(const struct key *key, const void *description) +{ + const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); + const char *spec = description; + const char *id, *kid; + ptrdiff_t speclen; + size_t idlen, kidlen; + + if (!subtype || !spec || !*spec) + return 0; + + /* See if the full key description matches as is */ + if (key->description && strcmp(key->description, description) == 0) + return 1; + + /* All tests from here on break the criterion description into a + * specifier, a colon and then an identifier. + */ + id = strchr(spec, ':'); + if (!id) + return 0; + + speclen = id - spec; + id++; + + /* Anything after here requires a partial match on the ID string */ + kid = asymmetric_key_id(key); + if (!kid) + return 0; + + idlen = strlen(id); + kidlen = strlen(kid); + if (idlen > kidlen) + return 0; + + kid += kidlen - idlen; + if (strcasecmp(id, kid) != 0) + return 0; + + if (speclen == 2 && + memcmp(spec, "id", 2) == 0) + return 1; + + if (speclen == subtype->name_len && + memcmp(spec, subtype->name, speclen) == 0) + return 1; + + return 0; +} + +/* + * Describe the asymmetric key + */ +static void asymmetric_key_describe(const struct key *key, struct seq_file *m) +{ + const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); + const char *kid = asymmetric_key_id(key); + size_t n; + + seq_puts(m, key->description); + + if (subtype) { + seq_puts(m, ": "); + subtype->describe(key, m); + + if (kid) { + seq_putc(m, ' '); + n = strlen(kid); + if (n <= 8) + seq_puts(m, kid); + else + seq_puts(m, kid + n - 8); + } + + seq_puts(m, " ["); + /* put something here to indicate the key's capabilities */ + seq_putc(m, ']'); + } +} + +/* + * Preparse a asymmetric payload to get format the contents appropriately for the + * internal payload to cut down on the number of scans of the data performed. + * + * We also generate a proposed description from the contents of the key that + * can be used to name the key if the user doesn't want to provide one. + */ +static int asymmetric_key_preparse(struct key_preparsed_payload *prep) +{ + struct asymmetric_key_parser *parser; + int ret; + + pr_devel("==>%s()\n", __func__); + + if (prep->datalen == 0) + return -EINVAL; + + down_read(&asymmetric_key_parsers_sem); + + ret = -EBADMSG; + list_for_each_entry(parser, &asymmetric_key_parsers, link) { + pr_debug("Trying parser '%s'\n", parser->name); + + ret = parser->parse(prep); + if (ret != -EBADMSG) { + pr_debug("Parser recognised the format (ret %d)\n", + ret); + break; + } + } + + up_read(&asymmetric_key_parsers_sem); + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} + +/* + * Clean up the preparse data + */ +static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) +{ + struct asymmetric_key_subtype *subtype = prep->type_data[0]; + + pr_devel("==>%s()\n", __func__); + + if (subtype) { + subtype->destroy(prep->payload); + module_put(subtype->owner); + } + kfree(prep->type_data[1]); + kfree(prep->description); +} + +/* + * Instantiate a asymmetric_key defined key. The key was preparsed, so we just + * have to transfer the data here. + */ +static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + int ret; + + pr_devel("==>%s()\n", __func__); + + ret = key_payload_reserve(key, prep->quotalen); + if (ret == 0) { + key->type_data.p[0] = prep->type_data[0]; + key->type_data.p[1] = prep->type_data[1]; + key->payload.data = prep->payload; + prep->type_data[0] = NULL; + prep->type_data[1] = NULL; + prep->payload = NULL; + } + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} + +/* + * dispose of the data dangling from the corpse of a asymmetric key + */ +static void asymmetric_key_destroy(struct key *key) +{ + struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); + if (subtype) { + subtype->destroy(key->payload.data); + module_put(subtype->owner); + key->type_data.p[0] = NULL; + } + kfree(key->type_data.p[1]); + key->type_data.p[1] = NULL; +} + +struct key_type key_type_asymmetric = { + .name = "asymmetric", + .preparse = asymmetric_key_preparse, + .free_preparse = asymmetric_key_free_preparse, + .instantiate = asymmetric_key_instantiate, + .match = asymmetric_key_match, + .destroy = asymmetric_key_destroy, + .describe = asymmetric_key_describe, +}; +EXPORT_SYMBOL_GPL(key_type_asymmetric); + +/** + * register_asymmetric_key_parser - Register a asymmetric key blob parser + * @parser: The parser to register + */ +int register_asymmetric_key_parser(struct asymmetric_key_parser *parser) +{ + struct asymmetric_key_parser *cursor; + int ret; + + down_write(&asymmetric_key_parsers_sem); + + list_for_each_entry(cursor, &asymmetric_key_parsers, link) { + if (strcmp(cursor->name, parser->name) == 0) { + pr_err("Asymmetric key parser '%s' already registered\n", + parser->name); + ret = -EEXIST; + goto out; + } + } + + list_add_tail(&parser->link, &asymmetric_key_parsers); + + pr_notice("Asymmetric key parser '%s' registered\n", parser->name); + ret = 0; + +out: + up_write(&asymmetric_key_parsers_sem); + return ret; +} +EXPORT_SYMBOL_GPL(register_asymmetric_key_parser); + +/** + * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser + * @parser: The parser to unregister + */ +void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser) +{ + down_write(&asymmetric_key_parsers_sem); + list_del(&parser->link); + up_write(&asymmetric_key_parsers_sem); + + pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name); +} +EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser); + +/* + * Module stuff + */ +static int __init asymmetric_key_init(void) +{ + return register_key_type(&key_type_asymmetric); +} + +static void __exit asymmetric_key_cleanup(void) +{ + unregister_key_type(&key_type_asymmetric); +} + +module_init(asymmetric_key_init); +module_exit(asymmetric_key_cleanup); diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c new file mode 100644 index 0000000..cb2e291 --- /dev/null +++ b/crypto/asymmetric_keys/public_key.c @@ -0,0 +1,108 @@ +/* In-software asymmetric public-key crypto subtype + * + * See Documentation/crypto/asymmetric-keys.txt + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PKEY: "fmt +#include <linux/module.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/seq_file.h> +#include <keys/asymmetric-subtype.h> +#include "public_key.h" + +MODULE_LICENSE("GPL"); + +const char *const pkey_algo[PKEY_ALGO__LAST] = { + [PKEY_ALGO_DSA] = "DSA", + [PKEY_ALGO_RSA] = "RSA", +}; +EXPORT_SYMBOL_GPL(pkey_algo); + +const char *const pkey_hash_algo[PKEY_HASH__LAST] = { + [PKEY_HASH_MD4] = "md4", + [PKEY_HASH_MD5] = "md5", + [PKEY_HASH_SHA1] = "sha1", + [PKEY_HASH_RIPE_MD_160] = "rmd160", + [PKEY_HASH_SHA256] = "sha256", + [PKEY_HASH_SHA384] = "sha384", + [PKEY_HASH_SHA512] = "sha512", + [PKEY_HASH_SHA224] = "sha224", +}; +EXPORT_SYMBOL_GPL(pkey_hash_algo); + +const char *const pkey_id_type[PKEY_ID_TYPE__LAST] = { + [PKEY_ID_PGP] = "PGP", + [PKEY_ID_X509] = "X509", +}; +EXPORT_SYMBOL_GPL(pkey_id_type); + +/* + * Provide a part of a description of the key for /proc/keys. + */ +static void public_key_describe(const struct key *asymmetric_key, + struct seq_file *m) +{ + struct public_key *key = asymmetric_key->payload.data; + + if (key) + seq_printf(m, "%s.%s", + pkey_id_type[key->id_type], key->algo->name); +} + +/* + * Destroy a public key algorithm key. + */ +void public_key_destroy(void *payload) +{ + struct public_key *key = payload; + int i; + + if (key) { + for (i = 0; i < ARRAY_SIZE(key->mpi); i++) + mpi_free(key->mpi[i]); + kfree(key); + } +} +EXPORT_SYMBOL_GPL(public_key_destroy); + +/* + * Verify a signature using a public key. + */ +static int public_key_verify_signature(const struct key *key, + const struct public_key_signature *sig) +{ + const struct public_key *pk = key->payload.data; + + if (!pk->algo->verify_signature) + return -ENOTSUPP; + + if (sig->nr_mpi != pk->algo->n_sig_mpi) { + pr_debug("Signature has %u MPI not %u\n", + sig->nr_mpi, pk->algo->n_sig_mpi); + return -EINVAL; + } + + return pk->algo->verify_signature(pk, sig); +} + +/* + * Public key algorithm asymmetric key subtype + */ +struct asymmetric_key_subtype public_key_subtype = { + .owner = THIS_MODULE, + .name = "public_key", + .describe = public_key_describe, + .destroy = public_key_destroy, + .verify_signature = public_key_verify_signature, +}; +EXPORT_SYMBOL_GPL(public_key_subtype); diff --git a/crypto/asymmetric_keys/public_key.h b/crypto/asymmetric_keys/public_key.h new file mode 100644 index 0000000..5e5e356 --- /dev/null +++ b/crypto/asymmetric_keys/public_key.h @@ -0,0 +1,30 @@ +/* Public key algorithm internals + * + * See Documentation/crypto/asymmetric-keys.txt + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <crypto/public_key.h> + +extern struct asymmetric_key_subtype public_key_subtype; + +/* + * Public key algorithm definition. + */ +struct public_key_algorithm { + const char *name; + u8 n_pub_mpi; /* Number of MPIs in public key */ + u8 n_sec_mpi; /* Number of MPIs in secret key */ + u8 n_sig_mpi; /* Number of MPIs in a signature */ + int (*verify_signature)(const struct public_key *key, + const struct public_key_signature *sig); +}; + +extern const struct public_key_algorithm RSA_public_key_algorithm; diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c new file mode 100644 index 0000000..4a6a069 --- /dev/null +++ b/crypto/asymmetric_keys/rsa.c @@ -0,0 +1,277 @@ +/* RSA asymmetric public-key algorithm [RFC3447] + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "RSA: "fmt +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include "public_key.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("RSA Public Key Algorithm"); + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +/* + * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2]. + */ +static const u8 RSA_digest_info_MD5[] = { + 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* OID */ + 0x05, 0x00, 0x04, 0x10 +}; + +static const u8 RSA_digest_info_SHA1[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2B, 0x0E, 0x03, 0x02, 0x1A, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 RSA_digest_info_RIPE_MD_160[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2B, 0x24, 0x03, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 RSA_digest_info_SHA224[] = { + 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, + 0x05, 0x00, 0x04, 0x1C +}; + +static const u8 RSA_digest_info_SHA256[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x20 +}; + +static const u8 RSA_digest_info_SHA384[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, + 0x05, 0x00, 0x04, 0x30 +}; + +static const u8 RSA_digest_info_SHA512[] = { + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x05, 0x00, 0x04, 0x40 +}; + +static const struct { + const u8 *data; + size_t size; +} RSA_ASN1_templates[PKEY_HASH__LAST] = { +#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) } + [PKEY_HASH_MD5] = _(MD5), + [PKEY_HASH_SHA1] = _(SHA1), + [PKEY_HASH_RIPE_MD_160] = _(RIPE_MD_160), + [PKEY_HASH_SHA256] = _(SHA256), + [PKEY_HASH_SHA384] = _(SHA384), + [PKEY_HASH_SHA512] = _(SHA512), + [PKEY_HASH_SHA224] = _(SHA224), +#undef _ +}; + +/* + * RSAVP1() function [RFC3447 sec 5.2.2] + */ +static int RSAVP1(const struct public_key *key, MPI s, MPI *_m) +{ + MPI m; + int ret; + + /* (1) Validate 0 <= s < n */ + if (mpi_cmp_ui(s, 0) < 0) { + kleave(" = -EBADMSG [s < 0]"); + return -EBADMSG; + } + if (mpi_cmp(s, key->rsa.n) >= 0) { + kleave(" = -EBADMSG [s >= n]"); + return -EBADMSG; + } + + m = mpi_alloc(0); + if (!m) + return -ENOMEM; + + /* (2) m = s^e mod n */ + ret = mpi_powm(m, s, key->rsa.e, key->rsa.n); + if (ret < 0) { + mpi_free(m); + return ret; + } + + *_m = m; + return 0; +} + +/* + * Integer to Octet String conversion [RFC3447 sec 4.1] + */ +static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X) +{ + unsigned X_size, x_size; + int X_sign; + u8 *X; + + /* Make sure the string is the right length. The number should begin + * with { 0x00, 0x01, ... } so we have to account for 15 leading zero + * bits not being reported by MPI. + */ + x_size = mpi_get_nbits(x); + pr_devel("size(x)=%u xLen*8=%zu\n", x_size, xLen * 8); + if (x_size != xLen * 8 - 15) + return -ERANGE; + + X = mpi_get_buffer(x, &X_size, &X_sign); + if (!X) + return -ENOMEM; + if (X_sign < 0) { + kfree(X); + return -EBADMSG; + } + if (X_size != xLen - 1) { + kfree(X); + return -EBADMSG; + } + + *_X = X; + return 0; +} + +/* + * Perform the RSA signature verification. + * @H: Value of hash of data and metadata + * @EM: The computed signature value + * @k: The size of EM (EM[0] is an invalid location but should hold 0x00) + * @hash_size: The size of H + * @asn1_template: The DigestInfo ASN.1 template + * @asn1_size: Size of asm1_template[] + */ +static int RSA_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size, + const u8 *asn1_template, size_t asn1_size) +{ + unsigned PS_end, T_offset, i; + + kenter(",,%zu,%zu,%zu", k, hash_size, asn1_size); + + if (k < 2 + 1 + asn1_size + hash_size) + return -EBADMSG; + + /* Decode the EMSA-PKCS1-v1_5 */ + if (EM[1] != 0x01) { + kleave(" = -EBADMSG [EM[1] == %02u]", EM[1]); + return -EBADMSG; + } + + T_offset = k - (asn1_size + hash_size); + PS_end = T_offset - 1; + if (EM[PS_end] != 0x00) { + kleave(" = -EBADMSG [EM[T-1] == %02u]", EM[PS_end]); + return -EBADMSG; + } + + for (i = 2; i < PS_end; i++) { + if (EM[i] != 0xff) { + kleave(" = -EBADMSG [EM[PS%x] == %02u]", i - 2, EM[i]); + return -EBADMSG; + } + } + + if (memcmp(asn1_template, EM + T_offset, asn1_size) != 0) { + kleave(" = -EBADMSG [EM[T] ASN.1 mismatch]"); + return -EBADMSG; + } + + if (memcmp(H, EM + T_offset + asn1_size, hash_size) != 0) { + kleave(" = -EKEYREJECTED [EM[T] hash mismatch]"); + return -EKEYREJECTED; + } + + kleave(" = 0"); + return 0; +} + +/* + * Perform the verification step [RFC3447 sec 8.2.2]. + */ +static int RSA_verify_signature(const struct public_key *key, + const struct public_key_signature *sig) +{ + size_t tsize; + int ret; + + /* Variables as per RFC3447 sec 8.2.2 */ + const u8 *H = sig->digest; + u8 *EM = NULL; + MPI m = NULL; + size_t k; + + kenter(""); + + if (!RSA_ASN1_templates[sig->pkey_hash_algo].data) + return -ENOTSUPP; + + /* (1) Check the signature size against the public key modulus size */ + k = mpi_get_nbits(key->rsa.n); + tsize = mpi_get_nbits(sig->rsa.s); + + /* According to RFC 4880 sec 3.2, length of MPI is computed starting + * from most significant bit. So the RFC 3447 sec 8.2.2 size check + * must be relaxed to conform with shorter signatures - so we fail here + * only if signature length is longer than modulus size. + */ + pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize); + if (k < tsize) { + ret = -EBADMSG; + goto error; + } + + /* Round up and convert to octets */ + k = (k + 7) / 8; + + /* (2b) Apply the RSAVP1 verification primitive to the public key */ + ret = RSAVP1(key, sig->rsa.s, &m); + if (ret < 0) + goto error; + + /* (2c) Convert the message representative (m) to an encoded message + * (EM) of length k octets. + * + * NOTE! The leading zero byte is suppressed by MPI, so we pass a + * pointer to the _preceding_ byte to RSA_verify()! + */ + ret = RSA_I2OSP(m, k, &EM); + if (ret < 0) + goto error; + + ret = RSA_verify(H, EM - 1, k, sig->digest_size, + RSA_ASN1_templates[sig->pkey_hash_algo].data, + RSA_ASN1_templates[sig->pkey_hash_algo].size); + +error: + kfree(EM); + mpi_free(m); + kleave(" = %d", ret); + return ret; +} + +const struct public_key_algorithm RSA_public_key_algorithm = { + .name = "RSA", + .n_pub_mpi = 2, + .n_sec_mpi = 3, + .n_sig_mpi = 1, + .verify_signature = RSA_verify_signature, +}; +EXPORT_SYMBOL_GPL(RSA_public_key_algorithm); diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c new file mode 100644 index 0000000..50b3f88 --- /dev/null +++ b/crypto/asymmetric_keys/signature.c @@ -0,0 +1,49 @@ +/* Signature verification with an asymmetric key + * + * See Documentation/security/asymmetric-keys.txt + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <keys/asymmetric-subtype.h> +#include <linux/module.h> +#include <linux/err.h> +#include <crypto/public_key.h> +#include "asymmetric_keys.h" + +/** + * verify_signature - Initiate the use of an asymmetric key to verify a signature + * @key: The asymmetric key to verify against + * @sig: The signature to check + * + * Returns 0 if successful or else an error. + */ +int verify_signature(const struct key *key, + const struct public_key_signature *sig) +{ + const struct asymmetric_key_subtype *subtype; + int ret; + + pr_devel("==>%s()\n", __func__); + + if (key->type != &key_type_asymmetric) + return -EINVAL; + subtype = asymmetric_key_subtype(key); + if (!subtype || + !key->payload.data) + return -EINVAL; + if (!subtype->verify_signature) + return -ENOTSUPP; + + ret = subtype->verify_signature(key, sig); + + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL_GPL(verify_signature); diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1 new file mode 100644 index 0000000..bf32b3d --- /dev/null +++ b/crypto/asymmetric_keys/x509.asn1 @@ -0,0 +1,60 @@ +Certificate ::= SEQUENCE { + tbsCertificate TBSCertificate ({ x509_note_tbs_certificate }), + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING ({ x509_note_signature }) + } + +TBSCertificate ::= SEQUENCE { + version [ 0 ] Version DEFAULT, + serialNumber CertificateSerialNumber, + signature AlgorithmIdentifier ({ x509_note_pkey_algo }), + issuer Name ({ x509_note_issuer }), + validity Validity, + subject Name ({ x509_note_subject }), + subjectPublicKeyInfo SubjectPublicKeyInfo, + issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL, + subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL, + extensions [ 3 ] Extensions OPTIONAL + } + +Version ::= INTEGER +CertificateSerialNumber ::= INTEGER + +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER ({ x509_note_OID }), + parameters ANY OPTIONAL +} + +Name ::= SEQUENCE OF RelativeDistinguishedName + +RelativeDistinguishedName ::= SET OF AttributeValueAssertion + +AttributeValueAssertion ::= SEQUENCE { + attributeType OBJECT IDENTIFIER ({ x509_note_OID }), + attributeValue ANY ({ x509_extract_name_segment }) + } + +Validity ::= SEQUENCE { + notBefore Time ({ x509_note_not_before }), + notAfter Time ({ x509_note_not_after }) + } + +Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime + } + +SubjectPublicKeyInfo ::= SEQUENCE { + algorithm AlgorithmIdentifier, + subjectPublicKey BIT STRING ({ x509_extract_key_data }) + } + +UniqueIdentifier ::= BIT STRING + +Extensions ::= SEQUENCE OF Extension + +Extension ::= SEQUENCE { + extnid OBJECT IDENTIFIER ({ x509_note_OID }), + critical BOOLEAN DEFAULT, + extnValue OCTET STRING ({ x509_process_extension }) + } diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c new file mode 100644 index 0000000..7fabc4c --- /dev/null +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -0,0 +1,496 @@ +/* X.509 certificate parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "X.509: "fmt +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/oid_registry.h> +#include "public_key.h" +#include "x509_parser.h" +#include "x509-asn1.h" +#include "x509_rsakey-asn1.h" + +struct x509_parse_context { + struct x509_certificate *cert; /* Certificate being constructed */ + unsigned long data; /* Start of data */ + const void *cert_start; /* Start of cert content */ + const void *key; /* Key data */ + size_t key_size; /* Size of key data */ + enum OID last_oid; /* Last OID encountered */ + enum OID algo_oid; /* Algorithm OID */ + unsigned char nr_mpi; /* Number of MPIs stored */ + u8 o_size; /* Size of organizationName (O) */ + u8 cn_size; /* Size of commonName (CN) */ + u8 email_size; /* Size of emailAddress */ + u16 o_offset; /* Offset of organizationName (O) */ + u16 cn_offset; /* Offset of commonName (CN) */ + u16 email_offset; /* Offset of emailAddress */ +}; + +/* + * Free an X.509 certificate + */ +void x509_free_certificate(struct x509_certificate *cert) +{ + if (cert) { + public_key_destroy(cert->pub); + kfree(cert->issuer); + kfree(cert->subject); + kfree(cert->fingerprint); + kfree(cert->authority); + kfree(cert); + } +} + +/* + * Parse an X.509 certificate + */ +struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) +{ + struct x509_certificate *cert; + struct x509_parse_context *ctx; + long ret; + + ret = -ENOMEM; + cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL); + if (!cert) + goto error_no_cert; + cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); + if (!cert->pub) + goto error_no_ctx; + ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL); + if (!ctx) + goto error_no_ctx; + + ctx->cert = cert; + ctx->data = (unsigned long)data; + + /* Attempt to decode the certificate */ + ret = asn1_ber_decoder(&x509_decoder, ctx, data, datalen); + if (ret < 0) + goto error_decode; + + /* Decode the public key */ + ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx, + ctx->key, ctx->key_size); + if (ret < 0) + goto error_decode; + + kfree(ctx); + return cert; + +error_decode: + kfree(ctx); +error_no_ctx: + x509_free_certificate(cert); +error_no_cert: + return ERR_PTR(ret); +} + +/* + * Note an OID when we find one for later processing when we know how + * to interpret it. + */ +int x509_note_OID(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + + ctx->last_oid = look_up_OID(value, vlen); + if (ctx->last_oid == OID__NR) { + char buffer[50]; + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_debug("Unknown OID: [%lu] %s\n", + (unsigned long)value - ctx->data, buffer); + } + return 0; +} + +/* + * Save the position of the TBS data so that we can check the signature over it + * later. + */ +int x509_note_tbs_certificate(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + + pr_debug("x509_note_tbs_certificate(,%zu,%02x,%ld,%zu)!\n", + hdrlen, tag, (unsigned long)value - ctx->data, vlen); + + ctx->cert->tbs = value - hdrlen; + ctx->cert->tbs_size = vlen + hdrlen; + return 0; +} + +/* + * Record the public key algorithm + */ +int x509_note_pkey_algo(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + + pr_debug("PubKey Algo: %u\n", ctx->last_oid); + + switch (ctx->last_oid) { + case OID_md2WithRSAEncryption: + case OID_md3WithRSAEncryption: + default: + return -ENOPKG; /* Unsupported combination */ + + case OID_md4WithRSAEncryption: + ctx->cert->sig_hash_algo = PKEY_HASH_MD5; + ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; + break; + + case OID_sha1WithRSAEncryption: + ctx->cert->sig_hash_algo = PKEY_HASH_SHA1; + ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; + break; + + case OID_sha256WithRSAEncryption: + ctx->cert->sig_hash_algo = PKEY_HASH_SHA256; + ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; + break; + + case OID_sha384WithRSAEncryption: + ctx->cert->sig_hash_algo = PKEY_HASH_SHA384; + ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; + break; + + case OID_sha512WithRSAEncryption: + ctx->cert->sig_hash_algo = PKEY_HASH_SHA512; + ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; + break; + + case OID_sha224WithRSAEncryption: + ctx->cert->sig_hash_algo = PKEY_HASH_SHA224; + ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; + break; + } + + ctx->algo_oid = ctx->last_oid; + return 0; +} + +/* + * Note the whereabouts and type of the signature. + */ +int x509_note_signature(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + + pr_debug("Signature type: %u size %zu\n", ctx->last_oid, vlen); + + if (ctx->last_oid != ctx->algo_oid) { + pr_warn("Got cert with pkey (%u) and sig (%u) algorithm OIDs\n", + ctx->algo_oid, ctx->last_oid); + return -EINVAL; + } + + ctx->cert->sig = value; + ctx->cert->sig_size = vlen; + return 0; +} + +/* + * Note some of the name segments from which we'll fabricate a name. + */ +int x509_extract_name_segment(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + + switch (ctx->last_oid) { + case OID_commonName: + ctx->cn_size = vlen; + ctx->cn_offset = (unsigned long)value - ctx->data; + break; + case OID_organizationName: + ctx->o_size = vlen; + ctx->o_offset = (unsigned long)value - ctx->data; + break; + case OID_email_address: + ctx->email_size = vlen; + ctx->email_offset = (unsigned long)value - ctx->data; + break; + default: + break; + } + + return 0; +} + +/* + * Fabricate and save the issuer and subject names + */ +static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen, + unsigned char tag, + char **_name, size_t vlen) +{ + const void *name, *data = (const void *)ctx->data; + size_t namesize; + char *buffer; + + if (*_name) + return -EINVAL; + + /* Empty name string if no material */ + if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) { + buffer = kmalloc(1, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + buffer[0] = 0; + goto done; + } + + if (ctx->cn_size && ctx->o_size) { + /* Consider combining O and CN, but use only the CN if it is + * prefixed by the O, or a significant portion thereof. + */ + namesize = ctx->cn_size; + name = data + ctx->cn_offset; + if (ctx->cn_size >= ctx->o_size && + memcmp(data + ctx->cn_offset, data + ctx->o_offset, + ctx->o_size) == 0) + goto single_component; + if (ctx->cn_size >= 7 && + ctx->o_size >= 7 && + memcmp(data + ctx->cn_offset, data + ctx->o_offset, 7) == 0) + goto single_component; + + buffer = kmalloc(ctx->o_size + 2 + ctx->cn_size + 1, + GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + memcpy(buffer, + data + ctx->o_offset, ctx->o_size); + buffer[ctx->o_size + 0] = ':'; + buffer[ctx->o_size + 1] = ' '; + memcpy(buffer + ctx->o_size + 2, + data + ctx->cn_offset, ctx->cn_size); + buffer[ctx->o_size + 2 + ctx->cn_size] = 0; + goto done; + + } else if (ctx->cn_size) { + namesize = ctx->cn_size; + name = data + ctx->cn_offset; + } else if (ctx->o_size) { + namesize = ctx->o_size; + name = data + ctx->o_offset; + } else { + namesize = ctx->email_size; + name = data + ctx->email_offset; + } + +single_component: + buffer = kmalloc(namesize + 1, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + memcpy(buffer, name, namesize); + buffer[namesize] = 0; + +done: + *_name = buffer; + ctx->cn_size = 0; + ctx->o_size = 0; + ctx->email_size = 0; + return 0; +} + +int x509_note_issuer(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen); +} + +int x509_note_subject(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen); +} + +/* + * Extract the data for the public key algorithm + */ +int x509_extract_key_data(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + + if (ctx->last_oid != OID_rsaEncryption) + return -ENOPKG; + + /* There seems to be an extraneous 0 byte on the front of the data */ + ctx->cert->pkey_algo = PKEY_ALGO_RSA; + ctx->key = value + 1; + ctx->key_size = vlen - 1; + return 0; +} + +/* + * Extract a RSA public key value + */ +int rsa_extract_mpi(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + MPI mpi; + + if (ctx->nr_mpi >= ARRAY_SIZE(ctx->cert->pub->mpi)) { + pr_err("Too many public key MPIs in certificate\n"); + return -EBADMSG; + } + + mpi = mpi_read_raw_data(value, vlen); + if (!mpi) + return -ENOMEM; + + ctx->cert->pub->mpi[ctx->nr_mpi++] = mpi; + return 0; +} + +/* + * Process certificate extensions that are used to qualify the certificate. + */ +int x509_process_extension(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + const unsigned char *v = value; + char *f; + int i; + + pr_debug("Extension: %u\n", ctx->last_oid); + + if (ctx->last_oid == OID_subjectKeyIdentifier) { + /* Get hold of the key fingerprint */ + if (vlen < 3) + return -EBADMSG; + if (v[0] != ASN1_OTS || v[1] != vlen - 2) + return -EBADMSG; + v += 2; + vlen -= 2; + + f = kmalloc(vlen * 2 + 1, GFP_KERNEL); + if (!f) + return -ENOMEM; + for (i = 0; i < vlen; i++) + sprintf(f + i * 2, "%02x", v[i]); + pr_debug("fingerprint %s\n", f); + ctx->cert->fingerprint = f; + return 0; + } + + if (ctx->last_oid == OID_authorityKeyIdentifier) { + /* Get hold of the CA key fingerprint */ + if (vlen < 5) + return -EBADMSG; + if (v[0] != (ASN1_SEQ | (ASN1_CONS << 5)) || + v[1] != vlen - 2 || + v[2] != (ASN1_CONT << 6) || + v[3] != vlen - 4) + return -EBADMSG; + v += 4; + vlen -= 4; + + f = kmalloc(vlen * 2 + 1, GFP_KERNEL); + if (!f) + return -ENOMEM; + for (i = 0; i < vlen; i++) + sprintf(f + i * 2, "%02x", v[i]); + pr_debug("authority %s\n", f); + ctx->cert->authority = f; + return 0; + } + + return 0; +} + +/* + * Record a certificate time. + */ +static int x509_note_time(struct tm *tm, size_t hdrlen, + unsigned char tag, + const unsigned char *value, size_t vlen) +{ + const unsigned char *p = value; + +#define dec2bin(X) ((X) - '0') +#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; + else + tm->tm_year += 2000; + } else if (tag == ASN1_GENTIM) { + /* GenTime: YYYYMMDDHHMMSSZ */ + if (vlen != 15) + goto unsupported_time; + tm->tm_year = DD2bin(p) * 100 + DD2bin(p); + } 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); + + if (*p != 'Z') + goto unsupported_time; + + return 0; + +unsupported_time: + pr_debug("Got unsupported time [tag %02x]: '%*.*s'\n", + tag, (int)vlen, (int)vlen, value); + return -EBADMSG; +} + +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); +} + +int x509_note_not_after(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_to, hdrlen, tag, value, vlen); +} diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h new file mode 100644 index 0000000..f86dc5f --- /dev/null +++ b/crypto/asymmetric_keys/x509_parser.h @@ -0,0 +1,36 @@ +/* X.509 certificate parser internal definitions + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <crypto/public_key.h> + +struct x509_certificate { + struct x509_certificate *next; + struct public_key *pub; /* Public key details */ + char *issuer; /* Name of certificate issuer */ + char *subject; /* Name of certificate subject */ + char *fingerprint; /* Key fingerprint as hex */ + char *authority; /* Authority key fingerprint as hex */ + struct tm valid_from; + struct tm valid_to; + enum pkey_algo pkey_algo : 8; /* Public key algorithm */ + enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */ + enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */ + const void *tbs; /* Signed data */ + size_t tbs_size; /* Size of signed data */ + const void *sig; /* Signature data */ + size_t sig_size; /* Size of sigature */ +}; + +/* + * x509_cert_parser.c + */ +extern void x509_free_certificate(struct x509_certificate *cert); +extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c new file mode 100644 index 0000000..06007f0 --- /dev/null +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -0,0 +1,239 @@ +/* Instantiate a public key crypto key from an X.509 Certificate + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "X.509: "fmt +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/mpi.h> +#include <linux/asn1_decoder.h> +#include <keys/asymmetric-subtype.h> +#include <keys/asymmetric-parser.h> +#include <crypto/hash.h> +#include "asymmetric_keys.h" +#include "public_key.h" +#include "x509_parser.h" + +static const +struct public_key_algorithm *x509_public_key_algorithms[PKEY_ALGO__LAST] = { + [PKEY_ALGO_DSA] = NULL, +#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \ + defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE) + [PKEY_ALGO_RSA] = &RSA_public_key_algorithm, +#endif +}; + +/* + * Check the signature on a certificate using the provided public key + */ +static int x509_check_signature(const struct public_key *pub, + const struct x509_certificate *cert) +{ + struct public_key_signature *sig; + 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(pkey_hash_algo[cert->sig_hash_algo], 0, 0); + if (IS_ERR(tfm)) + return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + + /* We allocate the hash operational data storage on the end of our + * context data. + */ + ret = -ENOMEM; + sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL); + if (!sig) + goto error_no_sig; + + sig->pkey_hash_algo = cert->sig_hash_algo; + sig->digest = (u8 *)sig + sizeof(*sig) + desc_size; + sig->digest_size = digest_size; + + desc = (void *)sig + sizeof(*sig); + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + ret = -ENOMEM; + sig->rsa.s = mpi_read_raw_data(cert->sig, cert->sig_size); + if (!sig->rsa.s) + goto error; + + ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest); + if (ret < 0) + goto error_mpi; + + ret = pub->algo->verify_signature(pub, sig); + + pr_debug("Cert Verification: %d\n", ret); + +error_mpi: + mpi_free(sig->rsa.s); +error: + kfree(sig); +error_no_sig: + crypto_free_shash(tfm); + + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} + +/* + * Attempt to parse a data blob for a key as an X509 certificate. + */ +static int x509_key_preparse(struct key_preparsed_payload *prep) +{ + struct x509_certificate *cert; + struct tm now; + size_t srlen, sulen; + char *desc = NULL; + int ret; + + cert = x509_cert_parse(prep->data, prep->datalen); + if (IS_ERR(cert)) + return PTR_ERR(cert); + + pr_devel("Cert Issuer: %s\n", cert->issuer); + pr_devel("Cert Subject: %s\n", cert->subject); + pr_devel("Cert Key Algo: %s\n", pkey_algo[cert->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 Signature: %s + %s\n", + pkey_algo[cert->sig_pkey_algo], + pkey_hash_algo[cert->sig_hash_algo]); + + if (!cert->fingerprint || !cert->authority) { + pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n", + cert->subject); + ret = -EKEYREJECTED; + goto error_free_cert; + } + + time_to_tm(CURRENT_TIME.tv_sec, 0, &now); + pr_devel("Now: %04ld-%02d-%02d %02d:%02d:%02d\n", + now.tm_year + 1900, now.tm_mon + 1, now.tm_mday, + now.tm_hour, now.tm_min, now.tm_sec); + if (now.tm_year < cert->valid_from.tm_year || + (now.tm_year == cert->valid_from.tm_year && + (now.tm_mon < cert->valid_from.tm_mon || + (now.tm_mon == cert->valid_from.tm_mon && + (now.tm_mday < cert->valid_from.tm_mday || + (now.tm_mday == cert->valid_from.tm_mday && + (now.tm_hour < cert->valid_from.tm_hour || + (now.tm_hour == cert->valid_from.tm_hour && + (now.tm_min < cert->valid_from.tm_min || + (now.tm_min == cert->valid_from.tm_min && + (now.tm_sec < cert->valid_from.tm_sec + ))))))))))) { + pr_warn("Cert %s is not yet valid\n", cert->fingerprint); + ret = -EKEYREJECTED; + goto error_free_cert; + } + if (now.tm_year > cert->valid_to.tm_year || + (now.tm_year == cert->valid_to.tm_year && + (now.tm_mon > cert->valid_to.tm_mon || + (now.tm_mon == cert->valid_to.tm_mon && + (now.tm_mday > cert->valid_to.tm_mday || + (now.tm_mday == cert->valid_to.tm_mday && + (now.tm_hour > cert->valid_to.tm_hour || + (now.tm_hour == cert->valid_to.tm_hour && + (now.tm_min > cert->valid_to.tm_min || + (now.tm_min == cert->valid_to.tm_min && + (now.tm_sec > cert->valid_to.tm_sec + ))))))))))) { + pr_warn("Cert %s has expired\n", cert->fingerprint); + ret = -EKEYEXPIRED; + goto error_free_cert; + } + + cert->pub->algo = x509_public_key_algorithms[cert->pkey_algo]; + cert->pub->id_type = PKEY_ID_X509; + + /* Check the signature on the key */ + if (strcmp(cert->fingerprint, cert->authority) == 0) { + ret = x509_check_signature(cert->pub, cert); + if (ret < 0) + goto error_free_cert; + } + + /* Propose a description */ + sulen = strlen(cert->subject); + srlen = strlen(cert->fingerprint); + ret = -ENOMEM; + desc = kmalloc(sulen + 2 + srlen + 1, GFP_KERNEL); + if (!desc) + goto error_free_cert; + memcpy(desc, cert->subject, sulen); + desc[sulen] = ':'; + desc[sulen + 1] = ' '; + memcpy(desc + sulen + 2, cert->fingerprint, srlen); + desc[sulen + 2 + srlen] = 0; + + /* We're pinning the module by being linked against it */ + __module_get(public_key_subtype.owner); + prep->type_data[0] = &public_key_subtype; + prep->type_data[1] = cert->fingerprint; + prep->payload = cert->pub; + prep->description = desc; + prep->quotalen = 100; + + /* We've finished with the certificate */ + cert->pub = NULL; + cert->fingerprint = NULL; + desc = NULL; + ret = 0; + +error_free_cert: + x509_free_certificate(cert); + return ret; +} + +static struct asymmetric_key_parser x509_key_parser = { + .owner = THIS_MODULE, + .name = "x509", + .parse = x509_key_preparse, +}; + +/* + * Module stuff + */ +static int __init x509_key_init(void) +{ + return register_asymmetric_key_parser(&x509_key_parser); +} + +static void __exit x509_key_exit(void) +{ + unregister_asymmetric_key_parser(&x509_key_parser); +} + +module_init(x509_key_init); +module_exit(x509_key_exit); diff --git a/crypto/asymmetric_keys/x509_rsakey.asn1 b/crypto/asymmetric_keys/x509_rsakey.asn1 new file mode 100644 index 0000000..4ec7cc6 --- /dev/null +++ b/crypto/asymmetric_keys/x509_rsakey.asn1 @@ -0,0 +1,4 @@ +RSAPublicKey ::= SEQUENCE { + modulus INTEGER ({ rsa_extract_mpi }), -- n + publicExponent INTEGER ({ rsa_extract_mpi }) -- e + } diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index e622863..086f381 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -31,18 +31,18 @@ /* create a new cifs key */ static int -cifs_spnego_key_instantiate(struct key *key, const void *data, size_t datalen) +cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep) { char *payload; int ret; ret = -ENOMEM; - payload = kmalloc(datalen, GFP_KERNEL); + payload = kmalloc(prep->datalen, GFP_KERNEL); if (!payload) goto error; /* attach the data */ - memcpy(payload, data, datalen); + memcpy(payload, prep->data, prep->datalen); key->payload.data = payload; ret = 0; diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 2ee5c54..fc783e2 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -167,17 +167,17 @@ static struct shrinker cifs_shrinker = { }; static int -cifs_idmap_key_instantiate(struct key *key, const void *data, size_t datalen) +cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep) { char *payload; - payload = kmalloc(datalen, GFP_KERNEL); + payload = kmalloc(prep->datalen, GFP_KERNEL); if (!payload) return -ENOMEM; - memcpy(payload, data, datalen); + memcpy(payload, prep->data, prep->datalen); key->payload.data = payload; - key->datalen = datalen; + key->datalen = prep->datalen; return 0; } diff --git a/include/asm-generic/bitops/count_zeros.h b/include/asm-generic/bitops/count_zeros.h new file mode 100644 index 0000000..97520d2 --- /dev/null +++ b/include/asm-generic/bitops/count_zeros.h @@ -0,0 +1,57 @@ +/* Count leading and trailing zeros functions + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_ +#define _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_ + +#include <asm/bitops.h> + +/** + * count_leading_zeros - Count the number of zeros from the MSB back + * @x: The value + * + * Count the number of leading zeros from the MSB going towards the LSB in @x. + * + * If the MSB of @x is set, the result is 0. + * If only the LSB of @x is set, then the result is BITS_PER_LONG-1. + * If @x is 0 then the result is COUNT_LEADING_ZEROS_0. + */ +static inline int count_leading_zeros(unsigned long x) +{ + if (sizeof(x) == 4) + return BITS_PER_LONG - fls(x); + else + return BITS_PER_LONG - fls64(x); +} + +#define COUNT_LEADING_ZEROS_0 BITS_PER_LONG + +/** + * count_trailing_zeros - Count the number of zeros from the LSB forwards + * @x: The value + * + * Count the number of trailing zeros from the LSB going towards the MSB in @x. + * + * If the LSB of @x is set, the result is 0. + * If only the MSB of @x is set, then the result is BITS_PER_LONG-1. + * If @x is 0 then the result is COUNT_TRAILING_ZEROS_0. + */ +static inline int count_trailing_zeros(unsigned long x) +{ +#define COUNT_TRAILING_ZEROS_0 (-1) + + if (sizeof(x) == 4) + return ffs(x); + else + return (x != 0) ? __ffs(x) : COUNT_TRAILING_ZEROS_0; +} + +#endif /* _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_ */ diff --git a/include/asm-generic/module.h b/include/asm-generic/module.h index ed5b44d..14dc41d 100644 --- a/include/asm-generic/module.h +++ b/include/asm-generic/module.h @@ -5,18 +5,44 @@ * Many architectures just need a simple module * loader without arch specific data. */ +#ifndef CONFIG_HAVE_MOD_ARCH_SPECIFIC struct mod_arch_specific { }; +#endif #ifdef CONFIG_64BIT -#define Elf_Shdr Elf64_Shdr -#define Elf_Sym Elf64_Sym -#define Elf_Ehdr Elf64_Ehdr -#else -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr +#define Elf_Shdr Elf64_Shdr +#define Elf_Phdr Elf64_Phdr +#define Elf_Sym Elf64_Sym +#define Elf_Dyn Elf64_Dyn +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Addr Elf64_Addr +#ifdef CONFIG_MODULES_USE_ELF_REL +#define Elf_Rel Elf64_Rel +#endif +#ifdef CONFIG_MODULES_USE_ELF_RELA +#define Elf_Rela Elf64_Rela +#endif +#define ELF_R_TYPE(X) ELF64_R_TYPE(X) +#define ELF_R_SYM(X) ELF64_R_SYM(X) + +#else /* CONFIG_64BIT */ + +#define Elf_Shdr Elf32_Shdr +#define Elf_Phdr Elf32_Phdr +#define Elf_Sym Elf32_Sym +#define Elf_Dyn Elf32_Dyn +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Addr Elf32_Addr +#ifdef CONFIG_MODULES_USE_ELF_REL +#define Elf_Rel Elf32_Rel +#endif +#ifdef CONFIG_MODULES_USE_ELF_RELA +#define Elf_Rela Elf32_Rela +#endif +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELF_R_SYM(X) ELF32_R_SYM(X) #endif #endif /* __ASM_GENERIC_MODULE_H */ diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h new file mode 100644 index 0000000..f5b0224 --- /dev/null +++ b/include/crypto/public_key.h @@ -0,0 +1,108 @@ +/* Asymmetric public-key algorithm definitions + * + * See Documentation/crypto/asymmetric-keys.txt + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_PUBLIC_KEY_H +#define _LINUX_PUBLIC_KEY_H + +#include <linux/mpi.h> + +enum pkey_algo { + PKEY_ALGO_DSA, + PKEY_ALGO_RSA, + PKEY_ALGO__LAST +}; + +extern const char *const pkey_algo[PKEY_ALGO__LAST]; + +enum pkey_hash_algo { + PKEY_HASH_MD4, + PKEY_HASH_MD5, + PKEY_HASH_SHA1, + PKEY_HASH_RIPE_MD_160, + PKEY_HASH_SHA256, + PKEY_HASH_SHA384, + PKEY_HASH_SHA512, + PKEY_HASH_SHA224, + PKEY_HASH__LAST +}; + +extern const char *const pkey_hash_algo[PKEY_HASH__LAST]; + +enum pkey_id_type { + PKEY_ID_PGP, /* OpenPGP generated key ID */ + PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */ + PKEY_ID_TYPE__LAST +}; + +extern const char *const pkey_id_type[PKEY_ID_TYPE__LAST]; + +/* + * Cryptographic data for the public-key subtype of the asymmetric key type. + * + * Note that this may include private part of the key as well as the public + * part. + */ +struct public_key { + const struct public_key_algorithm *algo; + u8 capabilities; +#define PKEY_CAN_ENCRYPT 0x01 +#define PKEY_CAN_DECRYPT 0x02 +#define PKEY_CAN_SIGN 0x04 +#define PKEY_CAN_VERIFY 0x08 + enum pkey_id_type id_type : 8; + union { + MPI mpi[5]; + struct { + MPI p; /* DSA prime */ + MPI q; /* DSA group order */ + MPI g; /* DSA group generator */ + MPI y; /* DSA public-key value = g^x mod p */ + MPI x; /* DSA secret exponent (if present) */ + } dsa; + struct { + MPI n; /* RSA public modulus */ + MPI e; /* RSA public encryption exponent */ + MPI d; /* RSA secret encryption exponent (if present) */ + MPI p; /* RSA secret prime (if present) */ + MPI q; /* RSA secret prime (if present) */ + } rsa; + }; +}; + +extern void public_key_destroy(void *payload); + +/* + * Public key cryptography signature data + */ +struct public_key_signature { + u8 *digest; + u8 digest_size; /* Number of bytes in digest */ + u8 nr_mpi; /* Occupancy of mpi[] */ + enum pkey_hash_algo pkey_hash_algo : 8; + union { + MPI mpi[2]; + struct { + MPI s; /* m^d mod n */ + } rsa; + struct { + MPI r; + MPI s; + } dsa; + }; +}; + +struct key; +extern int verify_signature(const struct key *key, + const struct public_key_signature *sig); + +#endif /* _LINUX_PUBLIC_KEY_H */ diff --git a/include/keys/asymmetric-parser.h b/include/keys/asymmetric-parser.h new file mode 100644 index 0000000..09b3b48 --- /dev/null +++ b/include/keys/asymmetric-parser.h @@ -0,0 +1,37 @@ +/* Asymmetric public-key cryptography data parser + * + * See Documentation/crypto/asymmetric-keys.txt + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _KEYS_ASYMMETRIC_PARSER_H +#define _KEYS_ASYMMETRIC_PARSER_H + +/* + * Key data parser. Called during key instantiation. + */ +struct asymmetric_key_parser { + struct list_head link; + struct module *owner; + const char *name; + + /* Attempt to parse a key from the data blob passed to add_key() or + * keyctl_instantiate(). Should also generate a proposed description + * that the caller can optionally use for the key. + * + * Return EBADMSG if not recognised. + */ + int (*parse)(struct key_preparsed_payload *prep); +}; + +extern int register_asymmetric_key_parser(struct asymmetric_key_parser *); +extern void unregister_asymmetric_key_parser(struct asymmetric_key_parser *); + +#endif /* _KEYS_ASYMMETRIC_PARSER_H */ diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h new file mode 100644 index 0000000..4b840e8 --- /dev/null +++ b/include/keys/asymmetric-subtype.h @@ -0,0 +1,55 @@ +/* Asymmetric public-key cryptography key subtype + * + * See Documentation/security/asymmetric-keys.txt + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _KEYS_ASYMMETRIC_SUBTYPE_H +#define _KEYS_ASYMMETRIC_SUBTYPE_H + +#include <linux/seq_file.h> +#include <keys/asymmetric-type.h> + +struct public_key_signature; + +/* + * Keys of this type declare a subtype that indicates the handlers and + * capabilities. + */ +struct asymmetric_key_subtype { + struct module *owner; + const char *name; + unsigned short name_len; /* length of name */ + + /* Describe a key of this subtype for /proc/keys */ + void (*describe)(const struct key *key, struct seq_file *m); + + /* Destroy a key of this subtype */ + void (*destroy)(void *payload); + + /* Verify the signature on a key of this subtype (optional) */ + int (*verify_signature)(const struct key *key, + const struct public_key_signature *sig); +}; + +/** + * asymmetric_key_subtype - Get the subtype from an asymmetric key + * @key: The key of interest. + * + * Retrieves and returns the subtype pointer of the asymmetric key from the + * type-specific data attached to the key. + */ +static inline +struct asymmetric_key_subtype *asymmetric_key_subtype(const struct key *key) +{ + return key->type_data.p[0]; +} + +#endif /* _KEYS_ASYMMETRIC_SUBTYPE_H */ diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h new file mode 100644 index 0000000..7dd4734 --- /dev/null +++ b/include/keys/asymmetric-type.h @@ -0,0 +1,25 @@ +/* Asymmetric Public-key cryptography key type interface + * + * See Documentation/security/asymmetric-keys.txt + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _KEYS_ASYMMETRIC_TYPE_H +#define _KEYS_ASYMMETRIC_TYPE_H + +#include <linux/key-type.h> + +extern struct key_type key_type_asymmetric; + +/* + * The payload is at the discretion of the subtype. + */ + +#endif /* _KEYS_ASYMMETRIC_TYPE_H */ diff --git a/include/keys/user-type.h b/include/keys/user-type.h index bc9ec1d..5e452c8 100644 --- a/include/keys/user-type.h +++ b/include/keys/user-type.h @@ -35,8 +35,10 @@ struct user_key_payload { extern struct key_type key_type_user; extern struct key_type key_type_logon; -extern int user_instantiate(struct key *key, const void *data, size_t datalen); -extern int user_update(struct key *key, const void *data, size_t datalen); +struct key_preparsed_payload; + +extern int user_instantiate(struct key *key, struct key_preparsed_payload *prep); +extern int user_update(struct key *key, struct key_preparsed_payload *prep); extern int user_match(const struct key *key, const void *criterion); extern void user_revoke(struct key *key); extern void user_destroy(struct key *key); diff --git a/include/linux/asn1.h b/include/linux/asn1.h new file mode 100644 index 0000000..5c3f4e4 --- /dev/null +++ b/include/linux/asn1.h @@ -0,0 +1,67 @@ +/* ASN.1 BER/DER/CER encoding definitions + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_ASN1_H +#define _LINUX_ASN1_H + +/* Class */ +enum asn1_class { + ASN1_UNIV = 0, /* Universal */ + ASN1_APPL = 1, /* Application */ + ASN1_CONT = 2, /* Context */ + ASN1_PRIV = 3 /* Private */ +}; +#define ASN1_CLASS_BITS 0xc0 + + +enum asn1_method { + ASN1_PRIM = 0, /* Primitive */ + ASN1_CONS = 1 /* Constructed */ +}; +#define ASN1_CONS_BIT 0x20 + +/* Tag */ +enum asn1_tag { + ASN1_EOC = 0, /* End Of Contents or N/A */ + ASN1_BOOL = 1, /* Boolean */ + ASN1_INT = 2, /* Integer */ + ASN1_BTS = 3, /* Bit String */ + ASN1_OTS = 4, /* Octet String */ + ASN1_NULL = 5, /* Null */ + ASN1_OID = 6, /* Object Identifier */ + ASN1_ODE = 7, /* Object Description */ + ASN1_EXT = 8, /* External */ + ASN1_REAL = 9, /* Real float */ + ASN1_ENUM = 10, /* Enumerated */ + ASN1_EPDV = 11, /* Embedded PDV */ + ASN1_UTF8STR = 12, /* UTF8 String */ + ASN1_RELOID = 13, /* Relative OID */ + /* 14 - Reserved */ + /* 15 - Reserved */ + ASN1_SEQ = 16, /* Sequence and Sequence of */ + ASN1_SET = 17, /* Set and Set of */ + ASN1_NUMSTR = 18, /* Numerical String */ + ASN1_PRNSTR = 19, /* Printable String */ + ASN1_TEXSTR = 20, /* T61 String / Teletext String */ + ASN1_VIDSTR = 21, /* Videotex String */ + ASN1_IA5STR = 22, /* IA5 String */ + ASN1_UNITIM = 23, /* Universal Time */ + ASN1_GENTIM = 24, /* General Time */ + ASN1_GRASTR = 25, /* Graphic String */ + ASN1_VISSTR = 26, /* Visible String */ + ASN1_GENSTR = 27, /* General String */ + ASN1_UNISTR = 28, /* Universal String */ + ASN1_CHRSTR = 29, /* Character String */ + ASN1_BMPSTR = 30, /* BMP String */ + ASN1_LONG_TAG = 31 /* Long form tag */ +}; + +#endif /* _LINUX_ASN1_H */ diff --git a/include/linux/asn1_ber_bytecode.h b/include/linux/asn1_ber_bytecode.h new file mode 100644 index 0000000..945d44a --- /dev/null +++ b/include/linux/asn1_ber_bytecode.h @@ -0,0 +1,87 @@ +/* ASN.1 BER/DER/CER parsing state machine internal definitions + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_ASN1_BER_BYTECODE_H +#define _LINUX_ASN1_BER_BYTECODE_H + +#ifdef __KERNEL__ +#include <linux/types.h> +#endif +#include <linux/asn1.h> + +typedef int (*asn1_action_t)(void *context, + size_t hdrlen, /* In case of ANY type */ + unsigned char tag, /* In case of ANY type */ + const void *value, size_t vlen); + +struct asn1_decoder { + const unsigned char *machine; + size_t machlen; + const asn1_action_t *actions; +}; + +enum asn1_opcode { + /* The tag-matching ops come first and the odd-numbered slots + * are for OR_SKIP ops. + */ +#define ASN1_OP_MATCH__SKIP 0x01 +#define ASN1_OP_MATCH__ACT 0x02 +#define ASN1_OP_MATCH__JUMP 0x04 +#define ASN1_OP_MATCH__ANY 0x08 +#define ASN1_OP_MATCH__COND 0x10 + + ASN1_OP_MATCH = 0x00, + ASN1_OP_MATCH_OR_SKIP = 0x01, + ASN1_OP_MATCH_ACT = 0x02, + ASN1_OP_MATCH_ACT_OR_SKIP = 0x03, + ASN1_OP_MATCH_JUMP = 0x04, + ASN1_OP_MATCH_JUMP_OR_SKIP = 0x05, + ASN1_OP_MATCH_ANY = 0x08, + ASN1_OP_MATCH_ANY_ACT = 0x0a, + /* 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_ACT = 0x1a, + + /* Everything before here will want a tag from the data */ +#define ASN1_OP__MATCHES_TAG ASN1_OP_COND_MATCH_ANY_ACT + + /* 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, + + /* The following eight have bit 0 -> SET, 1 -> OF, 2 -> ACT */ + ASN1_OP_END_SEQ = 0x20, + ASN1_OP_END_SET = 0x21, + ASN1_OP_END_SEQ_OF = 0x22, + ASN1_OP_END_SET_OF = 0x23, + ASN1_OP_END_SEQ_ACT = 0x24, + ASN1_OP_END_SET_ACT = 0x25, + ASN1_OP_END_SEQ_OF_ACT = 0x26, + ASN1_OP_END_SET_OF_ACT = 0x27, +#define ASN1_OP_END__SET 0x01 +#define ASN1_OP_END__OF 0x02 +#define ASN1_OP_END__ACT 0x04 + + ASN1_OP__NR +}; + +#define _tag(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | ASN1_##TAG) +#define _tagn(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | TAG) +#define _jump_target(N) (N) +#define _action(N) (N) + +#endif /* _LINUX_ASN1_BER_BYTECODE_H */ diff --git a/include/linux/asn1_decoder.h b/include/linux/asn1_decoder.h new file mode 100644 index 0000000..fa2ff5b --- /dev/null +++ b/include/linux/asn1_decoder.h @@ -0,0 +1,24 @@ +/* ASN.1 decoder + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_ASN1_DECODER_H +#define _LINUX_ASN1_DECODER_H + +#include <linux/asn1.h> + +struct asn1_decoder; + +extern int asn1_ber_decoder(const struct asn1_decoder *decoder, + void *context, + const unsigned char *data, + size_t datalen); + +#endif /* _LINUX_ASN1_DECODER_H */ diff --git a/include/linux/key-type.h b/include/linux/key-type.h index f0c651c..518a53a 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -26,6 +26,27 @@ struct key_construction { struct key *authkey;/* authorisation for key being constructed */ }; +/* + * Pre-parsed payload, used by key add, update and instantiate. + * + * This struct will be cleared and data and datalen will be set with the data + * and length parameters from the caller and quotalen will be set from + * def_datalen from the key type. Then if the preparse() op is provided by the + * key type, that will be called. Then the struct will be passed to the + * instantiate() or the update() op. + * + * If the preparse() op is given, the free_preparse() op will be called to + * clear the contents. + */ +struct key_preparsed_payload { + char *description; /* Proposed key description (or NULL) */ + void *type_data[2]; /* Private key-type data */ + void *payload; /* Proposed payload */ + const void *data; /* Raw data */ + size_t datalen; /* Raw datalen */ + size_t quotalen; /* Quota length for proposed payload */ +}; + typedef int (*request_key_actor_t)(struct key_construction *key, const char *op, void *aux); @@ -45,18 +66,28 @@ struct key_type { /* vet a description */ int (*vet_description)(const char *description); + /* Preparse the data blob from userspace that is to be the payload, + * generating a proposed description and payload that will be handed to + * the instantiate() and update() ops. + */ + int (*preparse)(struct key_preparsed_payload *prep); + + /* Free a preparse data structure. + */ + void (*free_preparse)(struct key_preparsed_payload *prep); + /* instantiate a key of this type * - this method should call key_payload_reserve() to determine if the * user's quota will hold the payload */ - int (*instantiate)(struct key *key, const void *data, size_t datalen); + int (*instantiate)(struct key *key, struct key_preparsed_payload *prep); /* update a key of this type (optional) * - this method should call key_payload_reserve() to recalculate the * quota consumption * - the key must be locked against read when modifying */ - int (*update)(struct key *key, const void *data, size_t datalen); + int (*update)(struct key *key, struct key_preparsed_payload *prep); /* match a key against a description */ int (*match)(const struct key *key, const void *desc); diff --git a/include/linux/module.h b/include/linux/module.h index fbcafe2..7760c6d 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -21,6 +21,9 @@ #include <linux/percpu.h> #include <asm/module.h> +/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */ +#define MODULE_SIG_STRING "~Module signature appended~\n" + /* Not Yet Implemented */ #define MODULE_SUPPORTED_DEVICE(name) @@ -260,6 +263,11 @@ struct module const unsigned long *unused_gpl_crcs; #endif +#ifdef CONFIG_MODULE_SIG + /* Signature was verified. */ + bool sig_ok; +#endif + /* symbols that will be GPL-only in the near future. */ const struct kernel_symbol *gpl_future_syms; const unsigned long *gpl_future_crcs; diff --git a/include/linux/moduleloader.h b/include/linux/moduleloader.h index b2be02e..560ca53 100644 --- a/include/linux/moduleloader.h +++ b/include/linux/moduleloader.h @@ -28,21 +28,49 @@ void *module_alloc(unsigned long size); /* Free memory returned from module_alloc. */ void module_free(struct module *mod, void *module_region); -/* Apply the given relocation to the (simplified) ELF. Return -error - or 0. */ +/* + * Apply the given relocation to the (simplified) ELF. Return -error + * or 0. + */ +#ifdef CONFIG_MODULES_USE_ELF_REL int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *mod); +#else +static inline int apply_relocate(Elf_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + printk(KERN_ERR "module %s: REL relocation unsupported\n", me->name); + return -ENOEXEC; +} +#endif -/* Apply the given add relocation to the (simplified) ELF. Return - -error or 0 */ +/* + * Apply the given add relocation to the (simplified) ELF. Return + * -error or 0 + */ +#ifdef CONFIG_MODULES_USE_ELF_RELA int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *mod); +#else +static inline int apply_relocate_add(Elf_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + printk(KERN_ERR "module %s: REL relocation unsupported\n", me->name); + return -ENOEXEC; +} +#endif /* Any final processing of module before access. Return -error or 0. */ int module_finalize(const Elf_Ehdr *hdr, diff --git a/include/linux/mpi.h b/include/linux/mpi.h index d02cca6..5af1b81 100644 --- a/include/linux/mpi.h +++ b/include/linux/mpi.h @@ -76,6 +76,7 @@ void mpi_swap(MPI a, MPI b); /*-- mpicoder.c --*/ MPI do_encode_md(const void *sha_buffer, unsigned nbits); +MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes); MPI mpi_read_from_buffer(const void *buffer, unsigned *ret_nread); int mpi_fromstr(MPI val, const char *str); u32 mpi_get_keyid(MPI a, u32 *keyid); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h new file mode 100644 index 0000000..6926db7 --- /dev/null +++ b/include/linux/oid_registry.h @@ -0,0 +1,92 @@ +/* ASN.1 Object identifier (OID) registry + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_OID_REGISTRY_H +#define _LINUX_OID_REGISTRY_H + +#include <linux/types.h> + +/* + * OIDs are turned into these values if possible, or OID__NR if not held here. + * + * NOTE! Do not mess with the format of each line as this is read by + * build_OID_registry.pl to generate the data for look_up_OID(). + */ +enum OID { + OID_id_dsa_with_sha1, /* 1.2.840.10030.4.3 */ + OID_id_dsa, /* 1.2.840.10040.4.1 */ + OID_id_ecdsa_with_sha1, /* 1.2.840.10045.4.1 */ + OID_id_ecPublicKey, /* 1.2.840.10045.2.1 */ + + /* PKCS#1 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1)} */ + OID_rsaEncryption, /* 1.2.840.113549.1.1.1 */ + OID_md2WithRSAEncryption, /* 1.2.840.113549.1.1.2 */ + OID_md3WithRSAEncryption, /* 1.2.840.113549.1.1.3 */ + OID_md4WithRSAEncryption, /* 1.2.840.113549.1.1.4 */ + OID_sha1WithRSAEncryption, /* 1.2.840.113549.1.1.5 */ + OID_sha256WithRSAEncryption, /* 1.2.840.113549.1.1.11 */ + OID_sha384WithRSAEncryption, /* 1.2.840.113549.1.1.12 */ + OID_sha512WithRSAEncryption, /* 1.2.840.113549.1.1.13 */ + OID_sha224WithRSAEncryption, /* 1.2.840.113549.1.1.14 */ + /* PKCS#7 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-7(7)} */ + OID_data, /* 1.2.840.113549.1.7.1 */ + 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_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 */ + OID_smimeAuthenticatedAttrs, /* 1.2.840.113549.1.9.16.2.11 */ + + /* {iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2)} */ + OID_md2, /* 1.2.840.113549.2.2 */ + OID_md4, /* 1.2.840.113549.2.4 */ + OID_md5, /* 1.2.840.113549.2.5 */ + + OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ + OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ + OID_sha1, /* 1.3.14.3.2.26 */ + + /* Distinguished Name attribute IDs [RFC 2256] */ + OID_commonName, /* 2.5.4.3 */ + OID_surname, /* 2.5.4.4 */ + OID_countryName, /* 2.5.4.6 */ + OID_locality, /* 2.5.4.7 */ + OID_stateOrProvinceName, /* 2.5.4.8 */ + OID_organizationName, /* 2.5.4.10 */ + OID_organizationUnitName, /* 2.5.4.11 */ + OID_title, /* 2.5.4.12 */ + OID_description, /* 2.5.4.13 */ + OID_name, /* 2.5.4.41 */ + OID_givenName, /* 2.5.4.42 */ + OID_initials, /* 2.5.4.43 */ + OID_generationalQualifier, /* 2.5.4.44 */ + + /* Certificate extension IDs */ + OID_subjectKeyIdentifier, /* 2.5.29.14 */ + OID_keyUsage, /* 2.5.29.15 */ + OID_subjectAltName, /* 2.5.29.17 */ + OID_issuerAltName, /* 2.5.29.18 */ + OID_basicConstraints, /* 2.5.29.19 */ + OID_crlDistributionPoints, /* 2.5.29.31 */ + OID_certPolicies, /* 2.5.29.32 */ + OID_authorityKeyIdentifier, /* 2.5.29.35 */ + OID_extKeyUsage, /* 2.5.29.37 */ + + OID__NR +}; + +extern enum OID look_up_OID(const void *data, size_t datasize); +extern int sprint_oid(const void *, size_t, char *, size_t); +extern int sprint_OID(enum OID, char *, size_t); + +#endif /* _LINUX_OID_REGISTRY_H */ diff --git a/init/Kconfig b/init/Kconfig index 89e4cf6..6fdd6e3 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1574,6 +1574,66 @@ config MODULE_SRCVERSION_ALL the version). With this option, such a "srcversion" field will be created for all modules. If unsure, say N. +config MODULE_SIG + bool "Module signature verification" + depends on MODULES + 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 + help + Check modules for valid signatures upon load: the signature + is simply appended to the module. For more information see + Documentation/module-signing.txt. + + !!!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 + inclusion into an initramfs that wants the module size reduced. + +config MODULE_SIG_FORCE + bool "Require modules to be validly signed" + depends on MODULE_SIG + help + Reject unsigned modules or signed modules for which we don't have a + key. Without this, such modules will simply taint the kernel. + +choice + prompt "Which hash algorithm should modules be signed with?" + depends on MODULE_SIG + help + This determines which sort of hashing algorithm will be used during + signature generation. This algorithm _must_ be built into the kernel + directly so that signature verification can take place. It is not + possible to load a signed module containing the algorithm to check + the signature on that module. + +config MODULE_SIG_SHA1 + bool "Sign modules with SHA-1" + select CRYPTO_SHA1 + +config MODULE_SIG_SHA224 + bool "Sign modules with SHA-224" + select CRYPTO_SHA256 + +config MODULE_SIG_SHA256 + bool "Sign modules with SHA-256" + select CRYPTO_SHA256 + +config MODULE_SIG_SHA384 + bool "Sign modules with SHA-384" + select CRYPTO_SHA512 + +config MODULE_SIG_SHA512 + bool "Sign modules with SHA-512" + select CRYPTO_SHA512 + +endchoice + endif # MODULES config INIT_ALL_POSSIBLE @@ -1607,4 +1667,12 @@ config PADATA config BROKEN_RODATA bool +config ASN1 + tristate + help + Build a simple ASN.1 grammar compiler that produces a bytecode output + that can be interpreted by the ASN.1 stream decoder and used to + inform it as to what tags are to be expected in a stream and what + functions to call on what tags. + source "kernel/Kconfig.locks" diff --git a/kernel/Makefile b/kernel/Makefile index 5404911..0dfeca4 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o obj-$(CONFIG_PROVE_LOCKING) += spinlock.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_MODULE_SIG) += module_signing.o modsign_pubkey.o obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_KEXEC) += kexec.o @@ -130,3 +131,79 @@ quiet_cmd_timeconst = TIMEC $@ targets += timeconst.h $(obj)/timeconst.h: $(src)/timeconst.pl FORCE $(call if_changed,timeconst) + +ifeq ($(CONFIG_MODULE_SIG),y) +# +# Pull the signing certificate and any extra certificates into the kernel +# +extra_certificates: + touch $@ + +kernel/modsign_pubkey.o: signing_key.x509 extra_certificates + +############################################################################### +# +# 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. +# +############################################################################### +sign_key_with_hash := +ifeq ($(CONFIG_MODULE_SIG_SHA1),y) +sign_key_with_hash := -sha1 +endif +ifeq ($(CONFIG_MODULE_SIG_SHA224),y) +sign_key_with_hash := -sha224 +endif +ifeq ($(CONFIG_MODULE_SIG_SHA256),y) +sign_key_with_hash := -sha256 +endif +ifeq ($(CONFIG_MODULE_SIG_SHA384),y) +sign_key_with_hash := -sha384 +endif +ifeq ($(CONFIG_MODULE_SIG_SHA512),y) +sign_key_with_hash := -sha512 +endif +ifeq ($(sign_key_with_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 should use a hardware random" + @echo "### number generator if one is available, eg:" + @echo "###" + @echo "### rngd -r /dev/hwrandom" + @echo "###" + openssl req -new -nodes -utf8 $(sign_key_with_hash) -days 36500 -batch \ + -x509 -config x509.genkey \ + -outform DER -out signing_key.x509 \ + -keyout signing_key.priv + @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 = Magrathea" + @echo >>x509.genkey "CN = Glacier signing key" + @echo >>x509.genkey "emailAddress = slartibartfast@magrathea.h2g2" + @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 diff --git a/kernel/modsign_pubkey.c b/kernel/modsign_pubkey.c new file mode 100644 index 0000000..4646eb2 --- /dev/null +++ b/kernel/modsign_pubkey.c @@ -0,0 +1,113 @@ +/* Public keys for module signature verification + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/cred.h> +#include <linux/err.h> +#include <keys/asymmetric-type.h> +#include "module-internal.h" + +struct key *modsign_keyring; + +extern __initdata const u8 modsign_certificate_list[]; +extern __initdata const u8 modsign_certificate_list_end[]; +asm(".section .init.data,\"aw\"\n" + "modsign_certificate_list:\n" + ".incbin \"signing_key.x509\"\n" + ".incbin \"extra_certificates\"\n" + "modsign_certificate_list_end:" + ); + +/* + * We need to make sure ccache doesn't cache the .o file as it doesn't notice + * if modsign.pub changes. + */ +static __initdata const char annoy_ccache[] = __TIME__ "foo"; + +/* + * Load the compiled-in keys + */ +static __init int module_verify_init(void) +{ + pr_notice("Initialise module verification\n"); + + modsign_keyring = key_alloc(&key_type_keyring, ".module_sign", + KUIDT_INIT(0), KGIDT_INIT(0), + current_cred(), + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(modsign_keyring)) + panic("Can't allocate module signing keyring\n"); + + if (key_instantiate_and_link(modsign_keyring, NULL, 0, NULL, NULL) < 0) + panic("Can't instantiate module signing keyring\n"); + + return 0; +} + +/* + * Must be initialised before we try and load the keys into the keyring. + */ +device_initcall(module_verify_init); + +/* + * Load the compiled-in keys + */ +static __init int load_module_signing_keys(void) +{ + key_ref_t key; + const u8 *p, *end; + size_t plen; + + pr_notice("Loading module verification certificates\n"); + + end = modsign_certificate_list_end; + p = modsign_certificate_list; + while (p < end) { + /* Each cert begins with an ASN.1 SEQUENCE tag and must be more + * than 256 bytes in size. + */ + if (end - p < 4) + goto dodgy_cert; + if (p[0] != 0x30 && + p[1] != 0x82) + goto dodgy_cert; + plen = (p[2] << 8) | p[3]; + plen += 4; + if (plen > end - p) + goto dodgy_cert; + + key = key_create_or_update(make_key_ref(modsign_keyring, 1), + "asymmetric", + NULL, + p, + plen, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(key)) + pr_err("MODSIGN: Problem loading in-kernel X.509 certificate (%ld)\n", + PTR_ERR(key)); + else + pr_notice("MODSIGN: Loaded cert '%s'\n", + key_ref_to_ptr(key)->description); + p += plen; + } + + return 0; + +dodgy_cert: + pr_err("MODSIGN: Problem parsing in-kernel X.509 certificate list\n"); + return 0; +} +late_initcall(load_module_signing_keys); diff --git a/kernel/module-internal.h b/kernel/module-internal.h new file mode 100644 index 0000000..6114a13 --- /dev/null +++ b/kernel/module-internal.h @@ -0,0 +1,15 @@ +/* Module internals + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +extern struct key *modsign_keyring; + +extern int mod_verify_sig(const void *mod, unsigned long modlen, + const void *sig, unsigned long siglen); diff --git a/kernel/module.c b/kernel/module.c index 4edbd9c..0e2da86 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -58,6 +58,8 @@ #include <linux/jump_label.h> #include <linux/pfn.h> #include <linux/bsearch.h> +#include <linux/fips.h> +#include "module-internal.h" #define CREATE_TRACE_POINTS #include <trace/events/module.h> @@ -102,6 +104,43 @@ static LIST_HEAD(modules); struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */ #endif /* CONFIG_KGDB_KDB */ +#ifdef CONFIG_MODULE_SIG +#ifdef CONFIG_MODULE_SIG_FORCE +static bool sig_enforce = true; +#else +static bool sig_enforce = false; + +static int param_set_bool_enable_only(const char *val, + const struct kernel_param *kp) +{ + int err; + bool test; + struct kernel_param dummy_kp = *kp; + + dummy_kp.arg = &test; + + err = param_set_bool(val, &dummy_kp); + if (err) + return err; + + /* Don't let them unset it once it's set! */ + if (!test && sig_enforce) + return -EROFS; + + if (test) + sig_enforce = true; + return 0; +} + +static const struct kernel_param_ops param_ops_bool_enable_only = { + .set = param_set_bool_enable_only, + .get = param_get_bool, +}; +#define param_check_bool_enable_only param_check_bool + +module_param(sig_enforce, bool_enable_only, 0644); +#endif /* !CONFIG_MODULE_SIG_FORCE */ +#endif /* CONFIG_MODULE_SIG */ /* Block module loading/unloading? */ int modules_disabled = 0; @@ -136,6 +175,7 @@ struct load_info { unsigned long symoffs, stroffs; struct _ddebug *debug; unsigned int num_debug; + bool sig_ok; struct { unsigned int sym, str, mod, vers, info, pcpu; } index; @@ -1949,26 +1989,6 @@ static int simplify_symbols(struct module *mod, const struct load_info *info) return ret; } -int __weak apply_relocate(Elf_Shdr *sechdrs, - const char *strtab, - unsigned int symindex, - unsigned int relsec, - struct module *me) -{ - pr_err("module %s: REL relocation unsupported\n", me->name); - return -ENOEXEC; -} - -int __weak apply_relocate_add(Elf_Shdr *sechdrs, - const char *strtab, - unsigned int symindex, - unsigned int relsec, - struct module *me) -{ - pr_err("module %s: RELA relocation unsupported\n", me->name); - return -ENOEXEC; -} - static int apply_relocations(struct module *mod, const struct load_info *info) { unsigned int i; @@ -2399,7 +2419,52 @@ static inline void kmemleak_load_module(const struct module *mod, } #endif -/* Sets info->hdr and info->len. */ +#ifdef CONFIG_MODULE_SIG +static int module_sig_check(struct load_info *info, + const void *mod, unsigned long *len) +{ + int err = -ENOKEY; + const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1; + const void *p = mod, *end = mod + *len; + + /* Poor man's memmem. */ + while ((p = memchr(p, MODULE_SIG_STRING[0], end - p))) { + if (p + markerlen > end) + break; + + if (memcmp(p, MODULE_SIG_STRING, markerlen) == 0) { + const void *sig = p + markerlen; + /* Truncate module up to signature. */ + *len = p - mod; + err = mod_verify_sig(mod, *len, sig, end - sig); + break; + } + p++; + } + + if (!err) { + info->sig_ok = true; + return 0; + } + + /* Not having a signature is only an error if we're strict. */ + if (err < 0 && fips_enabled) + panic("Module verification failed with error %d in FIPS mode\n", + err); + if (err == -ENOKEY && !sig_enforce) + err = 0; + + return err; +} +#else /* !CONFIG_MODULE_SIG */ +static int module_sig_check(struct load_info *info, + void *mod, unsigned long *len) +{ + return 0; +} +#endif /* !CONFIG_MODULE_SIG */ + +/* Sets info->hdr, info->len and info->sig_ok. */ static int copy_and_check(struct load_info *info, const void __user *umod, unsigned long len, const char __user *uargs) @@ -2419,6 +2484,10 @@ static int copy_and_check(struct load_info *info, goto free_hdr; } + err = module_sig_check(info, hdr, &len); + if (err) + goto free_hdr; + /* Sanity checks against insmoding binaries or wrong arch, weird elf version */ if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0 @@ -2730,6 +2799,10 @@ static int check_module_license_and_versions(struct module *mod) if (strcmp(mod->name, "driverloader") == 0) add_taint_module(mod, TAINT_PROPRIETARY_MODULE); + /* lve claims to be GPL but upstream won't provide source */ + if (strcmp(mod->name, "lve") == 0) + add_taint_module(mod, TAINT_PROPRIETARY_MODULE); + #ifdef CONFIG_MODVERSIONS if ((mod->num_syms && !mod->crcs) || (mod->num_gpl_syms && !mod->gpl_crcs) @@ -2861,6 +2934,20 @@ static int post_relocation(struct module *mod, const struct load_info *info) return module_finalize(info->hdr, info->sechdrs, mod); } +/* Is this module of this name done loading? No locks held. */ +static bool finished_loading(const char *name) +{ + struct module *mod; + bool ret; + + mutex_lock(&module_mutex); + mod = find_module(name); + ret = !mod || mod->state != MODULE_STATE_COMING; + mutex_unlock(&module_mutex); + + return ret; +} + /* Allocate and load the module: note that size of section 0 is always zero, and we rely on this for optional sections. */ static struct module *load_module(void __user *umod, @@ -2868,7 +2955,7 @@ static struct module *load_module(void __user *umod, const char __user *uargs) { struct load_info info = { NULL, }; - struct module *mod; + struct module *mod, *old; long err; pr_debug("load_module: umod=%p, len=%lu, uargs=%p\n", @@ -2886,6 +2973,12 @@ static struct module *load_module(void __user *umod, goto free_copy; } +#ifdef CONFIG_MODULE_SIG + mod->sig_ok = info.sig_ok; + if (!mod->sig_ok) + add_taint_module(mod, TAINT_FORCED_MODULE); +#endif + /* Now module is in final location, initialize linked lists, etc. */ err = module_unload_init(mod); if (err) @@ -2934,8 +3027,18 @@ static struct module *load_module(void __user *umod, * function to insert in a way safe to concurrent readers. * The mutex protects against concurrent writers. */ +again: mutex_lock(&module_mutex); - if (find_module(mod->name)) { + if ((old = find_module(mod->name)) != NULL) { + if (old->state == MODULE_STATE_COMING) { + /* Wait in case it fails to load. */ + mutex_unlock(&module_mutex); + err = wait_event_interruptible(module_wq, + finished_loading(mod->name)); + if (err) + goto free_arch_cleanup; + goto again; + } err = -EEXIST; goto unlock; } @@ -2975,7 +3078,7 @@ static struct module *load_module(void __user *umod, /* Unlink carefully: kallsyms could be walking list. */ list_del_rcu(&mod->list); module_bug_cleanup(mod); - + wake_up_all(&module_wq); ddebug: dynamic_debug_remove(info.debug); unlock: @@ -3050,7 +3153,7 @@ SYSCALL_DEFINE3(init_module, void __user *, umod, blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GOING, mod); free_module(mod); - wake_up(&module_wq); + wake_up_all(&module_wq); return ret; } if (ret > 0) { @@ -3062,9 +3165,8 @@ SYSCALL_DEFINE3(init_module, void __user *, umod, dump_stack(); } - /* Now it's a first class citizen! Wake up anyone waiting for it. */ + /* Now it's a first class citizen! */ mod->state = MODULE_STATE_LIVE; - wake_up(&module_wq); blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_LIVE, mod); @@ -3087,6 +3189,7 @@ SYSCALL_DEFINE3(init_module, void __user *, umod, mod->init_ro_size = 0; mod->init_text_size = 0; mutex_unlock(&module_mutex); + wake_up_all(&module_wq); return 0; } diff --git a/kernel/module_signing.c b/kernel/module_signing.c new file mode 100644 index 0000000..6b09f69 --- /dev/null +++ b/kernel/module_signing.c @@ -0,0 +1,243 @@ +/* Module signature checker + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <crypto/public_key.h> +#include <crypto/hash.h> +#include <keys/asymmetric-type.h> +#include "module-internal.h" + +/* + * Module signature information block. + * + * The constituents of the signature section are, in order: + * + * - Signer's name + * - Key identifier + * - Signature data + * - Information block + */ +struct module_signature { + enum pkey_algo algo : 8; /* Public-key crypto algorithm */ + enum pkey_hash_algo hash : 8; /* Digest algorithm */ + enum pkey_id_type id_type : 8; /* Key identifier type */ + u8 signer_len; /* Length of signer's name */ + u8 key_id_len; /* Length of key identifier */ + u8 __pad[3]; + __be32 sig_len; /* Length of signature data */ +}; + +/* + * Digest the module contents. + */ +static struct public_key_signature *mod_make_digest(enum pkey_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(pkey_hash_algo[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(modsign_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, + const void *sig, unsigned long siglen) +{ + struct public_key_signature *pks; + struct module_signature ms; + struct key *key; + size_t sig_len; + int ret; + + pr_devel("==>%s(,%lu,,%lu,)\n", __func__, modlen, siglen); + + if (siglen <= sizeof(ms)) + return -EBADMSG; + + memcpy(&ms, sig + (siglen - sizeof(ms)), sizeof(ms)); + siglen -= sizeof(ms); + + sig_len = be32_to_cpu(ms.sig_len); + if (sig_len >= siglen || + siglen - sig_len != (size_t)ms.signer_len + ms.key_id_len) + return -EBADMSG; + + /* For the moment, only support RSA and X.509 identifiers */ + if (ms.algo != PKEY_ALGO_RSA || + ms.id_type != PKEY_ID_X509) + return -ENOPKG; + + if (ms.hash >= PKEY_HASH__LAST || + !pkey_hash_algo[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; + + 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; +} diff --git a/lib/.gitignore b/lib/.gitignore index 3bef1ea..09aae85 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -3,4 +3,4 @@ # gen_crc32table crc32table.h - +oid_registry_data.c diff --git a/lib/Kconfig b/lib/Kconfig index bb94c1b..4b31a46 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -396,4 +396,9 @@ config SIGNATURE config LIBFDT bool +config OID_REGISTRY + tristate + help + Enable fast lookup object identifier registry. + endmenu diff --git a/lib/Makefile b/lib/Makefile index 3128e35..821a162 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -145,6 +145,8 @@ obj-$(CONFIG_INTERVAL_TREE_TEST) += interval_tree_test.o interval_tree_test-objs := interval_tree_test_main.o interval_tree.o +obj-$(CONFIG_ASN1) += asn1_decoder.o + hostprogs-y := gen_crc32table clean-files := crc32table.h @@ -155,3 +157,19 @@ quiet_cmd_crc32 = GEN $@ $(obj)/crc32table.h: $(obj)/gen_crc32table $(call cmd,crc32) + +# +# Build a fast OID lookip registry from include/linux/oid_registry.h +# +obj-$(CONFIG_OID_REGISTRY) += oid_registry.o + +$(obj)/oid_registry.c: $(obj)/oid_registry_data.c + +$(obj)/oid_registry_data.c: $(srctree)/include/linux/oid_registry.h \ + $(src)/build_OID_registry + $(call cmd,build_OID_registry) + +quiet_cmd_build_OID_registry = GEN $@ + cmd_build_OID_registry = perl $(srctree)/$(src)/build_OID_registry $< $@ + +clean-files += oid_registry_data.c diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c new file mode 100644 index 0000000..de2c8b5 --- /dev/null +++ b/lib/asn1_decoder.c @@ -0,0 +1,487 @@ +/* Decoder for ASN.1 BER/DER/CER encoded bytestream + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/asn1_decoder.h> +#include <linux/asn1_ber_bytecode.h> + +static const unsigned char asn1_op_lengths[ASN1_OP__NR] = { + /* OPC TAG JMP ACT */ + [ASN1_OP_MATCH] = 1 + 1, + [ASN1_OP_MATCH_OR_SKIP] = 1 + 1, + [ASN1_OP_MATCH_ACT] = 1 + 1 + 1, + [ASN1_OP_MATCH_ACT_OR_SKIP] = 1 + 1 + 1, + [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_ACT] = 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_ACT] = 1 + 1, + [ASN1_OP_COND_FAIL] = 1, + [ASN1_OP_COMPLETE] = 1, + [ASN1_OP_ACT] = 1 + 1, + [ASN1_OP_RETURN] = 1, + [ASN1_OP_END_SEQ] = 1, + [ASN1_OP_END_SEQ_OF] = 1 + 1, + [ASN1_OP_END_SET] = 1, + [ASN1_OP_END_SET_OF] = 1 + 1, + [ASN1_OP_END_SEQ_ACT] = 1 + 1, + [ASN1_OP_END_SEQ_OF_ACT] = 1 + 1 + 1, + [ASN1_OP_END_SET_ACT] = 1 + 1, + [ASN1_OP_END_SET_OF_ACT] = 1 + 1 + 1, +}; + +/* + * Find the length of an indefinite length object + * @data: The data buffer + * @datalen: The end of the innermost containing element in the buffer + * @_dp: The data parse cursor (updated before returning) + * @_len: Where to return the size of the element. + * @_errmsg: Where to return a pointer to an error message on error + */ +static int asn1_find_indefinite_length(const unsigned char *data, size_t datalen, + size_t *_dp, size_t *_len, + const char **_errmsg) +{ + unsigned char tag, tmp; + size_t dp = *_dp, len, n; + int indef_level = 1; + +next_tag: + if (unlikely(datalen - dp < 2)) { + if (datalen == dp) + goto missing_eoc; + goto data_overrun_error; + } + + /* Extract a tag from the data */ + tag = data[dp++]; + if (tag == 0) { + /* It appears to be an EOC. */ + if (data[dp++] != 0) + goto invalid_eoc; + if (--indef_level <= 0) { + *_len = dp - *_dp; + *_dp = dp; + return 0; + } + goto next_tag; + } + + if (unlikely((tag & 0x1f) == 0x1f)) { + do { + if (unlikely(datalen - dp < 2)) + goto data_overrun_error; + tmp = data[dp++]; + } while (tmp & 0x80); + } + + /* Extract the length */ + len = data[dp++]; + if (len < 0x7f) { + dp += len; + goto next_tag; + } + + if (unlikely(len == 0x80)) { + /* Indefinite length */ + if (unlikely((tag & ASN1_CONS_BIT) == ASN1_PRIM << 5)) + goto indefinite_len_primitive; + indef_level++; + goto next_tag; + } + + n = len - 0x80; + if (unlikely(n > sizeof(size_t) - 1)) + goto length_too_long; + if (unlikely(n > datalen - dp)) + goto data_overrun_error; + for (len = 0; n > 0; n--) { + len <<= 8; + len |= data[dp++]; + } + dp += len; + goto next_tag; + +length_too_long: + *_errmsg = "Unsupported length"; + goto error; +indefinite_len_primitive: + *_errmsg = "Indefinite len primitive not permitted"; + goto error; +invalid_eoc: + *_errmsg = "Invalid length EOC"; + goto error; +data_overrun_error: + *_errmsg = "Data overrun error"; + goto error; +missing_eoc: + *_errmsg = "Missing EOC in indefinite len cons"; +error: + *_dp = dp; + return -1; +} + +/** + * asn1_ber_decoder - Decoder BER/DER/CER ASN.1 according to pattern + * @decoder: The decoder definition (produced by asn1_compiler) + * @context: The caller's context (to be passed to the action functions) + * @data: The encoded data + * @datasize: The size of the encoded data + * + * Decode BER/DER/CER encoded ASN.1 data according to a bytecode pattern + * produced by asn1_compiler. Action functions are called on marked tags to + * allow the caller to retrieve significant data. + * + * LIMITATIONS: + * + * To keep down the amount of stack used by this function, the following limits + * have been imposed: + * + * (1) This won't handle datalen > 65535 without increasing the size of the + * cons stack elements and length_too_long checking. + * + * (2) The stack of constructed types is 10 deep. If the depth of non-leaf + * constructed types exceeds this, the decode will fail. + * + * (3) The SET type (not the SET OF type) isn't really supported as tracking + * what members of the set have been seen is a pain. + */ +int asn1_ber_decoder(const struct asn1_decoder *decoder, + void *context, + const unsigned char *data, + size_t datalen) +{ + const unsigned char *machine = decoder->machine; + const asn1_action_t *actions = decoder->actions; + size_t machlen = decoder->machlen; + enum asn1_opcode op; + unsigned char tag = 0, csp = 0, jsp = 0, optag = 0, hdr = 0; + const char *errmsg; + size_t pc = 0, dp = 0, tdp = 0, len = 0; + int ret; + + unsigned char flags = 0; +#define FLAG_INDEFINITE_LENGTH 0x01 +#define FLAG_MATCHED 0x02 +#define FLAG_CONS 0x20 /* Corresponds to CONS bit in the opcode tag + * - ie. whether or not we are going to parse + * a compound type. + */ + +#define NR_CONS_STACK 10 + unsigned short cons_dp_stack[NR_CONS_STACK]; + unsigned short cons_datalen_stack[NR_CONS_STACK]; + unsigned char cons_hdrlen_stack[NR_CONS_STACK]; +#define NR_JUMP_STACK 10 + unsigned char jump_stack[NR_JUMP_STACK]; + + if (datalen > 65535) + return -EMSGSIZE; + +next_op: + pr_debug("next_op: pc=\e[32m%zu\e[m/%zu dp=\e[33m%zu\e[m/%zu C=%d J=%d\n", + pc, machlen, dp, datalen, csp, jsp); + if (unlikely(pc >= machlen)) + goto machine_overrun_error; + op = machine[pc]; + if (unlikely(pc + asn1_op_lengths[op] > machlen)) + goto machine_overrun_error; + + /* If this command is meant to match a tag, then do that before + * evaluating the command. + */ + if (op <= ASN1_OP__MATCHES_TAG) { + unsigned char tmp; + + /* Skip conditional matches if possible */ + if ((op & ASN1_OP_MATCH__COND && + flags & FLAG_MATCHED) || + dp == datalen) { + pc += asn1_op_lengths[op]; + goto next_op; + } + + flags = 0; + hdr = 2; + + /* Extract a tag from the data */ + if (unlikely(dp >= datalen - 1)) + goto data_overrun_error; + tag = data[dp++]; + if (unlikely((tag & 0x1f) == 0x1f)) + goto long_tag_not_supported; + + if (op & ASN1_OP_MATCH__ANY) { + pr_debug("- any %02x\n", tag); + } else { + /* Extract the tag from the machine + * - Either CONS or PRIM are permitted in the data if + * CONS is not set in the op stream, otherwise CONS + * is mandatory. + */ + optag = machine[pc + 1]; + flags |= optag & FLAG_CONS; + + /* Determine whether the tag matched */ + tmp = optag ^ tag; + tmp &= ~(optag & ASN1_CONS_BIT); + pr_debug("- match? %02x %02x %02x\n", tag, optag, tmp); + if (tmp != 0) { + /* All odd-numbered tags are MATCH_OR_SKIP. */ + if (op & ASN1_OP_MATCH__SKIP) { + pc += asn1_op_lengths[op]; + dp--; + goto next_op; + } + goto tag_mismatch; + } + } + flags |= FLAG_MATCHED; + + len = data[dp++]; + if (len > 0x7f) { + if (unlikely(len == 0x80)) { + /* Indefinite length */ + if (unlikely(!(tag & ASN1_CONS_BIT))) + goto indefinite_len_primitive; + flags |= FLAG_INDEFINITE_LENGTH; + if (unlikely(2 > datalen - dp)) + goto data_overrun_error; + } else { + int n = len - 0x80; + if (unlikely(n > 2)) + goto length_too_long; + if (unlikely(dp >= datalen - n)) + goto data_overrun_error; + hdr += n; + for (len = 0; n > 0; n--) { + len <<= 8; + len |= data[dp++]; + } + if (unlikely(len > datalen - dp)) + goto data_overrun_error; + } + } + + if (flags & FLAG_CONS) { + /* For expected compound forms, we stack the positions + * of the start and end of the data. + */ + if (unlikely(csp >= NR_CONS_STACK)) + goto cons_stack_overflow; + cons_dp_stack[csp] = dp; + cons_hdrlen_stack[csp] = hdr; + if (!(flags & FLAG_INDEFINITE_LENGTH)) { + cons_datalen_stack[csp] = datalen; + datalen = dp + len; + } else { + cons_datalen_stack[csp] = 0; + } + csp++; + } + + pr_debug("- TAG: %02x %zu%s\n", + tag, len, flags & FLAG_CONS ? " CONS" : ""); + tdp = dp; + } + + /* Decide how to handle the operation */ + switch (op) { + case ASN1_OP_MATCH_ANY_ACT: + case ASN1_OP_COND_MATCH_ANY_ACT: + ret = actions[machine[pc + 1]](context, hdr, tag, data + dp, len); + if (ret < 0) + return ret; + goto skip_data; + + case ASN1_OP_MATCH_ACT: + case ASN1_OP_MATCH_ACT_OR_SKIP: + case ASN1_OP_COND_MATCH_ACT_OR_SKIP: + ret = actions[machine[pc + 2]](context, hdr, tag, data + dp, len); + if (ret < 0) + return ret; + goto skip_data; + + case ASN1_OP_MATCH: + case ASN1_OP_MATCH_OR_SKIP: + case ASN1_OP_MATCH_ANY: + case ASN1_OP_COND_MATCH_OR_SKIP: + case ASN1_OP_COND_MATCH_ANY: + skip_data: + if (!(flags & FLAG_CONS)) { + if (flags & FLAG_INDEFINITE_LENGTH) { + ret = asn1_find_indefinite_length( + data, datalen, &dp, &len, &errmsg); + if (ret < 0) + goto error; + } else { + dp += len; + } + pr_debug("- LEAF: %zu\n", len); + } + pc += asn1_op_lengths[op]; + goto next_op; + + case ASN1_OP_MATCH_JUMP: + case ASN1_OP_MATCH_JUMP_OR_SKIP: + case ASN1_OP_COND_MATCH_JUMP_OR_SKIP: + pr_debug("- MATCH_JUMP\n"); + if (unlikely(jsp == NR_JUMP_STACK)) + goto jump_stack_overflow; + jump_stack[jsp++] = pc + asn1_op_lengths[op]; + pc = machine[pc + 2]; + goto next_op; + + case ASN1_OP_COND_FAIL: + if (unlikely(!(flags & FLAG_MATCHED))) + goto tag_mismatch; + pc += asn1_op_lengths[op]; + goto next_op; + + case ASN1_OP_COMPLETE: + if (unlikely(jsp != 0 || csp != 0)) { + pr_err("ASN.1 decoder error: Stacks not empty at completion (%u, %u)\n", + jsp, csp); + return -EBADMSG; + } + return 0; + + case ASN1_OP_END_SET: + case ASN1_OP_END_SET_ACT: + if (unlikely(!(flags & FLAG_MATCHED))) + goto tag_mismatch; + case ASN1_OP_END_SEQ: + case ASN1_OP_END_SET_OF: + case ASN1_OP_END_SEQ_OF: + case ASN1_OP_END_SEQ_ACT: + case ASN1_OP_END_SET_OF_ACT: + case ASN1_OP_END_SEQ_OF_ACT: + if (unlikely(csp <= 0)) + goto cons_stack_underflow; + csp--; + tdp = cons_dp_stack[csp]; + hdr = cons_hdrlen_stack[csp]; + len = datalen; + datalen = cons_datalen_stack[csp]; + pr_debug("- end cons t=%zu dp=%zu l=%zu/%zu\n", + tdp, dp, len, datalen); + if (datalen == 0) { + /* Indefinite length - check for the EOC. */ + datalen = len; + if (unlikely(datalen - dp < 2)) + goto data_overrun_error; + if (data[dp++] != 0) { + if (op & ASN1_OP_END__OF) { + dp--; + csp++; + pc = machine[pc + 1]; + pr_debug("- continue\n"); + goto next_op; + } + goto missing_eoc; + } + if (data[dp++] != 0) + goto invalid_eoc; + len = dp - tdp - 2; + } else { + if (dp < len && (op & ASN1_OP_END__OF)) { + datalen = len; + csp++; + pc = machine[pc + 1]; + pr_debug("- continue\n"); + goto next_op; + } + if (dp != len) + goto cons_length_error; + len -= tdp; + pr_debug("- cons len l=%zu d=%zu\n", len, dp - tdp); + } + + if (op & ASN1_OP_END__ACT) { + unsigned char act; + if (op & ASN1_OP_END__OF) + act = machine[pc + 2]; + else + act = machine[pc + 1]; + ret = actions[act](context, hdr, 0, data + tdp, len); + } + pc += asn1_op_lengths[op]; + goto next_op; + + case ASN1_OP_ACT: + ret = actions[machine[pc + 1]](context, hdr, tag, data + tdp, len); + pc += asn1_op_lengths[op]; + goto next_op; + + case ASN1_OP_RETURN: + if (unlikely(jsp <= 0)) + goto jump_stack_underflow; + pc = jump_stack[--jsp]; + goto next_op; + + default: + break; + } + + /* Shouldn't reach here */ + pr_err("ASN.1 decoder error: Found reserved opcode (%u)\n", op); + return -EBADMSG; + +data_overrun_error: + errmsg = "Data overrun error"; + goto error; +machine_overrun_error: + errmsg = "Machine overrun error"; + goto error; +jump_stack_underflow: + errmsg = "Jump stack underflow"; + goto error; +jump_stack_overflow: + errmsg = "Jump stack overflow"; + goto error; +cons_stack_underflow: + errmsg = "Cons stack underflow"; + goto error; +cons_stack_overflow: + errmsg = "Cons stack overflow"; + goto error; +cons_length_error: + errmsg = "Cons length error"; + goto error; +missing_eoc: + errmsg = "Missing EOC in indefinite len cons"; + goto error; +invalid_eoc: + errmsg = "Invalid length EOC"; + goto error; +length_too_long: + errmsg = "Unsupported length"; + goto error; +indefinite_len_primitive: + errmsg = "Indefinite len primitive not permitted"; + goto error; +tag_mismatch: + errmsg = "Unexpected tag"; + goto error; +long_tag_not_supported: + errmsg = "Long tag not supported"; +error: + pr_debug("\nASN1: %s [m=%zu d=%zu ot=%02x t=%02x l=%zu]\n", + errmsg, pc, dp, optag, tag, len); + return -EBADMSG; +} +EXPORT_SYMBOL_GPL(asn1_ber_decoder); diff --git a/lib/build_OID_registry b/lib/build_OID_registry new file mode 100755 index 0000000..dfbdaab --- /dev/null +++ b/lib/build_OID_registry @@ -0,0 +1,209 @@ +#!/usr/bin/perl -w +# +# Build a static ASN.1 Object Identified (OID) registry +# +# Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# + +use strict; + +my @names = (); +my @oids = (); + +if ($#ARGV != 1) { + print STDERR "Format: ", $0, " <in-h-file> <out-c-file>\n"; + exit(2); +} + +# +# Open the file to read from +# +open IN_FILE, "<$ARGV[0]" || die; +while (<IN_FILE>) { + chomp; + if (m!\s+OID_([a-zA-z][a-zA-Z0-9_]+),\s+/[*]\s+([012][.0-9]*)\s+[*]/!) { + push @names, $1; + push @oids, $2; + } +} +close IN_FILE || die; + +# +# Open the files to write into +# +open C_FILE, ">$ARGV[1]" or die; +print C_FILE "/*\n"; +print C_FILE " * Automatically generated by ", $0, ". Do not edit\n"; +print C_FILE " */\n"; + +# +# Split the data up into separate lists and also determine the lengths of the +# encoded data arrays. +# +my @indices = (); +my @lengths = (); +my $total_length = 0; + +print "Compiling ", $#names + 1, " OIDs\n"; + +for (my $i = 0; $i <= $#names; $i++) { + my $name = $names[$i]; + my $oid = $oids[$i]; + + my @components = split(/[.]/, $oid); + + # Determine the encoded length of this OID + my $size = $#components; + for (my $loop = 2; $loop <= $#components; $loop++) { + my $c = $components[$loop]; + + # We will base128 encode the number + my $tmp = ($c == 0) ? 0 : int(log($c)/log(2)); + $tmp = int($tmp / 7); + $size += $tmp; + } + push @lengths, $size; + push @indices, $total_length; + $total_length += $size; +} + +# +# Emit the look-up-by-OID index table +# +print C_FILE "\n"; +if ($total_length <= 255) { + print C_FILE "static const unsigned char oid_index[OID__NR + 1] = {\n"; +} else { + print C_FILE "static const unsigned short oid_index[OID__NR + 1] = {\n"; +} +for (my $i = 0; $i <= $#names; $i++) { + print C_FILE "\t[OID_", $names[$i], "] = ", $indices[$i], ",\n" +} +print C_FILE "\t[OID__NR] = ", $total_length, "\n"; +print C_FILE "};\n"; + +# +# Encode the OIDs +# +my @encoded_oids = (); + +for (my $i = 0; $i <= $#names; $i++) { + my @octets = (); + + my @components = split(/[.]/, $oids[$i]); + + push @octets, $components[0] * 40 + $components[1]; + + for (my $loop = 2; $loop <= $#components; $loop++) { + my $c = $components[$loop]; + + # Base128 encode the number + my $tmp = ($c == 0) ? 0 : int(log($c)/log(2)); + $tmp = int($tmp / 7); + + for (; $tmp > 0; $tmp--) { + push @octets, (($c >> $tmp * 7) & 0x7f) | 0x80; + } + push @octets, $c & 0x7f; + } + + push @encoded_oids, \@octets; +} + +# +# Create a hash value for each OID +# +my @hash_values = (); +for (my $i = 0; $i <= $#names; $i++) { + my @octets = @{$encoded_oids[$i]}; + + my $hash = $#octets; + foreach (@octets) { + $hash += $_ * 33; + } + + $hash = ($hash >> 24) ^ ($hash >> 16) ^ ($hash >> 8) ^ ($hash); + + push @hash_values, $hash & 0xff; +} + +# +# Emit the OID data +# +print C_FILE "\n"; +print C_FILE "static const unsigned char oid_data[", $total_length, "] = {\n"; +for (my $i = 0; $i <= $#names; $i++) { + my @octets = @{$encoded_oids[$i]}; + print C_FILE "\t"; + print C_FILE $_, ", " foreach (@octets); + print C_FILE "\t// ", $names[$i]; + print C_FILE "\n"; +} +print C_FILE "};\n"; + +# +# Build the search index table (ordered by length then hash then content) +# +my @index_table = ( 0 .. $#names ); + +@index_table = sort { + my @octets_a = @{$encoded_oids[$a]}; + my @octets_b = @{$encoded_oids[$b]}; + + return $hash_values[$a] <=> $hash_values[$b] + if ($hash_values[$a] != $hash_values[$b]); + return $#octets_a <=> $#octets_b + if ($#octets_a != $#octets_b); + for (my $i = $#octets_a; $i >= 0; $i--) { + return $octets_a[$i] <=> $octets_b[$i] + if ($octets_a[$i] != $octets_b[$i]); + } + return 0; + +} @index_table; + +# +# Emit the search index and hash value table +# +print C_FILE "\n"; +print C_FILE "static const struct {\n"; +print C_FILE "\tunsigned char hash;\n"; +if ($#names <= 255) { + print C_FILE "\tenum OID oid : 8;\n"; +} else { + print C_FILE "\tenum OID oid : 16;\n"; +} +print C_FILE "} oid_search_table[OID__NR] = {\n"; +for (my $i = 0; $i <= $#names; $i++) { + my @octets = @{$encoded_oids[$index_table[$i]]}; + printf(C_FILE "\t[%3u] = { %3u, OID_%-35s }, // ", + $i, + $hash_values[$index_table[$i]], + $names[$index_table[$i]]); + printf C_FILE "%02x", $_ foreach (@octets); + print C_FILE "\n"; +} +print C_FILE "};\n"; + +# +# Emit the OID debugging name table +# +#print C_FILE "\n"; +#print C_FILE "const char *const oid_name_table[OID__NR + 1] = {\n"; +# +#for (my $i = 0; $i <= $#names; $i++) { +# print C_FILE "\t\"", $names[$i], "\",\n" +#} +#print C_FILE "\t\"Unknown-OID\"\n"; +#print C_FILE "};\n"; + +# +# Polish off +# +close C_FILE or die; diff --git a/lib/mpi/Makefile b/lib/mpi/Makefile index 45ca90a..019a68c 100644 --- a/lib/mpi/Makefile +++ b/lib/mpi/Makefile @@ -14,6 +14,7 @@ mpi-y = \ generic_mpih-add1.o \ mpicoder.o \ mpi-bit.o \ + mpi-cmp.o \ mpih-cmp.o \ mpih-div.o \ mpih-mul.o \ diff --git a/lib/mpi/longlong.h b/lib/mpi/longlong.h index 29f9862..678ce4f 100644 --- a/lib/mpi/longlong.h +++ b/lib/mpi/longlong.h @@ -19,6 +19,8 @@ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ +#include <asm-generic/bitops/count_zeros.h> + /* You have to define the following before including this file: * * UWtype -- An unsigned type, default type for operations (typically a "word") @@ -146,12 +148,6 @@ do { \ : "1" ((USItype)(n1)), \ "r" ((USItype)(n0)), \ "r" ((USItype)(d))) - -#define count_leading_zeros(count, x) \ - __asm__ ("clz %0,%1" \ - : "=r" ((USItype)(count)) \ - : "r" ((USItype)(x))) -#define COUNT_LEADING_ZEROS_0 32 #endif /* __a29k__ */ #if defined(__alpha) && W_TYPE_SIZE == 64 @@ -298,11 +294,6 @@ extern UDItype __udiv_qrnnd(); : "1" ((USItype)(nh)), \ "0" ((USItype)(nl)), \ "g" ((USItype)(d))) -#define count_leading_zeros(count, x) \ - __asm__ ("bsch/1 %1,%0" \ - : "=g" (count) \ - : "g" ((USItype)(x)), \ - "0" ((USItype)0)) #endif /*************************************** @@ -354,27 +345,6 @@ do { USItype __r; \ } while (0) extern USItype __udiv_qrnnd(); #endif /* LONGLONG_STANDALONE */ -#define count_leading_zeros(count, x) \ -do { \ - USItype __tmp; \ - __asm__ ( \ - "ldi 1,%0\n" \ - "extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \ - "extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n" \ - "ldo 16(%0),%0 ; Yes. Perform add.\n" \ - "extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \ - "extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n" \ - "ldo 8(%0),%0 ; Yes. Perform add.\n" \ - "extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \ - "extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n" \ - "ldo 4(%0),%0 ; Yes. Perform add.\n" \ - "extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \ - "extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n" \ - "ldo 2(%0),%0 ; Yes. Perform add.\n" \ - "extru %1,30,1,%1 ; Extract bit 1.\n" \ - "sub %0,%1,%0 ; Subtract it. " \ - : "=r" (count), "=r" (__tmp) : "1" (x)); \ -} while (0) #endif /* hppa */ /*************************************** @@ -457,15 +427,6 @@ do { \ : "0" ((USItype)(n0)), \ "1" ((USItype)(n1)), \ "rm" ((USItype)(d))) -#define count_leading_zeros(count, x) \ -do { \ - USItype __cbtmp; \ - __asm__ ("bsrl %1,%0" \ - : "=r" (__cbtmp) : "rm" ((USItype)(x))); \ - (count) = __cbtmp ^ 31; \ -} while (0) -#define count_trailing_zeros(count, x) \ - __asm__ ("bsfl %1,%0" : "=r" (count) : "rm" ((USItype)(x))) #ifndef UMUL_TIME #define UMUL_TIME 40 #endif @@ -536,15 +497,6 @@ do { \ "dI" ((USItype)(d))); \ (r) = __rq.__i.__l; (q) = __rq.__i.__h; \ } while (0) -#define count_leading_zeros(count, x) \ -do { \ - USItype __cbtmp; \ - __asm__ ("scanbit %1,%0" \ - : "=r" (__cbtmp) \ - : "r" ((USItype)(x))); \ - (count) = __cbtmp ^ 31; \ -} while (0) -#define COUNT_LEADING_ZEROS_0 (-32) /* sic */ #if defined(__i960mx) /* what is the proper symbol to test??? */ #define rshift_rhlc(r, h, l, c) \ do { \ @@ -603,11 +555,6 @@ do { \ : "0" ((USItype)(n0)), \ "1" ((USItype)(n1)), \ "dmi" ((USItype)(d))) -#define count_leading_zeros(count, x) \ - __asm__ ("bfffo %1{%b2:%b2},%0" \ - : "=d" ((USItype)(count)) \ - : "od" ((USItype)(x)), "n" (0)) -#define COUNT_LEADING_ZEROS_0 32 #else /* not mc68020 */ #define umul_ppmm(xh, xl, a, b) \ do { USItype __umul_tmp1, __umul_tmp2; \ @@ -664,15 +611,6 @@ do { USItype __umul_tmp1, __umul_tmp2; \ "rJ" ((USItype)(bh)), \ "rJ" ((USItype)(al)), \ "rJ" ((USItype)(bl))) -#define count_leading_zeros(count, x) \ -do { \ - USItype __cbtmp; \ - __asm__ ("ff1 %0,%1" \ - : "=r" (__cbtmp) \ - : "r" ((USItype)(x))); \ - (count) = __cbtmp ^ 31; \ -} while (0) -#define COUNT_LEADING_ZEROS_0 63 /* sic */ #if defined(__m88110__) #define umul_ppmm(wh, wl, u, v) \ do { \ @@ -779,12 +717,6 @@ do { \ : "0" (__xx.__ll), \ "g" ((USItype)(d))); \ (r) = __xx.__i.__l; (q) = __xx.__i.__h; }) -#define count_trailing_zeros(count, x) \ -do { \ - __asm__("ffsd %2,%0" \ - : "=r"((USItype) (count)) \ - : "0"((USItype) 0), "r"((USItype) (x))); \ - } while (0) #endif /* __ns32000__ */ /*************************************** @@ -855,11 +787,6 @@ do { \ "rI" ((USItype)(al)), \ "r" ((USItype)(bl))); \ } while (0) -#define count_leading_zeros(count, x) \ - __asm__ ("{cntlz|cntlzw} %0,%1" \ - : "=r" ((USItype)(count)) \ - : "r" ((USItype)(x))) -#define COUNT_LEADING_ZEROS_0 32 #if defined(_ARCH_PPC) #define umul_ppmm(ph, pl, m0, m1) \ do { \ @@ -1001,19 +928,6 @@ do { \ } while (0) #define UMUL_TIME 20 #define UDIV_TIME 200 -#define count_leading_zeros(count, x) \ -do { \ - if ((x) >= 0x10000) \ - __asm__ ("clz %0,%1" \ - : "=r" ((USItype)(count)) \ - : "r" ((USItype)(x) >> 16)); \ - else { \ - __asm__ ("clz %0,%1" \ - : "=r" ((USItype)(count)) \ - : "r" ((USItype)(x))); \ - (count) += 16; \ - } \ -} while (0) #endif /* RT/ROMP */ /*************************************** @@ -1142,13 +1056,6 @@ do { \ "rI" ((USItype)(d)) \ : "%g1" __AND_CLOBBER_CC) #define UDIV_TIME 37 -#define count_leading_zeros(count, x) \ - __asm__ ("scan %1,0,%0" \ - : "=r" ((USItype)(x)) \ - : "r" ((USItype)(count))) -/* Early sparclites return 63 for an argument of 0, but they warn that future - implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0 - undefined. */ #endif /* __sparclite__ */ #endif /* __sparc_v8__ */ /* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd. */ @@ -1454,47 +1361,6 @@ do { \ #define udiv_qrnnd __udiv_qrnnd_c #endif -#undef count_leading_zeros -#if !defined(count_leading_zeros) - extern -#ifdef __STDC__ - const -#endif - unsigned char __clz_tab[]; -#define count_leading_zeros(count, x) \ -do { \ - UWtype __xr = (x); \ - UWtype __a; \ - \ - if (W_TYPE_SIZE <= 32) { \ - __a = __xr < ((UWtype) 1 << 2*__BITS4) \ - ? (__xr < ((UWtype) 1 << __BITS4) ? 0 : __BITS4) \ - : (__xr < ((UWtype) 1 << 3*__BITS4) ? 2*__BITS4 : 3*__BITS4); \ - } \ - else { \ - for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \ - if (((__xr >> __a) & 0xff) != 0) \ - break; \ - } \ - \ - (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \ -} while (0) - /* This version gives a well-defined value for zero. */ -#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE -#endif - -#if !defined(count_trailing_zeros) -/* Define count_trailing_zeros using count_leading_zeros. The latter might be - defined in asm, but if it is not, the C version above is good enough. */ -#define count_trailing_zeros(count, x) \ -do { \ - UWtype __ctz_x = (x); \ - UWtype __ctz_c; \ - count_leading_zeros(__ctz_c, __ctz_x & -__ctz_x); \ - (count) = W_TYPE_SIZE - 1 - __ctz_c; \ -} while (0) -#endif - #ifndef UDIV_NEEDS_NORMALIZATION #define UDIV_NEEDS_NORMALIZATION 0 #endif diff --git a/lib/mpi/mpi-bit.c b/lib/mpi/mpi-bit.c index 5687248..503537e 100644 --- a/lib/mpi/mpi-bit.c +++ b/lib/mpi/mpi-bit.c @@ -45,7 +45,7 @@ unsigned mpi_get_nbits(MPI a) if (a->nlimbs) { mpi_limb_t alimb = a->d[a->nlimbs - 1]; if (alimb) - count_leading_zeros(n, alimb); + n = count_leading_zeros(alimb); else n = BITS_PER_MPI_LIMB; n = BITS_PER_MPI_LIMB - n + (a->nlimbs - 1) * BITS_PER_MPI_LIMB; diff --git a/lib/mpi/mpi-cmp.c b/lib/mpi/mpi-cmp.c new file mode 100644 index 0000000..1871e7b --- /dev/null +++ b/lib/mpi/mpi-cmp.c @@ -0,0 +1,70 @@ +/* mpi-cmp.c - MPI functions + * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "mpi-internal.h" + +int mpi_cmp_ui(MPI u, unsigned long v) +{ + mpi_limb_t limb = v; + + mpi_normalize(u); + if (!u->nlimbs && !limb) + return 0; + if (u->sign) + return -1; + if (u->nlimbs > 1) + return 1; + + if (u->d[0] == limb) + return 0; + else if (u->d[0] > limb) + return 1; + else + return -1; +} +EXPORT_SYMBOL_GPL(mpi_cmp_ui); + +int mpi_cmp(MPI u, MPI v) +{ + mpi_size_t usize, vsize; + int cmp; + + mpi_normalize(u); + mpi_normalize(v); + usize = u->nlimbs; + vsize = v->nlimbs; + if (!u->sign && v->sign) + return 1; + if (u->sign && !v->sign) + return -1; + if (usize != vsize && !u->sign && !v->sign) + return usize - vsize; + if (usize != vsize && u->sign && v->sign) + return vsize + usize; + if (!usize) + return 0; + cmp = mpihelp_cmp(u->d, v->d, usize); + if (!cmp) + return 0; + if ((cmp < 0 ? 1 : 0) == (u->sign ? 1 : 0)) + return 1; + return -1; +} +EXPORT_SYMBOL_GPL(mpi_cmp); diff --git a/lib/mpi/mpi-pow.c b/lib/mpi/mpi-pow.c index 67f3e79..5464c87 100644 --- a/lib/mpi/mpi-pow.c +++ b/lib/mpi/mpi-pow.c @@ -77,7 +77,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod) mp = mp_marker = mpi_alloc_limb_space(msize); if (!mp) goto enomem; - count_leading_zeros(mod_shift_cnt, mod->d[msize - 1]); + mod_shift_cnt = count_leading_zeros(mod->d[msize - 1]); if (mod_shift_cnt) mpihelp_lshift(mp, mod->d, msize, mod_shift_cnt); else @@ -169,7 +169,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod) i = esize - 1; e = ep[i]; - count_leading_zeros(c, e); + c = count_leading_zeros(e); e = (e << c) << 1; /* shift the exp bits to the left, lose msb */ c = BITS_PER_MPI_LIMB - 1 - c; diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c index f0fa659..3962b7f 100644 --- a/lib/mpi/mpicoder.c +++ b/lib/mpi/mpicoder.c @@ -18,10 +18,65 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ +#include <linux/bitops.h> +#include <asm-generic/bitops/count_zeros.h> #include "mpi-internal.h" #define MAX_EXTERN_MPI_BITS 16384 +/** + * mpi_read_raw_data - Read a raw byte stream as a positive integer + * @xbuffer: The data to read + * @nbytes: The amount of data to read + */ +MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes) +{ + const uint8_t *buffer = xbuffer; + int i, j; + unsigned nbits, nlimbs; + mpi_limb_t a; + MPI val = NULL; + + while (nbytes >= 0 && buffer[0] == 0) { + buffer++; + nbytes--; + } + + nbits = nbytes * 8; + if (nbits > MAX_EXTERN_MPI_BITS) { + pr_info("MPI: mpi too large (%u bits)\n", nbits); + return NULL; + } + if (nbytes > 0) + nbits -= count_leading_zeros(buffer[0]); + else + nbits = 0; + + nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB; + val = mpi_alloc(nlimbs); + if (!val) + return NULL; + val->nbits = nbits; + val->sign = 0; + val->nlimbs = nlimbs; + + if (nbytes > 0) { + i = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB; + i %= BYTES_PER_MPI_LIMB; + for (j = nlimbs; j > 0; j--) { + a = 0; + for (; i < BYTES_PER_MPI_LIMB; i++) { + a <<= 8; + a |= *buffer++; + } + i = 0; + val->d[j - 1] = a; + } + } + return val; +} +EXPORT_SYMBOL_GPL(mpi_read_raw_data); + MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) { const uint8_t *buffer = xbuffer; diff --git a/lib/oid_registry.c b/lib/oid_registry.c new file mode 100644 index 0000000..d8de11f --- /dev/null +++ b/lib/oid_registry.c @@ -0,0 +1,170 @@ +/* ASN.1 Object identifier (OID) registry + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/export.h> +#include <linux/oid_registry.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/bug.h> +#include "oid_registry_data.c" + +/** + * look_up_OID - Find an OID registration for the specified data + * @data: Binary representation of the OID + * @datasize: Size of the binary representation + */ +enum OID look_up_OID(const void *data, size_t datasize) +{ + const unsigned char *octets = data; + enum OID oid; + unsigned char xhash; + unsigned i, j, k, hash; + size_t len; + + /* Hash the OID data */ + hash = datasize - 1; + + for (i = 0; i < datasize; i++) + hash += octets[i] * 33; + hash = (hash >> 24) ^ (hash >> 16) ^ (hash >> 8) ^ hash; + hash &= 0xff; + + /* Binary search the OID registry. OIDs are stored in ascending order + * of hash value then ascending order of size and then in ascending + * order of reverse value. + */ + i = 0; + k = OID__NR; + while (i < k) { + j = (i + k) / 2; + + xhash = oid_search_table[j].hash; + if (xhash > hash) { + k = j; + continue; + } + if (xhash < hash) { + i = j + 1; + continue; + } + + oid = oid_search_table[j].oid; + len = oid_index[oid + 1] - oid_index[oid]; + if (len > datasize) { + k = j; + continue; + } + if (len < datasize) { + i = j + 1; + continue; + } + + /* Variation is most likely to be at the tail end of the + * OID, so do the comparison in reverse. + */ + while (len > 0) { + unsigned char a = oid_data[oid_index[oid] + --len]; + unsigned char b = octets[len]; + if (a > b) { + k = j; + goto next; + } + if (a < b) { + i = j + 1; + goto next; + } + } + return oid; + next: + ; + } + + return OID__NR; +} +EXPORT_SYMBOL_GPL(look_up_OID); + +/* + * sprint_OID - Print an Object Identifier into a buffer + * @data: The encoded OID to print + * @datasize: The size of the encoded OID + * @buffer: The buffer to render into + * @bufsize: The size of the buffer + * + * The OID is rendered into the buffer in "a.b.c.d" format and the number of + * bytes is returned. -EBADMSG is returned if the data could not be intepreted + * and -ENOBUFS if the buffer was too small. + */ +int sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize) +{ + const unsigned char *v = data, *end = v + datasize; + unsigned long num; + unsigned char n; + size_t ret; + int count; + + if (v >= end) + return -EBADMSG; + + n = *v++; + ret = count = snprintf(buffer, bufsize, "%u.%u", n / 40, n % 40); + buffer += count; + bufsize -= count; + if (bufsize == 0) + return -ENOBUFS; + + while (v < end) { + num = 0; + n = *v++; + if (!(n & 0x80)) { + num = n; + } else { + num = n & 0x7f; + do { + if (v >= end) + return -EBADMSG; + n = *v++; + num <<= 7; + num |= n & 0x7f; + } while (n & 0x80); + } + ret += count = snprintf(buffer, bufsize, ".%lu", num); + buffer += count; + bufsize -= count; + if (bufsize == 0) + return -ENOBUFS; + } + + return ret; +} +EXPORT_SYMBOL_GPL(sprint_oid); + +/** + * sprint_OID - Print an Object Identifier into a buffer + * @oid: The OID to print + * @buffer: The buffer to render into + * @bufsize: The size of the buffer + * + * The OID is rendered into the buffer in "a.b.c.d" format and the number of + * bytes is returned. + */ +int sprint_OID(enum OID oid, char *buffer, size_t bufsize) +{ + int ret; + + BUG_ON(oid >= OID__NR); + + ret = sprint_oid(oid_data + oid_index[oid], + oid_index[oid + 1] - oid_index[oid], + buffer, bufsize); + BUG_ON(ret == -EBADMSG); + return ret; +} +EXPORT_SYMBOL_GPL(sprint_OID); diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c index 9da7fdd..af14cb4 100644 --- a/net/ceph/crypto.c +++ b/net/ceph/crypto.c @@ -423,14 +423,15 @@ int ceph_encrypt2(struct ceph_crypto_key *secret, void *dst, size_t *dst_len, } } -int ceph_key_instantiate(struct key *key, const void *data, size_t datalen) +int ceph_key_instantiate(struct key *key, struct key_preparsed_payload *prep) { struct ceph_crypto_key *ckey; + size_t datalen = prep->datalen; int ret; void *p; ret = -EINVAL; - if (datalen <= 0 || datalen > 32767 || !data) + if (datalen <= 0 || datalen > 32767 || !prep->data) goto err; ret = key_payload_reserve(key, datalen); @@ -443,8 +444,8 @@ int ceph_key_instantiate(struct key *key, const void *data, size_t datalen) goto err; /* TODO ceph_crypto_key_decode should really take const input */ - p = (void *)data; - ret = ceph_crypto_key_decode(ckey, &p, (char*)data+datalen); + p = (void *)prep->data; + ret = ceph_crypto_key_decode(ckey, &p, (char*)prep->data+datalen); if (ret < 0) goto err_ckey; diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index 9807945..8aa4b11 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -59,13 +59,13 @@ const struct cred *dns_resolver_cache; * "ip1,ip2,...#foo=bar" */ static int -dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen) +dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep) { struct user_key_payload *upayload; unsigned long derrno; int ret; - size_t result_len = 0; - const char *data = _data, *end, *opt; + size_t datalen = prep->datalen, result_len = 0; + const char *data = prep->data, *end, *opt; kenter("%%%d,%s,'%*.*s',%zu", key->serial, key->description, diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index 011d238..7633a75 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -26,8 +26,8 @@ #include "ar-internal.h" static int rxrpc_vet_description_s(const char *); -static int rxrpc_instantiate(struct key *, const void *, size_t); -static int rxrpc_instantiate_s(struct key *, const void *, size_t); +static int rxrpc_instantiate(struct key *, struct key_preparsed_payload *); +static int rxrpc_instantiate_s(struct key *, struct key_preparsed_payload *); static void rxrpc_destroy(struct key *); static void rxrpc_destroy_s(struct key *); static void rxrpc_describe(const struct key *, struct seq_file *); @@ -678,7 +678,7 @@ error: * * if no data is provided, then a no-security key is made */ -static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen) +static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep) { const struct rxrpc_key_data_v1 *v1; struct rxrpc_key_token *token, **pp; @@ -686,26 +686,26 @@ static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen) u32 kver; int ret; - _enter("{%x},,%zu", key_serial(key), datalen); + _enter("{%x},,%zu", key_serial(key), prep->datalen); /* handle a no-security key */ - if (!data && datalen == 0) + if (!prep->data && prep->datalen == 0) return 0; /* determine if the XDR payload format is being used */ - if (datalen > 7 * 4) { - ret = rxrpc_instantiate_xdr(key, data, datalen); + if (prep->datalen > 7 * 4) { + ret = rxrpc_instantiate_xdr(key, prep->data, prep->datalen); if (ret != -EPROTO) return ret; } /* get the key interface version number */ ret = -EINVAL; - if (datalen <= 4 || !data) + if (prep->datalen <= 4 || !prep->data) goto error; - memcpy(&kver, data, sizeof(kver)); - data += sizeof(kver); - datalen -= sizeof(kver); + memcpy(&kver, prep->data, sizeof(kver)); + prep->data += sizeof(kver); + prep->datalen -= sizeof(kver); _debug("KEY I/F VERSION: %u", kver); @@ -715,11 +715,11 @@ static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen) /* deal with a version 1 key */ ret = -EINVAL; - if (datalen < sizeof(*v1)) + if (prep->datalen < sizeof(*v1)) goto error; - v1 = data; - if (datalen != sizeof(*v1) + v1->ticket_length) + v1 = prep->data; + if (prep->datalen != sizeof(*v1) + v1->ticket_length) goto error; _debug("SCIX: %u", v1->security_index); @@ -784,17 +784,17 @@ error: * instantiate a server secret key * data should be a pointer to the 8-byte secret key */ -static int rxrpc_instantiate_s(struct key *key, const void *data, - size_t datalen) +static int rxrpc_instantiate_s(struct key *key, + struct key_preparsed_payload *prep) { struct crypto_blkcipher *ci; - _enter("{%x},,%zu", key_serial(key), datalen); + _enter("{%x},,%zu", key_serial(key), prep->datalen); - if (datalen != 8) + if (prep->datalen != 8) return -EINVAL; - memcpy(&key->type_data, data, 8); + memcpy(&key->type_data, prep->data, 8); ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(ci)) { @@ -802,7 +802,7 @@ static int rxrpc_instantiate_s(struct key *key, const void *data, return PTR_ERR(ci); } - if (crypto_blkcipher_setkey(ci, data, 8) < 0) + if (crypto_blkcipher_setkey(ci, prep->data, 8) < 0) BUG(); key->payload.data = ci; diff --git a/scripts/.gitignore b/scripts/.gitignore index 65f362d..fb070fa 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -10,3 +10,4 @@ ihex2fw recordmcount docproc sortextable +asn1_compiler diff --git a/scripts/Makefile b/scripts/Makefile index a55b006..01e7adb 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -16,8 +16,10 @@ hostprogs-$(CONFIG_VT) += conmakehash hostprogs-$(CONFIG_IKCONFIG) += bin2c hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable +hostprogs-$(CONFIG_ASN1) += asn1_compiler HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include +HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include always := $(hostprogs-y) $(hostprogs-m) diff --git a/scripts/Makefile.build b/scripts/Makefile.build index ff1720d..0e801c3 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -354,6 +354,17 @@ quiet_cmd_cpp_lds_S = LDS $@ $(obj)/%.lds: $(src)/%.lds.S FORCE $(call if_changed_dep,cpp_lds_S) +# ASN.1 grammar +# --------------------------------------------------------------------------- +quiet_cmd_asn1_compiler = ASN.1 $@ + cmd_asn1_compiler = $(objtree)/scripts/asn1_compiler $< \ + $(subst .h,.c,$@) $(subst .c,.h,$@) + +.PRECIOUS: $(objtree)/$(obj)/%-asn1.c $(objtree)/$(obj)/%-asn1.h + +$(obj)/%-asn1.c $(obj)/%-asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler + $(call cmd,asn1_compiler) + # Build the compiled-in targets # --------------------------------------------------------------------------- diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index a1cb022..0020891 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -14,7 +14,8 @@ # 3) create one <module>.mod.c file pr. module # 4) create one Module.symvers file with CRC for all exported symbols # 5) compile all <module>.mod.c files -# 6) final link of the module to a <module.ko> file +# 6) final link of the module to a <module.ko> (or <module.unsigned>) file +# 7) signs the modules to a <module.ko> file # Step 3 is used to place certain information in the module's ELF # section, including information such as: @@ -32,6 +33,8 @@ # Step 4 is solely used to allow module versioning in external modules, # where the CRC of each module is retrieved from the Module.symvers file. +# Step 7 is dependent on CONFIG_MODULE_SIG being enabled. + # KBUILD_MODPOST_WARN can be set to avoid error out in case of undefined # symbols in the final module linking stage # KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules. @@ -116,6 +119,7 @@ $(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE targets += $(modules:.ko=.mod.o) # Step 6), final link of the modules +ifneq ($(CONFIG_MODULE_SIG),y) quiet_cmd_ld_ko_o = LD [M] $@ cmd_ld_ko_o = $(LD) -r $(LDFLAGS) \ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \ @@ -125,7 +129,78 @@ $(modules): %.ko :%.o %.mod.o FORCE $(call if_changed,ld_ko_o) targets += $(modules) +else +quiet_cmd_ld_ko_unsigned_o = LD [M] $@ + cmd_ld_ko_unsigned_o = \ + $(LD) -r $(LDFLAGS) \ + $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \ + -o $@ $(filter-out FORCE,$^) \ + $(if $(AFTER_LINK),; $(AFTER_LINK)) + +$(modules:.ko=.ko.unsigned): %.ko.unsigned :%.o %.mod.o FORCE + $(call if_changed,ld_ko_unsigned_o) + +targets += $(modules:.ko=.ko.unsigned) + +# Step 7), sign the modules +MODSECKEY = ./signing_key.priv +MODPUBKEY = ./signing_key.x509 + +ifeq ($(wildcard $(MODSECKEY))+$(wildcard $(MODPUBKEY)),$(MODSECKEY)+$(MODPUBKEY)) +ifeq ($(KBUILD_SRC),) + # no O= is being used + SCRIPTS_DIR := scripts +else + SCRIPTS_DIR := $(KBUILD_SRC)/scripts +endif +SIGN_MODULES := 1 +else +SIGN_MODULES := 0 +endif + +# only sign if it's an in-tree module +ifneq ($(KBUILD_EXTMOD),) +SIGN_MODULES := 0 +endif +# We strip the module as best we can - note that using both strip and eu-strip +# results in a smaller module than using either alone. +EU_STRIP = $(shell which eu-strip || echo true) + +quiet_cmd_sign_ko_stripped_ko_unsigned = STRIP [M] $@ + cmd_sign_ko_stripped_ko_unsigned = \ + cp $< $@ && \ + strip -x -g $@ && \ + $(EU_STRIP) $@ + +ifeq ($(SIGN_MODULES),1) + +quiet_cmd_genkeyid = GENKEYID $@ + cmd_genkeyid = \ + perl $(SCRIPTS_DIR)/x509keyid $< $<.signer $<.keyid + +%.signer %.keyid: % + $(call if_changed,genkeyid) + +KEYRING_DEP := $(MODSECKEY) $(MODPUBKEY) $(MODPUBKEY).signer $(MODPUBKEY).keyid +quiet_cmd_sign_ko_ko_stripped = SIGN [M] $@ + cmd_sign_ko_ko_stripped = \ + sh $(SCRIPTS_DIR)/sign-file $(MODSECKEY) $(MODPUBKEY) $< $@ +else +KEYRING_DEP := +quiet_cmd_sign_ko_ko_unsigned = NO SIGN [M] $@ + cmd_sign_ko_ko_unsigned = \ + cp $< $@ +endif + +$(modules): %.ko :%.ko.stripped $(KEYRING_DEP) FORCE + $(call if_changed,sign_ko_ko_stripped) + +$(patsubst %.ko,%.ko.stripped,$(modules)): %.ko.stripped :%.ko.unsigned FORCE + $(call if_changed,sign_ko_stripped_ko_unsigned) + +targets += $(modules) +endif # Add FORCE to the prequisites of a target to force it to be always rebuilt. # --------------------------------------------------------------------------- diff --git a/scripts/asn1_compiler.c b/scripts/asn1_compiler.c new file mode 100644 index 0000000..db0e5cd --- /dev/null +++ b/scripts/asn1_compiler.c @@ -0,0 +1,1545 @@ +/* Simplified ASN.1 notation parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <linux/asn1_ber_bytecode.h> + +enum token_type { + DIRECTIVE_ABSENT, + DIRECTIVE_ALL, + DIRECTIVE_ANY, + DIRECTIVE_APPLICATION, + DIRECTIVE_AUTOMATIC, + DIRECTIVE_BEGIN, + DIRECTIVE_BIT, + DIRECTIVE_BMPString, + DIRECTIVE_BOOLEAN, + DIRECTIVE_BY, + DIRECTIVE_CHARACTER, + DIRECTIVE_CHOICE, + DIRECTIVE_CLASS, + DIRECTIVE_COMPONENT, + DIRECTIVE_COMPONENTS, + DIRECTIVE_CONSTRAINED, + DIRECTIVE_CONTAINING, + DIRECTIVE_DEFAULT, + DIRECTIVE_DEFINED, + DIRECTIVE_DEFINITIONS, + DIRECTIVE_EMBEDDED, + DIRECTIVE_ENCODED, + DIRECTIVE_ENCODING_CONTROL, + DIRECTIVE_END, + DIRECTIVE_ENUMERATED, + DIRECTIVE_EXCEPT, + DIRECTIVE_EXPLICIT, + DIRECTIVE_EXPORTS, + DIRECTIVE_EXTENSIBILITY, + DIRECTIVE_EXTERNAL, + DIRECTIVE_FALSE, + DIRECTIVE_FROM, + DIRECTIVE_GeneralString, + DIRECTIVE_GeneralizedTime, + DIRECTIVE_GraphicString, + DIRECTIVE_IA5String, + DIRECTIVE_IDENTIFIER, + DIRECTIVE_IMPLICIT, + DIRECTIVE_IMPLIED, + DIRECTIVE_IMPORTS, + DIRECTIVE_INCLUDES, + DIRECTIVE_INSTANCE, + DIRECTIVE_INSTRUCTIONS, + DIRECTIVE_INTEGER, + DIRECTIVE_INTERSECTION, + DIRECTIVE_ISO646String, + DIRECTIVE_MAX, + DIRECTIVE_MIN, + DIRECTIVE_MINUS_INFINITY, + DIRECTIVE_NULL, + DIRECTIVE_NumericString, + DIRECTIVE_OBJECT, + DIRECTIVE_OCTET, + DIRECTIVE_OF, + DIRECTIVE_OPTIONAL, + DIRECTIVE_ObjectDescriptor, + DIRECTIVE_PATTERN, + DIRECTIVE_PDV, + DIRECTIVE_PLUS_INFINITY, + DIRECTIVE_PRESENT, + DIRECTIVE_PRIVATE, + DIRECTIVE_PrintableString, + DIRECTIVE_REAL, + DIRECTIVE_RELATIVE_OID, + DIRECTIVE_SEQUENCE, + DIRECTIVE_SET, + DIRECTIVE_SIZE, + DIRECTIVE_STRING, + DIRECTIVE_SYNTAX, + DIRECTIVE_T61String, + DIRECTIVE_TAGS, + DIRECTIVE_TRUE, + DIRECTIVE_TeletexString, + DIRECTIVE_UNION, + DIRECTIVE_UNIQUE, + DIRECTIVE_UNIVERSAL, + DIRECTIVE_UTCTime, + DIRECTIVE_UTF8String, + DIRECTIVE_UniversalString, + DIRECTIVE_VideotexString, + DIRECTIVE_VisibleString, + DIRECTIVE_WITH, + NR__DIRECTIVES, + TOKEN_ASSIGNMENT = NR__DIRECTIVES, + TOKEN_OPEN_CURLY, + TOKEN_CLOSE_CURLY, + TOKEN_OPEN_SQUARE, + TOKEN_CLOSE_SQUARE, + TOKEN_OPEN_ACTION, + TOKEN_CLOSE_ACTION, + TOKEN_COMMA, + TOKEN_NUMBER, + TOKEN_TYPE_NAME, + TOKEN_ELEMENT_NAME, + NR__TOKENS +}; + +static const unsigned char token_to_tag[NR__TOKENS] = { + /* EOC goes first */ + [DIRECTIVE_BOOLEAN] = ASN1_BOOL, + [DIRECTIVE_INTEGER] = ASN1_INT, + [DIRECTIVE_BIT] = ASN1_BTS, + [DIRECTIVE_OCTET] = ASN1_OTS, + [DIRECTIVE_NULL] = ASN1_NULL, + [DIRECTIVE_OBJECT] = ASN1_OID, + [DIRECTIVE_ObjectDescriptor] = ASN1_ODE, + [DIRECTIVE_EXTERNAL] = ASN1_EXT, + [DIRECTIVE_REAL] = ASN1_REAL, + [DIRECTIVE_ENUMERATED] = ASN1_ENUM, + [DIRECTIVE_EMBEDDED] = 0, + [DIRECTIVE_UTF8String] = ASN1_UTF8STR, + [DIRECTIVE_RELATIVE_OID] = ASN1_RELOID, + /* 14 */ + /* 15 */ + [DIRECTIVE_SEQUENCE] = ASN1_SEQ, + [DIRECTIVE_SET] = ASN1_SET, + [DIRECTIVE_NumericString] = ASN1_NUMSTR, + [DIRECTIVE_PrintableString] = ASN1_PRNSTR, + [DIRECTIVE_T61String] = ASN1_TEXSTR, + [DIRECTIVE_TeletexString] = ASN1_TEXSTR, + [DIRECTIVE_VideotexString] = ASN1_VIDSTR, + [DIRECTIVE_IA5String] = ASN1_IA5STR, + [DIRECTIVE_UTCTime] = ASN1_UNITIM, + [DIRECTIVE_GeneralizedTime] = ASN1_GENTIM, + [DIRECTIVE_GraphicString] = ASN1_GRASTR, + [DIRECTIVE_VisibleString] = ASN1_VISSTR, + [DIRECTIVE_GeneralString] = ASN1_GENSTR, + [DIRECTIVE_UniversalString] = ASN1_UNITIM, + [DIRECTIVE_CHARACTER] = ASN1_CHRSTR, + [DIRECTIVE_BMPString] = ASN1_BMPSTR, +}; + +static const char asn1_classes[4][5] = { + [ASN1_UNIV] = "UNIV", + [ASN1_APPL] = "APPL", + [ASN1_CONT] = "CONT", + [ASN1_PRIV] = "PRIV" +}; + +static const char asn1_methods[2][5] = { + [ASN1_UNIV] = "PRIM", + [ASN1_APPL] = "CONS" +}; + +static const char *const asn1_universal_tags[32] = { + "EOC", + "BOOL", + "INT", + "BTS", + "OTS", + "NULL", + "OID", + "ODE", + "EXT", + "REAL", + "ENUM", + "EPDV", + "UTF8STR", + "RELOID", + NULL, /* 14 */ + NULL, /* 15 */ + "SEQ", + "SET", + "NUMSTR", + "PRNSTR", + "TEXSTR", + "VIDSTR", + "IA5STR", + "UNITIM", + "GENTIM", + "GRASTR", + "VISSTR", + "GENSTR", + "UNISTR", + "CHRSTR", + "BMPSTR", + NULL /* 31 */ +}; + +static const char *filename; +static const char *grammar_name; +static const char *outputname; +static const char *headername; + +static const char *const directives[NR__DIRECTIVES] = { +#define _(X) [DIRECTIVE_##X] = #X + _(ABSENT), + _(ALL), + _(ANY), + _(APPLICATION), + _(AUTOMATIC), + _(BEGIN), + _(BIT), + _(BMPString), + _(BOOLEAN), + _(BY), + _(CHARACTER), + _(CHOICE), + _(CLASS), + _(COMPONENT), + _(COMPONENTS), + _(CONSTRAINED), + _(CONTAINING), + _(DEFAULT), + _(DEFINED), + _(DEFINITIONS), + _(EMBEDDED), + _(ENCODED), + [DIRECTIVE_ENCODING_CONTROL] = "ENCODING-CONTROL", + _(END), + _(ENUMERATED), + _(EXCEPT), + _(EXPLICIT), + _(EXPORTS), + _(EXTENSIBILITY), + _(EXTERNAL), + _(FALSE), + _(FROM), + _(GeneralString), + _(GeneralizedTime), + _(GraphicString), + _(IA5String), + _(IDENTIFIER), + _(IMPLICIT), + _(IMPLIED), + _(IMPORTS), + _(INCLUDES), + _(INSTANCE), + _(INSTRUCTIONS), + _(INTEGER), + _(INTERSECTION), + _(ISO646String), + _(MAX), + _(MIN), + [DIRECTIVE_MINUS_INFINITY] = "MINUS-INFINITY", + [DIRECTIVE_NULL] = "NULL", + _(NumericString), + _(OBJECT), + _(OCTET), + _(OF), + _(OPTIONAL), + _(ObjectDescriptor), + _(PATTERN), + _(PDV), + [DIRECTIVE_PLUS_INFINITY] = "PLUS-INFINITY", + _(PRESENT), + _(PRIVATE), + _(PrintableString), + _(REAL), + [DIRECTIVE_RELATIVE_OID] = "RELATIVE-OID", + _(SEQUENCE), + _(SET), + _(SIZE), + _(STRING), + _(SYNTAX), + _(T61String), + _(TAGS), + _(TRUE), + _(TeletexString), + _(UNION), + _(UNIQUE), + _(UNIVERSAL), + _(UTCTime), + _(UTF8String), + _(UniversalString), + _(VideotexString), + _(VisibleString), + _(WITH) +}; + +struct action { + struct action *next; + unsigned char index; + char name[]; +}; + +static struct action *action_list; +static unsigned nr_actions; + +struct token { + unsigned short line; + enum token_type token_type : 8; + unsigned char size; + struct action *action; + const char *value; + struct type *type; +}; + +static struct token *token_list; +static unsigned nr_tokens; + +static int directive_compare(const void *_key, const void *_pdir) +{ + const struct token *token = _key; + const char *const *pdir = _pdir, *dir = *pdir; + size_t dlen, clen; + int val; + + dlen = strlen(dir); + clen = (dlen < token->size) ? dlen : token->size; + + //printf("cmp(%*.*s,%s) = ", + // (int)token->size, (int)token->size, token->value, + // dir); + + val = memcmp(token->value, dir, clen); + if (val != 0) { + //printf("%d [cmp]\n", val); + return val; + } + + if (dlen == token->size) { + //printf("0\n"); + return 0; + } + //printf("%d\n", (int)dlen - (int)token->size); + return dlen - token->size; /* shorter -> negative */ +} + +/* + * Tokenise an ASN.1 grammar + */ +static void tokenise(char *buffer, char *end) +{ + struct token *tokens; + char *line, *nl, *p, *q; + unsigned tix, lineno; + + /* Assume we're going to have half as many tokens as we have + * characters + */ + token_list = tokens = calloc((end - buffer) / 2, sizeof(struct token)); + if (!tokens) { + perror(NULL); + exit(1); + } + tix = 0; + + lineno = 0; + while (buffer < end) { + /* First of all, break out a line */ + lineno++; + line = buffer; + nl = memchr(line, '\n', end - buffer); + if (!nl) { + buffer = nl = end; + } else { + buffer = nl + 1; + *nl = '\0'; + } + + /* Remove "--" comments */ + p = line; + next_comment: + while ((p = memchr(p, '-', nl - p))) { + if (p[1] == '-') { + /* Found a comment; see if there's a terminator */ + q = p + 2; + while ((q = memchr(q, '-', nl - q))) { + if (q[1] == '-') { + /* There is - excise the comment */ + q += 2; + memmove(p, q, nl - q); + goto next_comment; + } + q++; + } + *p = '\0'; + nl = p; + break; + } else { + p++; + } + } + + p = line; + while (p < nl) { + /* Skip white space */ + while (p < nl && isspace(*p)) + *(p++) = 0; + if (p >= nl) + break; + + tokens[tix].line = lineno; + tokens[tix].value = p; + + /* Handle string tokens */ + if (isalpha(*p)) { + const char **dir; + + /* Can be a directive, type name or element + * name. Find the end of the name. + */ + q = p + 1; + while (q < nl && (isalnum(*q) || *q == '-' || *q == '_')) + q++; + tokens[tix].size = q - p; + p = q; + + /* If it begins with a lowercase letter then + * it's an element name + */ + if (islower(tokens[tix].value[0])) { + tokens[tix++].token_type = TOKEN_ELEMENT_NAME; + continue; + } + + /* Otherwise we need to search the directive + * table + */ + dir = bsearch(&tokens[tix], directives, + sizeof(directives) / sizeof(directives[1]), + sizeof(directives[1]), + directive_compare); + if (dir) { + tokens[tix++].token_type = dir - directives; + continue; + } + + tokens[tix++].token_type = TOKEN_TYPE_NAME; + continue; + } + + /* Handle numbers */ + if (isdigit(*p)) { + /* Find the end of the number */ + q = p + 1; + while (q < nl && (isdigit(*q))) + q++; + tokens[tix].size = q - p; + p = q; + tokens[tix++].token_type = TOKEN_NUMBER; + continue; + } + + if (nl - p >= 3) { + if (memcmp(p, "::=", 3) == 0) { + p += 3; + tokens[tix].size = 3; + tokens[tix++].token_type = TOKEN_ASSIGNMENT; + continue; + } + } + + if (nl - p >= 2) { + if (memcmp(p, "({", 2) == 0) { + p += 2; + tokens[tix].size = 2; + tokens[tix++].token_type = TOKEN_OPEN_ACTION; + continue; + } + if (memcmp(p, "})", 2) == 0) { + p += 2; + tokens[tix].size = 2; + tokens[tix++].token_type = TOKEN_CLOSE_ACTION; + continue; + } + } + + if (nl - p >= 1) { + tokens[tix].size = 1; + switch (*p) { + case '{': + p += 1; + tokens[tix++].token_type = TOKEN_OPEN_CURLY; + continue; + case '}': + p += 1; + tokens[tix++].token_type = TOKEN_CLOSE_CURLY; + continue; + case '[': + p += 1; + tokens[tix++].token_type = TOKEN_OPEN_SQUARE; + continue; + case ']': + p += 1; + tokens[tix++].token_type = TOKEN_CLOSE_SQUARE; + continue; + case ',': + p += 1; + tokens[tix++].token_type = TOKEN_COMMA; + continue; + default: + break; + } + } + + fprintf(stderr, "%s:%u: Unknown character in grammar: '%c'\n", + filename, lineno, *p); + exit(1); + } + } + + nr_tokens = tix; + printf("Extracted %u tokens\n", nr_tokens); + +#if 0 + { + int n; + for (n = 0; n < nr_tokens; n++) + printf("Token %3u: '%*.*s'\n", + n, + (int)token_list[n].size, (int)token_list[n].size, + token_list[n].value); + } +#endif +} + +static void build_type_list(void); +static void parse(void); +static void render(FILE *out, FILE *hdr); + +/* + * + */ +int main(int argc, char **argv) +{ + struct stat st; + ssize_t readlen; + FILE *out, *hdr; + char *buffer, *p; + int fd; + + if (argc != 4) { + fprintf(stderr, "Format: %s <grammar-file> <c-file> <hdr-file>\n", + argv[0]); + exit(2); + } + + filename = argv[1]; + outputname = argv[2]; + headername = argv[3]; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror(filename); + exit(1); + } + + if (fstat(fd, &st) < 0) { + perror(filename); + exit(1); + } + + if (!(buffer = malloc(st.st_size + 1))) { + perror(NULL); + exit(1); + } + + if ((readlen = read(fd, buffer, st.st_size)) < 0) { + perror(filename); + exit(1); + } + + if (close(fd) < 0) { + perror(filename); + exit(1); + } + + if (readlen != st.st_size) { + fprintf(stderr, "%s: Short read\n", filename); + exit(1); + } + + p = strrchr(argv[1], '/'); + p = p ? p + 1 : argv[1]; + grammar_name = strdup(p); + if (!p) { + perror(NULL); + exit(1); + } + p = strchr(grammar_name, '.'); + if (p) + *p = '\0'; + + buffer[readlen] = 0; + tokenise(buffer, buffer + readlen); + build_type_list(); + parse(); + + out = fopen(outputname, "w"); + if (!out) { + perror(outputname); + exit(1); + } + + hdr = fopen(headername, "w"); + if (!out) { + perror(headername); + exit(1); + } + + render(out, hdr); + + if (fclose(out) < 0) { + perror(outputname); + exit(1); + } + + if (fclose(hdr) < 0) { + perror(headername); + exit(1); + } + + return 0; +} + +enum compound { + NOT_COMPOUND, + SET, + SET_OF, + SEQUENCE, + SEQUENCE_OF, + CHOICE, + ANY, + TYPE_REF, + TAG_OVERRIDE +}; + +struct element { + struct type *type_def; + struct token *name; + struct token *type; + struct action *action; + struct element *children; + struct element *next; + struct element *render_next; + struct element *list_next; + uint8_t n_elements; + enum compound compound : 8; + enum asn1_class class : 8; + enum asn1_method method : 8; + uint8_t tag; + unsigned entry_index; + unsigned flags; +#define ELEMENT_IMPLICIT 0x0001 +#define ELEMENT_EXPLICIT 0x0002 +#define ELEMENT_MARKED 0x0004 +#define ELEMENT_RENDERED 0x0008 +#define ELEMENT_SKIPPABLE 0x0010 +#define ELEMENT_CONDITIONAL 0x0020 +}; + +struct type { + struct token *name; + struct token *def; + struct element *element; + unsigned ref_count; + unsigned flags; +#define TYPE_STOP_MARKER 0x0001 +#define TYPE_BEGIN 0x0002 +}; + +static struct type *type_list; +static struct type **type_index; +static unsigned nr_types; + +static int type_index_compare(const void *_a, const void *_b) +{ + const struct type *const *a = _a, *const *b = _b; + + if ((*a)->name->size != (*b)->name->size) + return (*a)->name->size - (*b)->name->size; + else + return memcmp((*a)->name->value, (*b)->name->value, + (*a)->name->size); +} + +static int type_finder(const void *_key, const void *_ti) +{ + const struct token *token = _key; + const struct type *const *ti = _ti; + const struct type *type = *ti; + + if (token->size != type->name->size) + return token->size - type->name->size; + else + return memcmp(token->value, type->name->value, + token->size); +} + +/* + * Build up a list of types and a sorted index to that list. + */ +static void build_type_list(void) +{ + struct type *types; + unsigned nr, t, n; + + nr = 0; + for (n = 0; n < nr_tokens - 1; n++) + if (token_list[n + 0].token_type == TOKEN_TYPE_NAME && + token_list[n + 1].token_type == TOKEN_ASSIGNMENT) + nr++; + + if (nr == 0) { + fprintf(stderr, "%s: No defined types\n", filename); + exit(1); + } + + nr_types = nr; + types = type_list = calloc(nr + 1, sizeof(type_list[0])); + if (!type_list) { + perror(NULL); + exit(1); + } + type_index = calloc(nr, sizeof(type_index[0])); + if (!type_index) { + perror(NULL); + exit(1); + } + + t = 0; + types[t].flags |= TYPE_BEGIN; + for (n = 0; n < nr_tokens - 1; n++) { + if (token_list[n + 0].token_type == TOKEN_TYPE_NAME && + token_list[n + 1].token_type == TOKEN_ASSIGNMENT) { + types[t].name = &token_list[n]; + type_index[t] = &types[t]; + t++; + } + } + types[t].name = &token_list[n + 1]; + types[t].flags |= TYPE_STOP_MARKER; + + qsort(type_index, nr, sizeof(type_index[0]), type_index_compare); + + printf("Extracted %u types\n", nr_types); +#if 0 + for (n = 0; n < nr_types; n++) { + struct type *type = type_index[n]; + printf("- %*.*s\n", + (int)type->name->size, + (int)type->name->size, + type->name->value); + } +#endif +} + +static struct element *parse_type(struct token **_cursor, struct token *stop, + struct token *name); + +/* + * Parse the token stream + */ +static void parse(void) +{ + struct token *cursor; + struct type *type; + + /* Parse one type definition statement at a time */ + type = type_list; + do { + cursor = type->name; + + if (cursor[0].token_type != TOKEN_TYPE_NAME || + cursor[1].token_type != TOKEN_ASSIGNMENT) + abort(); + cursor += 2; + + type->element = parse_type(&cursor, type[1].name, NULL); + 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); + exit(1); + } + + } while (type++, !(type->flags & TYPE_STOP_MARKER)); + + printf("Extracted %u actions\n", nr_actions); +} + +static struct element *element_list; + +static struct element *alloc_elem(struct token *type) +{ + struct element *e = calloc(1, sizeof(*e)); + if (!e) { + perror(NULL); + exit(1); + } + e->list_next = element_list; + element_list = e; + return e; +} + +static struct element *parse_compound(struct token **_cursor, struct token *end, + int alternates); + +/* + * Parse one type definition statement + */ +static struct element *parse_type(struct token **_cursor, struct token *end, + struct token *name) +{ + struct element *top, *element; + struct action *action, **ppaction; + struct token *cursor = *_cursor; + struct type **ref; + char *p; + int labelled = 0, implicit = 0; + + top = element = alloc_elem(cursor); + element->class = ASN1_UNIV; + element->method = ASN1_PRIM; + element->tag = token_to_tag[cursor->token_type]; + element->name = name; + + /* Extract the tag value if one given */ + if (cursor->token_type == TOKEN_OPEN_SQUARE) { + cursor++; + if (cursor >= end) + goto overrun_error; + switch (cursor->token_type) { + case DIRECTIVE_UNIVERSAL: + element->class = ASN1_UNIV; + cursor++; + break; + case DIRECTIVE_APPLICATION: + element->class = ASN1_APPL; + cursor++; + break; + case TOKEN_NUMBER: + element->class = ASN1_CONT; + break; + case DIRECTIVE_PRIVATE: + element->class = ASN1_PRIV; + cursor++; + break; + default: + fprintf(stderr, "%s:%d: Unrecognised tag class token '%*.*s'\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + 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); + exit(1); + } + + element->tag &= ~0x1f; + element->tag |= strtoul(cursor->value, &p, 10); + if (p - cursor->value != 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); + exit(1); + } + cursor++; + if (cursor >= end) + goto overrun_error; + labelled = 1; + } + + /* Handle implicit and explicit markers */ + if (cursor->token_type == DIRECTIVE_IMPLICIT) { + element->flags |= ELEMENT_IMPLICIT; + implicit = 1; + cursor++; + if (cursor >= end) + goto overrun_error; + } else if (cursor->token_type == DIRECTIVE_EXPLICIT) { + element->flags |= ELEMENT_EXPLICIT; + cursor++; + if (cursor >= end) + goto overrun_error; + } + + if (labelled) { + if (!implicit) + element->method |= ASN1_CONS; + element->compound = implicit ? TAG_OVERRIDE : SEQUENCE; + element->children = alloc_elem(cursor); + element = element->children; + element->class = ASN1_UNIV; + element->method = ASN1_PRIM; + element->tag = token_to_tag[cursor->token_type]; + element->name = name; + } + + /* Extract the type we're expecting here */ + element->type = cursor; + switch (cursor->token_type) { + case DIRECTIVE_ANY: + element->compound = ANY; + cursor++; + break; + + case DIRECTIVE_NULL: + case DIRECTIVE_BOOLEAN: + case DIRECTIVE_ENUMERATED: + case DIRECTIVE_INTEGER: + element->compound = NOT_COMPOUND; + cursor++; + break; + + case DIRECTIVE_EXTERNAL: + element->method = ASN1_CONS; + + case DIRECTIVE_BMPString: + case DIRECTIVE_GeneralString: + case DIRECTIVE_GraphicString: + case DIRECTIVE_IA5String: + case DIRECTIVE_ISO646String: + case DIRECTIVE_NumericString: + case DIRECTIVE_PrintableString: + case DIRECTIVE_T61String: + case DIRECTIVE_TeletexString: + case DIRECTIVE_UniversalString: + case DIRECTIVE_UTF8String: + case DIRECTIVE_VideotexString: + case DIRECTIVE_VisibleString: + case DIRECTIVE_ObjectDescriptor: + case DIRECTIVE_GeneralizedTime: + case DIRECTIVE_UTCTime: + element->compound = NOT_COMPOUND; + cursor++; + break; + + case DIRECTIVE_BIT: + case DIRECTIVE_OCTET: + element->compound = NOT_COMPOUND; + cursor++; + if (cursor >= end) + goto overrun_error; + if (cursor->token_type != DIRECTIVE_STRING) + goto parse_error; + cursor++; + break; + + case DIRECTIVE_OBJECT: + element->compound = NOT_COMPOUND; + cursor++; + if (cursor >= end) + goto overrun_error; + if (cursor->token_type != DIRECTIVE_IDENTIFIER) + goto parse_error; + cursor++; + break; + + case TOKEN_TYPE_NAME: + element->compound = TYPE_REF; + 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); + exit(1); + } + cursor->type = *ref; + (*ref)->ref_count++; + cursor++; + break; + + case DIRECTIVE_CHOICE: + element->compound = CHOICE; + cursor++; + element->children = parse_compound(&cursor, end, 1); + break; + + case DIRECTIVE_SEQUENCE: + element->compound = SEQUENCE; + element->method = ASN1_CONS; + cursor++; + if (cursor >= end) + goto overrun_error; + if (cursor->token_type == DIRECTIVE_OF) { + element->compound = SEQUENCE_OF; + cursor++; + if (cursor >= end) + goto overrun_error; + element->children = parse_type(&cursor, end, NULL); + } else { + element->children = parse_compound(&cursor, end, 0); + } + break; + + case DIRECTIVE_SET: + element->compound = SET; + element->method = ASN1_CONS; + cursor++; + if (cursor >= end) + goto overrun_error; + if (cursor->token_type == DIRECTIVE_OF) { + element->compound = SET_OF; + cursor++; + if (cursor >= end) + goto parse_error; + element->children = parse_type(&cursor, end, NULL); + } else { + element->children = parse_compound(&cursor, end, 1); + } + 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); + exit(1); + } + + /* Handle elements that are optional */ + if (cursor < end && (cursor->token_type == DIRECTIVE_OPTIONAL || + cursor->token_type == DIRECTIVE_DEFAULT) + ) { + cursor++; + top->flags |= ELEMENT_SKIPPABLE; + } + + if (cursor < end && cursor->token_type == TOKEN_OPEN_ACTION) { + cursor++; + 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); + exit(1); + } + + action = malloc(sizeof(struct action) + cursor->size + 1); + if (!action) { + perror(NULL); + exit(1); + } + action->index = 0; + memcpy(action->name, cursor->value, cursor->size); + action->name[cursor->size] = 0; + + for (ppaction = &action_list; + *ppaction; + ppaction = &(*ppaction)->next + ) { + int cmp = strcmp(action->name, (*ppaction)->name); + if (cmp == 0) { + free(action); + action = *ppaction; + goto found; + } + if (cmp < 0) { + action->next = *ppaction; + *ppaction = action; + nr_actions++; + goto found; + } + } + action->next = NULL; + *ppaction = action; + nr_actions++; + found: + + element->action = action; + cursor->action = action; + cursor++; + 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); + exit(1); + } + cursor++; + } + + *_cursor = cursor; + return top; + +parse_error: + fprintf(stderr, "%s:%d: Unexpected token '%*.*s'\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + +overrun_error: + fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename); + exit(1); +} + +/* + * Parse a compound type list + */ +static struct element *parse_compound(struct token **_cursor, struct token *end, + int alternates) +{ + struct element *children, **child_p = &children, *element; + 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); + exit(1); + } + cursor++; + if (cursor >= end) + goto overrun_error; + + if (cursor->token_type == TOKEN_OPEN_CURLY) { + fprintf(stderr, "%s:%d: Empty compound\n", + filename, cursor->line); + exit(1); + } + + for (;;) { + name = NULL; + if (cursor->token_type == TOKEN_ELEMENT_NAME) { + name = cursor; + cursor++; + if (cursor >= end) + goto overrun_error; + } + + element = parse_type(&cursor, end, name); + if (alternates) + element->flags |= ELEMENT_SKIPPABLE | ELEMENT_CONDITIONAL; + + *child_p = element; + child_p = &element->next; + + if (cursor >= end) + goto overrun_error; + if (cursor->token_type != TOKEN_COMMA) + break; + cursor++; + if (cursor >= end) + goto overrun_error; + } + + 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); + exit(1); + } + cursor++; + + *_cursor = cursor; + return children; + +overrun_error: + fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename); + exit(1); +} + +static void render_element(FILE *out, struct element *e, struct element *tag); +static void render_out_of_line_list(FILE *out); + +static int nr_entries; +static int render_depth = 1; +static struct element *render_list, **render_list_p = &render_list; + +__attribute__((format(printf, 2, 3))) +static void render_opcode(FILE *out, const char *fmt, ...) +{ + va_list va; + + if (out) { + fprintf(out, "\t[%4d] =%*s", nr_entries, render_depth, ""); + va_start(va, fmt); + vfprintf(out, fmt, va); + va_end(va); + } + nr_entries++; +} + +__attribute__((format(printf, 2, 3))) +static void render_more(FILE *out, const char *fmt, ...) +{ + va_list va; + + if (out) { + va_start(va, fmt); + vfprintf(out, fmt, va); + va_end(va); + } +} + +/* + * Render the grammar into a state machine definition. + */ +static void render(FILE *out, FILE *hdr) +{ + struct element *e; + struct action *action; + struct type *root; + int index; + + fprintf(hdr, "/*\n"); + fprintf(hdr, " * Automatically generated by asn1_compiler. Do not edit\n"); + fprintf(hdr, " *\n"); + fprintf(hdr, " * ASN.1 parser for %s\n", grammar_name); + fprintf(hdr, " */\n"); + fprintf(hdr, "#include <linux/asn1_decoder.h>\n"); + fprintf(hdr, "\n"); + fprintf(hdr, "extern const struct asn1_decoder %s_decoder;\n", grammar_name); + if (ferror(hdr)) { + perror(headername); + exit(1); + } + + fprintf(out, "/*\n"); + fprintf(out, " * Automatically generated by asn1_compiler. Do not edit\n"); + fprintf(out, " *\n"); + fprintf(out, " * ASN.1 parser for %s\n", grammar_name); + fprintf(out, " */\n"); + fprintf(out, "#include <linux/asn1_ber_bytecode.h>\n"); + fprintf(out, "#include \"%s-asn1.h\"\n", grammar_name); + fprintf(out, "\n"); + if (ferror(out)) { + perror(outputname); + exit(1); + } + + /* Tabulate the action functions we might have to call */ + fprintf(hdr, "\n"); + index = 0; + for (action = action_list; action; action = action->next) { + action->index = index++; + fprintf(hdr, + "extern int %s(void *, size_t, unsigned char," + " const void *, size_t);\n", + action->name); + } + fprintf(hdr, "\n"); + + fprintf(out, "enum %s_actions {\n", grammar_name); + for (action = action_list; action; action = action->next) + fprintf(out, "\tACT_%s = %u,\n", + action->name, action->index); + fprintf(out, "\tNR__%s_actions = %u\n", grammar_name, nr_actions); + fprintf(out, "};\n"); + + fprintf(out, "\n"); + fprintf(out, "static const asn1_action_t %s_action_table[NR__%s_actions] = {\n", + grammar_name, grammar_name); + for (action = action_list; action; action = action->next) + fprintf(out, "\t[%4u] = %s,\n", action->index, action->name); + fprintf(out, "};\n"); + + if (ferror(out)) { + perror(outputname); + exit(1); + } + + /* We do two passes - the first one calculates all the offsets */ + printf("Pass 1\n"); + nr_entries = 0; + root = &type_list[0]; + render_element(NULL, root->element, NULL); + render_opcode(NULL, "ASN1_OP_COMPLETE,\n"); + render_out_of_line_list(NULL); + + for (e = element_list; e; e = e->list_next) + e->flags &= ~ELEMENT_RENDERED; + + /* And then we actually render */ + printf("Pass 2\n"); + fprintf(out, "\n"); + fprintf(out, "static const unsigned char %s_machine[] = {\n", + grammar_name); + + nr_entries = 0; + root = &type_list[0]; + render_element(out, root->element, NULL); + render_opcode(out, "ASN1_OP_COMPLETE,\n"); + render_out_of_line_list(out); + + fprintf(out, "};\n"); + + fprintf(out, "\n"); + fprintf(out, "const struct asn1_decoder %s_decoder = {\n", grammar_name); + fprintf(out, "\t.machine = %s_machine,\n", grammar_name); + fprintf(out, "\t.machlen = sizeof(%s_machine),\n", grammar_name); + fprintf(out, "\t.actions = %s_action_table,\n", grammar_name); + fprintf(out, "};\n"); +} + +/* + * Render the out-of-line elements + */ +static void render_out_of_line_list(FILE *out) +{ + struct element *e, *ce; + const char *act; + int entry; + + while ((e = render_list)) { + render_list = e->render_next; + if (!render_list) + render_list_p = &render_list; + + render_more(out, "\n"); + e->entry_index = entry = nr_entries; + render_depth++; + for (ce = e->children; ce; ce = ce->next) + render_element(out, ce, NULL); + render_depth--; + + act = e->action ? "_ACT" : ""; + switch (e->compound) { + case SEQUENCE: + render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act); + break; + case SEQUENCE_OF: + render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act); + render_opcode(out, "_jump_target(%u),\n", entry); + break; + case SET: + render_opcode(out, "ASN1_OP_END_SET%s,\n", act); + break; + case SET_OF: + render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act); + render_opcode(out, "_jump_target(%u),\n", entry); + break; + } + if (e->action) + render_opcode(out, "_action(ACT_%s),\n", + e->action->name); + render_opcode(out, "ASN1_OP_RETURN,\n"); + } +} + +/* + * Render an element. + */ +static void render_element(FILE *out, struct element *e, struct element *tag) +{ + struct element *ec; + const char *cond, *act; + int entry, skippable = 0, outofline = 0; + + if (e->flags & ELEMENT_SKIPPABLE || + (tag && tag->flags & ELEMENT_SKIPPABLE)) + skippable = 1; + + if ((e->type_def && e->type_def->ref_count > 1) || + skippable) + 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 the operation */ + cond = (e->flags & ELEMENT_CONDITIONAL || + (tag && tag->flags & ELEMENT_CONDITIONAL)) ? "COND_" : ""; + act = e->action ? "_ACT" : ""; + switch (e->compound) { + case ANY: + render_opcode(out, "ASN1_OP_%sMATCH_ANY%s,", cond, act); + if (e->name) + render_more(out, "\t\t// %*.*s", + (int)e->name->size, (int)e->name->size, + e->name->value); + render_more(out, "\n"); + goto dont_render_tag; + + case TAG_OVERRIDE: + render_element(out, e->children, e); + return; + + case SEQUENCE: + case SEQUENCE_OF: + case SET: + case SET_OF: + render_opcode(out, "ASN1_OP_%sMATCH%s%s,", + cond, + outofline ? "_JUMP" : "", + skippable ? "_OR_SKIP" : ""); + break; + + case CHOICE: + goto dont_render_tag; + + case TYPE_REF: + if (e->class == ASN1_UNIV && e->method == ASN1_PRIM && e->tag == 0) + goto dont_render_tag; + default: + render_opcode(out, "ASN1_OP_%sMATCH%s%s,", + cond, act, + skippable ? "_OR_SKIP" : ""); + break; + } + + if (e->name) + render_more(out, "\t\t// %*.*s", + (int)e->name->size, (int)e->name->size, + e->name->value); + render_more(out, "\n"); + + /* Render the tag */ + if (!tag) + tag = e; + if (tag->class == ASN1_UNIV && + tag->tag != 14 && + tag->tag != 15 && + tag->tag != 31) + render_opcode(out, "_tag(%s, %s, %s),\n", + asn1_classes[tag->class], + asn1_methods[tag->method | e->method], + asn1_universal_tags[tag->tag]); + else + render_opcode(out, "_tagn(%s, %s, %2u),\n", + asn1_classes[tag->class], + asn1_methods[tag->method | e->method], + tag->tag); + tag = NULL; +dont_render_tag: + + /* Deal with compound types */ + switch (e->compound) { + case TYPE_REF: + render_element(out, e->type->type->element, tag); + if (e->action) + render_opcode(out, "ASN1_OP_ACT,\n"); + break; + + case SEQUENCE: + if (outofline) { + /* Render out-of-line for multiple use or + * 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, "\n"); + if (!(e->flags & ELEMENT_RENDERED)) { + e->flags |= ELEMENT_RENDERED; + *render_list_p = e; + render_list_p = &e->render_next; + } + return; + } else { + /* Render inline for single use */ + render_depth++; + for (ec = e->children; ec; ec = ec->next) + render_element(out, ec, NULL); + render_depth--; + render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act); + } + break; + + case SEQUENCE_OF: + case SET_OF: + if (outofline) { + /* Render out-of-line for multiple use or + * 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, "\n"); + if (!(e->flags & ELEMENT_RENDERED)) { + e->flags |= ELEMENT_RENDERED; + *render_list_p = e; + render_list_p = &e->render_next; + } + return; + } else { + /* Render inline for single use */ + entry = nr_entries; + render_depth++; + render_element(out, e->children, NULL); + render_depth--; + if (e->compound == SEQUENCE_OF) + render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act); + else + render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act); + render_opcode(out, "_jump_target(%u),\n", entry); + } + break; + + case SET: + /* I can't think of a nice way to do SET support without having + * a stack of bitmasks to make sure no element is repeated. + * The bitmask has also to be checked that no non-optional + * elements are left out whilst not preventing optional + * elements from being left out. + */ + fprintf(stderr, "The ASN.1 SET type is not currently supported.\n"); + exit(1); + + case CHOICE: + for (ec = e->children; ec; ec = ec->next) + render_element(out, ec, NULL); + if (!skippable) + render_opcode(out, "ASN1_OP_COND_FAIL,\n"); + if (e->action) + render_opcode(out, "ASN1_OP_ACT,\n"); + break; + + default: + break; + } + + if (e->action) + render_opcode(out, "_action(ACT_%s),\n", e->action->name); +} diff --git a/scripts/sign-file b/scripts/sign-file new file mode 100644 index 0000000..e58e34e --- /dev/null +++ b/scripts/sign-file @@ -0,0 +1,115 @@ +#!/bin/sh +# +# Sign a module file using the given key. +# +# Format: sign-file <key> <x509> <src-file> <dst-file> +# + +scripts=`dirname $0` + +CONFIG_MODULE_SIG_SHA512=y +if [ -r .config ] +then + . ./.config +fi + +key="$1" +x509="$2" +src="$3" +dst="$4" + +if [ ! -r "$key" ] +then + echo "Can't read private key" >&2 + exit 2 +fi + +if [ ! -r "$x509" ] +then + echo "Can't read X.509 certificate" >&2 + exit 2 +fi +if [ ! -r "$x509.signer" ] +then + echo "Can't read Signer name" >&2 + exit 2; +fi +if [ ! -r "$x509.keyid" ] +then + echo "Can't read Key identifier" >&2 + exit 2; +fi + +# +# Signature parameters +# +algo=1 # Public-key crypto algorithm: RSA +hash= # Digest algorithm +id_type=1 # Identifier type: X.509 + +# +# Digest the data +# +dgst= +if [ "$CONFIG_MODULE_SIG_SHA1" = "y" ] +then + prologue="0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14" + dgst=-sha1 + hash=2 +elif [ "$CONFIG_MODULE_SIG_SHA224" = "y" ] +then + prologue="0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C" + dgst=-sha224 + hash=7 +elif [ "$CONFIG_MODULE_SIG_SHA256" = "y" ] +then + prologue="0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20" + dgst=-sha256 + hash=4 +elif [ "$CONFIG_MODULE_SIG_SHA384" = "y" ] +then + prologue="0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30" + dgst=-sha384 + hash=5 +elif [ "$CONFIG_MODULE_SIG_SHA512" = "y" ] +then + prologue="0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40" + dgst=-sha512 + hash=6 +else + echo "$0: Can't determine hash algorithm" >&2 + exit 2 +fi + +( +perl -e "binmode STDOUT; print pack(\"C*\", $prologue)" || exit $? +openssl dgst $dgst -binary $src || exit $? +) >$src.dig || exit $? + +# +# Generate the binary signature, which will be just the integer that comprises +# the signature with no metadata attached. +# +openssl rsautl -sign -inkey $key -keyform PEM -in $src.dig -out $src.sig || exit $? +signerlen=`stat -c %s $x509.signer` +keyidlen=`stat -c %s $x509.keyid` +siglen=`stat -c %s $src.sig` + +# +# Build the signed binary +# +( + cat $src || exit $? + echo '~Module signature appended~' || exit $? + cat $x509.signer $x509.keyid || exit $? + + # Preface each signature integer with a 2-byte BE length + perl -e "binmode STDOUT; print pack(\"n\", $siglen)" || exit $? + cat $src.sig || exit $? + + # Generate the information block + perl -e "binmode STDOUT; print pack(\"CCCCCxxxN\", $algo, $hash, $id_type, $signerlen, $keyidlen, $siglen + 2)" || exit $? +) >$dst~ || exit $? + +# Permit in-place signing +mv $dst~ $dst || exit $? diff --git a/scripts/x509keyid b/scripts/x509keyid new file mode 100755 index 0000000..c8e91a4 --- /dev/null +++ b/scripts/x509keyid @@ -0,0 +1,268 @@ +#!/usr/bin/perl -w +# +# Generate an identifier from an X.509 certificate that can be placed in a +# module signature to indentify the key to use. +# +# Format: +# +# ./scripts/x509keyid <x509-cert> <signer's-name> <key-id> +# +# We read the DER-encoded X509 certificate and parse it to extract the Subject +# name and Subject Key Identifier. The provide 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 and written to the second named file. +# +# The subject key ID to select which of that signer's certificates we're +# intending to use to sign the module is written to the third named file. +# +use strict; + +my $raw_data; + +die "Need three filenames\n" if ($#ARGV != 2); + +my $src = $ARGV[0]; + +open(FD, "<$src") || die $src; +binmode FD; +my @st = stat(FD); +die $src if (!@st); +read(FD, $raw_data, $st[7]) || die $src; +close(FD); + +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 $src, ": ", $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 $src, ": ", $cursor->[0], ": ASN.1 unexpected tag (", $tag, + " not ", $expected_tag, ")\n"; + } + + $cursor->[0] += 2; + $cursor->[1] -= 2; + + die $src, ": ", $cursor->[0], ": ASN.1 long tag\n" + if (($tag & 0x1f) == 0x1f); + die $src, ": ", $cursor->[0], ": ASN.1 indefinite length\n" + if ($len == 0x80); + + if ($len > 0x80) { + my $l = $len - 0x80; + die $src, ": ", $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 $src, ": ", $cursor->[0], ": ASN.1 element too long (", $l, ")\n"; + } + + $cursor->[0] += $l; + $cursor->[1] -= $l; + } + + die $src, ": ", $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($raw_data), \$raw_data ]; + +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 $id_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) { + $id_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)) { + $id_name = $cn; + goto got_id_name; + } + + $id_name = $org . ": " . $cn; +} elsif ($org) { + $id_name = $org; +} elsif ($cn) { + $id_name = $cn; +} + +got_id_name: + +############################################################################### +# +# Output the signer's name and the key identifier that we're going to include +# in module signatures. +# +############################################################################### +die $src, ": ", "X.509: Couldn't find the Subject Key Identifier extension\n" + if (!$subject_key_id); + +my $id_key_id = asn1_retrieve($subject_key_id->[1]); + +open(OUTFD, ">$ARGV[1]") || die $ARGV[1]; +print OUTFD $id_name; +close OUTFD || die $ARGV[1]; + +open(OUTFD, ">$ARGV[2]") || die $ARGV[2]; +print OUTFD $id_key_id; +close OUTFD || die $ARGV[2]; diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 2d1bb8a..9e1e005 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -773,8 +773,8 @@ static int encrypted_init(struct encrypted_key_payload *epayload, * * On success, return 0. Otherwise return errno. */ -static int encrypted_instantiate(struct key *key, const void *data, - size_t datalen) +static int encrypted_instantiate(struct key *key, + struct key_preparsed_payload *prep) { struct encrypted_key_payload *epayload = NULL; char *datablob = NULL; @@ -782,16 +782,17 @@ static int encrypted_instantiate(struct key *key, const void *data, char *master_desc = NULL; char *decrypted_datalen = NULL; char *hex_encoded_iv = NULL; + size_t datalen = prep->datalen; int ret; - if (datalen <= 0 || datalen > 32767 || !data) + if (datalen <= 0 || datalen > 32767 || !prep->data) return -EINVAL; datablob = kmalloc(datalen + 1, GFP_KERNEL); if (!datablob) return -ENOMEM; datablob[datalen] = 0; - memcpy(datablob, data, datalen); + memcpy(datablob, prep->data, datalen); ret = datablob_parse(datablob, &format, &master_desc, &decrypted_datalen, &hex_encoded_iv); if (ret < 0) @@ -834,16 +835,17 @@ static void encrypted_rcu_free(struct rcu_head *rcu) * * On success, return 0. Otherwise return errno. */ -static int encrypted_update(struct key *key, const void *data, size_t datalen) +static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) { struct encrypted_key_payload *epayload = key->payload.data; struct encrypted_key_payload *new_epayload; char *buf; char *new_master_desc = NULL; const char *format = NULL; + size_t datalen = prep->datalen; int ret = 0; - if (datalen <= 0 || datalen > 32767 || !data) + if (datalen <= 0 || datalen > 32767 || !prep->data) return -EINVAL; buf = kmalloc(datalen + 1, GFP_KERNEL); @@ -851,7 +853,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) return -ENOMEM; buf[datalen] = 0; - memcpy(buf, data, datalen); + memcpy(buf, prep->data, datalen); ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL); if (ret < 0) goto out; diff --git a/security/keys/key.c b/security/keys/key.c index a30e927..a15c9da 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -405,8 +405,7 @@ EXPORT_SYMBOL(key_payload_reserve); * key_construction_mutex. */ static int __key_instantiate_and_link(struct key *key, - const void *data, - size_t datalen, + struct key_preparsed_payload *prep, struct key *keyring, struct key *authkey, unsigned long *_prealloc) @@ -424,7 +423,7 @@ static int __key_instantiate_and_link(struct key *key, /* can't instantiate twice */ if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { /* instantiate the key */ - ret = key->type->instantiate(key, data, datalen); + ret = key->type->instantiate(key, prep); if (ret == 0) { /* mark the key as being instantiated */ @@ -475,22 +474,37 @@ int key_instantiate_and_link(struct key *key, struct key *keyring, struct key *authkey) { + struct key_preparsed_payload prep; unsigned long prealloc; int ret; + memset(&prep, 0, sizeof(prep)); + prep.data = data; + prep.datalen = datalen; + prep.quotalen = key->type->def_datalen; + if (key->type->preparse) { + ret = key->type->preparse(&prep); + if (ret < 0) + goto error; + } + if (keyring) { ret = __key_link_begin(keyring, key->type, key->description, &prealloc); if (ret < 0) - return ret; + goto error_free_preparse; } - ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey, + ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &prealloc); if (keyring) __key_link_end(keyring, key->type, prealloc); +error_free_preparse: + if (key->type->preparse) + key->type->free_preparse(&prep); +error: return ret; } @@ -699,7 +713,7 @@ void key_type_put(struct key_type *ktype) * if we get an error. */ static inline key_ref_t __key_update(key_ref_t key_ref, - const void *payload, size_t plen) + struct key_preparsed_payload *prep) { struct key *key = key_ref_to_ptr(key_ref); int ret; @@ -715,7 +729,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref, down_write(&key->sem); - ret = key->type->update(key, payload, plen); + ret = key->type->update(key, prep); if (ret == 0) /* updating a negative key instantiates it */ clear_bit(KEY_FLAG_NEGATIVE, &key->flags); @@ -767,6 +781,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, unsigned long flags) { unsigned long prealloc; + struct key_preparsed_payload prep; const struct cred *cred = current_cred(); struct key_type *ktype; struct key *keyring, *key = NULL; @@ -782,8 +797,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } key_ref = ERR_PTR(-EINVAL); - if (!ktype->match || !ktype->instantiate) - goto error_2; + if (!ktype->match || !ktype->instantiate || + (!description && !ktype->preparse)) + goto error_put_type; keyring = key_ref_to_ptr(keyring_ref); @@ -791,18 +807,37 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, key_ref = ERR_PTR(-ENOTDIR); if (keyring->type != &key_type_keyring) - goto error_2; + goto error_put_type; + + memset(&prep, 0, sizeof(prep)); + prep.data = payload; + prep.datalen = plen; + prep.quotalen = ktype->def_datalen; + if (ktype->preparse) { + ret = ktype->preparse(&prep); + if (ret < 0) { + key_ref = ERR_PTR(ret); + goto error_put_type; + } + if (!description) + description = prep.description; + key_ref = ERR_PTR(-EINVAL); + if (!description) + goto error_free_prep; + } ret = __key_link_begin(keyring, ktype, description, &prealloc); - if (ret < 0) - goto error_2; + if (ret < 0) { + key_ref = ERR_PTR(ret); + goto error_free_prep; + } /* if we're going to allocate a new key, we're going to have * to modify the keyring */ ret = key_permission(keyring_ref, KEY_WRITE); if (ret < 0) { key_ref = ERR_PTR(ret); - goto error_3; + goto error_link_end; } /* if it's possible to update this type of key, search for an existing @@ -833,25 +868,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, perm, flags); if (IS_ERR(key)) { key_ref = ERR_CAST(key); - goto error_3; + goto error_link_end; } /* instantiate it and link it into the target keyring */ - ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL, - &prealloc); + ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc); if (ret < 0) { key_put(key); key_ref = ERR_PTR(ret); - goto error_3; + goto error_link_end; } key_ref = make_key_ref(key, is_key_possessed(keyring_ref)); - error_3: +error_link_end: __key_link_end(keyring, ktype, prealloc); - error_2: +error_free_prep: + if (ktype->preparse) + ktype->free_preparse(&prep); +error_put_type: key_type_put(ktype); - error: +error: return key_ref; found_matching_key: @@ -859,10 +896,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, * - we can drop the locks first as we have the key pinned */ __key_link_end(keyring, ktype, prealloc); - key_type_put(ktype); - key_ref = __key_update(key_ref, payload, plen); - goto error; + key_ref = __key_update(key_ref, &prep); + goto error_free_prep; } EXPORT_SYMBOL(key_create_or_update); @@ -881,6 +917,7 @@ EXPORT_SYMBOL(key_create_or_update); */ int key_update(key_ref_t key_ref, const void *payload, size_t plen) { + struct key_preparsed_payload prep; struct key *key = key_ref_to_ptr(key_ref); int ret; @@ -893,18 +930,31 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) /* attempt to update it if supported */ ret = -EOPNOTSUPP; - if (key->type->update) { - down_write(&key->sem); - - ret = key->type->update(key, payload, plen); - if (ret == 0) - /* updating a negative key instantiates it */ - clear_bit(KEY_FLAG_NEGATIVE, &key->flags); + if (!key->type->update) + goto error; - up_write(&key->sem); + memset(&prep, 0, sizeof(prep)); + prep.data = payload; + prep.datalen = plen; + prep.quotalen = key->type->def_datalen; + if (key->type->preparse) { + ret = key->type->preparse(&prep); + if (ret < 0) + goto error; } - error: + down_write(&key->sem); + + ret = key->type->update(key, &prep); + if (ret == 0) + /* updating a negative key instantiates it */ + clear_bit(KEY_FLAG_NEGATIVE, &key->flags); + + up_write(&key->sem); + + if (key->type->preparse) + key->type->free_preparse(&prep); +error: return ret; } EXPORT_SYMBOL(key_update); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 305ecb7..5d34b4e 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -46,6 +46,9 @@ static int key_get_type_from_user(char *type, * Extract the description of a new key from userspace and either add it as a * new key to the specified keyring or update a matching key in that keyring. * + * If the description is NULL or an empty string, the key type is asked to + * generate one from the payload. + * * The keyring must be writable so that we can attach the key to it. * * If successful, the new key's serial number is returned, otherwise an error @@ -72,10 +75,17 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, if (ret < 0) goto error; - description = strndup_user(_description, PAGE_SIZE); - if (IS_ERR(description)) { - ret = PTR_ERR(description); - goto error; + description = NULL; + if (_description) { + description = strndup_user(_description, PAGE_SIZE); + if (IS_ERR(description)) { + ret = PTR_ERR(description); + goto error; + } + if (!*description) { + kfree(description); + description = NULL; + } } /* pull the payload in if one was supplied */ diff --git a/security/keys/keyring.c b/security/keys/keyring.c index a5f5c4b..6e42df1 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -66,7 +66,7 @@ static inline unsigned keyring_hash(const char *desc) * operations. */ static int keyring_instantiate(struct key *keyring, - const void *data, size_t datalen); + struct key_preparsed_payload *prep); static int keyring_match(const struct key *keyring, const void *criterion); static void keyring_revoke(struct key *keyring); static void keyring_destroy(struct key *keyring); @@ -121,12 +121,12 @@ static void keyring_publish_name(struct key *keyring) * Returns 0 on success, -EINVAL if given any data. */ static int keyring_instantiate(struct key *keyring, - const void *data, size_t datalen) + struct key_preparsed_payload *prep) { int ret; ret = -EINVAL; - if (datalen == 0) { + if (prep->datalen == 0) { /* make the keyring available by name if it has one */ keyring_publish_name(keyring); ret = 0; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 60d4e3f..85730d5 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -19,7 +19,8 @@ #include <asm/uaccess.h> #include "internal.h" -static int request_key_auth_instantiate(struct key *, const void *, size_t); +static int request_key_auth_instantiate(struct key *, + struct key_preparsed_payload *); static void request_key_auth_describe(const struct key *, struct seq_file *); static void request_key_auth_revoke(struct key *); static void request_key_auth_destroy(struct key *); @@ -42,10 +43,9 @@ struct key_type key_type_request_key_auth = { * Instantiate a request-key authorisation key. */ static int request_key_auth_instantiate(struct key *key, - const void *data, - size_t datalen) + struct key_preparsed_payload *prep) { - key->payload.data = (struct request_key_auth *) data; + key->payload.data = (struct request_key_auth *)prep->data; return 0; } diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 3f163d0..e13fcf7 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -895,23 +895,24 @@ static struct trusted_key_payload *trusted_payload_alloc(struct key *key) * * On success, return 0. Otherwise return errno. */ -static int trusted_instantiate(struct key *key, const void *data, - size_t datalen) +static int trusted_instantiate(struct key *key, + struct key_preparsed_payload *prep) { struct trusted_key_payload *payload = NULL; struct trusted_key_options *options = NULL; + size_t datalen = prep->datalen; char *datablob; int ret = 0; int key_cmd; size_t key_len; - if (datalen <= 0 || datalen > 32767 || !data) + if (datalen <= 0 || datalen > 32767 || !prep->data) return -EINVAL; datablob = kmalloc(datalen + 1, GFP_KERNEL); if (!datablob) return -ENOMEM; - memcpy(datablob, data, datalen); + memcpy(datablob, prep->data, datalen); datablob[datalen] = '\0'; options = trusted_options_alloc(); @@ -981,17 +982,18 @@ static void trusted_rcu_free(struct rcu_head *rcu) /* * trusted_update - reseal an existing key with new PCR values */ -static int trusted_update(struct key *key, const void *data, size_t datalen) +static int trusted_update(struct key *key, struct key_preparsed_payload *prep) { struct trusted_key_payload *p = key->payload.data; struct trusted_key_payload *new_p; struct trusted_key_options *new_o; + size_t datalen = prep->datalen; char *datablob; int ret = 0; if (!p->migratable) return -EPERM; - if (datalen <= 0 || datalen > 32767 || !data) + if (datalen <= 0 || datalen > 32767 || !prep->data) return -EINVAL; datablob = kmalloc(datalen + 1, GFP_KERNEL); @@ -1008,7 +1010,7 @@ static int trusted_update(struct key *key, const void *data, size_t datalen) goto out; } - memcpy(datablob, data, datalen); + memcpy(datablob, prep->data, datalen); datablob[datalen] = '\0'; ret = datablob_parse(datablob, new_p, new_o); if (ret != Opt_update) { diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index c7660a2..55dc889 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -58,13 +58,14 @@ EXPORT_SYMBOL_GPL(key_type_logon); /* * instantiate a user defined key */ -int user_instantiate(struct key *key, const void *data, size_t datalen) +int user_instantiate(struct key *key, struct key_preparsed_payload *prep) { struct user_key_payload *upayload; + size_t datalen = prep->datalen; int ret; ret = -EINVAL; - if (datalen <= 0 || datalen > 32767 || !data) + if (datalen <= 0 || datalen > 32767 || !prep->data) goto error; ret = key_payload_reserve(key, datalen); @@ -78,7 +79,7 @@ int user_instantiate(struct key *key, const void *data, size_t datalen) /* attach the data */ upayload->datalen = datalen; - memcpy(upayload->data, data, datalen); + memcpy(upayload->data, prep->data, datalen); rcu_assign_keypointer(key, upayload); ret = 0; @@ -92,13 +93,14 @@ EXPORT_SYMBOL_GPL(user_instantiate); * update a user defined key * - the key's semaphore is write-locked */ -int user_update(struct key *key, const void *data, size_t datalen) +int user_update(struct key *key, struct key_preparsed_payload *prep) { struct user_key_payload *upayload, *zap; + size_t datalen = prep->datalen; int ret; ret = -EINVAL; - if (datalen <= 0 || datalen > 32767 || !data) + if (datalen <= 0 || datalen > 32767 || !prep->data) goto error; /* construct a replacement payload */ @@ -108,7 +110,7 @@ int user_update(struct key *key, const void *data, size_t datalen) goto error; upayload->datalen = datalen; - memcpy(upayload->data, data, datalen); + memcpy(upayload->data, prep->data, datalen); /* check the quota and attach the new data */ zap = upayload; |