Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 May 2016 16:21:36 +0000 (09:21 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 May 2016 16:21:36 +0000 (09:21 -0700)
Pull security subsystem updates from James Morris:
 "Highlights:

   - A new LSM, "LoadPin", from Kees Cook is added, which allows forcing
     of modules and firmware to be loaded from a specific device (this
     is from ChromeOS, where the device as a whole is verified
     cryptographically via dm-verity).

     This is disabled by default but can be configured to be enabled by
     default (don't do this if you don't know what you're doing).

   - Keys: allow authentication data to be stored in an asymmetric key.
     Lots of general fixes and updates.

   - SELinux: add restrictions for loading of kernel modules via
     finit_module().  Distinguish non-init user namespace capability
     checks.  Apply execstack check on thread stacks"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (48 commits)
  LSM: LoadPin: provide enablement CONFIG
  Yama: use atomic allocations when reporting
  seccomp: Fix comment typo
  ima: add support for creating files using the mknodat syscall
  ima: fix ima_inode_post_setattr
  vfs: forbid write access when reading a file into memory
  fs: fix over-zealous use of "const"
  selinux: apply execstack check on thread stacks
  selinux: distinguish non-init user namespace capability checks
  LSM: LoadPin for kernel file loading restrictions
  fs: define a string representation of the kernel_read_file_id enumeration
  Yama: consolidate error reporting
  string_helpers: add kstrdup_quotable_file
  string_helpers: add kstrdup_quotable_cmdline
  string_helpers: add kstrdup_quotable
  selinux: check ss_initialized before revalidating an inode label
  selinux: delay inode label lookup as long as possible
  selinux: don't revalidate an inode's label when explicitly setting it
  selinux: Change bool variable name to index.
  KEYS: Add KEYCTL_DH_COMPUTE command
  ...

82 files changed:
Documentation/security/LoadPin.txt [new file with mode: 0644]
Documentation/security/keys.txt
MAINTAINERS
arch/x86/kernel/kexec-bzimage64.c
certs/Kconfig
certs/system_keyring.c
crypto/asymmetric_keys/Kconfig
crypto/asymmetric_keys/Makefile
crypto/asymmetric_keys/asymmetric_keys.h
crypto/asymmetric_keys/asymmetric_type.c
crypto/asymmetric_keys/mscode_parser.c
crypto/asymmetric_keys/pkcs7_key_type.c
crypto/asymmetric_keys/pkcs7_parser.c
crypto/asymmetric_keys/pkcs7_parser.h
crypto/asymmetric_keys/pkcs7_trust.c
crypto/asymmetric_keys/pkcs7_verify.c
crypto/asymmetric_keys/public_key.c
crypto/asymmetric_keys/restrict.c [new file with mode: 0644]
crypto/asymmetric_keys/signature.c
crypto/asymmetric_keys/verify_pefile.c
crypto/asymmetric_keys/verify_pefile.h
crypto/asymmetric_keys/x509_cert_parser.c
crypto/asymmetric_keys/x509_parser.h
crypto/asymmetric_keys/x509_public_key.c
fs/cifs/cifsacl.c
fs/exec.c
fs/namei.c
fs/nfs/nfs4idmap.c
include/crypto/pkcs7.h
include/crypto/public_key.h
include/keys/asymmetric-subtype.h
include/keys/asymmetric-type.h
include/keys/system_keyring.h
include/linux/fs.h
include/linux/ima.h
include/linux/key-type.h
include/linux/key.h
include/linux/lsm_hooks.h
include/linux/string_helpers.h
include/linux/verification.h [new file with mode: 0644]
include/linux/verify_pefile.h [deleted file]
include/uapi/linux/keyctl.h
kernel/module_signing.c
kernel/seccomp.c
lib/string_helpers.c
net/dns_resolver/dns_key.c
net/netlabel/netlabel_kapi.c
net/rxrpc/ar-key.c
security/Kconfig
security/Makefile
security/integrity/Kconfig
security/integrity/digsig.c
security/integrity/ima/Kconfig
security/integrity/ima/Makefile
security/integrity/ima/ima_appraise.c
security/integrity/ima/ima_main.c
security/integrity/ima/ima_mok.c
security/integrity/integrity.h
security/keys/Kconfig
security/keys/Makefile
security/keys/big_key.c
security/keys/compat.c
security/keys/dh.c [new file with mode: 0644]
security/keys/internal.h
security/keys/key.c
security/keys/keyctl.c
security/keys/keyring.c
security/keys/persistent.c
security/keys/process_keys.c
security/keys/request_key.c
security/keys/request_key_auth.c
security/keys/user_defined.c
security/loadpin/Kconfig [new file with mode: 0644]
security/loadpin/Makefile [new file with mode: 0644]
security/loadpin/loadpin.c [new file with mode: 0644]
security/security.c
security/selinux/hooks.c
security/selinux/include/classmap.h
security/selinux/include/conditional.h
security/selinux/include/objsec.h
security/selinux/ss/services.c
security/yama/yama_lsm.c

diff --git a/Documentation/security/LoadPin.txt b/Documentation/security/LoadPin.txt
new file mode 100644 (file)
index 0000000..e11877f
--- /dev/null
@@ -0,0 +1,17 @@
+LoadPin is a Linux Security Module that ensures all kernel-loaded files
+(modules, firmware, etc) all originate from the same filesystem, with
+the expectation that such a filesystem is backed by a read-only device
+such as dm-verity or CDROM. This allows systems that have a verified
+and/or unchangeable filesystem to enforce module and firmware loading
+restrictions without needing to sign the files individually.
+
+The LSM is selectable at build-time with CONFIG_SECURITY_LOADPIN, and
+can be controlled at boot-time with the kernel command line option
+"loadpin.enabled". By default, it is enabled, but can be disabled at
+boot ("loadpin.enabled=0").
+
+LoadPin starts pinning when it sees the first file loaded. If the
+block device backing the filesystem is not read-only, a sysctl is
+created to toggle pinning: /proc/sys/kernel/loadpin/enabled. (Having
+a mutable filesystem means pinning is mutable too, but having the
+sysctl allows for easy testing on systems with a mutable filesystem.)
index 8c18387..20d0571 100644 (file)
@@ -823,6 +823,36 @@ The keyctl syscall functions are:
      A process must have search permission on the key for this function to be
      successful.
 
+ (*) Compute a Diffie-Hellman shared secret or public key
+
+       long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params,
+                  char *buffer, size_t buflen);
+
+     The params struct contains serial numbers for three keys:
+
+        - The prime, p, known to both parties
+        - The local private key
+        - The base integer, which is either a shared generator or the
+          remote public key
+
+     The value computed is:
+
+       result = base ^ private (mod prime)
+
+     If the base is the shared generator, the result is the local
+     public key.  If the base is the remote public key, the result is
+     the shared secret.
+
+     The buffer length must be at least the length of the prime, or zero.
+
+     If the buffer length is nonzero, the length of the result is
+     returned when it is successfully calculated and copied in to the
+     buffer. When the buffer length is zero, the minimum required
+     buffer length is returned.
+
+     This function will return error EOPNOTSUPP if the key type is not
+     supported, error ENOKEY if the key could not be found, or error
+     EACCES if the key is not readable by the caller.
 
 ===============
 KERNEL SERVICES
@@ -999,6 +1029,10 @@ payload contents" for more information.
        struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
                                  const struct cred *cred,
                                  key_perm_t perm,
+                                 int (*restrict_link)(struct key *,
+                                                      const struct key_type *,
+                                                      unsigned long,
+                                                      const union key_payload *),
                                  unsigned long flags,
                                  struct key *dest);
 
@@ -1010,6 +1044,24 @@ payload contents" for more information.
     KEY_ALLOC_NOT_IN_QUOTA in flags if the keyring shouldn't be accounted
     towards the user's quota).  Error ENOMEM can also be returned.
 
+    If restrict_link not NULL, it should point to a function that will be
+    called each time an attempt is made to link a key into the new keyring.
+    This function is called to check whether a key may be added into the keying
+    or not.  Callers of key_create_or_update() within the kernel can pass
+    KEY_ALLOC_BYPASS_RESTRICTION to suppress the check.  An example of using
+    this is to manage rings of cryptographic keys that are set up when the
+    kernel boots where userspace is also permitted to add keys - provided they
+    can be verified by a key the kernel already has.
+
+    When called, the restriction function will be passed the keyring being
+    added to, the key flags value and the type and payload of the key being
+    added.  Note that when a new key is being created, this is called between
+    payload preparsing and actual key creation.  The function should return 0
+    to allow the link or an error to reject it.
+
+    A convenience function, restrict_link_reject, exists to always return
+    -EPERM to in this case.
+
 
 (*) To check the validity of a key, this function can be called:
 
index add406a..71bcef4 100644 (file)
@@ -10025,6 +10025,12 @@ T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git
 S:     Supported
 F:     security/apparmor/
 
+LOADPIN SECURITY MODULE
+M:     Kees Cook <keescook@chromium.org>
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git lsm/loadpin
+S:     Supported
+F:     security/loadpin/
+
 YAMA SECURITY MODULE
 M:     Kees Cook <keescook@chromium.org>
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip
index 2af478e..f2356bd 100644 (file)
@@ -19,8 +19,7 @@
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/efi.h>
-#include <linux/verify_pefile.h>
-#include <keys/system_keyring.h>
+#include <linux/verification.h>
 
 #include <asm/bootparam.h>
 #include <asm/setup.h>
@@ -529,18 +528,9 @@ static int bzImage64_cleanup(void *loader_data)
 #ifdef CONFIG_KEXEC_BZIMAGE_VERIFY_SIG
 static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len)
 {
-       bool trusted;
-       int ret;
-
-       ret = verify_pefile_signature(kernel, kernel_len,
-                                     system_trusted_keyring,
-                                     VERIFYING_KEXEC_PE_SIGNATURE,
-                                     &trusted);
-       if (ret < 0)
-               return ret;
-       if (!trusted)
-               return -EKEYREJECTED;
-       return 0;
+       return verify_pefile_signature(kernel, kernel_len,
+                                      NULL,
+                                      VERIFYING_KEXEC_PE_SIGNATURE);
 }
 #endif
 
index f0f8a44..fc5955f 100644 (file)
@@ -17,6 +17,7 @@ config MODULE_SIG_KEY
 config SYSTEM_TRUSTED_KEYRING
        bool "Provide system-wide ring of trusted keys"
        depends on KEYS
+       depends on ASYMMETRIC_KEY_TYPE
        help
          Provide a system keyring to which trusted keys can be added.  Keys in
          the keyring are considered to be trusted.  Keys may be added at will
@@ -55,4 +56,12 @@ config SYSTEM_EXTRA_CERTIFICATE_SIZE
          This is the number of bytes reserved in the kernel image for a
          certificate to be inserted.
 
+config SECONDARY_TRUSTED_KEYRING
+       bool "Provide a keyring to which extra trustable keys may be added"
+       depends on SYSTEM_TRUSTED_KEYRING
+       help
+         If set, provide a keyring to which extra keys may be added, provided
+         those keys are not blacklisted and are vouched for by a key built
+         into the kernel or already in the secondary trusted keyring.
+
 endmenu
index f418032..50979d6 100644 (file)
 #include <keys/system_keyring.h>
 #include <crypto/pkcs7.h>
 
-struct key *system_trusted_keyring;
-EXPORT_SYMBOL_GPL(system_trusted_keyring);
+static struct key *builtin_trusted_keys;
+#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
+static struct key *secondary_trusted_keys;
+#endif
 
 extern __initconst const u8 system_certificate_list[];
 extern __initconst const unsigned long system_certificate_list_size;
 
+/**
+ * restrict_link_to_builtin_trusted - Restrict keyring addition by built in CA
+ *
+ * Restrict the addition of keys into a keyring based on the key-to-be-added
+ * being vouched for by a key in the built in system keyring.
+ */
+int restrict_link_by_builtin_trusted(struct key *keyring,
+                                    const struct key_type *type,
+                                    const union key_payload *payload)
+{
+       return restrict_link_by_signature(builtin_trusted_keys, type, payload);
+}
+
+#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
+/**
+ * restrict_link_by_builtin_and_secondary_trusted - Restrict keyring
+ *   addition by both builtin and secondary keyrings
+ *
+ * Restrict the addition of keys into a keyring based on the key-to-be-added
+ * being vouched for by a key in either the built-in or the secondary system
+ * keyrings.
+ */
+int restrict_link_by_builtin_and_secondary_trusted(
+       struct key *keyring,
+       const struct key_type *type,
+       const union key_payload *payload)
+{
+       /* If we have a secondary trusted keyring, then that contains a link
+        * through to the builtin keyring and the search will follow that link.
+        */
+       if (type == &key_type_keyring &&
+           keyring == secondary_trusted_keys &&
+           payload == &builtin_trusted_keys->payload)
+               /* Allow the builtin keyring to be added to the secondary */
+               return 0;
+
+       return restrict_link_by_signature(secondary_trusted_keys, type, payload);
+}
+#endif
+
 /*
- * Load the compiled-in keys
+ * Create the trusted keyrings
  */
 static __init int system_trusted_keyring_init(void)
 {
-       pr_notice("Initialise system trusted keyring\n");
+       pr_notice("Initialise system trusted keyrings\n");
 
-       system_trusted_keyring =
-               keyring_alloc(".system_keyring",
+       builtin_trusted_keys =
+               keyring_alloc(".builtin_trusted_keys",
                              KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
                              ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
                              KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH),
-                             KEY_ALLOC_NOT_IN_QUOTA, NULL);
-       if (IS_ERR(system_trusted_keyring))
-               panic("Can't allocate system trusted keyring\n");
+                             KEY_ALLOC_NOT_IN_QUOTA,
+                             NULL, NULL);
+       if (IS_ERR(builtin_trusted_keys))
+               panic("Can't allocate builtin trusted keyring\n");
+
+#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
+       secondary_trusted_keys =
+               keyring_alloc(".secondary_trusted_keys",
+                             KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
+                             ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                              KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH |
+                              KEY_USR_WRITE),
+                             KEY_ALLOC_NOT_IN_QUOTA,
+                             restrict_link_by_builtin_and_secondary_trusted,
+                             NULL);
+       if (IS_ERR(secondary_trusted_keys))
+               panic("Can't allocate secondary trusted keyring\n");
+
+       if (key_link(secondary_trusted_keys, builtin_trusted_keys) < 0)
+               panic("Can't link trusted keyrings\n");
+#endif
 
-       set_bit(KEY_FLAG_TRUSTED_ONLY, &system_trusted_keyring->flags);
        return 0;
 }
 
@@ -76,7 +135,7 @@ static __init int load_system_certificate_list(void)
                if (plen > end - p)
                        goto dodgy_cert;
 
-               key = key_create_or_update(make_key_ref(system_trusted_keyring, 1),
+               key = key_create_or_update(make_key_ref(builtin_trusted_keys, 1),
                                           "asymmetric",
                                           NULL,
                                           p,
@@ -84,8 +143,8 @@ static __init int load_system_certificate_list(void)
                                           ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
                                           KEY_USR_VIEW | KEY_USR_READ),
                                           KEY_ALLOC_NOT_IN_QUOTA |
-                                          KEY_ALLOC_TRUSTED |
-                                          KEY_ALLOC_BUILT_IN);
+                                          KEY_ALLOC_BUILT_IN |
+                                          KEY_ALLOC_BYPASS_RESTRICTION);
                if (IS_ERR(key)) {
                        pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
                               PTR_ERR(key));
@@ -108,19 +167,27 @@ late_initcall(load_system_certificate_list);
 #ifdef CONFIG_SYSTEM_DATA_VERIFICATION
 
 /**
- * Verify a PKCS#7-based signature on system data.
- * @data: The data to be verified.
+ * verify_pkcs7_signature - Verify a PKCS#7-based signature on system data.
+ * @data: The data to be verified (NULL if expecting internal data).
  * @len: Size of @data.
  * @raw_pkcs7: The PKCS#7 message that is the signature.
  * @pkcs7_len: The size of @raw_pkcs7.
+ * @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only,
+ *                                     (void *)1UL for all trusted keys).
  * @usage: The use to which the key is being put.
+ * @view_content: Callback to gain access to content.
+ * @ctx: Context for callback.
  */
