1 /*-
2 * Copyright (c) 2019 Stormshield.
3 * Copyright (c) 2019 Semihalf.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 #include <sys/param.h>
29 #include <sys/ctype.h>
30 #include <sys/eventhandler.h>
31 #include <sys/fcntl.h>
32 #include <sys/lock.h>
33 #include <sys/module.h>
34 #include <sys/mutex.h>
35 #include <sys/namei.h>
36 #include <sys/proc.h>
37 #include <sys/systm.h>
38 #include <sys/vnode.h>
39
40 #include <crypto/sha2/sha256.h>
41 #include <crypto/sha2/sha384.h>
42 #include <crypto/sha2/sha512.h>
43
44 #include <security/mac_veriexec/mac_veriexec.h>
45 #include <security/mac_veriexec/mac_veriexec_internal.h>
46
47 /* The following are based on sbin/veriexec */
48 struct fingerprint_type {
49 const char *fp_type;
50 int fp_size;
51 };
52
53 struct fp_flag {
54 const char *flag_name;
55 int flag;
56 };
57
58 static const struct fingerprint_type fp_table[] = {
59 {"sha256=", SHA256_DIGEST_LENGTH},
60 #if MAXFINGERPRINTLEN >= SHA384_DIGEST_LENGTH
61 {"sha384=", SHA384_DIGEST_LENGTH},
62 #endif
63 #if MAXFINGERPRINTLEN >= SHA512_DIGEST_LENGTH
64 {"sha512=", SHA512_DIGEST_LENGTH},
65 #endif
66 {NULL, 0}
67 };
68
69 static const struct fp_flag flags_table[] = {
70 {"indirect", VERIEXEC_INDIRECT},
71 {"no_ptrace", VERIEXEC_NOTRACE},
72 {"trusted", VERIEXEC_TRUSTED},
73 {"no_fips", VERIEXEC_NOFIPS},
74 {NULL, 0}
75 };
76
77 extern struct mtx ve_mutex;
78
79 static unsigned char hexchar_to_byte(unsigned char c);
80 static int hexstring_to_bin(unsigned char *buf);
81
82 static int get_flags(const char *entry);
83 static int get_fp(const char *entry, char **type,
84 unsigned char **digest, int *flags);
85 static int verify_digest(const char *data, size_t len,
86 const unsigned char *expected_hash);
87
88 static int open_file(const char *path, struct nameidata *nid);
89 static char *read_manifest(char *path, unsigned char *digest);
90 static int parse_entry(char *entry, char *prefix);
91 static int parse_manifest(char *path, unsigned char *hash, char *prefix);
92
93 static unsigned char
hexchar_to_byte(unsigned char c)94 hexchar_to_byte(unsigned char c)
95 {
96
97 if (isdigit(c))
98 return (c - '0');
99
100 return (isupper(c) ? c - 'A' + 10 : c - 'a' + 10);
101 }
102
103 static int
hexstring_to_bin(unsigned char * buf)104 hexstring_to_bin(unsigned char *buf)
105 {
106 size_t i, len;
107 unsigned char byte;
108
109 len = strlen(buf);
110 for (i = 0; i < len / 2; i++) {
111 if (!isxdigit(buf[2 * i]) || !isxdigit(buf[2 * i + 1]))
112 return (EINVAL);
113
114 byte = hexchar_to_byte(buf[2 * i]) << 4;
115 byte += hexchar_to_byte(buf[2 * i + 1]);
116 buf[i] = byte;
117 }
118 return (0);
119 }
120
121 static int
get_flags(const char * entry)122 get_flags(const char *entry)
123 {
124 int i;
125 int result = 0;
126
127 for (i = 0; flags_table[i].flag_name != NULL; i++)
128 if (strstr(entry, flags_table[i].flag_name) != NULL)
129 result |= flags_table[i].flag;
130
131 return (result);
132 }
133
134 /*
135 * Parse a single line of manifest looking for a digest and its type.
136 * We expect it to be in form of "path shaX=hash".
137 * The line will be split into path, hash type and hash value.
138 */
139 static int
get_fp(const char * entry,char ** type,unsigned char ** digest,int * flags)140 get_fp(const char *entry, char **type, unsigned char **digest, int *flags)
141 {
142 char *delimiter;
143 char *local_digest;
144 char *fp_type;
145 char *prev_fp_type;
146 size_t min_len;
147 int i;
148
149 delimiter = NULL;
150 fp_type = NULL;
151 prev_fp_type = NULL;
152
153 for (i = 0; fp_table[i].fp_type != NULL; i++) {
154 fp_type = strstr(entry, fp_table[i].fp_type);
155 /* Look for the last "shaX=hash" in line */
156 while (fp_type != NULL) {
157 prev_fp_type = fp_type;
158 fp_type++;
159 fp_type = strstr(fp_type, fp_table[i].fp_type);
160 }
161 fp_type = prev_fp_type;
162 if (fp_type != NULL) {
163 if (fp_type == entry || fp_type[-1] != ' ')
164 return (EINVAL);
165
166 /*
167 * The entry should contain at least
168 * fp_type and digest in hexadecimal form.
169 */
170 min_len = strlen(fp_table[i].fp_type) +
171 2 * fp_table[i].fp_size;
172
173 if (strnlen(fp_type, min_len) < min_len)
174 return (EINVAL);
175
176 local_digest = &fp_type[strlen(fp_table[i].fp_type)];
177 delimiter = &local_digest[2 * fp_table[i].fp_size];
178
179 /*
180 * Make sure that digest is followed by
181 * some kind of delimiter.
182 */
183 if (*delimiter != '\n' &&
184 *delimiter != '\0' &&
185 *delimiter != ' ')
186 return (EINVAL);
187
188 /*
189 * Does the entry contain flags we need to parse?
190 */
191 if (*delimiter == ' ' && flags != NULL)
192 *flags = get_flags(delimiter);
193
194 /*
195 * Split entry into three parts:
196 * path, fp_type and digest.
197 */
198 local_digest[-1] = '\0';
199 *delimiter = '\0';
200 fp_type[-1] = '\0';
201 break;
202 }
203 }
204
205 if (fp_type == NULL)
206 return (EINVAL);
207
208 if (type != NULL)
209 *type = fp_type;
210
211 if (digest != NULL)
212 *digest = local_digest;
213
214 return (0);
215 }
216
217 /*
218 * Currently we verify manifest using sha256.
219 * In future another env with hash type could be introduced.
220 */
221 static int
verify_digest(const char * data,size_t len,const unsigned char * expected_hash)222 verify_digest(const char *data, size_t len, const unsigned char *expected_hash)
223 {
224 SHA256_CTX ctx;
225 unsigned char hash[SHA256_DIGEST_LENGTH];
226
227 SHA256_Init(&ctx);
228 SHA256_Update(&ctx, data, len);
229 SHA256_Final(hash, &ctx);
230
231 return (memcmp(expected_hash, hash, SHA256_DIGEST_LENGTH));
232 }
233
234 static int
open_file(const char * path,struct nameidata * nid)235 open_file(const char *path, struct nameidata *nid)
236 {
237 int flags, rc;
238
239 flags = FREAD;
240
241 pwd_ensure_dirs();
242
243 NDINIT(nid, LOOKUP, 0, UIO_SYSSPACE, path);
244 rc = vn_open(nid, &flags, 0, NULL);
245 NDFREE_PNBUF(nid);
246 if (rc != 0)
247 return (rc);
248
249 return (0);
250 }
251
252 /*
253 * Read the manifest from location specified in path and verify its digest.
254 */
255 static char*
read_manifest(char * path,unsigned char * digest)256 read_manifest(char *path, unsigned char *digest)
257 {
258 struct nameidata nid;
259 struct vattr va;
260 char *data;
261 ssize_t bytes_read, resid;
262 int rc;
263
264 data = NULL;
265 bytes_read = 0;
266
267 rc = open_file(path, &nid);
268 if (rc != 0)
269 goto fail;
270
271 rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred);
272 if (rc != 0)
273 goto fail;
274
275 data = (char *)malloc(va.va_size + 1, M_VERIEXEC, M_WAITOK);
276
277 while (bytes_read < va.va_size) {
278 rc = vn_rdwr(
279 UIO_READ, nid.ni_vp, data,
280 va.va_size - bytes_read, bytes_read,
281 UIO_SYSSPACE, IO_NODELOCKED,
282 curthread->td_ucred, NOCRED, &resid, curthread);
283 if (rc != 0)
284 goto fail;
285
286 bytes_read = va.va_size - resid;
287 }
288
289 data[bytes_read] = '\0';
290
291 VOP_UNLOCK(nid.ni_vp);
292 (void)vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread);
293
294 /*
295 * If digest is wrong someone might be trying to fool us.
296 */
297 if (verify_digest(data, va.va_size, digest))
298 panic("Manifest hash doesn't match expected value!");
299
300 return (data);
301
302 fail:
303 if (data != NULL)
304 free(data, M_VERIEXEC);
305
306 return (NULL);
307 }
308
309 /*
310 * Process single line.
311 * First split it into path, digest_type and digest.
312 * Then try to open the file and insert its fingerprint into metadata store.
313 */
314 static int
parse_entry(char * entry,char * prefix)315 parse_entry(char *entry, char *prefix)
316 {
317 struct nameidata nid;
318 struct vattr va;
319 char path[MAXPATHLEN];
320 char *fp_type;
321 unsigned char *digest;
322 int rc, is_exec, flags;
323
324 fp_type = NULL;
325 digest = NULL;
326 flags = 0;
327
328 rc = get_fp(entry, &fp_type, &digest, &flags);
329 if (rc != 0)
330 return (rc);
331
332 rc = hexstring_to_bin(digest);
333 if (rc != 0)
334 return (rc);
335
336 if (strnlen(entry, MAXPATHLEN) == MAXPATHLEN)
337 return (EINVAL);
338
339 /* If the path is not absolute prepend it with a prefix */
340 if (prefix != NULL && entry[0] != '/') {
341 rc = snprintf(path, MAXPATHLEN, "%s/%s",
342 prefix, entry);
343 if (rc < 0)
344 return (-rc);
345 } else {
346 strcpy(path, entry);
347 }
348
349 rc = open_file(path, &nid);
350 NDFREE_PNBUF(&nid);
351 if (rc != 0)
352 return (rc);
353
354 rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred);
355 if (rc != 0)
356 goto out;
357
358 is_exec = (va.va_mode & VEXEC);
359
360 mtx_lock(&ve_mutex);
361 rc = mac_veriexec_metadata_add_file(
362 is_exec == 0,
363 va.va_fsid, va.va_fileid, va.va_gen,
364 digest,
365 NULL, 0,
366 flags, fp_type, 1);
367 mtx_unlock(&ve_mutex);
368
369 out:
370 VOP_UNLOCK(nid.ni_vp);
371 vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread);
372 return (rc);
373 }
374
375 /*
376 * Look for manifest in env that have beed passed by loader.
377 * This routine should be called right after the rootfs is mounted.
378 */
379 static int
parse_manifest(char * path,unsigned char * hash,char * prefix)380 parse_manifest(char *path, unsigned char *hash, char *prefix)
381 {
382 char *data;
383 char *entry;
384 char *next_entry;
385 int rc, success_count;
386
387 data = NULL;
388 success_count = 0;
389 rc = 0;
390
391 data = read_manifest(path, hash);
392 if (data == NULL) {
393 rc = EIO;
394 goto out;
395 }
396
397 entry = data;
398 while (entry != NULL) {
399 next_entry = strchr(entry, '\n');
400 if (next_entry != NULL) {
401 *next_entry = '\0';
402 next_entry++;
403 }
404 if (entry[0] == '\n' || entry[0] == '\0') {
405 entry = next_entry;
406 continue;
407 }
408 if ((rc = parse_entry(entry, prefix)))
409 printf("mac_veriexec_parser: Warning: Failed to parse"
410 " entry with rc:%d, entry:\"%s\"\n", rc, entry);
411 else
412 success_count++;
413
414 entry = next_entry;
415 }
416 rc = 0;
417
418 out:
419 if (data != NULL)
420 free(data, M_VERIEXEC);
421
422 if (success_count == 0)
423 rc = EINVAL;
424
425 return (rc);
426 }
427
428 static void
parse_manifest_event(void * dummy)429 parse_manifest_event(void *dummy)
430 {
431 char *manifest_path;
432 char *manifest_prefix;
433 unsigned char *manifest_hash;
434 int rc;
435
436 /* If the envs are not set fail silently */
437 manifest_path = kern_getenv("veriexec.manifest_path");
438 if (manifest_path == NULL)
439 return;
440
441 manifest_hash = kern_getenv("veriexec.manifest_hash");
442 if (manifest_hash == NULL) {
443 freeenv(manifest_path);
444 return;
445 }
446
447 manifest_prefix = kern_getenv("veriexec.manifest_prefix");
448
449 if (strlen(manifest_hash) != 2 * SHA256_DIGEST_LENGTH)
450 panic("veriexec.manifest_hash has incorrect size");
451
452 rc = hexstring_to_bin(manifest_hash);
453 if (rc != 0)
454 panic("mac_veriexec: veriexec.loader.manifest_hash"
455 " doesn't contain a hash in hexadecimal form");
456
457 rc = parse_manifest(manifest_path, manifest_hash, manifest_prefix);
458 if (rc != 0)
459 panic("mac_veriexec: Failed to parse manifest err=%d", rc);
460
461 mtx_lock(&ve_mutex);
462 mac_veriexec_set_state(
463 VERIEXEC_STATE_LOADED | VERIEXEC_STATE_ACTIVE |
464 VERIEXEC_STATE_LOCKED | VERIEXEC_STATE_ENFORCE);
465 mtx_unlock(&ve_mutex);
466
467 freeenv(manifest_path);
468 freeenv(manifest_hash);
469 if (manifest_prefix != NULL)
470 freeenv(manifest_prefix);
471 }
472
473 EVENTHANDLER_DEFINE(mountroot, parse_manifest_event, NULL, 0);
474