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 == 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 *
maybe_skip(char * fp,struct fingerprint_info * fip,size_t * nplenp)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 *
fingerprint_info_lookup(int fd,const char * path)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
verify_fingerprint(int fd,const char * path,const char * cp,off_t off)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
verify_fd(int fd,const char * path,off_t off,struct stat * stp)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
verify_open(const char * path,int flags)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