xref: /freebsd/sys/security/mac_veriexec_parser/mac_veriexec_parser.c (revision 6e45b50342d5adadf9dd08e3476fc90f715be1fc)
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