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