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