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 */
50struct fingerprint_type {
51	const char	*fp_type;
52	int		fp_size;
53};
54
55struct fp_flag {
56	const char	*flag_name;
57	int		flag;
58};
59
60static 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
71static 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
79extern struct mtx ve_mutex;
80
81static unsigned char	hexchar_to_byte(unsigned char c);
82static int		hexstring_to_bin(unsigned char *buf);
83
84static int	get_flags(const char *entry);
85static int	get_fp(const char *entry, char **type,
86		    unsigned char **digest, int *flags);
87static int	verify_digest(const char *data, size_t len,
88		    const unsigned char *expected_hash);
89
90static int	open_file(const char *path, struct nameidata *nid);
91static char	*read_manifest(char *path, unsigned char *digest);
92static int	parse_entry(char *entry, char *prefix);
93static int	parse_manifest(char *path, unsigned char *hash, char *prefix);
94
95static unsigned char
96hexchar_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
105static int
106hexstring_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
123static int
124get_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 */
141static int
142get_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 */
223static int
224verify_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
236static int
237open_file(const char *path, struct nameidata *nid)
238{
239	int flags, rc;
240
241	flags = FREAD;
242
243	pwd_ensure_dirs();
244
245	NDINIT(nid, LOOKUP, 0, UIO_SYSSPACE, path, curthread);
246	rc = vn_open(nid, &flags, 0, NULL);
247	NDFREE(nid, NDF_ONLY_PNBUF);
248	if (rc != 0)
249		return (rc);
250
251	return (0);
252}
253
254/*
255 * Read the manifest from location specified in path and verify its digest.
256 */
257static char*
258read_manifest(char *path, unsigned char *digest)
259{
260	struct nameidata	nid;
261	struct vattr		va;
262	char			*data;
263	ssize_t			bytes_read, resid;
264	int			rc;
265
266	data = NULL;
267	bytes_read = 0;
268
269	rc = open_file(path, &nid);
270	if (rc != 0)
271		goto fail;
272
273	rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred);
274	if (rc != 0)
275		goto fail;
276
277	data = (char *)malloc(va.va_size + 1, M_VERIEXEC, M_WAITOK);
278
279	while (bytes_read < va.va_size) {
280		rc = vn_rdwr(
281		    UIO_READ, nid.ni_vp, data,
282		    va.va_size - bytes_read, bytes_read,
283		    UIO_SYSSPACE, IO_NODELOCKED,
284		    curthread->td_ucred, NOCRED, &resid, curthread);
285		if (rc != 0)
286			goto fail;
287
288		bytes_read = va.va_size - resid;
289	}
290
291	data[bytes_read] = '\0';
292
293	VOP_UNLOCK(nid.ni_vp);
294	(void)vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread);
295
296	/*
297	 * If digest is wrong someone might be trying to fool us.
298	 */
299	if (verify_digest(data, va.va_size, digest))
300		panic("Manifest hash doesn't match expected value!");
301
302	return (data);
303
304fail:
305	if (data != NULL)
306		free(data, M_VERIEXEC);
307
308	return (NULL);
309}
310
311/*
312 * Process single line.
313 * First split it into path, digest_type and digest.
314 * Then try to open the file and insert its fingerprint into metadata store.
315 */
316static int
317parse_entry(char *entry, char *prefix)
318{
319	struct nameidata	nid;
320	struct vattr		va;
321	char			path[MAXPATHLEN];
322	char			*fp_type;
323	unsigned char		*digest;
324	int			rc, is_exec, flags;
325
326	fp_type = NULL;
327	digest = NULL;
328	flags = 0;
329
330	rc = get_fp(entry, &fp_type, &digest, &flags);
331	if (rc != 0)
332		return (rc);
333
334	rc = hexstring_to_bin(digest);
335	if (rc != 0)
336		return (rc);
337
338	if (strnlen(entry, MAXPATHLEN) == MAXPATHLEN)
339		return (EINVAL);
340
341	/* If the path is not absolute prepend it with a prefix */
342	if (prefix != NULL && entry[0] != '/') {
343		rc = snprintf(path, MAXPATHLEN, "%s/%s",
344			    prefix, entry);
345		if (rc < 0)
346			return (-rc);
347	} else {
348		strcpy(path, entry);
349	}
350
351	rc = open_file(path, &nid);
352	NDFREE(&nid, NDF_ONLY_PNBUF);
353	if (rc != 0)
354		return (rc);
355
356	rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred);
357	if (rc != 0)
358		goto out;
359
360	is_exec = (va.va_mode & VEXEC);
361
362	mtx_lock(&ve_mutex);
363	rc = mac_veriexec_metadata_add_file(
364	    is_exec == 0,
365	    va.va_fsid, va.va_fileid, va.va_gen,
366	    digest,
367	    NULL, 0,
368	    flags, fp_type, 1);
369	mtx_unlock(&ve_mutex);
370
371out:
372	VOP_UNLOCK(nid.ni_vp);
373	vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread);
374	return (rc);
375}
376
377/*
378 * Look for manifest in env that have beed passed by loader.
379 * This routine should be called right after the rootfs is mounted.
380 */
381static int
382parse_manifest(char *path, unsigned char *hash, char *prefix)
383{
384	char	*data;
385	char	*entry;
386	char	*next_entry;
387	int	rc, success_count;
388
389	data = NULL;
390	success_count = 0;
391	rc = 0;
392
393	data = read_manifest(path, hash);
394	if (data == NULL) {
395		rc = EIO;
396		goto out;
397	}
398
399	entry = data;
400	while (entry != NULL) {
401		next_entry = strchr(entry, '\n');
402		if (next_entry != NULL) {
403			*next_entry = '\0';
404			next_entry++;
405		}
406		if (entry[0] == '\n' || entry[0] == '\0') {
407			entry = next_entry;
408			continue;
409		}
410		if ((rc = parse_entry(entry, prefix)))
411			printf("mac_veriexec_parser: Warning: Failed to parse"
412			       " entry with rc:%d, entry:\"%s\"\n", rc, entry);
413		else
414			success_count++;
415
416		entry = next_entry;
417	}
418	rc = 0;
419
420out:
421	if (data != NULL)
422		free(data, M_VERIEXEC);
423
424	if (success_count == 0)
425		rc = EINVAL;
426
427	return (rc);
428}
429
430static void
431parse_manifest_event(void *dummy)
432{
433	char		*manifest_path;
434	char		*manifest_prefix;
435	unsigned char	*manifest_hash;
436	int		rc;
437
438	/* If the envs are not set fail silently */
439	manifest_path = kern_getenv("veriexec.manifest_path");
440	if (manifest_path == NULL)
441		return;
442
443	manifest_hash = kern_getenv("veriexec.manifest_hash");
444	if (manifest_hash == NULL) {
445		freeenv(manifest_path);
446		return;
447	}
448
449	manifest_prefix = kern_getenv("veriexec.manifest_prefix");
450
451	if (strlen(manifest_hash) != 2 * SHA256_DIGEST_LENGTH)
452		panic("veriexec.manifest_hash has incorrect size");
453
454	rc = hexstring_to_bin(manifest_hash);
455	if (rc != 0)
456		panic("mac_veriexec: veriexec.loader.manifest_hash"
457		    " doesn't contain a hash in hexadecimal form");
458
459	rc = parse_manifest(manifest_path, manifest_hash, manifest_prefix);
460	if (rc != 0)
461		panic("mac_veriexec: Failed to parse manifest err=%d", rc);
462
463	mtx_lock(&ve_mutex);
464	mac_veriexec_set_state(
465	    VERIEXEC_STATE_LOADED | VERIEXEC_STATE_ACTIVE |
466	    VERIEXEC_STATE_LOCKED | VERIEXEC_STATE_ENFORCE);
467	mtx_unlock(&ve_mutex);
468
469	freeenv(manifest_path);
470	freeenv(manifest_hash);
471	if (manifest_prefix != NULL)
472		freeenv(manifest_prefix);
473}
474
475EVENTHANDLER_DEFINE(mountroot, parse_manifest_event, NULL, 0);
476