xref: /freebsd/lib/libsecureboot/veopen.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
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 
31 struct 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 
41 static LIST_HEAD(, fingerprint_info) fi_list;
42 
43 static void
fingerprint_info_init(void)44 fingerprint_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  */
69 void
fingerprint_info_add(const char * filename,const char * prefix,const char * skip,const char * data,struct stat * stp)70 fingerprint_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  */
169 static char *
maybe_skip(char * fp,struct fingerprint_info * fip,size_t * nplenp)170 maybe_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 
189 char *
fingerprint_info_lookup(int fd,const char * path)190 fingerprint_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 
347 static int
verify_fingerprint(int fd,const char * path,const char * cp,off_t off)348 verify_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  */
415 int
verify_fd(int fd,const char * path,off_t off,struct stat * stp)416 verify_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  */
456 int
verify_open(const char * path,int flags)457 verify_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