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