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