-int system_verify_data(const void *data, unsigned long len,
-                      const void *raw_pkcs7, size_t pkcs7_len,
-                      enum key_being_used_for usage)
+int verify_pkcs7_signature(const void *data, size_t len,
+                          const void *raw_pkcs7, size_t pkcs7_len,
+                          struct key *trusted_keys,
+                          enum key_being_used_for usage,
+                          int (*view_content)(void *ctx,
+                                              const void *data, size_t len,
+                                              size_t asn1hdrlen),
+                          void *ctx)
 {
        struct pkcs7_message *pkcs7;
-       bool trusted;
        int ret;
 
        pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
@@ -128,7 +195,7 @@ int system_verify_data(const void *data, unsigned long len,
                return PTR_ERR(pkcs7);
 
        /* The data should be detached - so we need to supply it. */
-       if (pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
+       if (data && pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
                pr_err("PKCS#7 signature with non-detached data\n");
                ret = -EBADMSG;
                goto error;
@@ -138,13 +205,33 @@ int system_verify_data(const void *data, unsigned long len,
        if (ret < 0)
                goto error;
 
-       ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted);
-       if (ret < 0)
+       if (!trusted_keys) {
+               trusted_keys = builtin_trusted_keys;
+       } else if (trusted_keys == (void *)1UL) {
+#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
+               trusted_keys = secondary_trusted_keys;
+#else
+               trusted_keys = builtin_trusted_keys;
+#endif
+       }
+       ret = pkcs7_validate_trust(pkcs7, trusted_keys);
+       if (ret < 0) {
+               if (ret == -ENOKEY)
+                       pr_err("PKCS#7 signature not signed with a trusted key\n");
                goto error;
+       }
+
+       if (view_content) {
+               size_t asn1hdrlen;
+
+               ret = pkcs7_get_content_data(pkcs7, &data, &len, &asn1hdrlen);
+               if (ret < 0) {
+                       if (ret == -ENODATA)
+                               pr_devel("PKCS#7 message does not contain data\n");
+                       goto error;
+               }
 
-       if (!trusted) {
-               pr_err("PKCS#7 signature not signed with a trusted key\n");
-               ret = -ENOKEY;
+               ret = view_content(ctx, data, len, asn1hdrlen);
        }
 
 error:
@@ -152,6 +239,6 @@ error:
        pr_devel("<==%s() = %d\n", __func__, ret);
        return ret;
 }
-EXPORT_SYMBOL_GPL(system_verify_data);
+EXPORT_SYMBOL_GPL(verify_pkcs7_signature);
 
 #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
index 91a7e04..e28e912 100644 (file)
@@ -1,5 +1,5 @@
 menuconfig ASYMMETRIC_KEY_TYPE
-       tristate "Asymmetric (public-key cryptographic) key type"
+       bool "Asymmetric (public-key cryptographic) key type"
        depends on KEYS
        help
          This option provides support for a key type that holds the data for
@@ -40,8 +40,7 @@ config PKCS7_MESSAGE_PARSER
 
 config PKCS7_TEST_KEY
        tristate "PKCS#7 testing key type"
-       depends on PKCS7_MESSAGE_PARSER
-       select SYSTEM_TRUSTED_KEYRING
+       depends on SYSTEM_DATA_VERIFICATION
        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
@@ -54,6 +53,7 @@ config PKCS7_TEST_KEY
 config SIGNED_PE_FILE_VERIFICATION
        bool "Support for PE file signature verification"
        depends on PKCS7_MESSAGE_PARSER=y
+       depends on SYSTEM_DATA_VERIFICATION
        select ASN1
        select OID_REGISTRY
        help
index f904862..6516855 100644 (file)
@@ -4,7 +4,10 @@
 
 obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
 
-asymmetric_keys-y := asymmetric_type.o signature.o
+asymmetric_keys-y := \
+       asymmetric_type.o \
+       restrict.o \
+       signature.o
 
 obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
 
index 1d450b5..ca8e9ac 100644 (file)
@@ -9,6 +9,8 @@
  * 2 of the Licence, or (at your option) any later version.
  */
 
+#include <keys/asymmetric-type.h>
+
 extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id);
 
 extern int __asymmetric_key_hex_to_key_id(const char *id,
index 9f2165b..6600181 100644 (file)
@@ -35,6 +35,95 @@ static LIST_HEAD(asymmetric_key_parsers);
 static DECLARE_RWSEM(asymmetric_key_parsers_sem);
 
 /**
+ * find_asymmetric_key - Find a key by ID.
+ * @keyring: The keys to search.
+ * @id_0: The first ID to look for or NULL.
+ * @id_1: The second ID to look for or NULL.
+ * @partial: Use partial match if true, exact if false.
+ *
+ * Find a key in the given keyring by identifier.  The preferred identifier is
+ * the id_0 and the fallback identifier is the id_1.  If both are given, the
+ * lookup is by the former, but the latter must also match.
+ */
+struct key *find_asymmetric_key(struct key *keyring,
+                               const struct asymmetric_key_id *id_0,
+                               const struct asymmetric_key_id *id_1,
+                               bool partial)
+{
+       struct key *key;
+       key_ref_t ref;
+       const char *lookup;
+       char *req, *p;
+       int len;
+
+       if (id_0) {
+               lookup = id_0->data;
+               len = id_0->len;
+       } else {
+               lookup = id_1->data;
+               len = id_1->len;
+       }
+
+       /* Construct an identifier "id:<keyid>". */
+       p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
+       if (!req)
+               return ERR_PTR(-ENOMEM);
+
+       if (partial) {
+               *p++ = 'i';
+               *p++ = 'd';
+       } else {
+               *p++ = 'e';
+               *p++ = 'x';
+       }
+       *p++ = ':';
+       p = bin2hex(p, lookup, len);
+       *p = 0;
+
+       pr_debug("Look up: \"%s\"\n", req);
+
+       ref = keyring_search(make_key_ref(keyring, 1),
+                            &key_type_asymmetric, req);
+       if (IS_ERR(ref))
+               pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
+       kfree(req);
+
+       if (IS_ERR(ref)) {
+               switch (PTR_ERR(ref)) {
+                       /* Hide some search errors */
+               case -EACCES:
+               case -ENOTDIR:
+               case -EAGAIN:
+                       return ERR_PTR(-ENOKEY);
+               default:
+                       return ERR_CAST(ref);
+               }
+       }
+
+       key = key_ref_to_ptr(ref);
+       if (id_0 && id_1) {
+               const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+
+               if (!kids->id[0]) {
+                       pr_debug("First ID matches, but second is missing\n");
+                       goto reject;
+               }
+               if (!asymmetric_key_id_same(id_1, kids->id[1])) {
+                       pr_debug("First ID matches, but second does not\n");
+                       goto reject;
+               }
+       }
+
+       pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
+       return key;
+
+reject:
+       key_put(key);
+       return ERR_PTR(-EKEYREJECTED);
+}
+EXPORT_SYMBOL_GPL(find_asymmetric_key);
+
+/**
  * asymmetric_key_generate_id: Construct an asymmetric key ID
  * @val_1: First binary blob
  * @len_1: Length of first binary blob
@@ -331,7 +420,8 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
        pr_devel("==>%s()\n", __func__);
 
        if (subtype) {
-               subtype->destroy(prep->payload.data[asym_crypto]);
+               subtype->destroy(prep->payload.data[asym_crypto],
+                                prep->payload.data[asym_auth]);
                module_put(subtype->owner);
        }
        asymmetric_key_free_kids(kids);
@@ -346,13 +436,15 @@ static void asymmetric_key_destroy(struct key *key)
        struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
        struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids];
        void *data = key->payload.data[asym_crypto];
+       void *auth = key->payload.data[asym_auth];
 
        key->payload.data[asym_crypto] = NULL;
        key->payload.data[asym_subtype] = NULL;
        key->payload.data[asym_key_ids] = NULL;
+       key->payload.data[asym_auth] = NULL;
 
        if (subtype) {
-               subtype->destroy(data);
+               subtype->destroy(data, auth);
                module_put(subtype->owner);
        }
 
index 3242cbf..6a76d5c 100644 (file)
 /*
  * Parse a Microsoft Individual Code Signing blob
  */
-int mscode_parse(struct pefile_context *ctx)
+int mscode_parse(void *_ctx, const void *content_data, size_t data_len,
+                size_t asn1hdrlen)
 {
-       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;
-       }
+       struct pefile_context *ctx = _ctx;
 
+       content_data -= asn1hdrlen;
+       data_len += asn1hdrlen;
        pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len),
                 content_data);
 
@@ -129,7 +123,6 @@ int mscode_note_digest(void *context, size_t hdrlen,
 {
        struct pefile_context *ctx = context;
 
-       ctx->digest = value;
-       ctx->digest_len = vlen;
-       return 0;
+       ctx->digest = kmemdup(value, vlen, GFP_KERNEL);
+       return ctx->digest ? 0 : -ENOMEM;
 }
index e2d0edb..3b92523 100644 (file)
 #include <linux/key.h>
 #include <linux/err.h>
 #include <linux/module.h>
+#include <linux/verification.h>
 #include <linux/key-type.h>
-#include <keys/asymmetric-type.h>
-#include <crypto/pkcs7.h>
 #include <keys/user-type.h>
-#include <keys/system_keyring.h>
-#include "pkcs7_parser.h"
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("PKCS#7 testing key type");
@@ -29,60 +26,47 @@ MODULE_PARM_DESC(pkcs7_usage,
                 "Usage to specify when verifying the PKCS#7 message");
 
 /*
- * Preparse a PKCS#7 wrapped and validated data blob.
+ * Retrieve the PKCS#7 message content.
  */
-static int pkcs7_preparse(struct key_preparsed_payload *prep)
+static int pkcs7_view_content(void *ctx, const void *data, size_t len,
+                             size_t asn1hdrlen)
 {
-       enum key_being_used_for usage = pkcs7_usage;
-       struct pkcs7_message *pkcs7;
-       const void *data, *saved_prep_data;
-       size_t datalen, saved_prep_datalen;
-       bool trusted;
+       struct key_preparsed_payload *prep = ctx;
+       const void *saved_prep_data;
+       size_t saved_prep_datalen;
        int ret;
 
-       kenter("");
-
-       if (usage >= NR__KEY_BEING_USED_FOR) {
-               pr_err("Invalid usage type %d\n", usage);
-               return -EINVAL;
-       }
-
        saved_prep_data = prep->data;
        saved_prep_datalen = prep->datalen;
-       pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen);
-       if (IS_ERR(pkcs7)) {
-               ret = PTR_ERR(pkcs7);
-               goto error;
-       }
-
-       ret = pkcs7_verify(pkcs7, usage);
-       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;
+       prep->datalen = len;
+
        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;
 }
 
 /*
+ * Preparse a PKCS#7 wrapped and validated data blob.
+ */
+static int pkcs7_preparse(struct key_preparsed_payload *prep)
+{
+       enum key_being_used_for usage = pkcs7_usage;
+
+       if (usage >= NR__KEY_BEING_USED_FOR) {
+               pr_err("Invalid usage type %d\n", usage);
+               return -EINVAL;
+       }
+
+       return verify_pkcs7_signature(NULL, 0,
+                                     prep->data, prep->datalen,
+                                     NULL, usage,
+                                     pkcs7_view_content, prep);
+}
+
+/*
  * user defined keys take an arbitrary string as the description and an
  * arbitrary blob of data as the payload
  */
index bdd0d75..af4cd86 100644 (file)
@@ -44,9 +44,7 @@ struct pkcs7_parse_context {
 static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
 {
        if (sinfo) {
-               kfree(sinfo->sig.s);
-               kfree(sinfo->sig.digest);
-               kfree(sinfo->signing_cert_id);
+               public_key_signature_free(sinfo->sig);
                kfree(sinfo);
        }
 }
@@ -125,6 +123,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
        ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
        if (!ctx->sinfo)
                goto out_no_sinfo;
+       ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
+                                 GFP_KERNEL);
+       if (!ctx->sinfo->sig)
+               goto out_no_sig;
 
        ctx->data = (unsigned long)data;
        ctx->ppcerts = &ctx->certs;
@@ -150,6 +152,7 @@ out:
                ctx->certs = cert->next;
                x509_free_certificate(cert);
        }
+out_no_sig:
        pkcs7_free_signed_info(ctx->sinfo);
 out_no_sinfo:
        pkcs7_free_message(ctx->msg);
@@ -165,24 +168,25 @@ EXPORT_SYMBOL_GPL(pkcs7_parse_message);
  * @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
+ * @_headerlen: Size of ASN.1 header not included in _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.
+ * Get access to the data content of the PKCS#7 message.  The size of the
+ * header of the ASN.1 object that contains it is also provided and can be used
+ * to adjust *_data and *_data_len to get the entire object.
+ *
+ * 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 *_headerlen)
 {
-       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;
+       *_data = pkcs7->data;
+       *_data_len = pkcs7->data_len;
+       if (_headerlen)
+               *_headerlen = pkcs7->data_hdrlen;
        return 0;
 }
 EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
@@ -218,25 +222,25 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
 
        switch (ctx->last_oid) {
        case OID_md4:
-               ctx->sinfo->sig.hash_algo = "md4";
+               ctx->sinfo->sig->hash_algo = "md4";
                break;
        case OID_md5:
-               ctx->sinfo->sig.hash_algo = "md5";
+               ctx->sinfo->sig->hash_algo = "md5";
                break;
        case OID_sha1:
-               ctx->sinfo->sig.hash_algo = "sha1";
+               ctx->sinfo->sig->hash_algo = "sha1";
                break;
        case OID_sha256:
-               ctx->sinfo->sig.hash_algo = "sha256";
+               ctx->sinfo->sig->hash_algo = "sha256";
                break;
        case OID_sha384:
-               ctx->sinfo->sig.hash_algo = "sha384";
+               ctx->sinfo->sig->hash_algo = "sha384";
                break;
        case OID_sha512:
-               ctx->sinfo->sig.hash_algo = "sha512";
+               ctx->sinfo->sig->hash_algo = "sha512";
                break;
        case OID_sha224:
-               ctx->sinfo->sig.hash_algo = "sha224";
+               ctx->sinfo->sig->hash_algo = "sha224";
                break;
        default:
                printk("Unsupported digest algo: %u\n", ctx->last_oid);
@@ -256,7 +260,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
 
        switch (ctx->last_oid) {
        case OID_rsaEncryption:
-               ctx->sinfo->sig.pkey_algo = "rsa";
+               ctx->sinfo->sig->pkey_algo = "rsa";
                break;
        default:
                printk("Unsupported pkey algo: %u\n", ctx->last_oid);
@@ -616,11 +620,11 @@ int pkcs7_sig_note_signature(void *context, size_t hdrlen,
 {
        struct pkcs7_parse_context *ctx = context;
 
-       ctx->sinfo->sig.s = kmemdup(value, vlen, GFP_KERNEL);
-       if (!ctx->sinfo->sig.s)
+       ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);
+       if (!ctx->sinfo->sig->s)
                return -ENOMEM;
 
-       ctx->sinfo->sig.s_size = vlen;
+       ctx->sinfo->sig->s_size = vlen;
        return 0;
 }
 
@@ -656,12 +660,16 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen,
 
        pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);
 
-       sinfo->signing_cert_id = kid;
+       sinfo->sig->auth_ids[0] = kid;
        sinfo->index = ++ctx->sinfo_index;
        *ctx->ppsinfo = sinfo;
        ctx->ppsinfo = &sinfo->next;
        ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
        if (!ctx->sinfo)
                return -ENOMEM;
+       ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
+                                 GFP_KERNEL);
+       if (!ctx->sinfo->sig)
+               return -ENOMEM;
        return 0;
 }
index a66b19e..f4e8107 100644 (file)
@@ -22,7 +22,6 @@ struct pkcs7_signed_info {
        struct pkcs7_signed_info *next;
        struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
        unsigned        index;
-       bool            trusted;
        bool            unsupported_crypto;     /* T if not usable due to missing crypto */
 
        /* Message digest - the digest of the Content Data (or NULL) */
@@ -41,19 +40,17 @@ struct pkcs7_signed_info {
 #define        sinfo_has_ms_statement_type     5
        time64_t        signing_time;
 
-       /* Issuing cert serial number and issuer's name [PKCS#7 or CMS ver 1]
-        * or issuing cert's SKID [CMS ver 3].
-        */
-       struct asymmetric_key_id *signing_cert_id;
-
        /* Message signature.
         *
         * 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.
+        *
+        * THis also contains the issuing cert serial number and issuer's name
+        * [PKCS#7 or CMS ver 1] or issuing cert's SKID [CMS ver 3].
         */
-       struct public_key_signature sig;
+       struct public_key_signature *sig;
 };
 
 struct pkcs7_message {
index 7d7a39b..f6a009d 100644 (file)
@@ -27,10 +27,9 @@ static 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 public_key_signature *sig = sinfo->sig;
        struct x509_certificate *x509, *last = NULL, *p;
        struct key *key;
-       bool trusted;
        int ret;
 
        kenter(",%u,", sinfo->index);
@@ -42,10 +41,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
 
        for (x509 = sinfo->signer; x509; x509 = x509->signer) {
                if (x509->seen) {
-                       if (x509->verified) {
-                               trusted = x509->trusted;
+                       if (x509->verified)
                                goto verified;
-                       }
                        kleave(" = -ENOKEY [cached]");
                        return -ENOKEY;
                }
@@ -54,9 +51,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
                /* Look to see if this certificate is present in the trusted
                 * keys.
                 */
-               key = x509_request_asymmetric_key(trust_keyring,
-                                                 x509->id, x509->skid,
-                                                 false);
+               key = find_asymmetric_key(trust_keyring,
+                                         x509->id, x509->skid, false);
                if (!IS_ERR(key)) {
                        /* One of the X.509 certificates in the PKCS#7 message
                         * is apparently the same as one we already trust.
@@ -80,17 +76,17 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
 
                might_sleep();
                last = x509;
-               sig = &last->sig;
+               sig = last->sig;
        }
 
        /* No match - see if the root certificate has a signer amongst the
         * trusted keys.
         */
-       if (last && (last->akid_id || last->akid_skid)) {
-               key = x509_request_asymmetric_key(trust_keyring,
-                                                 last->akid_id,
-                                                 last->akid_skid,
-                                                 false);
+       if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) {
+               key = find_asymmetric_key(trust_keyring,
+                                         last->sig->auth_ids[0],
+                                         last->sig->auth_ids[1],
+                                         false);
                if (!IS_ERR(key)) {
                        x509 = last;
                        pr_devel("sinfo %u: Root cert %u signer is key %x\n",
@@ -104,10 +100,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
        /* As a last resort, see if we have a trusted public key that matches
         * the signed info directly.
         */
-       key = x509_request_asymmetric_key(trust_keyring,
-                                         sinfo->signing_cert_id,
-                                         NULL,
-                                         false);
+       key = find_asymmetric_key(trust_keyring,
+                                 sinfo->sig->auth_ids[0], NULL, false);
        if (!IS_ERR(key)) {
                pr_devel("sinfo %u: Direct signer is key %x\n",
                         sinfo->index, key_serial(key));
@@ -122,7 +116,6 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
 
 matched:
        ret = verify_signature(key, sig);
-       trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags);
        key_put(key);
        if (ret < 0) {
                if (ret == -ENOMEM)
@@ -134,12 +127,9 @@ matched:
 verified:
        if (x509) {
                x509->verified = true;
-               for (p = sinfo->signer; p != x509; p = p->signer) {
+               for (p = sinfo->signer; p != x509; p = p->signer)
                        p->verified = true;
-                       p->trusted = trusted;
-               }
        }
-       sinfo->trusted = trusted;
        kleave(" = 0");
        return 0;
 }
@@ -148,7 +138,6 @@ verified:
  * 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.
@@ -170,16 +159,13 @@ verified:
  * May also return -ENOMEM.
  */
 int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
-                        struct key *trust_keyring,
-                        bool *_trusted)
+                        struct key *trust_keyring)
 {
        struct pkcs7_signed_info *sinfo;
        struct x509_certificate *p;
        int cached_ret = -ENOKEY;
        int ret;
 
-       *_trusted = false;
-
        for (p = pkcs7->certs; p; p = p->next)
                p->seen = false;
 
@@ -193,7 +179,6 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
                                cached_ret = -ENOPKG;
                        continue;
                case 0:
-                       *_trusted |= sinfo->trusted;
                        cached_ret = 0;
                        continue;
                default:
index 50be2a1..44b746e 100644 (file)
 static int pkcs7_digest(struct pkcs7_message *pkcs7,
                        struct pkcs7_signed_info *sinfo)
 {
+       struct public_key_signature *sig = sinfo->sig;
        struct crypto_shash *tfm;
        struct shash_desc *desc;
-       size_t digest_size, desc_size;
-       void *digest;
+       size_t desc_size;
        int ret;
 
-       kenter(",%u,%s", sinfo->index, sinfo->sig.hash_algo);
+       kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo);
 
-       if (!sinfo->sig.hash_algo)
+       if (!sinfo->sig->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(sinfo->sig.hash_algo, 0, 0);
+       tfm = crypto_alloc_shash(sinfo->sig->hash_algo, 0, 0);
        if (IS_ERR(tfm))
                return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
 
        desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
-       sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm);
+       sig->digest_size = crypto_shash_digestsize(tfm);
 
        ret = -ENOMEM;
-       digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size,
-                        GFP_KERNEL);
-       if (!digest)
+       sig->digest = kmalloc(sig->digest_size, GFP_KERNEL);
+       if (!sig->digest)
+               goto error_no_desc;
+
+       desc = kzalloc(desc_size, GFP_KERNEL);
+       if (!desc)
                goto error_no_desc;
 
-       desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc));
        desc->tfm   = tfm;
        desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
 
@@ -60,10 +62,11 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
        ret = crypto_shash_init(desc);
        if (ret < 0)
                goto error;
-       ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest);
+       ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len,
+                                sig->digest);
        if (ret < 0)
                goto error;
-       pr_devel("MsgDigest = [%*ph]\n", 8, digest);
+       pr_devel("MsgDigest = [%*ph]\n", 8, sig->digest);
 
        /* However, if there are authenticated attributes, there must be a
         * message digest attribute amongst them which corresponds to the
@@ -78,14 +81,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
                        goto error;
                }
 
-               if (sinfo->msgdigest_len != sinfo->sig.digest_size) {
+               if (sinfo->msgdigest_len != 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) {
+               if (memcmp(sig->digest, sinfo->msgdigest,
+                          sinfo->msgdigest_len) != 0) {
                        pr_debug("Sig %u: Message digest doesn't match\n",
                                 sinfo->index);
                        ret = -EKEYREJECTED;
@@ -97,7 +101,7 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
                 * convert the attributes from a CONT.0 into a SET before we
                 * hash it.
                 */
-               memset(digest, 0, sinfo->sig.digest_size);
+               memset(sig->digest, 0, sig->digest_size);
 
                ret = crypto_shash_init(desc);
                if (ret < 0)
@@ -107,17 +111,14 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
                if (ret < 0)
                        goto error;
                ret = crypto_shash_finup(desc, sinfo->authattrs,
-                                        sinfo->authattrs_len, digest);
+                                        sinfo->authattrs_len, sig->digest);
                if (ret < 0)
                        goto error;
