1/*-
2 * Copyright (c) 2017-2018, Juniper Networks, Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25#include <sys/cdefs.h>
26__FBSDID("$FreeBSD$");
27#include <sys/queue.h>
28
29#include "libsecureboot-priv.h"
30
31
32struct fingerprint_info {
33	char		*fi_prefix;	/**< manifest entries relative to */
34	char		*fi_skip;	/**< manifest entries prefixed with  */
35	const char 	*fi_data;	/**< manifest data */
36	size_t		fi_prefix_len;	/**< length of prefix */
37	size_t		fi_skip_len;	/**< length of skip */
38	dev_t		fi_dev;		/**< device id  */
39	LIST_ENTRY(fingerprint_info) entries;
40};
41
42static LIST_HEAD(, fingerprint_info) fi_list;
43
44static void
45fingerprint_info_init(void)
46{
47	static int once;
48
49	if (once)
50		return;
51	LIST_INIT(&fi_list);
52	once = 1;
53}
54
55/**
56 * @brief
57 * add manifest data to list
58 *
59 * list is kept sorted by longest prefix.
60 *
61 * @param[in] prefix
62 *	path that all manifest entries are resolved via
63 *
64 * @param[in] skip
65 *	optional prefix within manifest entries which should be skipped
66 *
67 * @param[in] data
68 *	manifest data
69 */
70void
71fingerprint_info_add(const char *filename, const char *prefix,
72    const char *skip, const char *data, struct stat *stp)
73{
74	struct fingerprint_info *fip, *nfip, *lfip;
75	char *cp;
76	int n;
77
78	fingerprint_info_init();
79	nfip = malloc(sizeof(struct fingerprint_info));
80	if (prefix) {
81		nfip->fi_prefix = strdup(prefix);
82	} else {
83		if (!filename) {
84			free(nfip);
85			return;
86		}
87		nfip->fi_prefix = strdup(filename);
88		cp = strrchr(nfip->fi_prefix, '/');
89		if (cp == nfip->fi_prefix) {
90			cp[1] = '\0';
91		} else if (cp) {
92			*cp = '\0';
93		} else {
94			free(nfip->fi_prefix);
95			free(nfip);
96			return;
97		}
98	}
99	/* collapse any trailing ..[/] */
100	n = 0;
101	while ((cp = strrchr(nfip->fi_prefix, '/')) > nfip->fi_prefix) {
102		if (cp[1] == '\0') {	/* trailing "/" */
103			*cp = '\0';
104			continue;
105		}
106		if (strcmp(&cp[1], "..") == 0) {
107			n++;
108			*cp = '\0';
109			continue;
110		}
111		if (n > 0) {
112			n--;
113			*cp = '\0';
114		}
115		if (n == 0)
116			break;
117	}
118#ifdef UNIT_TEST
119	nfip->fi_dev = 0;
120#else
121	nfip->fi_dev = stp->st_dev;
122#endif
123	nfip->fi_data = data;
124	nfip->fi_prefix_len = strlen(nfip->fi_prefix);
125	if (skip) {
126		nfip->fi_skip_len = strlen(skip);
127		if (nfip->fi_skip_len)
128			nfip->fi_skip = strdup(skip);
129		else
130			nfip->fi_skip = NULL;
131	} else {
132		nfip->fi_skip = NULL;
133		nfip->fi_skip_len = 0;
134	}
135
136	if (LIST_EMPTY(&fi_list)) {
137		LIST_INSERT_HEAD(&fi_list, nfip, entries);
138		DEBUG_PRINTF(4, ("inserted %zu %s at head\n",
139			nfip->fi_prefix_len, nfip->fi_prefix));
140		return;
141	}
142	LIST_FOREACH(fip, &fi_list, entries) {
143		if (nfip->fi_prefix_len >= fip->fi_prefix_len) {
144			LIST_INSERT_BEFORE(fip, nfip, entries);
145			DEBUG_PRINTF(4, ("inserted %zu %s before %zu %s\n",
146				nfip->fi_prefix_len, nfip->fi_prefix,
147				fip->fi_prefix_len, fip->fi_prefix));
148			return;
149		}
150		lfip = fip;
151	}
152	LIST_INSERT_AFTER(lfip, nfip, entries);
153	DEBUG_PRINTF(4, ("inserted %zu %s after %zu %s\n",
154		nfip->fi_prefix_len, nfip->fi_prefix,
155		lfip->fi_prefix_len, lfip->fi_prefix));
156}
157
158#ifdef MANIFEST_SKIP_MAYBE
159/*
160 * Deal with old incompatible boot/manifest
161 * if fp[-1] is '/' and start of entry matches
162 * MANIFEST_SKIP_MAYBE, we want it.
163 */
164static char *
165maybe_skip(char *fp, struct fingerprint_info *fip, size_t *nplenp)
166{
167	char *tp;
168
169	tp = fp - sizeof(MANIFEST_SKIP_MAYBE);
170
171	if (tp >= fip->fi_data) {
172		DEBUG_PRINTF(3, ("maybe: %.48s\n", tp));
173		if ((tp == fip->fi_data || tp[-1] == '\n') &&
174		    strncmp(tp, MANIFEST_SKIP_MAYBE,
175			sizeof(MANIFEST_SKIP_MAYBE) - 1) == 0) {
176			fp = tp;
177			*nplenp += sizeof(MANIFEST_SKIP_MAYBE);
178		}
179	}
180	return (fp);
181}
182#endif
183
184char *
185fingerprint_info_lookup(int fd, const char *path)
186{
187	char pbuf[MAXPATHLEN+1];
188	char nbuf[MAXPATHLEN+1];
189	struct stat st;
190	struct fingerprint_info *fip;
191	char *cp, *ep, *fp, *np;
192	const char *prefix;
193	size_t n, plen, nlen, nplen;
194	dev_t dev = 0;
195
196	fingerprint_info_init();
197
198	n = strlcpy(pbuf, path, sizeof(pbuf));
199	if (n >= sizeof(pbuf))
200		return (NULL);
201#ifndef UNIT_TEST
202	if (fstat(fd, &st) == 0)
203		dev = st.st_dev;
204#endif
205	/*
206	 * get the first entry - it will have longest prefix
207	 * so we can can work out how to initially split path
208	 */
209	fip = LIST_FIRST(&fi_list);
210	if (!fip)
211		return (NULL);
212	prefix = pbuf;
213	ep = NULL;
214	cp = &pbuf[fip->fi_prefix_len];
215	do {
216		if (ep) {
217			*ep = '/';
218			cp -= 2;
219			if (cp < pbuf)
220				break;
221		}
222		nlen = plen = 0;	/* keep gcc quiet */
223		if (cp > pbuf) {
224			for ( ; cp >= pbuf && *cp != '/'; cp--)
225				;	/* nothing */
226			if (cp > pbuf) {
227				ep = cp++;
228				*ep = '\0';
229			} else {
230				cp = pbuf;
231			}
232			if (ep) {
233				plen = ep - pbuf;
234				nlen = n - plen - 1;
235			}
236		}
237		if (cp == pbuf) {
238			prefix = "/";
239			plen = 1;
240			if (*cp == '/') {
241				nlen = n - 1;
242				cp++;
243			} else
244				nlen = n;
245			ep = NULL;
246		}
247
248		DEBUG_PRINTF(2, ("looking for %s %zu %s\n", prefix, plen, cp));
249
250		LIST_FOREACH(fip, &fi_list, entries) {
251			DEBUG_PRINTF(4, ("at %zu %s\n",
252				fip->fi_prefix_len, fip->fi_prefix));
253
254			if (fip->fi_prefix_len < plen) {
255				DEBUG_PRINTF(3, ("skipping prefix=%s %zu %zu\n",
256					fip->fi_prefix, fip->fi_prefix_len,
257					plen));
258				break;
259			}
260			if (fip->fi_prefix_len == plen) {
261				if (fip->fi_dev != 0 && fip->fi_dev != dev) {
262					DEBUG_PRINTF(3, (
263						"skipping dev=%ld != %ld\n",
264						(long)fip->fi_dev,
265						(long)dev));
266					continue;
267				}
268				if (strcmp(prefix, fip->fi_prefix)) {
269					DEBUG_PRINTF(3, (
270						"skipping prefix=%s\n",
271						fip->fi_prefix));
272					continue;
273				}
274				DEBUG_PRINTF(3, ("checking prefix=%s\n",
275					fip->fi_prefix));
276				if (fip->fi_skip_len) {
277					np = nbuf;
278					nplen = snprintf(nbuf, sizeof(nbuf),
279					    "%s/%s",
280					    fip->fi_skip, cp);
281					nplen = MIN(nplen, sizeof(nbuf) - 1);
282				} else {
283					np = cp;
284					nplen = nlen;
285				}
286				DEBUG_PRINTF(3, ("lookup: '%s'\n", np));
287				if (!(fp = strstr(fip->fi_data, np)))
288					continue;
289#ifdef MANIFEST_SKIP_MAYBE
290				if (fip->fi_skip_len == 0 &&
291				    fp > fip->fi_data && fp[-1] == '/') {
292					fp = maybe_skip(fp, fip, &nplen);
293				}
294#endif
295				/*
296				 * when we find a match:
297				 * fp[nplen] will be space and
298				 * fp will be fip->fi_data or
299				 * fp[-1] will be \n
300				 */
301				if (!((fp == fip->fi_data || fp[-1] == '\n') &&
302					fp[nplen] == ' ')) {
303					do {
304						fp++;
305						fp = strstr(fp, np);
306						if (fp) {
307#ifdef MANIFEST_SKIP_MAYBE
308							if (fip->fi_skip_len == 0 &&
309							    fp > fip->fi_data &&
310							    fp[-1] == '/') {
311								fp = maybe_skip(fp, fip, &nplen);
312							}
313#endif
314							DEBUG_PRINTF(3,
315							    ("fp[-1]=%#x fp[%zu]=%#x fp=%.78s\n",
316								fp[-1], nplen,
317								fp[nplen],
318								fp));
319						}
320					} while (fp != NULL &&
321					    !(fp[-1] == '\n' &&
322						fp[nplen] == ' '));
323					if (!fp)
324						continue;
325				}
326				DEBUG_PRINTF(2, ("found %.78s\n", fp));
327				/* we have a match! */
328				for (cp = &fp[nplen]; *cp == ' '; cp++)
329					; /* nothing */
330				return (cp);
331			} else {
332				DEBUG_PRINTF(3,
333				    ("Ignoring prefix=%s\n", fip->fi_prefix));
334			}
335		}
336	} while (cp > &pbuf[1]);
337
338	return (NULL);
339}
340
341static int
342verify_fingerprint(int fd, const char *path, const char *cp, off_t off)
343{
344	unsigned char buf[PAGE_SIZE];
345	const br_hash_class *md;
346	br_hash_compat_context mctx;
347	size_t hlen;
348	int n;
349
350	if (strncmp(cp, "no_hash", 7) == 0) {
351		return (VE_FINGERPRINT_IGNORE);
352	} else if (strncmp(cp, "sha256=", 7) == 0) {
353		md = &br_sha256_vtable;
354		hlen = br_sha256_SIZE;
355		cp += 7;
356#ifdef VE_SHA1_SUPPORT
357	} else if (strncmp(cp, "sha1=", 5) == 0) {
358		md = &br_sha1_vtable;
359		hlen = br_sha1_SIZE;
360		cp += 5;
361#endif
362#ifdef VE_SHA384_SUPPORT
363	} else if (strncmp(cp, "sha384=", 7) == 0) {
364		md = &br_sha384_vtable;
365		hlen = br_sha384_SIZE;
366		cp += 7;
367#endif
368#ifdef VE_SHA512_SUPPORT
369	} else if (strncmp(cp, "sha512=", 7) == 0) {
370		md = &br_sha512_vtable;
371		hlen = br_sha512_SIZE;
372		cp += 7;
373#endif
374	} else {
375		ve_error_set("%s: no supported fingerprint", path);
376		return (VE_FINGERPRINT_UNKNOWN);
377	}
378
379	md->init(&mctx.vtable);
380	if (off)
381		lseek(fd, 0, SEEK_SET);
382	do {
383		n = read(fd, buf, sizeof(buf));
384		if (n < 0)
385			return (n);
386		if (n > 0)
387			md->update(&mctx.vtable, buf, n);
388	} while (n > 0);
389	lseek(fd, off, SEEK_SET);
390	return (ve_check_hash(&mctx, md, path, cp, hlen));
391}
392
393
394/**
395 * @brief
396 * verify an open file
397 *
398 * @param[in] fd
399 *	open descriptor
400 *
401 * @param[in] path
402 *	pathname to open
403 *
404 * @param[in] off
405 *	current offset
406 *
407 * @return 0, VE_FINGERPRINT_OK or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG
408 */
409int
410verify_fd(int fd, const char *path, off_t off, struct stat *stp)
411{
412	struct stat st;
413	char *cp;
414	int rc;
415
416	if (!stp) {
417		if (fstat(fd, &st) == 0)
418			stp = &st;
419	}
420	if (stp && !S_ISREG(stp->st_mode))
421		return (0);		/* not relevant */
422	cp = fingerprint_info_lookup(fd, path);
423	if (!cp) {
424		ve_error_set("%s: no entry", path);
425		return (VE_FINGERPRINT_NONE);
426	}
427	rc = verify_fingerprint(fd, path, cp, off);
428	switch (rc) {
429	case VE_FINGERPRINT_OK:
430	case VE_FINGERPRINT_IGNORE:
431	case VE_FINGERPRINT_UNKNOWN:
432		return (rc);
433	default:
434		return (VE_FINGERPRINT_WRONG);
435	}
436}
437
438/**
439 * @brief
440 * open a file if it can be verified
441 *
442 * @param[in] path
443 *	pathname to open
444 *
445 * @param[in] flags
446 *	flags for open
447 *
448 * @return fd or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG
449 */
450int
451verify_open(const char *path, int flags)
452{
453	int fd;
454	int rc;
455
456	if ((fd = open(path, flags)) >= 0) {
457		if ((rc = verify_fd(fd, path, 0, NULL)) < 0) {
458			close(fd);
459			fd = rc;
460		}
461	}
462	return (fd);
463}
464