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