-               pr_devel("AADigest = [%*ph]\n", 8, digest);
+               pr_devel("AADigest = [%*ph]\n", 8, sig->digest);
        }
 
-       sinfo->sig.digest = digest;
-       digest = NULL;
-
 error:
-       kfree(digest);
+       kfree(desc);
 error_no_desc:
        crypto_free_shash(tfm);
        kleave(" = %d", ret);
@@ -144,12 +145,12 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
                 * PKCS#7 message - but I can't be 100% sure of that.  It's
                 * possible this will need element-by-element comparison.
                 */
-               if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id))
+               if (!asymmetric_key_id_same(x509->id, sinfo->sig->auth_ids[0]))
                        continue;
                pr_devel("Sig %u: Found cert serial match X.509[%u]\n",
                         sinfo->index, certix);
 
-               if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) {
+               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;
@@ -164,7 +165,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
         */
        pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n",
                 sinfo->index,
-                sinfo->signing_cert_id->len, sinfo->signing_cert_id->data);
+                sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data);
        return 0;
 }
 
@@ -174,6 +175,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
 static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                                  struct pkcs7_signed_info *sinfo)
 {
+       struct public_key_signature *sig;
        struct x509_certificate *x509 = sinfo->signer, *p;
        struct asymmetric_key_id *auth;
        int ret;
@@ -188,34 +190,26 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                         x509->subject,
                         x509->raw_serial_size, x509->raw_serial);
                x509->seen = true;
-               ret = x509_get_sig_params(x509);
-               if (ret < 0)
-                       goto maybe_missing_crypto_in_x509;
+               if (x509->unsupported_key)
+                       goto unsupported_crypto_in_x509;
 
                pr_debug("- issuer %s\n", x509->issuer);
-               if (x509->akid_id)
+               sig = x509->sig;
+               if (sig->auth_ids[0])
                        pr_debug("- authkeyid.id %*phN\n",
-                                x509->akid_id->len, x509->akid_id->data);
-               if (x509->akid_skid)
+                                sig->auth_ids[0]->len, sig->auth_ids[0]->data);
+               if (sig->auth_ids[1])
                        pr_debug("- authkeyid.skid %*phN\n",
-                                x509->akid_skid->len, x509->akid_skid->data);
+                                sig->auth_ids[1]->len, sig->auth_ids[1]->data);
 
-               if ((!x509->akid_id && !x509->akid_skid) ||
-                   strcmp(x509->subject, x509->issuer) == 0) {
+               if (x509->self_signed) {
                        /* 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)
-                               goto maybe_missing_crypto_in_x509;
+                       if (x509->unsupported_sig)
+                               goto unsupported_crypto_in_x509;
                        x509->signer = x509;
                        pr_debug("- self-signed\n");
                        return 0;
@@ -224,7 +218,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                /* Look through the X.509 certificates in the PKCS#7 message's
                 * list to see if the next one is there.
                 */
-               auth = x509->akid_id;
+               auth = sig->auth_ids[0];
                if (auth) {
                        pr_debug("- want %*phN\n", auth->len, auth->data);
                        for (p = pkcs7->certs; p; p = p->next) {
@@ -234,7 +228,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                                        goto found_issuer_check_skid;
                        }
                } else {
-                       auth = x509->akid_skid;
+                       auth = sig->auth_ids[1];
                        pr_debug("- want %*phN\n", auth->len, auth->data);
                        for (p = pkcs7->certs; p; p = p->next) {
                                if (!p->skid)
@@ -254,8 +248,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                /* We matched issuer + serialNumber, but if there's an
                 * authKeyId.keyId, that must match the CA subjKeyId also.
                 */
-               if (x509->akid_skid &&
-                   !asymmetric_key_id_same(p->skid, x509->akid_skid)) {
+               if (sig->auth_ids[1] &&
+                   !asymmetric_key_id_same(p->skid, sig->auth_ids[1])) {
                        pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n",
                                sinfo->index, x509->index, p->index);
                        return -EKEYREJECTED;
@@ -267,7 +261,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                                sinfo->index);
                        return 0;
                }
-               ret = x509_check_signature(p->pub, x509);
+               ret = public_key_verify_signature(p->pub, p->sig);
                if (ret < 0)
                        return ret;
                x509->signer = p;
@@ -279,16 +273,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                might_sleep();
        }
 
-maybe_missing_crypto_in_x509:
+unsupported_crypto_in_x509:
        /* Just prune the certificate chain at this point if we lack some
         * crypto module to go further.  Note, however, we don't want to set
-        * sinfo->missing_crypto as the signed info block may still be
+        * sinfo->unsupported_crypto as the signed info block may still be
         * validatable against an X.509 cert lower in the chain that we have a
         * trusted copy of.
         */
-       if (ret == -ENOPKG)
-               return 0;
-       return ret;
+       return 0;
 }
 
 /*
@@ -332,7 +324,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
        }
 
        /* Verify the PKCS#7 binary against the key */
-       ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig);
+       ret = public_key_verify_signature(sinfo->signer->pub, sinfo->sig);
        if (ret < 0)
                return ret;
 
@@ -375,9 +367,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
                 enum key_being_used_for usage)
 {
        struct pkcs7_signed_info *sinfo;
-       struct x509_certificate *x509;
        int enopkg = -ENOPKG;
-       int ret, n;
+       int ret;
 
        kenter("");
 
@@ -419,12 +410,6 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
                return -EINVAL;
        }
 
-       for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) {
-               ret = x509_get_sig_params(x509);
-               if (ret < 0)
-                       return ret;
-       }
-
        for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
                ret = pkcs7_verify_one(pkcs7, sinfo);
                if (ret < 0) {
index 0f8b264..fd76b5f 100644 (file)
@@ -39,15 +39,23 @@ static void public_key_describe(const struct key *asymmetric_key,
 /*
  * Destroy a public key algorithm key.
  */
-void public_key_destroy(void *payload)
+void public_key_free(struct public_key *key)
 {
-       struct public_key *key = payload;
-
-       if (key)
+       if (key) {
                kfree(key->key);
-       kfree(key);
+               kfree(key);
+       }
+}
+EXPORT_SYMBOL_GPL(public_key_free);
+
+/*
+ * Destroy a public key algorithm key.
+ */
+static void public_key_destroy(void *payload0, void *payload3)
+{
+       public_key_free(payload0);
+       public_key_signature_free(payload3);
 }
-EXPORT_SYMBOL_GPL(public_key_destroy);
 
 struct public_key_completion {
        struct completion completion;
diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c
new file mode 100644 (file)
index 0000000..ac4bddf
--- /dev/null
@@ -0,0 +1,108 @@
+/* Instantiate a public key crypto key from an X.509 Certificate
+ *
+ * Copyright (C) 2012, 2016 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) "ASYM: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <crypto/public_key.h>
+#include "asymmetric_keys.h"
+
+static bool use_builtin_keys;
+static struct asymmetric_key_id *ca_keyid;
+
+#ifndef MODULE
+static struct {
+       struct asymmetric_key_id id;
+       unsigned char data[10];
+} cakey;
+
+static int __init ca_keys_setup(char *str)
+{
+       if (!str)               /* default system keyring */
+               return 1;
+
+       if (strncmp(str, "id:", 3) == 0) {
+               struct asymmetric_key_id *p = &cakey.id;
+               size_t hexlen = (strlen(str) - 3) / 2;
+               int ret;
+
+               if (hexlen == 0 || hexlen > sizeof(cakey.data)) {
+                       pr_err("Missing or invalid ca_keys id\n");
+                       return 1;
+               }
+
+               ret = __asymmetric_key_hex_to_key_id(str + 3, p, hexlen);
+               if (ret < 0)
+                       pr_err("Unparsable ca_keys id hex string\n");
+               else
+                       ca_keyid = p;   /* owner key 'id:xxxxxx' */
+       } else if (strcmp(str, "builtin") == 0) {
+               use_builtin_keys = true;
+       }
+
+       return 1;
+}
+__setup("ca_keys=", ca_keys_setup);
+#endif
+
+/**
+ * restrict_link_by_signature - Restrict additions to a ring of public keys
+ * @trust_keyring: A ring of keys that can be used to vouch for the new cert.
+ * @type: The type of key being added.
+ * @payload: The payload of the new key.
+ *
+ * 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.
+ *
+ * Returns 0 if the new certificate was accepted, -ENOKEY if we couldn't find a
+ * matching parent certificate in the trusted list, -EKEYREJECTED if the
+ * signature check fails or the key is blacklisted and some other error if
+ * there is a matching certificate but the signature check cannot be performed.
+ */
+int restrict_link_by_signature(struct key *trust_keyring,
+                              const struct key_type *type,
+                              const union key_payload *payload)
+{
+       const struct public_key_signature *sig;
+       struct key *key;
+       int ret;
+
+       pr_devel("==>%s()\n", __func__);
+
+       if (!trust_keyring)
+               return -ENOKEY;
+
+       if (type != &key_type_asymmetric)
+               return -EOPNOTSUPP;
+
+       sig = payload->data[asym_auth];
+       if (!sig->auth_ids[0] && !sig->auth_ids[1])
+               return 0;
+
+       if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid))
+               return -EPERM;
+
+       /* See if we have a key that signed this one. */
+       key = find_asymmetric_key(trust_keyring,
+                                 sig->auth_ids[0], sig->auth_ids[1],
+                                 false);
+       if (IS_ERR(key))
+               return -ENOKEY;
+
+       if (use_builtin_keys && !test_bit(KEY_FLAG_BUILTIN, &key->flags))
+               ret = -ENOKEY;
+       else
+               ret = verify_signature(key, sig);
+       key_put(key);
+       return ret;
+}
index 004d5fc..11b7ba1 100644 (file)
 #include <keys/asymmetric-subtype.h>
 #include <linux/export.h>
 #include <linux/err.h>
+#include <linux/slab.h>
 #include <crypto/public_key.h>
 #include "asymmetric_keys.h"
 
+/*
+ * Destroy a public key signature.
+ */
+void public_key_signature_free(struct public_key_signature *sig)
+{
+       int i;
+
+       if (sig) {
+               for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++)
+                       kfree(sig->auth_ids[i]);
+               kfree(sig->s);
+               kfree(sig->digest);
+               kfree(sig);
+       }
+}
+EXPORT_SYMBOL_GPL(public_key_signature_free);
+
 /**
  * verify_signature - Initiate the use of an asymmetric key to verify a signature
  * @key: The asymmetric key to verify against
index 7e8c233..672a94c 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/err.h>
 #include <linux/pe.h>
 #include <linux/asn1.h>
-#include <crypto/pkcs7.h>
+#include <linux/verification.h>
 #include <crypto/hash.h>
 #include "verify_pefile.h"
 
@@ -392,9 +392,8 @@ error_no_desc:
  * 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
+ * @trust_keys: Signing certificate(s) to use as starting points
  * @usage: The use to which the key is being put.
- * @_trusted: Set to true if trustworth, false otherwise
  *
  * Validate that the certificate chain inside the PKCS#7 message inside the PE
  * binary image intersects keys we already know and trust.
@@ -418,14 +417,10 @@ error_no_desc:
  * May also return -ENOMEM.
  */
 int verify_pefile_signature(const void *pebuf, unsigned pelen,
-                           struct key *trusted_keyring,
-                           enum key_being_used_for usage,
-                           bool *_trusted)
+                           struct key *trusted_keys,
+                           enum key_being_used_for usage)
 {
-       struct pkcs7_message *pkcs7;
        struct pefile_context ctx;
-       const void *data;
-       size_t datalen;
        int ret;
 
        kenter("");
@@ -439,19 +434,10 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
        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);
+       ret = verify_pkcs7_signature(NULL, 0,
+                                    pebuf + ctx.sig_offset, ctx.sig_len,
+                                    trusted_keys, usage,
+                                    mscode_parse, &ctx);
        if (ret < 0)
                goto error;
 
@@ -462,16 +448,8 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
         * contents.
         */
        ret = pefile_digest_pe(pebuf, pelen, &ctx);
-       if (ret < 0)
-               goto error;
-
-       ret = pkcs7_verify(pkcs7, usage);
-       if (ret < 0)
-               goto error;
-
-       ret = pkcs7_validate_trust(pkcs7, trusted_keyring, _trusted);
 
 error:
-       pkcs7_free_message(ctx.pkcs7);
+       kfree(ctx.digest);
        return ret;
 }
index a133eb8..cd4d209 100644 (file)
@@ -9,7 +9,6 @@
  * 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>
 
@@ -23,7 +22,6 @@ struct pefile_context {
        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 */
@@ -39,4 +37,5 @@ struct pefile_context {
 /*
  * mscode_parser.c
  */
-extern int mscode_parse(struct pefile_context *ctx);
+extern int mscode_parse(void *_ctx, const void *content_data, size_t data_len,
+                       size_t asn1hdrlen);
index 4a29bac..865f46e 100644 (file)
@@ -47,15 +47,12 @@ struct x509_parse_context {
 void x509_free_certificate(struct x509_certificate *cert)
 {
        if (cert) {
-               public_key_destroy(cert->pub);
+               public_key_free(cert->pub);
+               public_key_signature_free(cert->sig);
                kfree(cert->issuer);
                kfree(cert->subject);
                kfree(cert->id);
                kfree(cert->skid);
-               kfree(cert->akid_id);
-               kfree(cert->akid_skid);
-               kfree(cert->sig.digest);
-               kfree(cert->sig.s);
                kfree(cert);
        }
 }
@@ -78,6 +75,9 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
        cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
        if (!cert->pub)
                goto error_no_ctx;
+       cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL);
+       if (!cert->sig)
+               goto error_no_ctx;
        ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
        if (!ctx)
                goto error_no_ctx;
@@ -108,6 +108,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
 
        cert->pub->keylen = ctx->key_size;
 
+       /* Grab the signature bits */
+       ret = x509_get_sig_params(cert);
+       if (ret < 0)
+               goto error_decode;
+
        /* Generate cert issuer + serial number key ID */
        kid = asymmetric_key_generate_id(cert->raw_serial,
                                         cert->raw_serial_size,
@@ -119,6 +124,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
        }
        cert->id = kid;
 
+       /* Detect self-signed certificates */
+       ret = x509_check_for_self_signed(cert);
+       if (ret < 0)
+               goto error_decode;
+
        kfree(ctx);
        return cert;
 
@@ -188,33 +198,33 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
                return -ENOPKG; /* Unsupported combination */
 
        case OID_md4WithRSAEncryption:
-               ctx->cert->sig.hash_algo = "md4";
-               ctx->cert->sig.pkey_algo = "rsa";
+               ctx->cert->sig->hash_algo = "md4";
+               ctx->cert->sig->pkey_algo = "rsa";
                break;
 
        case OID_sha1WithRSAEncryption:
-               ctx->cert->sig.hash_algo = "sha1";
-               ctx->cert->sig.pkey_algo = "rsa";
+               ctx->cert->sig->hash_algo = "sha1";
+               ctx->cert->sig->pkey_algo = "rsa";
                break;
 
        case OID_sha256WithRSAEncryption:
-               ctx->cert->sig.hash_algo = "sha256";
-               ctx->cert->sig.pkey_algo = "rsa";
+               ctx->cert->sig->hash_algo = "sha256";
+               ctx->cert->sig->pkey_algo = "rsa";
                break;
 
        case OID_sha384WithRSAEncryption:
-               ctx->cert->sig.hash_algo = "sha384";
-               ctx->cert->sig.pkey_algo = "rsa";
+               ctx->cert->sig->hash_algo = "sha384";
+               ctx->cert->sig->pkey_algo = "rsa";
                break;
 
        case OID_sha512WithRSAEncryption:
-               ctx->cert->sig.hash_algo = "sha512";
-               ctx->cert->sig.pkey_algo = "rsa";
+               ctx->cert->sig->hash_algo = "sha512";
+               ctx->cert->sig->pkey_algo = "rsa";
                break;
 
        case OID_sha224WithRSAEncryption:
-               ctx->cert->sig.hash_algo = "sha224";
-               ctx->cert->sig.pkey_algo = "rsa";
+               ctx->cert->sig->hash_algo = "sha224";
+               ctx->cert->sig->pkey_algo = "rsa";
                break;
        }
 
@@ -572,14 +582,14 @@ int x509_akid_note_kid(void *context, size_t hdrlen,
 
        pr_debug("AKID: keyid: %*phN\n", (int)vlen, value);
 
-       if (ctx->cert->akid_skid)
+       if (ctx->cert->sig->auth_ids[1])
                return 0;
 
        kid = asymmetric_key_generate_id(value, vlen, "", 0);
        if (IS_ERR(kid))
                return PTR_ERR(kid);
        pr_debug("authkeyid %*phN\n", kid->len, kid->data);
-       ctx->cert->akid_skid = kid;
+       ctx->cert->sig->auth_ids[1] = kid;
        return 0;
 }
 
@@ -611,7 +621,7 @@ int x509_akid_note_serial(void *context, size_t hdrlen,
 
        pr_debug("AKID: serial: %*phN\n", (int)vlen, value);
 
-       if (!ctx->akid_raw_issuer || ctx->cert->akid_id)
+       if (!ctx->akid_raw_issuer || ctx->cert->sig->auth_ids[0])
                return 0;
 
        kid = asymmetric_key_generate_id(value,
@@ -622,6 +632,6 @@ int x509_akid_note_serial(void *context, size_t hdrlen,
                return PTR_ERR(kid);
 
        pr_debug("authkeyid %*phN\n", kid->len, kid->data);
-       ctx->cert->akid_id = kid;
+       ctx->cert->sig->auth_ids[0] = kid;
        return 0;
 }
index dbeed60..05eef1c 100644 (file)
@@ -17,13 +17,11 @@ 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 */
+       struct public_key_signature *sig;       /* Signature parameters */
        char            *issuer;                /* Name of certificate issuer */
        char            *subject;               /* Name of certificate subject */
        struct asymmetric_key_id *id;           /* Issuer + Serial number */
        struct asymmetric_key_id *skid;         /* Subject + subjectKeyId (optional) */
-       struct asymmetric_key_id *akid_id;      /* CA AuthKeyId matching ->id (optional) */
-       struct asymmetric_key_id *akid_skid;    /* CA AuthKeyId matching ->skid (optional) */
        time64_t        valid_from;
        time64_t        valid_to;
        const void      *tbs;                   /* Signed data */
@@ -41,8 +39,9 @@ struct x509_certificate {
        unsigned        index;
        bool            seen;                   /* Infinite recursion prevention */
        bool            verified;
-       bool            trusted;
-       bool            unsupported_crypto;     /* T if can't be verified due to missing crypto */
+       bool            self_signed;            /* T if self-signed (check unsupported_sig too) */
+       bool            unsupported_key;        /* T if key uses unsupported crypto */
+       bool            unsupported_sig;        /* T if signature uses unsupported crypto */
 };
 
 /*
@@ -58,5 +57,4 @@ extern int x509_decode_time(time64_t *_t,  size_t hdrlen,
  * x509_public_key.c
  */
 extern int x509_get_sig_params(struct x509_certificate *cert);
-extern int x509_check_signature(const struct public_key *pub,
-                               struct x509_certificate *cert);
+extern int x509_check_for_self_signed(struct x509_certificate *cert);
index 733c046..fb73229 100644 (file)
 #include "asymmetric_keys.h"
 #include "x509_parser.h"
 
-static bool use_builtin_keys;
-static struct asymmetric_key_id *ca_keyid;
-
-#ifndef MODULE
-static struct {
-       struct asymmetric_key_id id;
-       unsigned char data[10];
-} cakey;
-
-static int __init ca_keys_setup(char *str)
-{
-       if (!str)               /* default system keyring */
-               return 1;
-
-       if (strncmp(str, "id:", 3) == 0) {
-               struct asymmetric_key_id *p = &cakey.id;
-               size_t hexlen = (strlen(str) - 3) / 2;
-               int ret;
-
-               if (hexlen == 0 || hexlen > sizeof(cakey.data)) {
-                       pr_err("Missing or invalid ca_keys id\n");
-                       return 1;
-               }
-
-               ret = __asymmetric_key_hex_to_key_id(str + 3, p, hexlen);
-               if (ret < 0)
-                       pr_err("Unparsable ca_keys id hex string\n");
-               else
-                       ca_keyid = p;   /* 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.
- * @id: The issuer & serialNumber to look for or NULL.
- * @skid: The subjectKeyIdentifier to look for or NULL.
- * @partial: Use partial match if true, exact if false.
- *
- * Find a key in the given keyring by identifier.  The preferred identifier is
- * the issuer + serialNumber and the fallback identifier is the
- * subjectKeyIdentifier.  If both are given, the lookup is by the former, but
- * the latter must also match.
- */
-struct key *x509_request_asymmetric_key(struct key *keyring,
-                                       const struct asymmetric_key_id *id,
-                                       const struct asymmetric_key_id *skid,
-                                       bool partial)
-{
-       struct key *key;
-       key_ref_t ref;
-       const char *lookup;
-       char *req, *p;
-       int len;
-
-       if (id) {
-               lookup = id->data;
-               len = id->len;
-       } else {
-               lookup = skid->data;
-               len = skid->len;
-       }
-       
-       /* Construct an identifier "id:<keyid>". */
-       p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
-       if (!req)
-               return ERR_PTR(-ENOMEM);
-
-       if (partial) {
-               *p++ = 'i';
-               *p++ = 'd';
-       } else {
-               *p++ = 'e';
-               *p++ = 'x';
-       }
-       *p++ = ':';
-       p = bin2hex(p, lookup, len);
-       *p = 0;
-
-       pr_debug("Look up: \"%s\"\n", req);
-
-       ref = keyring_search(make_key_ref(keyring, 1),
-                            &key_type_asymmetric, req);
-       if (IS_ERR(ref))
-               pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
-       kfree(req);
-
-       if (IS_ERR(ref)) {
-               switch (PTR_ERR(ref)) {
-                       /* Hide some search errors */
-               case -EACCES:
-               case -ENOTDIR:
-               case -EAGAIN:
-                       return ERR_PTR(-ENOKEY);
-               default:
-                       return ERR_CAST(ref);
-               }
-       }
-
-       key = key_ref_to_ptr(ref);
-       if (id && skid) {
-               const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
-               if (!kids->id[1]) {
-                       pr_debug("issuer+serial match, but expected SKID missing\n");
-                       goto reject;
-               }
-               if (!asymmetric_key_id_same(skid, kids->id[1])) {
-                       pr_debug("issuer+serial match, but SKID does not\n");
-                       goto reject;
-               }
-       }
-       
-       pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
-       return key;
-
-reject:
-       key_put(key);
-       return ERR_PTR(-EKEYREJECTED);
-}
-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.
  */
 int x509_get_sig_params(struct x509_certificate *cert)
 {
+       struct public_key_signature *sig = cert->sig;
        struct crypto_shash *tfm;
        struct shash_desc *desc;
-       size_t digest_size, desc_size;
-       void *digest;
+       size_t desc_size;
        int ret;
 
        pr_devel("==>%s()\n", __func__);
 
-       if (cert->unsupported_crypto)
-               return -ENOPKG;
-       if (cert->sig.s)
+       if (!cert->pub->pkey_algo)
+               cert->unsupported_key = true;
+
+       if (!sig->pkey_algo)
+               cert->unsupported_sig = true;
+
+       /* We check the hash if we can - even if we can't then verify it */
+       if (!sig->hash_algo) {
+               cert->unsupported_sig = true;
                return 0;
+       }
 
-       cert->sig.s = kmemdup(cert->raw_sig, cert->raw_sig_size,
-                             GFP_KERNEL);
-       if (!cert->sig.s)
+       sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL);
+       if (!sig->s)
                return -ENOMEM;
 
-       cert->sig.s_size = cert->raw_sig_size;
+       sig->s_size = cert->raw_sig_size;
 
        /* Allocate the hashing algorithm we're going to need and find out how
         * big the hash operational data will be.
         */
-       tfm = crypto_alloc_shash(cert->sig.hash_algo, 0, 0);
+       tfm = crypto_alloc_shash(sig->hash_algo, 0, 0);
        if (IS_ERR(tfm)) {
                if (PTR_ERR(tfm) == -ENOENT) {
-                       cert->unsupported_crypto = true;
-                       return -ENOPKG;
+                       cert->unsupported_sig = true;
+                       return 0;
                }
                return PTR_ERR(tfm);
        }
 
        desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
-       digest_size = crypto_shash_digestsize(tfm);
+       sig->digest_size = crypto_shash_digestsize(tfm);
 
-       /* We allocate the hash operational data storage on the end of the
-        * digest storage space.
-        */
        ret = -ENOMEM;
-       digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size,
-                        GFP_KERNEL);
-       if (!digest)
+       sig->digest = kmalloc(sig->digest_size, GFP_KERNEL);
+       if (!sig->digest)
                goto error;
 
-       cert->sig.digest = digest;
-       cert->sig.digest_size = digest_size;
+       desc = kzalloc(desc_size, GFP_KERNEL);
+       if (!desc)
+               goto error;
 
-       desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc));
        desc->tfm = tfm;
        desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
 
        ret = crypto_shash_init(desc);
        if (ret < 0)
-               goto error;
+               goto error_2;
        might_sleep();
-       ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, digest);
+       ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest);
+
+error_2:
+       kfree(desc);
 error:
        crypto_free_shash(tfm);
        pr_devel("<==%s() = %d\n", __func__, ret);
        return ret;
 }
