linux/fs/verity/signature.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Verification of builtin signatures
   4 *
   5 * Copyright 2019 Google LLC
   6 */
   7
   8#include "fsverity_private.h"
   9
  10#include <linux/cred.h>
  11#include <linux/key.h>
  12#include <linux/slab.h>
  13#include <linux/verification.h>
  14
  15/*
  16 * /proc/sys/fs/verity/require_signatures
  17 * If 1, all verity files must have a valid builtin signature.
  18 */
  19static int fsverity_require_signatures;
  20
  21/*
  22 * Keyring that contains the trusted X.509 certificates.
  23 *
  24 * Only root (kuid=0) can modify this.  Also, root may use
  25 * keyctl_restrict_keyring() to prevent any more additions.
  26 */
  27static struct key *fsverity_keyring;
  28
  29/**
  30 * fsverity_verify_signature() - check a verity file's signature
  31 * @vi: the file's fsverity_info
  32 * @signature: the file's built-in signature
  33 * @sig_size: size of signature in bytes, or 0 if no signature
  34 *
  35 * If the file includes a signature of its fs-verity file digest, verify it
  36 * against the certificates in the fs-verity keyring.
  37 *
  38 * Return: 0 on success (signature valid or not required); -errno on failure
  39 */
  40int fsverity_verify_signature(const struct fsverity_info *vi,
  41                              const u8 *signature, size_t sig_size)
  42{
  43        const struct inode *inode = vi->inode;
  44        const struct fsverity_hash_alg *hash_alg = vi->tree_params.hash_alg;
  45        struct fsverity_formatted_digest *d;
  46        int err;
  47
  48        if (sig_size == 0) {
  49                if (fsverity_require_signatures) {
  50                        fsverity_err(inode,
  51                                     "require_signatures=1, rejecting unsigned file!");
  52                        return -EPERM;
  53                }
  54                return 0;
  55        }
  56
  57        d = kzalloc(sizeof(*d) + hash_alg->digest_size, GFP_KERNEL);
  58        if (!d)
  59                return -ENOMEM;
  60        memcpy(d->magic, "FSVerity", 8);
  61        d->digest_algorithm = cpu_to_le16(hash_alg - fsverity_hash_algs);
  62        d->digest_size = cpu_to_le16(hash_alg->digest_size);
  63        memcpy(d->digest, vi->file_digest, hash_alg->digest_size);
  64
  65        err = verify_pkcs7_signature(d, sizeof(*d) + hash_alg->digest_size,
  66                                     signature, sig_size, fsverity_keyring,
  67                                     VERIFYING_UNSPECIFIED_SIGNATURE,
  68                                     NULL, NULL);
  69        kfree(d);
  70
  71        if (err) {
  72                if (err == -ENOKEY)
  73                        fsverity_err(inode,
  74                                     "File's signing cert isn't in the fs-verity keyring");
  75                else if (err == -EKEYREJECTED)
  76                        fsverity_err(inode, "Incorrect file signature");
  77                else if (err == -EBADMSG)
  78                        fsverity_err(inode, "Malformed file signature");
  79                else
  80                        fsverity_err(inode, "Error %d verifying file signature",
  81                                     err);
  82                return err;
  83        }
  84
  85        pr_debug("Valid signature for file digest %s:%*phN\n",
  86                 hash_alg->name, hash_alg->digest_size, vi->file_digest);
  87        return 0;
  88}
  89
  90#ifdef CONFIG_SYSCTL
  91static struct ctl_table_header *fsverity_sysctl_header;
  92
  93static const struct ctl_path fsverity_sysctl_path[] = {
  94        { .procname = "fs", },
  95        { .procname = "verity", },
  96        { }
  97};
  98
  99static struct ctl_table fsverity_sysctl_table[] = {
 100        {
 101                .procname       = "require_signatures",
 102                .data           = &fsverity_require_signatures,
 103                .maxlen         = sizeof(int),
 104                .mode           = 0644,
 105                .proc_handler   = proc_dointvec_minmax,
 106                .extra1         = SYSCTL_ZERO,
 107                .extra2         = SYSCTL_ONE,
 108        },
 109        { }
 110};
 111
 112static int __init fsverity_sysctl_init(void)
 113{
 114        fsverity_sysctl_header = register_sysctl_paths(fsverity_sysctl_path,
 115                                                       fsverity_sysctl_table);
 116        if (!fsverity_sysctl_header) {
 117                pr_err("sysctl registration failed!\n");
 118                return -ENOMEM;
 119        }
 120        return 0;
 121}
 122#else /* !CONFIG_SYSCTL */
 123static inline int __init fsverity_sysctl_init(void)
 124{
 125        return 0;
 126}
 127#endif /* !CONFIG_SYSCTL */
 128
 129int __init fsverity_init_signature(void)
 130{
 131        struct key *ring;
 132        int err;
 133
 134        ring = keyring_alloc(".fs-verity", KUIDT_INIT(0), KGIDT_INIT(0),
 135                             current_cred(), KEY_POS_SEARCH |
 136                                KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE |
 137                                KEY_USR_SEARCH | KEY_USR_SETATTR,
 138                             KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
 139        if (IS_ERR(ring))
 140                return PTR_ERR(ring);
 141
 142        err = fsverity_sysctl_init();
 143        if (err)
 144                goto err_put_ring;
 145
 146        fsverity_keyring = ring;
 147        return 0;
 148
 149err_put_ring:
 150        key_put(ring);
 151        return err;
 152}
 153