]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libsecureboot/veopen.c
cdn-patch: offer option to mount /etc/keys before attaching geli devices
[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)
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 *
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 *
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
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
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
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 }