-EXPORT_SYMBOL_GPL(x509_get_sig_params);
 
 /*
- * Check the signature on a certificate using the provided public key
+ * Check for self-signedness in an X.509 cert and if found, check the signature
+ * immediately if we can.
  */
-int x509_check_signature(const struct public_key *pub,
-                        struct x509_certificate *cert)
+int x509_check_for_self_signed(struct x509_certificate *cert)
 {
-       int ret;
+       int ret = 0;
 
        pr_devel("==>%s()\n", __func__);
 
-       ret = x509_get_sig_params(cert);
-       if (ret < 0)
-               return ret;
+       if (cert->raw_subject_size != cert->raw_issuer_size ||
+           memcmp(cert->raw_subject, cert->raw_issuer,
+                  cert->raw_issuer_size) != 0)
+               goto not_self_signed;
+
+       if (cert->sig->auth_ids[0] || cert->sig->auth_ids[1]) {
+               /* If the AKID is present it may have one or two parts.  If
+                * both are supplied, both must match.
+                */
+               bool a = asymmetric_key_id_same(cert->skid, cert->sig->auth_ids[1]);
+               bool b = asymmetric_key_id_same(cert->id, cert->sig->auth_ids[0]);
+
+               if (!a && !b)
+                       goto not_self_signed;
+
+               ret = -EKEYREJECTED;
+               if (((a && !b) || (b && !a)) &&
+                   cert->sig->auth_ids[0] && cert->sig->auth_ids[1])
+                       goto out;
+       }
 
-       ret = public_key_verify_signature(pub, &cert->sig);
-       if (ret == -ENOPKG)
-               cert->unsupported_crypto = true;
-       pr_debug("Cert Verification: %d\n", ret);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(x509_check_signature);
+       ret = -EKEYREJECTED;
+       if (cert->pub->pkey_algo != cert->sig->pkey_algo)
+               goto out;
 
-/*
- * 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_key_id_partial(cert->akid_skid, ca_keyid))
-               return -EPERM;
-
-       key = x509_request_asymmetric_key(trust_keyring,
-                                         cert->akid_id, cert->akid_skid,
-                                         false);
-       if (!IS_ERR(key))  {
-               if (!use_builtin_keys
-                   || test_bit(KEY_FLAG_BUILTIN, &key->flags))
-                       ret = x509_check_signature(key->payload.data[asym_crypto],
-                                                  cert);
-               key_put(key);
+       ret = public_key_verify_signature(cert->pub, cert->sig);
+       if (ret < 0) {
+               if (ret == -ENOPKG) {
+                       cert->unsupported_sig = true;
+                       ret = 0;
+               }
+               goto out;
        }
+
+       pr_devel("Cert Self-signature verified");
+       cert->self_signed = true;
+
+out:
+       pr_devel("<==%s() = %d\n", __func__, ret);
        return ret;
+
+not_self_signed:
+       pr_devel("<==%s() = 0 [not]\n", __func__);
+       return 0;
 }
 
 /*
@@ -291,34 +168,22 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
        pr_devel("Cert Issuer: %s\n", cert->issuer);
        pr_devel("Cert Subject: %s\n", cert->subject);
 
-       if (!cert->pub->pkey_algo ||
-           !cert->sig.pkey_algo ||
-           !cert->sig.hash_algo) {
+       if (cert->unsupported_key) {
                ret = -ENOPKG;
                goto error_free_cert;
        }
 
        pr_devel("Cert Key Algo: %s\n", cert->pub->pkey_algo);
        pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to);
-       pr_devel("Cert Signature: %s + %s\n",
-                cert->sig.pkey_algo,
-                cert->sig.hash_algo);
 
        cert->pub->id_type = "X509";
 
-       /* Check the signature on the key if it appears to be self-signed */
-       if ((!cert->akid_skid && !cert->akid_id) ||
-           asymmetric_key_id_same(cert->skid, cert->akid_skid) ||
-           asymmetric_key_id_same(cert->id, cert->akid_id)) {
-               ret = x509_check_signature(cert->pub, cert); /* self-signed */
-               if (ret < 0)
-                       goto error_free_cert;
-       } else if (!prep->trusted) {
-               ret = x509_validate_trust(cert, get_system_trusted_keyring());
-               if (ret)
-                       ret = x509_validate_trust(cert, get_ima_mok_keyring());
-               if (!ret)
-                       prep->trusted = 1;
+       if (cert->unsupported_sig) {
+               public_key_signature_free(cert->sig);
+               cert->sig = NULL;
+       } else {
+               pr_devel("Cert Signature: %s + %s\n",
+                        cert->sig->pkey_algo, cert->sig->hash_algo);
        }
 
        /* Propose a description */
@@ -353,6 +218,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
        prep->payload.data[asym_subtype] = &public_key_subtype;
        prep->payload.data[asym_key_ids] = kids;
        prep->payload.data[asym_crypto] = cert->pub;
+       prep->payload.data[asym_auth] = cert->sig;
        prep->description = desc;
        prep->quotalen = 100;
 
@@ -360,6 +226,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
        cert->pub = NULL;
        cert->id = NULL;
        cert->skid = NULL;
+       cert->sig = NULL;
        desc = NULL;
        ret = 0;
 
index 3f93125..71e8a56 100644 (file)
@@ -360,7 +360,7 @@ init_cifs_idmap(void)
                                GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
                                (KEY_POS_ALL & ~KEY_POS_SETATTR) |
                                KEY_USR_VIEW | KEY_USR_READ,
-                               KEY_ALLOC_NOT_IN_QUOTA, NULL);
+                               KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto failed_put_cred;
index a98b21d..e92419f 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -850,15 +850,25 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size,
        if (ret)
                return ret;
 
+       ret = deny_write_access(file);
+       if (ret)
+               return ret;
+
        i_size = i_size_read(file_inode(file));
-       if (max_size > 0 && i_size > max_size)
-               return -EFBIG;
-       if (i_size <= 0)
-               return -EINVAL;
+       if (max_size > 0 && i_size > max_size) {
+               ret = -EFBIG;
+               goto out;
+       }
+       if (i_size <= 0) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        *buf = vmalloc(i_size);
-       if (!*buf)
-               return -ENOMEM;
+       if (!*buf) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        pos = 0;
        while (pos < i_size) {
@@ -876,18 +886,21 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size,
 
        if (pos != i_size) {
                ret = -EIO;
-               goto out;
+               goto out_free;
        }
 
        ret = security_kernel_post_read_file(file, *buf, i_size, id);
        if (!ret)
                *size = pos;
 
-out:
+out_free:
        if (ret < 0) {
                vfree(*buf);
                *buf = NULL;
        }
+
+out:
+       allow_write_access(file);
        return ret;
 }
 EXPORT_SYMBOL_GPL(kernel_read_file);
index 9d193d3..5375571 100644 (file)
@@ -3627,6 +3627,8 @@ retry:
        switch (mode & S_IFMT) {
                case 0: case S_IFREG:
                        error = vfs_create(path.dentry->d_inode,dentry,mode,true);
+                       if (!error)
+                               ima_post_path_mknod(dentry);
                        break;
                case S_IFCHR: case S_IFBLK:
                        error = vfs_mknod(path.dentry->d_inode,dentry,mode,
index 5ba22c6..c444285 100644 (file)
@@ -201,7 +201,7 @@ int nfs_idmap_init(void)
                                GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
                                (KEY_POS_ALL & ~KEY_POS_SETATTR) |
                                KEY_USR_VIEW | KEY_USR_READ,
-                               KEY_ALLOC_NOT_IN_QUOTA, NULL);
+                               KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto failed_put_cred;
index 441aff9..583f199 100644 (file)
@@ -12,6 +12,7 @@
 #ifndef _CRYPTO_PKCS7_H
 #define _CRYPTO_PKCS7_H
 
+#include <linux/verification.h>
 #include <crypto/public_key.h>
 
 struct key;
@@ -26,14 +27,13 @@ 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);
+                                 size_t *_headerlen);
 
 /*
  * pkcs7_trust.c
  */
 extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
