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