xref: /freebsd/lib/libsecureboot/veopen.c (revision 9e5787d2284e187abb5b654d924394a65772e004)
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 (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  */
164 static char *
165 maybe_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 
184 char *
185 fingerprint_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 
341 static int
342 verify_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  */
409 int
410 verify_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  */
450 int
451 verify_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