-                               struct key *trust_keyring,
-                               bool *_trusted);
+                               struct key *trust_keyring);
 
 /*
  * pkcs7_verify.c
index aa730ea..882ca0e 100644 (file)
 #define _LINUX_PUBLIC_KEY_H
 
 /*
- * The use to which an asymmetric key is being put.
- */
-enum key_being_used_for {
-       VERIFYING_MODULE_SIGNATURE,
-       VERIFYING_FIRMWARE_SIGNATURE,
-       VERIFYING_KEXEC_PE_SIGNATURE,
-       VERIFYING_KEY_SIGNATURE,
-       VERIFYING_KEY_SELF_SIGNATURE,
-       VERIFYING_UNSPECIFIED_SIGNATURE,
-       NR__KEY_BEING_USED_FOR
-};
-extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
-
-/*
  * Cryptographic data for the public-key subtype of the asymmetric key type.
  *
  * Note that this may include private part of the key as well as the public
@@ -41,12 +27,13 @@ struct public_key {
        const char *pkey_algo;
 };
 
-extern void public_key_destroy(void *payload);
+extern void public_key_free(struct public_key *key);
 
 /*
  * Public key cryptography signature data
  */
 struct public_key_signature {
+       struct asymmetric_key_id *auth_ids[2];
        u8 *s;                  /* Signature */
        u32 s_size;             /* Number of bytes in signature */
        u8 *digest;
@@ -55,17 +42,21 @@ struct public_key_signature {
        const char *hash_algo;
 };
 
+extern void public_key_signature_free(struct public_key_signature *sig);
+
 extern struct asymmetric_key_subtype public_key_subtype;
+
 struct key;
+struct key_type;
+union key_payload;
+
+extern int restrict_link_by_signature(struct key *trust_keyring,
+                                     const struct key_type *type,
+                                     const union key_payload *payload);
+
 extern int verify_signature(const struct key *key,
                            const struct public_key_signature *sig);
 
-struct asymmetric_key_id;
-extern struct key *x509_request_asymmetric_key(struct key *keyring,
-                                              const struct asymmetric_key_id *id,
-                                              const struct asymmetric_key_id *skid,
-                                              bool partial);
-
 int public_key_verify_signature(const struct public_key *pkey,
                                const struct public_key_signature *sig);
 
index 4915d40..2480469 100644 (file)
@@ -32,7 +32,7 @@ struct asymmetric_key_subtype {
        void (*describe)(const struct key *key, struct seq_file *m);
 
        /* Destroy a key of this subtype */
-       void (*destroy)(void *payload);
+       void (*destroy)(void *payload_crypto, void *payload_auth);
 
        /* Verify the signature on a key of this subtype (optional) */
        int (*verify_signature)(const struct key *key,
index 59c1df9..b382407 100644 (file)
@@ -15,6 +15,7 @@
 #define _KEYS_ASYMMETRIC_TYPE_H
 
 #include <linux/key-type.h>
+#include <linux/verification.h>
 
 extern struct key_type key_type_asymmetric;
 
@@ -23,9 +24,10 @@ extern struct key_type key_type_asymmetric;
  * follows:
  */
 enum asymmetric_payload_bits {
-       asym_crypto,
-       asym_subtype,
-       asym_key_ids,
+       asym_crypto,            /* The data representing the key */
+       asym_subtype,           /* Pointer to an asymmetric_key_subtype struct */
+       asym_key_ids,           /* Pointer to an asymmetric_key_ids struct */
+       asym_auth               /* The key's authorisation (signature, parent key ID) */
 };
 
 /*
@@ -74,6 +76,11 @@ const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key)
        return key->payload.data[asym_key_ids];
 }
 
+extern struct key *find_asymmetric_key(struct key *keyring,
+                                      const struct asymmetric_key_id *id_0,
+                                      const struct asymmetric_key_id *id_1,
+                                      bool partial);
+
 /*
  * The payload is at the discretion of the subtype.
  */
index 39fd38c..fbd4647 100644 (file)
 #ifndef _KEYS_SYSTEM_KEYRING_H
 #define _KEYS_SYSTEM_KEYRING_H
 
+#include <linux/key.h>
+
 #ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
 
-#include <linux/key.h>
-#include <crypto/public_key.h>
+extern int restrict_link_by_builtin_trusted(struct key *keyring,
+                                           const struct key_type *type,
+                                           const union key_payload *payload);
 
-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;
-}
+#define restrict_link_by_builtin_trusted restrict_link_reject
 #endif
 
-#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
-extern int system_verify_data(const void *data, unsigned long len,
-                             const void *raw_pkcs7, size_t pkcs7_len,
-                             enum key_being_used_for usage);
+#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
+extern int restrict_link_by_builtin_and_secondary_trusted(
+       struct key *keyring,
+       const struct key_type *type,
+       const union key_payload *payload);
+#else
+#define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted
 #endif
 
-#ifdef CONFIG_IMA_MOK_KEYRING
-extern struct key *ima_mok_keyring;
+#ifdef CONFIG_IMA_BLACKLIST_KEYRING
 extern struct key *ima_blacklist_keyring;
 
-static inline struct key *get_ima_mok_keyring(void)
-{
-       return ima_mok_keyring;
-}
 static inline struct key *get_ima_blacklist_keyring(void)
 {
        return ima_blacklist_keyring;
 }
 #else
-static inline struct key *get_ima_mok_keyring(void)
-{
-       return NULL;
-}
 static inline struct key *get_ima_blacklist_keyring(void)
 {
        return NULL;
 }
-#endif /* CONFIG_IMA_MOK_KEYRING */
+#endif /* CONFIG_IMA_BLACKLIST_KEYRING */
 
 
 #endif /* _KEYS_SYSTEM_KEYRING_H */
index 851390c..10d3d8f 100644 (file)
@@ -2634,15 +2634,34 @@ static inline void i_readcount_inc(struct inode *inode)
 #endif
 extern int do_pipe_flags(int *, int);
 
+#define __kernel_read_file_id(id) \
+       id(UNKNOWN, unknown)            \
+       id(FIRMWARE, firmware)          \
+       id(MODULE, kernel-module)               \
+       id(KEXEC_IMAGE, kexec-image)            \
+       id(KEXEC_INITRAMFS, kexec-initramfs)    \
+       id(POLICY, security-policy)             \
+       id(MAX_ID, )
+
+#define __fid_enumify(ENUM, dummy) READING_ ## ENUM,
+#define __fid_stringify(dummy, str) #str,
+
 enum kernel_read_file_id {
-       READING_FIRMWARE = 1,
-       READING_MODULE,
-       READING_KEXEC_IMAGE,
-       READING_KEXEC_INITRAMFS,
-       READING_POLICY,
-       READING_MAX_ID
+       __kernel_read_file_id(__fid_enumify)
+};
+
+static const char * const kernel_read_file_str[] = {
+       __kernel_read_file_id(__fid_stringify)
 };
 
+static inline const char *kernel_read_file_id_str(enum kernel_read_file_id id)
+{
+       if (id < 0 || id >= READING_MAX_ID)
+               return kernel_read_file_str[READING_UNKNOWN];
+
+       return kernel_read_file_str[id];
+}
+
 extern int kernel_read(struct file *, loff_t, char *, unsigned long);
 extern int kernel_read_file(struct file *, void **, loff_t *, loff_t,
                            enum kernel_read_file_id);
index e6516cb..0eb7c2e 100644 (file)
@@ -21,6 +21,7 @@ extern int ima_file_mmap(struct file *file, unsigned long prot);
 extern int ima_read_file(struct file *file, enum kernel_read_file_id id);
 extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
                              enum kernel_read_file_id id);
+extern void ima_post_path_mknod(struct dentry *dentry);
 
 #else
 static inline int ima_bprm_check(struct linux_binprm *bprm)
@@ -54,6 +55,11 @@ static inline int ima_post_read_file(struct file *file, void *buf, loff_t size,
        return 0;
 }
 
+static inline void ima_post_path_mknod(struct dentry *dentry)
+{
+       return;
+}
+
 #endif /* CONFIG_IMA */
 
 #ifdef CONFIG_IMA_APPRAISE
index 7463355..eaee981 100644 (file)
@@ -45,7 +45,6 @@ struct key_preparsed_payload {
        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 */
 };
 
 typedef int (*request_key_actor_t)(struct key_construction *key,
index 5f5b112..7229147 100644 (file)
@@ -173,11 +173,9 @@ struct key {
 #define KEY_FLAG_NEGATIVE      5       /* set if key is negative */
 #define KEY_FLAG_ROOT_CAN_CLEAR        6       /* set if key can be cleared by root without permission */
 #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 */
-#define KEY_FLAG_KEEP          12      /* set if key should not be removed */
+#define KEY_FLAG_BUILTIN       8       /* set if key is built in to the kernel */
+#define KEY_FLAG_ROOT_CAN_INVAL        9       /* set if key can be invalidated by root without permission */
+#define KEY_FLAG_KEEP          10      /* set if key should not be removed */
 
        /* the key type and key description string
         * - the desc is used to match a key against search criteria
@@ -205,6 +203,20 @@ struct key {
                };
                int reject_error;
        };
+
+       /* This is set on a keyring to restrict the addition of a link to a key
+        * to it.  If this method isn't provided then it is assumed that the
+        * keyring is open to any addition.  It is ignored for non-keyring
+        * keys.
+        *
+        * This is intended for use with rings of trusted keys whereby addition
+        * to the keyring needs to be controlled.  KEY_ALLOC_BYPASS_RESTRICTION
+        * overrides this, allowing the kernel to add extra keys without
+        * restriction.
+        */
+       int (*restrict_link)(struct key *keyring,
+                            const struct key_type *type,
+                            const union key_payload *payload);
 };
 
 extern struct key *key_alloc(struct key_type *type,
@@ -212,14 +224,17 @@ extern struct key *key_alloc(struct key_type *type,
                             kuid_t uid, kgid_t gid,
                             const struct cred *cred,
                             key_perm_t perm,
-                            unsigned long flags);
+                            unsigned long flags,
+                            int (*restrict_link)(struct key *,
+                                                 const struct key_type *,
+                                                 const union key_payload *));
 
 
-#define KEY_ALLOC_IN_QUOTA     0x0000  /* add to quota, reject if would overrun */
-#define KEY_ALLOC_QUOTA_OVERRUN        0x0001  /* add to quota, permit even if overrun */
-#define KEY_ALLOC_NOT_IN_QUOTA 0x0002  /* not in quota */
-#define KEY_ALLOC_TRUSTED      0x0004  /* Key should be flagged as trusted */
-#define KEY_ALLOC_BUILT_IN     0x0008  /* Key is built into kernel */
+#define KEY_ALLOC_IN_QUOTA             0x0000  /* add to quota, reject if would overrun */
+#define KEY_ALLOC_QUOTA_OVERRUN                0x0001  /* add to quota, permit even if overrun */
+#define KEY_ALLOC_NOT_IN_QUOTA         0x0002  /* not in quota */
+#define KEY_ALLOC_BUILT_IN             0x0004  /* Key is built into kernel */
+#define KEY_ALLOC_BYPASS_RESTRICTION   0x0008  /* Override the check on restricted keyrings */
 
 extern void key_revoke(struct key *key);
 extern void key_invalidate(struct key *key);
@@ -288,8 +303,15 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid
                                 const struct cred *cred,
                                 key_perm_t perm,
                                 unsigned long flags,
+                                int (*restrict_link)(struct key *,
+                                                     const struct key_type *,
+                                                     const union key_payload *),
                                 struct key *dest);
 
+extern int restrict_link_reject(struct key *keyring,
+                               const struct key_type *type,
+                               const union key_payload *payload);
+
 extern int keyring_clear(struct key *keyring);
 
 extern key_ref_t keyring_search(key_ref_t keyring,
index 512fd00..7ae3976 100644 (file)
@@ -1805,7 +1805,6 @@ struct security_hook_heads {
        struct list_head tun_dev_attach_queue;
        struct list_head tun_dev_attach;
        struct list_head tun_dev_open;
-       struct list_head skb_owned_by;
 #endif /* CONFIG_SECURITY_NETWORK */
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
        struct list_head xfrm_policy_alloc_security;
@@ -1894,5 +1893,10 @@ extern void __init yama_add_hooks(void);
 #else
 static inline void __init yama_add_hooks(void) { }
 #endif
+#ifdef CONFIG_SECURITY_LOADPIN
+void __init loadpin_add_hooks(void);
+#else
+static inline void loadpin_add_hooks(void) { };
+#endif
 
 #endif /* ! __LINUX_LSM_HOOKS_H */
index dabe643..5ce9538 100644 (file)
@@ -3,6 +3,8 @@
 
 #include <linux/types.h>
 
+struct file;
+
 /* Descriptions of the types of units to
  * print in */
 enum string_size_units {
@@ -68,4 +70,8 @@ static inline int string_escape_str_any_np(const char *src, char *dst,
        return string_escape_str(src, dst, sz, ESCAPE_ANY_NP, only);
 }
 
+char *kstrdup_quotable(const char *src, gfp_t gfp);
+char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp);
+char *kstrdup_quotable_file(struct file *file, gfp_t gfp);
+
 #endif
diff --git a/include/linux/verification.h b/include/linux/verification.h
new file mode 100644 (file)
index 0000000..a10549a
--- /dev/null
@@ -0,0 +1,49 @@
+/* Signature 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_VERIFICATION_H
+#define _LINUX_VERIFICATION_H
+
+/*
+ * The use to which an asymmetric key is being put.
+ */
+enum key_being_used_for {
+       VERIFYING_MODULE_SIGNATURE,
+       VERIFYING_FIRMWARE_SIGNATURE,
+       VERIFYING_KEXEC_PE_SIGNATURE,
+       VERIFYING_KEY_SIGNATURE,
+       VERIFYING_KEY_SELF_SIGNATURE,
+       VERIFYING_UNSPECIFIED_SIGNATURE,
+       NR__KEY_BEING_USED_FOR
+};
+extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
+
+#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
+
+struct key;
+
+extern int verify_pkcs7_signature(const void *data, size_t len,
+                                 const void *raw_pkcs7, size_t pkcs7_len,
+                                 struct key *trusted_keys,
+                                 enum key_being_used_for usage,
+                                 int (*view_content)(void *ctx,
+                                                     const void *data, size_t len,
+                                                     size_t asn1hdrlen),
+                                 void *ctx);
+
+#ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION
+extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
+                                  struct key *trusted_keys,
+                                  enum key_being_used_for usage);
+#endif
+
+#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
+#endif /* _LINUX_VERIFY_PEFILE_H */
diff --git a/include/linux/verify_pefile.h b/include/linux/verify_pefile.h
deleted file mode 100644 (file)
index da2049b..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/* 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
-
-#include <crypto/public_key.h>
-
-extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
-                                  struct key *trusted_keyring,
-                                  enum key_being_used_for usage,
-                                  bool *_trusted);
-
-#endif /* _LINUX_VERIFY_PEFILE_H */
index 840cb99..86eddd6 100644 (file)
@@ -12,6 +12,8 @@
 #ifndef _LINUX_KEYCTL_H
 #define _LINUX_KEYCTL_H
 
+#include <linux/types.h>
+
 /* special process keyring shortcut IDs */
 #define KEY_SPEC_THREAD_KEYRING                -1      /* - key ID for thread-specific keyring */
 #define KEY_SPEC_PROCESS_KEYRING       -2      /* - key ID for process-specific keyring */
 #define KEYCTL_INSTANTIATE_IOV         20      /* instantiate a partially constructed key */
 #define KEYCTL_INVALIDATE              21      /* invalidate a key */
 #define KEYCTL_GET_PERSISTENT          22      /* get a user's persistent keyring */
+#define KEYCTL_DH_COMPUTE              23      /* Compute Diffie-Hellman values */
+
+/* keyctl structures */
+struct keyctl_dh_params {
+       __s32 private;
+       __s32 prime;
+       __s32 base;
+};
 
 #endif /*  _LINUX_KEYCTL_H */
index 64b9dea..937c844 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/string.h>
-#include <keys/system_keyring.h>
+#include <linux/verification.h>
 #include <crypto/public_key.h>
 #include "module-internal.h"
 
@@ -80,6 +80,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
                return -EBADMSG;
        }
 
-       return system_verify_data(mod, modlen, mod + modlen, sig_len,
-                                 VERIFYING_MODULE_SIGNATURE);
+       return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
+                                     NULL, VERIFYING_MODULE_SIGNATURE,
+                                     NULL, NULL);
 }
index e1e5a35..6c9bb62 100644 (file)
@@ -915,7 +915,7 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
 
        fprog = filter->prog->orig_prog;
        if (!fprog) {
-               /* This must be a new non-cBPF filter, since we save every
+               /* This must be a new non-cBPF filter, since we save
                 * every cBPF filter's orig_prog above when
                 * CONFIG_CHECKPOINT_RESTORE is enabled.
                 */
index 5c88204..ecaac2c 100644 (file)
 #include <linux/export.h>
 #include <linux/ctype.h>
 #include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/limits.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/string_helpers.h>
 
@@ -534,3 +538,91 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
        return p - dst;
 }
 EXPORT_SYMBOL(string_escape_mem);
+
+/*
+ * Return an allocated string that has been escaped of special characters
+ * and double quotes, making it safe to log in quotes.
+ */
+char *kstrdup_quotable(const char *src, gfp_t gfp)
+{
+       size_t slen, dlen;
+       char *dst;
+       const int flags = ESCAPE_HEX;
+       const char esc[] = "\f\n\r\t\v\a\e\\\"";
+
+       if (!src)
+               return NULL;
+       slen = strlen(src);
+
+       dlen = string_escape_mem(src, slen, NULL, 0, flags, esc);
+       dst = kmalloc(dlen + 1, gfp);
+       if (!dst)
+               return NULL;
+
+       WARN_ON(string_escape_mem(src, slen, dst, dlen, flags, esc) != dlen);
+       dst[dlen] = '\0';
+
+       return dst;
+}
+EXPORT_SYMBOL_GPL(kstrdup_quotable);
+
+/*
+ * Returns allocated NULL-terminated string containing process
+ * command line, with inter-argument NULLs replaced with spaces,
+ * and other special characters escaped.
+ */
+char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp)
+{
+       char *buffer, *quoted;
+       int i, res;
+
+       buffer = kmalloc(PAGE_SIZE, GFP_TEMPORARY);
+       if (!buffer)
+               return NULL;
+
+       res = get_cmdline(task, buffer, PAGE_SIZE - 1);
+       buffer[res] = '\0';
+
+       /* Collapse trailing NULLs, leave res pointing to last non-NULL. */
+       while (--res >= 0 && buffer[res] == '\0')
+               ;
+
+       /* Replace inter-argument NULLs. */
+       for (i = 0; i <= res; i++)
+               if (buffer[i] == '\0')
+                       buffer[i] = ' ';
+
+       /* Make sure result is printable. */
+       quoted = kstrdup_quotable(buffer, gfp);
+       kfree(buffer);
+       return quoted;
+}
+EXPORT_SYMBOL_GPL(kstrdup_quotable_cmdline);
+
+/*
+ * Returns allocated NULL-terminated string containing pathname,
+ * with special characters escaped, able to be safely logged. If
+ * there is an error, the leading character will be "<".
+ */
+char *kstrdup_quotable_file(struct file *file, gfp_t gfp)
+{
+       char *temp, *pathname;
+
+       if (!file)
+               return kstrdup("<unknown>", gfp);
+
+       /* We add 11 spaces for ' (deleted)' to be appended */
+       temp = kmalloc(PATH_MAX + 11, GFP_TEMPORARY);
+       if (!temp)
+               return kstrdup("<no_memory>", gfp);
+
+       pathname = file_path(file, temp, PATH_MAX + 11);
+       if (IS_ERR(pathname))
+               pathname = kstrdup("<too_long>", gfp);
+       else
+               pathname = kstrdup_quotable(pathname, gfp);
+
+       kfree(temp);
+       return pathname;
+}
+EXPORT_SYMBOL_GPL(kstrdup_quotable_file);
index c79b85e..8737412 100644 (file)
@@ -281,7 +281,7 @@ static int __init init_dns_resolver(void)
                                GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
                                (KEY_POS_ALL & ~KEY_POS_SETATTR) |
                                KEY_USR_VIEW | KEY_USR_READ,
-                               KEY_ALLOC_NOT_IN_QUOTA, NULL);
+                               KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto failed_put_cred;
index 28cddc8..1325776 100644 (file)
@@ -677,7 +677,7 @@ int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap,
        u32 spot = start;
 
        while (rc == 0 && spot <= end) {
-               if (((spot & (BITS_PER_LONG - 1)) != 0) &&
+               if (((spot & (BITS_PER_LONG - 1)) == 0) &&
                    ((end - spot) > BITS_PER_LONG)) {
                        rc = netlbl_catmap_setlong(catmap,
                                                   spot,
index 3fb492e..1021b4c 100644 (file)
@@ -965,7 +965,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
 
        key = key_alloc(&key_type_rxrpc, "x",
                        GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0,
-                       KEY_ALLOC_NOT_IN_QUOTA);
+                       KEY_ALLOC_NOT_IN_QUOTA, NULL);
        if (IS_ERR(key)) {
                _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
                return -ENOMEM;
@@ -1012,7 +1012,7 @@ struct key *rxrpc_get_null_key(const char *keyname)
 
        key = key_alloc(&key_type_rxrpc, keyname,
                        GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
-                       KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA);
+                       KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, NULL);
        if (IS_ERR(key))
                return key;
 
index e452378..176758c 100644 (file)
@@ -122,6 +122,7 @@ source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
+source security/loadpin/Kconfig
 source security/yama/Kconfig
 
 source security/integrity/Kconfig
index c9bfbc8..f2d71cd 100644 (file)
@@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK)         += smack
 subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)     += apparmor
 subdir-$(CONFIG_SECURITY_YAMA)         += yama
+subdir-$(CONFIG_SECURITY_LOADPIN)      += loadpin
 
 # always enable default capabilities
 obj-y                                  += commoncap.o
@@ -22,6 +23,7 @@ obj-$(CONFIG_AUDIT)                   += lsm_audit.o
 obj-$(CONFIG_SECURITY_TOMOYO)          += tomoyo/
 obj-$(CONFIG_SECURITY_APPARMOR)                += apparmor/
 obj-$(CONFIG_SECURITY_YAMA)            += yama/
+obj-$(CONFIG_SECURITY_LOADPIN)         += loadpin/
 obj-$(CONFIG_CGROUP_DEVICE)            += device_cgroup.o
 
 # Object integrity file lists
index 979be65..da95658 100644 (file)
@@ -35,7 +35,6 @@ config INTEGRITY_ASYMMETRIC_KEYS
        default n
         select ASYMMETRIC_KEY_TYPE
         select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
-        select PUBLIC_KEY_ALGO_RSA
         select CRYPTO_RSA
         select X509_CERTIFICATE_PARSER
        help
index 8ef1511..4304372 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/cred.h>
 #include <linux/key-type.h>
 #include <linux/digsig.h>
+#include <crypto/public_key.h>
+#include <keys/system_keyring.h>
 
 #include "integrity.h"
 
@@ -40,6 +42,12 @@ static bool init_keyring __initdata = true;
 static bool init_keyring __initdata;
 #endif
 
+#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
+#define restrict_link_to_ima restrict_link_by_builtin_and_secondary_trusted
+#else
+#define restrict_link_to_ima restrict_link_by_builtin_trusted
+#endif
+
 int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
                            const char *digest, int digestlen)
 {
@@ -83,10 +91,9 @@ int __init integrity_init_keyring(const unsigned int id)
                                    ((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 {
+                                   KEY_ALLOC_NOT_IN_QUOTA,
+                                   restrict_link_to_ima, NULL);
+       if (IS_ERR(keyring[id])) {
                err = PTR_ERR(keyring[id]);
                pr_info("Can't allocate %s keyring (%d)\n",
                        keyring_name[id], err);
index e54a8a8..5487827 100644 (file)
@@ -155,23 +155,33 @@ config IMA_TRUSTED_KEYRING
 
           This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING
 
-config IMA_MOK_KEYRING
-       bool "Create IMA machine owner keys (MOK) and blacklist keyrings"
+config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
+       bool "Permit keys validly signed by a built-in or secondary CA cert (EXPERIMENTAL)"
+       depends on SYSTEM_TRUSTED_KEYRING
+       depends on SECONDARY_TRUSTED_KEYRING
+       depends on INTEGRITY_ASYMMETRIC_KEYS
+       select INTEGRITY_TRUSTED_KEYRING
+       default n
+       help
+         Keys may be added to the IMA or IMA blacklist keyrings, if the
+         key is validly signed by a CA cert in the system built-in or
+         secondary trusted keyrings.
+
+         Intermediate keys between those the kernel has compiled in and the
+         IMA keys to be added may be added to the system secondary keyring,
+         provided they are validly signed by a key already resident in the
+         built-in or secondary trusted keyrings.
+
+config IMA_BLACKLIST_KEYRING
+       bool "Create IMA machine owner blacklist keyrings (EXPERIMENTAL)"
        depends on SYSTEM_TRUSTED_KEYRING
        depends on IMA_TRUSTED_KEYRING
        default n
        help
-          This option creates IMA MOK and blacklist keyrings.  IMA MOK is an
-          intermediate keyring that sits between .system and .ima keyrings,
-          effectively forming a simple CA hierarchy.  To successfully import a
-          key into .ima_mok it must be signed by a key which CA is in .system
-          keyring.  On turn any key that needs to go in .ima keyring must be
-          signed by CA in either .system or .ima_mok keyrings. IMA MOK is empty
-          at kernel boot.
-
-          IMA blacklist keyring contains all revoked IMA keys.  It is consulted
-          before any other keyring.  If the search is successful the requested
-          operation is rejected and error is returned to the caller.
+          This option creates an IMA blacklist keyring, which contains all
+          revoked IMA keys.  It is consulted before any other keyring.  If
+          the search is successful the requested operation is rejected and
+          an error is returned to the caller.
 
 config IMA_LOAD_X509
        bool "Load X509 certificate onto the '.ima' trusted keyring"
index a8539f9..9aeaeda 100644 (file)
@@ -8,4 +8,4 @@ obj-$(CONFIG_IMA) += ima.o
 ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
         ima_policy.o ima_template.o ima_template_lib.o
 ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
-obj-$(CONFIG_IMA_MOK_KEYRING) += ima_mok.o
+obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
index 6b4694a..1bcbc12 100644 (file)
@@ -275,6 +275,11 @@ out:
                     xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
                        if (!ima_fix_xattr(dentry, iint))
                                status = INTEGRITY_PASS;
+               } else if ((inode->i_size == 0) &&
+                          (iint->flags & IMA_NEW_FILE) &&
+                          (xattr_value &&
+                           xattr_value->type == EVM_IMA_XATTR_DIGSIG)) {
+                       status = INTEGRITY_PASS;
                }
                integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
                                    op, cause, rc, 0);
@@ -328,7 +333,7 @@ void ima_inode_post_setattr(struct dentry *dentry)
        if (iint) {
                iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
                                 IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
-                                IMA_ACTION_FLAGS);
+                                IMA_ACTION_RULE_FLAGS);
                if (must_appraise)
                        iint->flags |= IMA_APPRAISE;
        }
