Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 6 Aug 2014 15:06:39 +0000 (08:06 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 6 Aug 2014 15:06:39 +0000 (08:06 -0700)
Pull security subsystem updates from James Morris:
 "In this release:

   - PKCS#7 parser for the key management subsystem from David Howells
   - appoint Kees Cook as seccomp maintainer
   - bugfixes and general maintenance across the subsystem"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (94 commits)
  X.509: Need to export x509_request_asymmetric_key()
  netlabel: shorter names for the NetLabel catmap funcs/structs
  netlabel: fix the catmap walking functions
  netlabel: fix the horribly broken catmap functions
  netlabel: fix a problem when setting bits below the previously lowest bit
  PKCS#7: X.509 certificate issuer and subject are mandatory fields in the ASN.1
  tpm: simplify code by using %*phN specifier
  tpm: Provide a generic means to override the chip returned timeouts
  tpm: missing tpm_chip_put in tpm_get_random()
  tpm: Properly clean sysfs entries in error path
  tpm: Add missing tpm_do_selftest to ST33 I2C driver
  PKCS#7: Use x509_request_asymmetric_key()
  Revert "selinux: fix the default socket labeling in sock_graft()"
  X.509: x509_request_asymmetric_keys() doesn't need string length arguments
  PKCS#7: fix sparse non static symbol warning
  KEYS: revert encrypted key change
  ima: add support for measuring and appraising firmware
  firmware_class: perform new LSM checks
  security: introduce kernel_fw_from_file hook
  PKCS#7: Missing inclusion of linux/err.h
  ...

110 files changed:
Documentation/ABI/testing/ima_policy
Documentation/kernel-parameters.txt
Documentation/security/keys.txt
MAINTAINERS
arch/Kconfig
arch/arm/include/uapi/asm/unistd.h
arch/arm/kernel/calls.S
arch/mips/include/uapi/asm/unistd.h
arch/mips/kernel/scall32-o32.S
arch/mips/kernel/scall64-64.S
arch/mips/kernel/scall64-n32.S
arch/mips/kernel/scall64-o32.S
arch/x86/syscalls/syscall_32.tbl
arch/x86/syscalls/syscall_64.tbl
crypto/asymmetric_keys/Kconfig
crypto/asymmetric_keys/Makefile
crypto/asymmetric_keys/asymmetric_keys.h
crypto/asymmetric_keys/asymmetric_type.c
crypto/asymmetric_keys/mscode.asn1 [new file with mode: 0644]
crypto/asymmetric_keys/mscode_parser.c [new file with mode: 0644]
crypto/asymmetric_keys/pkcs7.asn1 [new file with mode: 0644]
crypto/asymmetric_keys/pkcs7_key_type.c [new file with mode: 0644]
crypto/asymmetric_keys/pkcs7_parser.c [new file with mode: 0644]
crypto/asymmetric_keys/pkcs7_parser.h [new file with mode: 0644]
crypto/asymmetric_keys/pkcs7_trust.c [new file with mode: 0644]
crypto/asymmetric_keys/pkcs7_verify.c [new file with mode: 0644]
crypto/asymmetric_keys/verify_pefile.c [new file with mode: 0644]
crypto/asymmetric_keys/verify_pefile.h [new file with mode: 0644]
crypto/asymmetric_keys/x509.asn1
crypto/asymmetric_keys/x509_cert_parser.c
crypto/asymmetric_keys/x509_parser.h
crypto/asymmetric_keys/x509_public_key.c
drivers/base/firmware_class.c
drivers/char/tpm/tpm-interface.c
drivers/char/tpm/tpm_eventlog.c
drivers/char/tpm/tpm_i2c_stm_st33.c
drivers/char/tpm/tpm_tis.c
fs/exec.c
fs/nfs/idmap.c
fs/proc/array.c
include/crypto/pkcs7.h [new file with mode: 0644]
include/crypto/public_key.h
include/keys/big_key-type.h
include/keys/system_keyring.h
include/keys/user-type.h
include/linux/capability.h
include/linux/ima.h
include/linux/key-type.h
include/linux/key.h
include/linux/oid_registry.h
include/linux/pe.h [new file with mode: 0644]
include/linux/sched.h
include/linux/seccomp.h
include/linux/security.h
include/linux/syscalls.h
include/linux/tpm.h
include/linux/verify_pefile.h [new file with mode: 0644]
include/net/netlabel.h
include/uapi/asm-generic/unistd.h
include/uapi/linux/seccomp.h
kernel/audit.c
kernel/capability.c
kernel/fork.c
kernel/seccomp.c
kernel/sys.c
kernel/sys_ni.c
kernel/system_keyring.c
lib/Kconfig
net/ceph/crypto.c
net/dns_resolver/dns_key.c
net/dns_resolver/dns_query.c
net/ipv4/cipso_ipv4.c
net/netlabel/netlabel_kapi.c
net/rxrpc/ar-key.c
scripts/selinux/genheaders/Makefile
scripts/selinux/mdp/Makefile
security/apparmor/domain.c
security/capability.c
security/commoncap.c
security/integrity/digsig.c
security/integrity/ima/Kconfig
security/integrity/ima/ima.h
security/integrity/ima/ima_appraise.c
security/integrity/ima/ima_crypto.c
security/integrity/ima/ima_main.c
security/integrity/ima/ima_policy.c
security/integrity/integrity.h
security/keys/big_key.c
security/keys/key.c
security/keys/keyctl.c
security/keys/keyring.c
security/keys/request_key_auth.c
security/keys/user_defined.c
security/security.c
security/selinux/hooks.c
security/selinux/include/netif.h
security/selinux/include/netnode.h
security/selinux/include/netport.h
security/selinux/include/security.h
security/selinux/netif.c
security/selinux/netnode.c
security/selinux/netport.c
security/selinux/ss/conditional.c
security/selinux/ss/ebitmap.c
security/selinux/ss/ebitmap.h
security/selinux/ss/policydb.c
security/selinux/ss/services.c
security/smack/smack_access.c
security/smack/smack_lsm.c
security/smack/smackfs.c

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