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