index 391f417..68b26c3 100644 (file)
@@ -246,7 +246,8 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
                ima_audit_measurement(iint, pathname);
 
 out_digsig:
-       if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG))
+       if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG) &&
+            !(iint->flags & IMA_NEW_FILE))
                rc = -EACCES;
        kfree(xattr_value);
 out_free:
@@ -316,6 +317,28 @@ int ima_file_check(struct file *file, int mask, int opened)
 EXPORT_SYMBOL_GPL(ima_file_check);
 
 /**
+ * ima_post_path_mknod - mark as a new inode
+ * @dentry: newly created dentry
+ *
+ * Mark files created via the mknodat syscall as new, so that the
+ * file data can be written later.
+ */
+void ima_post_path_mknod(struct dentry *dentry)
+{
+       struct integrity_iint_cache *iint;
+       struct inode *inode = dentry->d_inode;
+       int must_appraise;
+
+       must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK);
+       if (!must_appraise)
+               return;
+
+       iint = integrity_inode_get(inode);
+       if (iint)
+               iint->flags |= IMA_NEW_FILE;
+}
+
+/**
  * ima_read_file - pre-measure/appraise hook decision based on policy
  * @file: pointer to the file to be measured/appraised/audit
  * @read_id: caller identifier
index 676885e..74a2799 100644 (file)
 #include <linux/cred.h>
 #include <linux/err.h>
 #include <linux/init.h>
-#include <keys/asymmetric-type.h>
+#include <keys/system_keyring.h>
 
 
-struct key *ima_mok_keyring;
 struct key *ima_blacklist_keyring;
 
 /*
- * Allocate the IMA MOK and blacklist keyrings
+ * Allocate the IMA blacklist keyring
  */
 __init int ima_mok_init(void)
 {
-       pr_notice("Allocating IMA MOK and blacklist keyrings.\n");
-
-       ima_mok_keyring = keyring_alloc(".ima_mok",
-                             KUIDT_INIT(0), KGIDT_INIT(0), current_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);
+       pr_notice("Allocating IMA blacklist keyring.\n");
 
        ima_blacklist_keyring = keyring_alloc(".ima_blacklist",
                                KUIDT_INIT(0), KGIDT_INIT(0), current_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);
+                               KEY_ALLOC_NOT_IN_QUOTA,
+                               restrict_link_by_builtin_trusted, NULL);
 
-       if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring))
-               panic("Can't allocate IMA MOK or blacklist keyrings.");
-       set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_mok_keyring->flags);
+       if (IS_ERR(ima_blacklist_keyring))
+               panic("Can't allocate IMA blacklist keyring.");
 
-       set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_blacklist_keyring->flags);
        set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags);
        return 0;
 }
index e08935c..90bc57d 100644 (file)
@@ -28,6 +28,7 @@
 
 /* iint cache flags */
 #define IMA_ACTION_FLAGS       0xff000000
+#define IMA_ACTION_RULE_FLAGS  0x06000000
 #define IMA_DIGSIG             0x01000000
 #define IMA_DIGSIG_REQUIRED    0x02000000
 #define IMA_PERMIT_DIRECTIO    0x04000000
index fe4d74e..f826e87 100644 (file)
@@ -41,6 +41,10 @@ config BIG_KEYS
        bool "Large payload keys"
        depends on KEYS
        depends on TMPFS
+       select CRYPTO
+       select CRYPTO_AES
+       select CRYPTO_ECB
+       select CRYPTO_RNG
        help
          This option provides support for holding large keys within the kernel
          (for example Kerberos ticket caches).  The data may be stored out to
@@ -81,3 +85,14 @@ config ENCRYPTED_KEYS
          Userspace only ever sees/stores encrypted blobs.
 
          If you are unsure as to whether this is required, answer N.
+
+config KEY_DH_OPERATIONS
+       bool "Diffie-Hellman operations on retained keys"
+       depends on KEYS
+       select MPILIB
+       help
+        This option provides support for calculating Diffie-Hellman
+        public keys and shared secrets using values stored as keys
+        in the kernel.
+
+        If you are unsure as to whether this is required, answer N.
index dfb3a7b..1fd4a16 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
 obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
+obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
 
 #
 # Key types
index c721e39..9e443fc 100644 (file)
 #include <linux/file.h>
 #include <linux/shmem_fs.h>
 #include <linux/err.h>
+#include <linux/scatterlist.h>
 #include <keys/user-type.h>
 #include <keys/big_key-type.h>
+#include <crypto/rng.h>
 
 /*
  * Layout of key payload words.
@@ -28,6 +30,14 @@ enum {
 };
 
 /*
+ * Crypto operation with big_key data
+ */
+enum big_key_op {
+       BIG_KEY_ENC,
+       BIG_KEY_DEC,
+};
+
+/*
  * If the data is under this limit, there's no point creating a shm file to
  * hold it as the permanently resident metadata for the shmem fs will be at
  * least as large as the data.
@@ -35,6 +45,11 @@ enum {
 #define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
 
 /*
+ * Key size for big_key data encryption
+ */
+#define ENC_KEY_SIZE   16
+
+/*
  * big_key defined keys take an arbitrary string as the description and an
  * arbitrary blob of data as the payload
  */
@@ -50,12 +65,62 @@ struct key_type key_type_big_key = {
 };
 
 /*
+ * Crypto names for big_key data encryption
+ */
+static const char big_key_rng_name[] = "stdrng";
+static const char big_key_alg_name[] = "ecb(aes)";
+
+/*
+ * Crypto algorithms for big_key data encryption
+ */
+static struct crypto_rng *big_key_rng;
+static struct crypto_blkcipher *big_key_blkcipher;
+
+/*
+ * Generate random key to encrypt big_key data
+ */
+static inline int big_key_gen_enckey(u8 *key)
+{
+       return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE);
+}
+
+/*
+ * Encrypt/decrypt big_key data
+ */
+static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key)
+{
+       int ret = -EINVAL;
+       struct scatterlist sgio;
+       struct blkcipher_desc desc;
+
+       if (crypto_blkcipher_setkey(big_key_blkcipher, key, ENC_KEY_SIZE)) {
+               ret = -EAGAIN;
+               goto error;
+       }
+
+       desc.flags = 0;
+       desc.tfm = big_key_blkcipher;
+
+       sg_init_one(&sgio, data, datalen);
+
+       if (op == BIG_KEY_ENC)
+               ret = crypto_blkcipher_encrypt(&desc, &sgio, &sgio, datalen);
+       else
+               ret = crypto_blkcipher_decrypt(&desc, &sgio, &sgio, datalen);
+
+error:
+       return ret;
+}
+
+/*
  * Preparse a big key
  */
 int big_key_preparse(struct key_preparsed_payload *prep)
 {
        struct path *path = (struct path *)&prep->payload.data[big_key_path];
        struct file *file;
+       u8 *enckey;
+       u8 *data = NULL;
        ssize_t written;
        size_t datalen = prep->datalen;
        int ret;
@@ -73,16 +138,43 @@ int big_key_preparse(struct key_preparsed_payload *prep)
                /* Create a shmem file to store the data in.  This will permit the data
                 * to be swapped out if needed.
                 *
-                * TODO: Encrypt the stored data with a temporary key.
+                * File content is stored encrypted with randomly generated key.
                 */
-               file = shmem_kernel_file_setup("", datalen, 0);
+               size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher));
+
+               /* prepare aligned data to encrypt */
+               data = kmalloc(enclen, GFP_KERNEL);
+               if (!data)
+                       return -ENOMEM;
+
+               memcpy(data, prep->data, datalen);
+               memset(data + datalen, 0x00, enclen - datalen);
+
+               /* generate random key */
+               enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL);
+               if (!enckey) {
+                       ret = -ENOMEM;
+                       goto error;
+               }
+
+               ret = big_key_gen_enckey(enckey);
+               if (ret)
+                       goto err_enckey;
+
+               /* encrypt aligned data */
+               ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey);
+               if (ret)
+                       goto err_enckey;
+
+               /* save aligned data to file */
+               file = shmem_kernel_file_setup("", enclen, 0);
                if (IS_ERR(file)) {
                        ret = PTR_ERR(file);
-                       goto error;
+                       goto err_enckey;
                }
 
-               written = kernel_write(file, prep->data, prep->datalen, 0);
-               if (written != datalen) {
+               written = kernel_write(file, data, enclen, 0);
+               if (written != enclen) {
                        ret = written;
                        if (written >= 0)
                                ret = -ENOMEM;
@@ -92,12 +184,15 @@ int big_key_preparse(struct key_preparsed_payload *prep)
                /* Pin the mount and dentry to the key so that we can open it again
                 * later
                 */
+               prep->payload.data[big_key_data] = enckey;
                *path = file->f_path;
                path_get(path);
                fput(file);
+               kfree(data);
        } else {
                /* Just store the data in a buffer */
                void *data = kmalloc(datalen, GFP_KERNEL);
+
                if (!data)
                        return -ENOMEM;
 
@@ -108,7 +203,10 @@ int big_key_preparse(struct key_preparsed_payload *prep)
 
 err_fput:
        fput(file);
+err_enckey:
+       kfree(enckey);
 error:
+       kfree(data);
        return ret;
 }
 
@@ -119,10 +217,10 @@ void big_key_free_preparse(struct key_preparsed_payload *prep)
 {
        if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
                struct path *path = (struct path *)&prep->payload.data[big_key_path];
+
                path_put(path);
-       } else {
-               kfree(prep->payload.data[big_key_data]);
        }
+       kfree(prep->payload.data[big_key_data]);
 }
 
 /*
@@ -147,15 +245,15 @@ void big_key_destroy(struct key *key)
 {
        size_t datalen = (size_t)key->payload.data[big_key_len];
 
-       if (datalen) {
+       if (datalen > BIG_KEY_FILE_THRESHOLD) {
                struct path *path = (struct path *)&key->payload.data[big_key_path];
+
                path_put(path);
                path->mnt = NULL;
                path->dentry = NULL;
-       } else {
-               kfree(key->payload.data[big_key_data]);
-               key->payload.data[big_key_data] = NULL;
        }
+       kfree(key->payload.data[big_key_data]);
+       key->payload.data[big_key_data] = NULL;
 }
 
 /*
@@ -188,17 +286,41 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
        if (datalen > BIG_KEY_FILE_THRESHOLD) {
                struct path *path = (struct path *)&key->payload.data[big_key_path];
                struct file *file;
-               loff_t pos;
+               u8 *data;
+               u8 *enckey = (u8 *)key->payload.data[big_key_data];
+               size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher));
+
+               data = kmalloc(enclen, GFP_KERNEL);
+               if (!data)
+                       return -ENOMEM;
 
                file = dentry_open(path, O_RDONLY, current_cred());
-               if (IS_ERR(file))
-                       return PTR_ERR(file);
+               if (IS_ERR(file)) {
+                       ret = PTR_ERR(file);
+                       goto error;
+               }
 
-               pos = 0;
-               ret = vfs_read(file, buffer, datalen, &pos);
-               fput(file);
-               if (ret >= 0 && ret != datalen)
+               /* read file to kernel and decrypt */
+               ret = kernel_read(file, 0, data, enclen);
+               if (ret >= 0 && ret != enclen) {
                        ret = -EIO;
+                       goto err_fput;
+               }
+
+               ret = big_key_crypt(BIG_KEY_DEC, data, enclen, enckey);
+               if (ret)
+                       goto err_fput;
+
+               ret = datalen;
+
+               /* copy decrypted data to user */
+               if (copy_to_user(buffer, data, datalen) != 0)
+                       ret = -EFAULT;
+
+err_fput:
+               fput(file);
+error:
+               kfree(data);
        } else {
                ret = datalen;
                if (copy_to_user(buffer, key->payload.data[big_key_data],
@@ -209,8 +331,48 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
        return ret;
 }
 
+/*
+ * Register key type
+ */
 static int __init big_key_init(void)
 {
        return register_key_type(&key_type_big_key);
 }
+
+/*
+ * Initialize big_key crypto and RNG algorithms
+ */
+static int __init big_key_crypto_init(void)
+{
+       int ret = -EINVAL;
+
+       /* init RNG */
+       big_key_rng = crypto_alloc_rng(big_key_rng_name, 0, 0);
+       if (IS_ERR(big_key_rng)) {
+               big_key_rng = NULL;
+               return -EFAULT;
+       }
+
+       /* seed RNG */
+       ret = crypto_rng_reset(big_key_rng, NULL, crypto_rng_seedsize(big_key_rng));
+       if (ret)
+               goto error;
+
+       /* init block cipher */
+       big_key_blkcipher = crypto_alloc_blkcipher(big_key_alg_name, 0, 0);
+       if (IS_ERR(big_key_blkcipher)) {
+               big_key_blkcipher = NULL;
+               ret = -EFAULT;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       crypto_free_rng(big_key_rng);
+       big_key_rng = NULL;
+       return ret;
+}
+
 device_initcall(big_key_init);
+late_initcall(big_key_crypto_init);
index 25430a3..c8783b3 100644 (file)
@@ -132,6 +132,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
        case KEYCTL_GET_PERSISTENT:
                return keyctl_get_persistent(arg2, arg3);
 
+       case KEYCTL_DH_COMPUTE:
+               return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3),
+                                        arg4);
+
        default:
                return -EOPNOTSUPP;
        }
diff --git a/security/keys/dh.c b/security/keys/dh.c
new file mode 100644 (file)
index 0000000..880505a
--- /dev/null
@@ -0,0 +1,160 @@
+/* Crypto operations using stored keys
+ *
+ * Copyright (c) 2016, Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/mpi.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <keys/user-type.h>
+#include "internal.h"
+
+/*
+ * Public key or shared secret generation function [RFC2631 sec 2.1.1]
+ *
+ * ya = g^xa mod p;
+ * or
+ * ZZ = yb^xa mod p;
+ *
+ * where xa is the local private key, ya is the local public key, g is
+ * the generator, p is the prime, yb is the remote public key, and ZZ
+ * is the shared secret.
+ *
+ * Both are the same calculation, so g or yb are the "base" and ya or
+ * ZZ are the "result".
+ */
+static int do_dh(MPI result, MPI base, MPI xa, MPI p)
+{
+       return mpi_powm(result, base, xa, p);
+}
+
+static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi)
+{
+       struct key *key;
+       key_ref_t key_ref;
+       long status;
+       ssize_t ret;
+
+       key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ);
+       if (IS_ERR(key_ref)) {
+               ret = -ENOKEY;
+               goto error;
+       }
+
+       key = key_ref_to_ptr(key_ref);
+
+       ret = -EOPNOTSUPP;
+       if (key->type == &key_type_user) {
+               down_read(&key->sem);
+               status = key_validate(key);
+               if (status == 0) {
+                       const struct user_key_payload *payload;
+
+                       payload = user_key_payload(key);
+
+                       if (maxlen == 0) {
+                               *mpi = NULL;
+                               ret = payload->datalen;
+                       } else if (payload->datalen <= maxlen) {
+                               *mpi = mpi_read_raw_data(payload->data,
+                                                        payload->datalen);
+                               if (*mpi)
+                                       ret = payload->datalen;
+                       } else {
+                               ret = -EINVAL;
+                       }
+               }
+               up_read(&key->sem);
+       }
+
+       key_put(key);
+error:
+       return ret;
+}
+
+long keyctl_dh_compute(struct keyctl_dh_params __user *params,
+                      char __user *buffer, size_t buflen)
+{
+       long ret;
+       MPI base, private, prime, result;
+       unsigned nbytes;
+       struct keyctl_dh_params pcopy;
+       uint8_t *kbuf;
+       ssize_t keylen;
+       size_t resultlen;
+
+       if (!params || (!buffer && buflen)) {
+               ret = -EINVAL;
+               goto out;
+       }
+       if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       keylen = mpi_from_key(pcopy.prime, buflen, &prime);
+       if (keylen < 0 || !prime) {
+               /* buflen == 0 may be used to query the required buffer size,
+                * which is the prime key length.
+                */
+               ret = keylen;
+               goto out;
+       }
+
+       /* The result is never longer than the prime */
+       resultlen = keylen;
+
+       keylen = mpi_from_key(pcopy.base, SIZE_MAX, &base);
+       if (keylen < 0 || !base) {
+               ret = keylen;
+               goto error1;
+       }
+
+       keylen = mpi_from_key(pcopy.private, SIZE_MAX, &private);
+       if (keylen < 0 || !private) {
+               ret = keylen;
+               goto error2;
+       }
+
+       result = mpi_alloc(0);
+       if (!result) {
+               ret = -ENOMEM;
+               goto error3;
+       }
+
+       kbuf = kmalloc(resultlen, GFP_KERNEL);
+       if (!kbuf) {
+               ret = -ENOMEM;
+               goto error4;
+       }
+
+       ret = do_dh(result, base, private, prime);
+       if (ret)
+               goto error5;
+
+       ret = mpi_read_buffer(result, kbuf, resultlen, &nbytes, NULL);
+       if (ret != 0)
+               goto error5;
+
+       ret = nbytes;
+       if (copy_to_user(buffer, kbuf, nbytes) != 0)
+               ret = -EFAULT;
+
+error5:
+       kfree(kbuf);
+error4:
+       mpi_free(result);
+error3:
+       mpi_free(private);
+error2:
+       mpi_free(base);
+error1:
+       mpi_free(prime);
+out:
+       return ret;
+}
index 5105c2c..8ec7a52 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/sched.h>
 #include <linux/key-type.h>
 #include <linux/task_work.h>
