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