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