+#include <linux/keyctl.h>
 
 struct iovec;
 
@@ -257,6 +258,17 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
 }
 #endif
 
+#ifdef CONFIG_KEY_DH_OPERATIONS
+extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
+                             size_t);
+#else
+static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
+                                    char __user *buffer, size_t buflen)
+{
+       return -EOPNOTSUPP;
+}
+#endif
+
 /*
  * Debugging key validation
  */
index b287551..bd5a272 100644 (file)
@@ -201,6 +201,7 @@ serial_exists:
  * @cred: The credentials specifying UID namespace.
  * @perm: The permissions mask of the new key.
  * @flags: Flags specifying quota properties.
+ * @restrict_link: Optional link restriction method for new keyrings.
  *
  * Allocate a key of the specified type with the attributes given.  The key is
  * returned in an uninstantiated state and the caller needs to instantiate the
@@ -223,7 +224,10 @@ serial_exists:
  */
 struct key *key_alloc(struct key_type *type, const char *desc,
                      kuid_t uid, kgid_t gid, const struct cred *cred,
-                     key_perm_t perm, unsigned long flags)
+                     key_perm_t perm, unsigned long flags,
+                     int (*restrict_link)(struct key *,
+                                          const struct key_type *,
+                                          const union key_payload *))
 {
        struct key_user *user = NULL;
        struct key *key;
@@ -291,11 +295,10 @@ struct key *key_alloc(struct key_type *type, const char *desc,
        key->uid = uid;
        key->gid = gid;
        key->perm = perm;
+       key->restrict_link = restrict_link;
 
        if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
                key->flags |= 1 << KEY_FLAG_IN_QUOTA;
-       if (flags & KEY_ALLOC_TRUSTED)
-               key->flags |= 1 << KEY_FLAG_TRUSTED;
        if (flags & KEY_ALLOC_BUILT_IN)
                key->flags |= 1 << KEY_FLAG_BUILTIN;
 
@@ -496,6 +499,12 @@ int key_instantiate_and_link(struct key *key,
        }
 
        if (keyring) {
+               if (keyring->restrict_link) {
+                       ret = keyring->restrict_link(keyring, key->type,
+                                                    &prep.payload);
+                       if (ret < 0)
+                               goto error;
+               }
                ret = __key_link_begin(keyring, &key->index_key, &edit);
                if (ret < 0)
                        goto error;
@@ -551,8 +560,12 @@ int key_reject_and_link(struct key *key,
        awaken = 0;
        ret = -EBUSY;
 
-       if (keyring)
+       if (keyring) {
+               if (keyring->restrict_link)
+                       return -EPERM;
+
                link_ret = __key_link_begin(keyring, &key->index_key, &edit);
+       }
 
        mutex_lock(&key_construction_mutex);
 
@@ -793,6 +806,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
        struct key *keyring, *key = NULL;
        key_ref_t key_ref;
        int ret;
+       int (*restrict_link)(struct key *,
+                            const struct key_type *,
+                            const union key_payload *) = NULL;
 
        /* look up the key type to see if it's one of the registered kernel
         * types */
@@ -811,6 +827,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
 
        key_check(keyring);
 
+       key_ref = ERR_PTR(-EPERM);
+       if (!(flags & KEY_ALLOC_BYPASS_RESTRICTION))
+               restrict_link = keyring->restrict_link;
+
        key_ref = ERR_PTR(-ENOTDIR);
        if (keyring->type != &key_type_keyring)
                goto error_put_type;
@@ -819,7 +839,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
        prep.data = payload;
        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);
@@ -835,10 +854,13 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
        }
        index_key.desc_len = strlen(index_key.description);
 
-       key_ref = ERR_PTR(-EPERM);
-       if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags))
-               goto error_free_prep;
-       flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0;
+       if (restrict_link) {
+               ret = restrict_link(keyring, index_key.type, &prep.payload);
+               if (ret < 0) {
+                       key_ref = ERR_PTR(ret);
+                       goto error_free_prep;
+               }
+       }
 
        ret = __key_link_begin(keyring, &index_key, &edit);
        if (ret < 0) {
@@ -879,7 +901,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
 
        /* allocate a new key */
        key = key_alloc(index_key.type, index_key.description,
-                       cred->fsuid, cred->fsgid, cred, perm, flags);
+                       cred->fsuid, cred->fsgid, cred, perm, flags, NULL);
        if (IS_ERR(key)) {
                key_ref = ERR_CAST(key);
                goto error_link_end;
index ed73c6c..3b135a0 100644 (file)
@@ -1686,6 +1686,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
        case KEYCTL_GET_PERSISTENT:
                return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
 
+       case KEYCTL_DH_COMPUTE:
+               return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2,
+                                        (char __user *) arg3,
+                                        (size_t) arg4);
+
        default:
                return -EOPNOTSUPP;
        }
index f931ccf..c91e4e0 100644 (file)
@@ -491,13 +491,17 @@ static long keyring_read(const struct key *keyring,
  */
 struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
                          const struct cred *cred, key_perm_t perm,
-                         unsigned long flags, struct key *dest)
+                         unsigned long flags,
+                         int (*restrict_link)(struct key *,
+                                              const struct key_type *,
+                                              const union key_payload *),
+                         struct key *dest)
 {
        struct key *keyring;
        int ret;
 
        keyring = key_alloc(&key_type_keyring, description,
-                           uid, gid, cred, perm, flags);
+                           uid, gid, cred, perm, flags, restrict_link);
        if (!IS_ERR(keyring)) {
                ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
                if (ret < 0) {
@@ -510,6 +514,26 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
 }
 EXPORT_SYMBOL(keyring_alloc);
 
+/**
+ * restrict_link_reject - Give -EPERM to restrict link
+ * @keyring: The keyring being added to.
+ * @type: The type of key being added.
+ * @payload: The payload of the key intended to be added.
+ *
+ * Reject the addition of any links to a keyring.  It can be overridden by
+ * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when
+ * adding a key to a keyring.
+ *
+ * This is meant to be passed as the restrict_link parameter to
+ * keyring_alloc().
+ */
+int restrict_link_reject(struct key *keyring,
+                        const struct key_type *type,
+                        const union key_payload *payload)
+{
+       return -EPERM;
+}
+
 /*
  * By default, we keys found by getting an exact match on their descriptions.
  */
@@ -1191,6 +1215,16 @@ void __key_link_end(struct key *keyring,
        up_write(&keyring->sem);
 }
 
+/*
+ * Check addition of keys to restricted keyrings.
+ */
+static int __key_link_check_restriction(struct key *keyring, struct key *key)
+{
+       if (!keyring->restrict_link)
+               return 0;
+       return keyring->restrict_link(keyring, key->type, &key->payload);
+}
+
 /**
  * key_link - Link a key to a keyring
  * @keyring: The keyring to make the link in.
@@ -1221,14 +1255,12 @@ int key_link(struct key *keyring, struct key *key)
        key_check(keyring);
        key_check(key);
 
-       if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) &&
-           !test_bit(KEY_FLAG_TRUSTED, &key->flags))
-               return -EPERM;
-
        ret = __key_link_begin(keyring, &key->index_key, &edit);
        if (ret == 0) {
                kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));
-               ret = __key_link_check_live_key(keyring, key);
+               ret = __key_link_check_restriction(keyring, key);
+               if (ret == 0)
+                       ret = __key_link_check_live_key(keyring, key);
                if (ret == 0)
                        __key_link(key, &edit);
                __key_link_end(keyring, &key->index_key, edit);
index c9fae5e..2ef45b3 100644 (file)
@@ -26,7 +26,7 @@ static int key_create_persistent_register(struct user_namespace *ns)
                                        current_cred(),
                                        ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
                                         KEY_USR_VIEW | KEY_USR_READ),
-                                       KEY_ALLOC_NOT_IN_QUOTA, NULL);
+                                       KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
        if (IS_ERR(reg))
                return PTR_ERR(reg);
 
@@ -60,7 +60,7 @@ static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
                                   uid, INVALID_GID, current_cred(),
                                   ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
                                    KEY_USR_VIEW | KEY_USR_READ),
-                                  KEY_ALLOC_NOT_IN_QUOTA,
+                                  KEY_ALLOC_NOT_IN_QUOTA, NULL,
                                   ns->persistent_keyring_register);
        if (IS_ERR(persistent))
                return ERR_CAST(persistent);
index e6d5017..40a8852 100644 (file)
@@ -76,7 +76,8 @@ int install_user_keyrings(void)
                if (IS_ERR(uid_keyring)) {
                        uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
                                                    cred, user_keyring_perm,
-                                                   KEY_ALLOC_IN_QUOTA, NULL);
+                                                   KEY_ALLOC_IN_QUOTA,
+                                                   NULL, NULL);
                        if (IS_ERR(uid_keyring)) {
                                ret = PTR_ERR(uid_keyring);
                                goto error;
@@ -92,7 +93,8 @@ int install_user_keyrings(void)
                        session_keyring =
                                keyring_alloc(buf, user->uid, INVALID_GID,
                                              cred, user_keyring_perm,
-                                             KEY_ALLOC_IN_QUOTA, NULL);
+                                             KEY_ALLOC_IN_QUOTA,
+                                             NULL, NULL);
                        if (IS_ERR(session_keyring)) {
                                ret = PTR_ERR(session_keyring);
                                goto error_release;
@@ -134,7 +136,8 @@ int install_thread_keyring_to_cred(struct cred *new)
 
        keyring = keyring_alloc("_tid", new->uid, new->gid, new,
                                KEY_POS_ALL | KEY_USR_VIEW,
-                               KEY_ALLOC_QUOTA_OVERRUN, NULL);
+                               KEY_ALLOC_QUOTA_OVERRUN,
+                               NULL, NULL);
        if (IS_ERR(keyring))
                return PTR_ERR(keyring);
 
@@ -180,7 +183,8 @@ int install_process_keyring_to_cred(struct cred *new)
 
        keyring = keyring_alloc("_pid", new->uid, new->gid, new,
                                KEY_POS_ALL | KEY_USR_VIEW,
-                               KEY_ALLOC_QUOTA_OVERRUN, NULL);
+                               KEY_ALLOC_QUOTA_OVERRUN,
+                               NULL, NULL);
        if (IS_ERR(keyring))
                return PTR_ERR(keyring);
 
@@ -231,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
 
                keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred,
                                        KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
-                                       flags, NULL);
+                                       flags, NULL, NULL);
                if (IS_ERR(keyring))
                        return PTR_ERR(keyring);
        } else {
@@ -785,7 +789,7 @@ long join_session_keyring(const char *name)
                keyring = keyring_alloc(
                        name, old->uid, old->gid, old,
                        KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK,
-                       KEY_ALLOC_IN_QUOTA, NULL);
+                       KEY_ALLOC_IN_QUOTA, NULL, NULL);
                if (IS_ERR(keyring)) {
                        ret = PTR_ERR(keyring);
                        goto error2;
index c7a117c..a29e355 100644 (file)
@@ -116,7 +116,7 @@ static int call_sbin_request_key(struct key_construction *cons,
        cred = get_current_cred();
        keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred,
                                KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
-                               KEY_ALLOC_QUOTA_OVERRUN, NULL);
+                               KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL);
        put_cred(cred);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
@@ -355,7 +355,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
 
        key = key_alloc(ctx->index_key.type, ctx->index_key.description,
                        ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred,
-                       perm, flags);
+                       perm, flags, NULL);
        if (IS_ERR(key))
                goto alloc_failed;
 
index 4f0f112..9db8b4a 100644 (file)
@@ -202,7 +202,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
        authkey = key_alloc(&key_type_request_key_auth, desc,
                            cred->fsuid, cred->fsgid, cred,
                            KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
-                           KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
+                           KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
        if (IS_ERR(authkey)) {
                ret = PTR_ERR(authkey);
                goto error_alloc;
index 8705d79..66b1840 100644 (file)
@@ -96,45 +96,25 @@ EXPORT_SYMBOL_GPL(user_free_preparse);
  */
 int user_update(struct key *key, struct key_preparsed_payload *prep)
 {
-       struct user_key_payload *upayload, *zap;
-       size_t datalen = prep->datalen;
+       struct user_key_payload *zap = NULL;
        int ret;
 
-       ret = -EINVAL;
-       if (datalen <= 0 || datalen > 32767 || !prep->data)
-               goto error;
-
-       /* construct a replacement payload */
-       ret = -ENOMEM;
-       upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
-       if (!upayload)
-               goto error;
-
-       upayload->datalen = datalen;
-       memcpy(upayload->data, prep->data, datalen);
-
        /* check the quota and attach the new data */
-       zap = upayload;
-
-       ret = key_payload_reserve(key, datalen);
-
-       if (ret == 0) {
-               /* attach the new data, displacing the old */
-               if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
-                       zap = key->payload.data[0];
-               else
-                       zap = NULL;
-               rcu_assign_keypointer(key, upayload);
-               key->expiry = 0;
-       }
+       ret = key_payload_reserve(key, prep->datalen);
+       if (ret < 0)
+               return ret;
+
+       /* attach the new data, displacing the old */
+       key->expiry = prep->expiry;
+       if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+               zap = rcu_dereference_key(key);
+       rcu_assign_keypointer(key, prep->payload.data[0]);
+       prep->payload.data[0] = NULL;
 
        if (zap)
                kfree_rcu(zap, rcu);
-
-error:
        return ret;
 }
-
 EXPORT_SYMBOL_GPL(user_update);
 
 /*
diff --git a/security/loadpin/Kconfig b/security/loadpin/Kconfig
new file mode 100644 (file)
index 0000000..dd01aa9
--- /dev/null
@@ -0,0 +1,19 @@
+config SECURITY_LOADPIN
+       bool "Pin load of kernel files (modules, fw, etc) to one filesystem"
+       depends on SECURITY && BLOCK
+       help
+         Any files read through the kernel file reading interface
+         (kernel modules, firmware, kexec images, security policy)
+         can be pinned to the first filesystem used for loading. When
+         enabled, any files that come from other filesystems will be
+         rejected. This is best used on systems without an initrd that
+         have a root filesystem backed by a read-only device such as
+         dm-verity or a CDROM.
+
+config SECURITY_LOADPIN_ENABLED
+       bool "Enforce LoadPin at boot"
+       depends on SECURITY_LOADPIN
+       help
+         If selected, LoadPin will enforce pinning at boot. If not
+         selected, it can be enabled at boot with the kernel parameter
+         "loadpin.enabled=1".
diff --git a/security/loadpin/Makefile b/security/loadpin/Makefile
new file mode 100644 (file)
index 0000000..c2d77f8
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_SECURITY_LOADPIN) += loadpin.o
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
new file mode 100644 (file)
index 0000000..89a46f1
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Module and Firmware Pinning Security Module
+ *
+ * Copyright 2011-2016 Google Inc.
+ *
+ * Author: Kees Cook <keescook@chromium.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "LoadPin: " fmt
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/fs_struct.h>
+#include <linux/lsm_hooks.h>
+#include <linux/mount.h>
+#include <linux/path.h>
+#include <linux/sched.h>       /* current */
+#include <linux/string_helpers.h>
+
+static void report_load(const char *origin, struct file *file, char *operation)
+{
+       char *cmdline, *pathname;
+
+       pathname = kstrdup_quotable_file(file, GFP_KERNEL);
+       cmdline = kstrdup_quotable_cmdline(current, GFP_KERNEL);
+
+       pr_notice("%s %s obj=%s%s%s pid=%d cmdline=%s%s%s\n",
+                 origin, operation,
+                 (pathname && pathname[0] != '<') ? "\"" : "",
+                 pathname,
+                 (pathname && pathname[0] != '<') ? "\"" : "",
+                 task_pid_nr(current),
+                 cmdline ? "\"" : "", cmdline, cmdline ? "\"" : "");
+
+       kfree(cmdline);
+       kfree(pathname);
+}
+
+static int enabled = IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENABLED);
+static struct super_block *pinned_root;
+static DEFINE_SPINLOCK(pinned_root_spinlock);
+
+#ifdef CONFIG_SYSCTL
+static int zero;
+static int one = 1;
+
+static struct ctl_path loadpin_sysctl_path[] = {
+       { .procname = "kernel", },
+       { .procname = "loadpin", },
+       { }
+};
+
+static struct ctl_table loadpin_sysctl_table[] = {
+       {
+               .procname       = "enabled",
+               .data           = &enabled,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &one,
+       },
+       { }
+};
+
+/*
+ * This must be called after early kernel init, since then the rootdev
+ * is available.
+ */
+static void check_pinning_enforcement(struct super_block *mnt_sb)
+{
+       bool ro = false;
+
+       /*
+        * If load pinning is not enforced via a read-only block
+        * device, allow sysctl to change modes for testing.
+        */
+       if (mnt_sb->s_bdev) {
+               ro = bdev_read_only(mnt_sb->s_bdev);
+               pr_info("dev(%u,%u): %s\n",
+                       MAJOR(mnt_sb->s_bdev->bd_dev),
+                       MINOR(mnt_sb->s_bdev->bd_dev),
+                       ro ? "read-only" : "writable");
+       } else
+               pr_info("mnt_sb lacks block device, treating as: writable\n");
+
+       if (!ro) {
+               if (!register_sysctl_paths(loadpin_sysctl_path,
+                                          loadpin_sysctl_table))
+                       pr_notice("sysctl registration failed!\n");
+               else
+                       pr_info("load pinning can be disabled.\n");
+       } else
+               pr_info("load pinning engaged.\n");
+}
+#else
+static void check_pinning_enforcement(struct super_block *mnt_sb)
+{
+       pr_info("load pinning engaged.\n");
+}
+#endif
+
+static void loadpin_sb_free_security(struct super_block *mnt_sb)
+{
+       /*
+        * When unmounting the filesystem we were using for load
+        * pinning, we acknowledge the superblock release, but make sure
+        * no other modules or firmware can be loaded.
+        */
+       if (!IS_ERR_OR_NULL(pinned_root) && mnt_sb == pinned_root) {
+               pinned_root = ERR_PTR(-EIO);
+               pr_info("umount pinned fs: refusing further loads\n");
+       }
+}
+
+static int loadpin_read_file(struct file *file, enum kernel_read_file_id id)
+{
+       struct super_block *load_root;
+       const char *origin = kernel_read_file_id_str(id);
+
+       /* This handles the older init_module API that has a NULL file. */
+       if (!file) {
+               if (!enabled) {
+                       report_load(origin, NULL, "old-api-pinning-ignored");
+                       return 0;
+               }
+
+               report_load(origin, NULL, "old-api-denied");
+               return -EPERM;
+       }
+
+       load_root = file->f_path.mnt->mnt_sb;
+
+       /* First loaded module/firmware defines the root for all others. */
+       spin_lock(&pinned_root_spinlock);
+       /*
+        * pinned_root is only NULL at startup. Otherwise, it is either
+        * a valid reference, or an ERR_PTR.
+        */
+       if (!pinned_root) {
+               pinned_root = load_root;
+               /*
+                * Unlock now since it's only pinned_root we care about.
+                * In the worst case, we will (correctly) report pinning
+                * failures before we have announced that pinning is
+                * enabled. This would be purely cosmetic.
+                */
+               spin_unlock(&pinned_root_spinlock);
+               check_pinning_enforcement(pinned_root);
+               report_load(origin, file, "pinned");
+       } else {
+               spin_unlock(&pinned_root_spinlock);
+       }
+
+       if (IS_ERR_OR_NULL(pinned_root) || load_root != pinned_root) {
+               if (unlikely(!enabled)) {
+                       report_load(origin, file, "pinning-ignored");
+                       return 0;
+               }
+
+               report_load(origin, file, "denied");
+               return -EPERM;
+       }
+
+       return 0;
+}
+
+static struct security_hook_list loadpin_hooks[] = {
+       LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security),
+       LSM_HOOK_INIT(kernel_read_file, loadpin_read_file),
+};
+
+void __init loadpin_add_hooks(void)
+{
+       pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
+       security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
+}
+
+/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
+module_param(enabled, int, 0);
+MODULE_PARM_DESC(enabled, "Pin module/firmware loading (default: true)");
index d17e4a6..7095693 100644 (file)
@@ -60,6 +60,7 @@ int __init security_init(void)
         */
        capability_add_hooks();
        yama_add_hooks();
