]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libsecureboot/veopen.c
bhyvectl(8): Normalize the man page date
[FreeBSD/FreeBSD.git] / lib / libsecureboot / veopen.c
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 }