+       loadpin_add_hooks();
 
        /*
         * Load all the remaining security modules.
@@ -1848,7 +1849,6 @@ struct security_hook_heads security_hook_heads = {
        .tun_dev_attach =
                LIST_HEAD_INIT(security_hook_heads.tun_dev_attach),
        .tun_dev_open = LIST_HEAD_INIT(security_hook_heads.tun_dev_open),
-       .skb_owned_by = LIST_HEAD_INIT(security_hook_heads.skb_owned_by),
 #endif /* CONFIG_SECURITY_NETWORK */
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
        .xfrm_policy_alloc_security =
index 3140efa..a86d537 100644 (file)
@@ -259,7 +259,7 @@ static int __inode_security_revalidate(struct inode *inode,
 
        might_sleep_if(may_sleep);
 
-       if (isec->initialized == LABEL_INVALID) {
+       if (ss_initialized && isec->initialized != LABEL_INITIALIZED) {
                if (!may_sleep)
                        return -ECHILD;
 
@@ -297,6 +297,13 @@ static struct inode_security_struct *inode_security(struct inode *inode)
        return inode->i_security;
 }
 
+static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
+{
+       struct inode *inode = d_backing_inode(dentry);
+
+       return inode->i_security;
+}
+
 /*
  * Get the security label of a dentry's backing inode.
  */
@@ -687,7 +694,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        struct superblock_security_struct *sbsec = sb->s_security;
        const char *name = sb->s_type->name;
        struct dentry *root = sbsec->sb->s_root;
-       struct inode_security_struct *root_isec = backing_inode_security(root);
+       struct inode_security_struct *root_isec;
        u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
        u32 defcontext_sid = 0;
        char **mount_options = opts->mnt_opts;
@@ -730,6 +737,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
            && (num_opts == 0))
                goto out;
 
+       root_isec = backing_inode_security_novalidate(root);
+
        /*
         * parse the mount options, check if they are valid sids.
         * also check if someone is trying to mount the same sb more
@@ -1623,7 +1632,7 @@ static int current_has_perm(const struct task_struct *tsk,
 
 /* Check whether a task is allowed to use a capability. */
 static int cred_has_capability(const struct cred *cred,
-                              int cap, int audit)
+                              int cap, int audit, bool initns)
 {
        struct common_audit_data ad;
        struct av_decision avd;
@@ -1637,10 +1646,10 @@ static int cred_has_capability(const struct cred *cred,
 
        switch (CAP_TO_INDEX(cap)) {
        case 0:
-               sclass = SECCLASS_CAPABILITY;
+               sclass = initns ? SECCLASS_CAPABILITY : SECCLASS_CAP_USERNS;
                break;
        case 1:
-               sclass = SECCLASS_CAPABILITY2;
+               sclass = initns ? SECCLASS_CAPABILITY2 : SECCLASS_CAP2_USERNS;
                break;
        default:
                printk(KERN_ERR
@@ -1782,7 +1791,6 @@ static int selinux_determine_inode_label(struct inode *dir,
                                         u32 *_new_isid)
 {
        const struct superblock_security_struct *sbsec = dir->i_sb->s_security;
-       const struct inode_security_struct *dsec = inode_security(dir);
        const struct task_security_struct *tsec = current_security();
 
        if ((sbsec->flags & SE_SBINITIALIZED) &&
@@ -1792,6 +1800,7 @@ static int selinux_determine_inode_label(struct inode *dir,
                   tsec->create_sid) {
                *_new_isid = tsec->create_sid;
        } else {
+               const struct inode_security_struct *dsec = inode_security(dir);
                return security_transition_sid(tsec->sid, dsec->sid, tclass,
                                               name, _new_isid);
        }
@@ -2076,7 +2085,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
        u32 sid = task_sid(to);
        struct file_security_struct *fsec = file->f_security;
        struct dentry *dentry = file->f_path.dentry;
-       struct inode_security_struct *isec = backing_inode_security(dentry);
+       struct inode_security_struct *isec;
        struct common_audit_data ad;
        int rc;
 
@@ -2095,6 +2104,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
        if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
                return 0;
 
+       isec = backing_inode_security(dentry);
        return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),
                            &ad);
 }
@@ -2143,7 +2153,7 @@ static int selinux_capset(struct cred *new, const struct cred *old,
 static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
                           int cap, int audit)
 {
-       return cred_has_capability(cred, cap, audit);
+       return cred_has_capability(cred, cap, audit, ns == &init_user_ns);
 }
 
 static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
@@ -2221,7 +2231,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
        int rc, cap_sys_admin = 0;
 
        rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN,
-                                       SECURITY_CAP_NOAUDIT);
+                                SECURITY_CAP_NOAUDIT, true);
        if (rc == 0)
                cap_sys_admin = 1;
 
@@ -2230,6 +2240,20 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
 
 /* binprm security operations */
 
+static u32 ptrace_parent_sid(struct task_struct *task)
+{
+       u32 sid = 0;
+       struct task_struct *tracer;
+
+       rcu_read_lock();
+       tracer = ptrace_parent(task);
+       if (tracer)
+               sid = task_sid(tracer);
+       rcu_read_unlock();
+
+       return sid;
+}
+
 static int check_nnp_nosuid(const struct linux_binprm *bprm,
                            const struct task_security_struct *old_tsec,
                            const struct task_security_struct *new_tsec)
@@ -2351,18 +2375,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
                 * changes its SID has the appropriate permit */
                if (bprm->unsafe &
                    (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
-                       struct task_struct *tracer;
-                       struct task_security_struct *sec;
-                       u32 ptsid = 0;
-
-                       rcu_read_lock();
-                       tracer = ptrace_parent(current);
-                       if (likely(tracer != NULL)) {
-                               sec = __task_cred(tracer)->security;
-                               ptsid = sec->sid;
-                       }
-                       rcu_read_unlock();
-
+                       u32 ptsid = ptrace_parent_sid(current);
                        if (ptsid != 0) {
                                rc = avc_has_perm(ptsid, new_tsec->sid,
                                                  SECCLASS_PROCESS,
@@ -3046,7 +3059,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
                                  const void *value, size_t size, int flags)
 {
        struct inode *inode = d_backing_inode(dentry);
-       struct inode_security_struct *isec = backing_inode_security(dentry);
+       struct inode_security_struct *isec;
        struct superblock_security_struct *sbsec;
        struct common_audit_data ad;
        u32 newsid, sid = current_sid();
@@ -3065,6 +3078,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
        ad.type = LSM_AUDIT_DATA_DENTRY;
        ad.u.dentry = dentry;
 
+       isec = backing_inode_security(dentry);
        rc = avc_has_perm(sid, isec->sid, isec->sclass,
                          FILE__RELABELFROM, &ad);
        if (rc)
@@ -3123,7 +3137,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
                                        int flags)
 {
        struct inode *inode = d_backing_inode(dentry);
-       struct inode_security_struct *isec = backing_inode_security(dentry);
+       struct inode_security_struct *isec;
        u32 newsid;
        int rc;
 
@@ -3140,6 +3154,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
                return;
        }
 
+       isec = backing_inode_security(dentry);
        isec->sclass = inode_mode_to_security_class(inode->i_mode);
        isec->sid = newsid;
        isec->initialized = LABEL_INITIALIZED;
@@ -3181,7 +3196,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
        u32 size;
        int error;
        char *context = NULL;
-       struct inode_security_struct *isec = inode_security(inode);
+       struct inode_security_struct *isec;
 
        if (strcmp(name, XATTR_SELINUX_SUFFIX))
                return -EOPNOTSUPP;
@@ -3199,7 +3214,8 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
                            SECURITY_CAP_NOAUDIT);
        if (!error)
                error = cred_has_capability(current_cred(), CAP_MAC_ADMIN,
-                                           SECURITY_CAP_NOAUDIT);
+                                           SECURITY_CAP_NOAUDIT, true);
+       isec = inode_security(inode);
        if (!error)
                error = security_sid_to_context_force(isec->sid, &context,
                                                      &size);
@@ -3220,7 +3236,7 @@ out_nofree:
 static int selinux_inode_setsecurity(struct inode *inode, const char *name,
                                     const void *value, size_t size, int flags)
 {
-       struct inode_security_struct *isec = inode_security(inode);
+       struct inode_security_struct *isec = inode_security_novalidate(inode);
        u32 newsid;
        int rc;
 
@@ -3309,7 +3325,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
        struct common_audit_data ad;
        struct file_security_struct *fsec = file->f_security;
        struct inode *inode = file_inode(file);
-       struct inode_security_struct *isec = inode_security(inode);
+       struct inode_security_struct *isec;
        struct lsm_ioctlop_audit ioctl;
        u32 ssid = cred_sid(cred);
        int rc;
@@ -3333,6 +3349,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
        if (unlikely(IS_PRIVATE(inode)))
                return 0;
 
+       isec = inode_security(inode);
        rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
                        requested, driver, xperm, &ad);
 out:
@@ -3374,7 +3391,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
        case KDSKBENT:
        case KDSKBSENT:
                error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG,
-                                           SECURITY_CAP_AUDIT);
+                                           SECURITY_CAP_AUDIT, true);
                break;
 
        /* default case assumes that the command will go
@@ -3463,8 +3480,9 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
                    vma->vm_end <= vma->vm_mm->brk) {
                        rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
                } else if (!vma->vm_file &&
-                          vma->vm_start <= vma->vm_mm->start_stack &&
-                          vma->vm_end >= vma->vm_mm->start_stack) {
+                          ((vma->vm_start <= vma->vm_mm->start_stack &&
+                            vma->vm_end >= vma->vm_mm->start_stack) ||
+                           vma_is_stack_for_task(vma, current))) {
                        rc = current_has_perm(current, PROCESS__EXECSTACK);
                } else if (vma->vm_file && vma->anon_vma) {
                        /*
@@ -3720,6 +3738,52 @@ static int selinux_kernel_module_request(char *kmod_name)
                            SYSTEM__MODULE_REQUEST, &ad);
 }
 
+static int selinux_kernel_module_from_file(struct file *file)
+{
+       struct common_audit_data ad;
+       struct inode_security_struct *isec;
+       struct file_security_struct *fsec;
+       u32 sid = current_sid();
+       int rc;
+
+       /* init_module */
+       if (file == NULL)
+               return avc_has_perm(sid, sid, SECCLASS_SYSTEM,
+                                       SYSTEM__MODULE_LOAD, NULL);
+
+       /* finit_module */
+
+       ad.type = LSM_AUDIT_DATA_PATH;
+       ad.u.path = file->f_path;
+
+       fsec = file->f_security;
+       if (sid != fsec->sid) {
+               rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad);
+               if (rc)
+                       return rc;
+       }
+
+       isec = inode_security(file_inode(file));
+       return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM,
+                               SYSTEM__MODULE_LOAD, &ad);
+}
+
+static int selinux_kernel_read_file(struct file *file,
+                                   enum kernel_read_file_id id)
+{
+       int rc = 0;
+
+       switch (id) {
+       case READING_MODULE:
+               rc = selinux_kernel_module_from_file(file);
+               break;
+       default:
+               break;
+       }
+
+       return rc;
+}
+
 static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
 {
        return current_has_perm(p, PROCESS__SETPGID);
@@ -4599,6 +4663,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
 {
        u32 peer_secid = SECSID_NULL;
        u16 family;
+       struct inode_security_struct *isec;
 
        if (skb && skb->protocol == htons(ETH_P_IP))
                family = PF_INET;
@@ -4609,9 +4674,10 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
        else
                goto out;
 
-       if (sock && family == PF_UNIX)
-               selinux_inode_getsecid(SOCK_INODE(sock), &peer_secid);
-       else if (skb)
+       if (sock && family == PF_UNIX) {
+               isec = inode_security_novalidate(SOCK_INODE(sock));
+               peer_secid = isec->sid;
+       } else if (skb)
                selinux_skb_peerlbl_sid(skb, family, &peer_secid);
 
 out:
@@ -5676,7 +5742,6 @@ static int selinux_setprocattr(struct task_struct *p,
                               char *name, void *value, size_t size)
 {
        struct task_security_struct *tsec;
-       struct task_struct *tracer;
        struct cred *new;
        u32 sid = 0, ptsid;
        int error;
@@ -5783,14 +5848,8 @@ static int selinux_setprocattr(struct task_struct *p,
 
                /* Check for ptracing, and update the task SID if ok.
                   Otherwise, leave SID unchanged and fail. */
-               ptsid = 0;
-               rcu_read_lock();
-               tracer = ptrace_parent(p);
-               if (tracer)
-                       ptsid = task_sid(tracer);
-               rcu_read_unlock();
-
-               if (tracer) {
+               ptsid = ptrace_parent_sid(p);
+               if (ptsid != 0) {
                        error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
                                             PROCESS__PTRACE, NULL);
                        if (error)
@@ -6021,6 +6080,7 @@ static struct security_hook_list selinux_hooks[] = {
        LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as),
        LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as),
        LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request),
+       LSM_HOOK_INIT(kernel_read_file, selinux_kernel_read_file),
        LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid),
        LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid),
        LSM_HOOK_INIT(task_getsid, selinux_task_getsid),
index ef83c4b..1f1f4b2 100644 (file)
 #define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \
            "write", "associate", "unix_read", "unix_write"
 
+#define COMMON_CAP_PERMS  "chown", "dac_override", "dac_read_search", \
+           "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", \
+           "linux_immutable", "net_bind_service", "net_broadcast", \
+           "net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module", \
+           "sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin", \
+           "sys_boot", "sys_nice", "sys_resource", "sys_time", \
+           "sys_tty_config", "mknod", "lease", "audit_write", \
+           "audit_control", "setfcap"
+
+#define COMMON_CAP2_PERMS  "mac_override", "mac_admin", "syslog", \
+               "wake_alarm", "block_suspend", "audit_read"
+
 /*
  * Note: The name for any socket class should be suffixed by "socket",
  *      and doesn't contain more than one substr of "socket".
@@ -32,16 +44,9 @@ struct security_class_mapping secclass_map[] = {
            "setsockcreate", NULL } },
        { "system",
          { "ipc_info", "syslog_read", "syslog_mod",
-           "syslog_console", "module_request", NULL } },
+           "syslog_console", "module_request", "module_load", NULL } },
        { "capability",
-         { "chown", "dac_override", "dac_read_search",
-           "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap",
-           "linux_immutable", "net_bind_service", "net_broadcast",
-           "net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module",
-           "sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin",
-           "sys_boot", "sys_nice", "sys_resource", "sys_time",
-           "sys_tty_config", "mknod", "lease", "audit_write",
-           "audit_control", "setfcap", NULL } },
+         { COMMON_CAP_PERMS, NULL } },
        { "filesystem",
          { "mount", "remount", "unmount", "getattr",
            "relabelfrom", "relabelto", "associate", "quotamod",
@@ -150,12 +155,15 @@ struct security_class_mapping secclass_map[] = {
        { "memprotect", { "mmap_zero", NULL } },
        { "peer", { "recv", NULL } },
        { "capability2",
-         { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend",
-           "audit_read", NULL } },
+         { COMMON_CAP2_PERMS, NULL } },
        { "kernel_service", { "use_as_override", "create_files_as", NULL } },
        { "tun_socket",
          { COMMON_SOCK_PERMS, "attach_queue", NULL } },
        { "binder", { "impersonate", "call", "set_context_mgr", "transfer",
                      NULL } },
+       { "cap_userns",
+         { COMMON_CAP_PERMS, NULL } },
+       { "cap2_userns",
+         { COMMON_CAP2_PERMS, NULL } },
        { NULL }
   };
index 67ce7a8..ff4fddc 100644 (file)
@@ -17,6 +17,6 @@ int security_get_bools(int *len, char ***names, int **values);
 
 int security_set_bools(int len, int *values);
 
-int security_get_bool_value(int bool);
+int security_get_bool_value(int index);
 
 #endif
index a2ae054..c21e135 100644 (file)
@@ -38,9 +38,8 @@ struct task_security_struct {
 };
 
 enum label_initialized {
-       LABEL_MISSING,          /* not initialized */
-       LABEL_INITIALIZED,      /* inizialized */
-       LABEL_INVALID           /* invalid */
+       LABEL_INVALID,          /* invalid or not initialized */
+       LABEL_INITIALIZED       /* initialized */
 };
 
 struct inode_security_struct {
index ebda973..89df646 100644 (file)
@@ -2696,7 +2696,7 @@ out:
        return rc;
 }
 
-int security_get_bool_value(int bool)
+int security_get_bool_value(int index)
 {
        int rc;
        int len;
@@ -2705,10 +2705,10 @@ int security_get_bool_value(int bool)
 
        rc = -EFAULT;
        len = policydb.p_bools.nprim;
-       if (bool >= len)
+       if (index >= len)
                goto out;
 
-       rc = policydb.bool_val_to_struct[bool]->state;
+       rc = policydb.bool_val_to_struct[index]->state;
 out:
        read_unlock(&policy_rwlock);
        return rc;
index cb6ed10..9b756b1 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/prctl.h>
 #include <linux/ratelimit.h>
 #include <linux/workqueue.h>
+#include <linux/string_helpers.h>
 
 #define YAMA_SCOPE_DISABLED    0
 #define YAMA_SCOPE_RELATIONAL  1
@@ -41,6 +42,22 @@ static DEFINE_SPINLOCK(ptracer_relations_lock);
 static void yama_relation_cleanup(struct work_struct *work);
 static DECLARE_WORK(yama_relation_work, yama_relation_cleanup);
 
+static void report_access(const char *access, struct task_struct *target,
+                         struct task_struct *agent)
+{
+       char *target_cmd, *agent_cmd;
+
+       target_cmd = kstrdup_quotable_cmdline(target, GFP_ATOMIC);
+       agent_cmd = kstrdup_quotable_cmdline(agent, GFP_ATOMIC);
+
+       pr_notice_ratelimited(
+               "ptrace %s of \"%s\"[%d] was attempted by \"%s\"[%d]\n",
+               access, target_cmd, target->pid, agent_cmd, agent->pid);
+
+       kfree(agent_cmd);
+       kfree(target_cmd);
+}
+
 /**
  * yama_relation_cleanup - remove invalid entries from the relation list
  *
@@ -307,11 +324,8 @@ static int yama_ptrace_access_check(struct task_struct *child,
                }
        }
 
-       if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0) {
-               printk_ratelimited(KERN_NOTICE
-                       "ptrace of pid %d was attempted by: %s (pid %d)\n",
-                       child->pid, current->comm, current->pid);
-       }
+       if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0)
+               report_access("attach", child, current);
 
        return rc;
 }
@@ -337,11 +351,8 @@ int yama_ptrace_traceme(struct task_struct *parent)
                break;
        }
 
-       if (rc) {
-               printk_ratelimited(KERN_NOTICE
-                       "ptraceme of pid %d was attempted by: %s (pid %d)\n",
-                       current->pid, parent->comm, parent->pid);
-       }
+       if (rc)
+               report_access("traceme", current, parent);
 
        return rc;
 }