]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libsecureboot/vectx.c
Optionally bind ktls threads to NUMA domains
[FreeBSD/FreeBSD.git] / lib / libsecureboot / vectx.c
1 /*-
2  * Copyright (c) 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
28 #ifndef _STANDALONE
29 /* Avoid unwanted userlandish components */
30 #define _KERNEL
31 #include <sys/errno.h>
32 #undef _KERNEL
33 #endif
34
35 #include "libsecureboot-priv.h"
36 #include <verify_file.h>
37
38 /**
39  * @file vectx.c
40  * @brief api to verify file while reading
41  *
42  * This API allows the hash of a file to be computed as it is read.
43  * Key to this is seeking by reading.
44  *
45  * On close an indication of the verification result is returned.
46  */
47
48 struct vectx {
49         br_hash_compat_context vec_ctx; /* hash ctx */
50         const br_hash_class *vec_md;    /* hash method */
51         const char      *vec_path;      /* path we are verifying */
52         const char      *vec_want;      /* hash value we want */
53         off_t           vec_off;        /* current offset */
54         off_t           vec_hashed;     /* where we have hashed to */
55         size_t          vec_size;       /* size of path */
56         size_t          vec_hashsz;     /* size of hash */
57         int             vec_fd;         /* file descriptor */
58         int             vec_status;     /* verification status */
59 };
60
61
62 /**
63  * @brief
64  * verify an open file as we read it
65  *
66  * If the file has no fingerprint to match, we will still return a
67  * verification context containing little more than the file
68  * descriptor, and an error code in @c error.
69  *
70  * @param[in] fd
71  *      open descriptor
72  *
73  * @param[in] path
74  *      pathname to open
75  *
76  * @param[in] off
77  *      current offset
78  *
79  * @param[in] stp
80  *      pointer to struct stat
81  *
82  * @param[out] error
83  *      @li 0 all is good
84  *      @li ENOMEM out of memory
85  *      @li VE_FINGERPRINT_NONE no entry found
86  *      @li VE_FINGERPRINT_UNKNOWN no fingerprint in entry
87  *
88  * @return ctx or NULL on error.
89  *      NULL is only returned for non-files or out-of-memory.
90  */
91 struct vectx *
92 vectx_open(int fd, const char *path, off_t off, struct stat *stp,
93     int *error, const char *caller)
94 {
95         struct vectx *ctx;
96         struct stat st;
97         size_t hashsz;
98         char *cp;
99         int rc;
100
101         if (!stp)
102             stp = &st;
103
104         rc = verify_prep(fd, path, off, stp, __func__);
105
106         DEBUG_PRINTF(2,
107             ("vectx_open: caller=%s,fd=%d,name='%s',prep_rc=%d\n",
108                 caller, fd, path, rc));
109
110         switch (rc) {
111         case VE_FINGERPRINT_NONE:
112         case VE_FINGERPRINT_UNKNOWN:
113         case VE_FINGERPRINT_WRONG:
114                 *error = rc;
115                 return (NULL);
116         }
117         ctx = malloc(sizeof(struct vectx));
118         if (!ctx)
119                 goto enomem;
120         ctx->vec_fd = fd;
121         ctx->vec_path = path;
122         ctx->vec_size = stp->st_size;
123         ctx->vec_off = 0;
124         ctx->vec_hashed = 0;
125         ctx->vec_want = NULL;
126         ctx->vec_status = 0;
127         ctx->vec_hashsz = hashsz = 0;
128
129         if (rc == 0) {
130                 /* we are not verifying this */
131                 *error = 0;
132                 return (ctx);
133         }
134         cp = fingerprint_info_lookup(fd, path);
135         if (!cp) {
136                 ctx->vec_status = VE_FINGERPRINT_NONE;
137                 ve_error_set("%s: no entry", path);
138         } else {
139                 if (strncmp(cp, "no_hash", 7) == 0) {
140                         ctx->vec_status = VE_FINGERPRINT_IGNORE;
141                         hashsz = 0;
142                 } else if (strncmp(cp, "sha256=", 7) == 0) {
143                         ctx->vec_md = &br_sha256_vtable;
144                         hashsz = br_sha256_SIZE;
145                         cp += 7;
146 #ifdef VE_SHA1_SUPPORT
147                 } else if (strncmp(cp, "sha1=", 5) == 0) {
148                         ctx->vec_md = &br_sha1_vtable;
149                         hashsz = br_sha1_SIZE;
150                         cp += 5;
151 #endif
152 #ifdef VE_SHA384_SUPPORT
153                 } else if (strncmp(cp, "sha384=", 7) == 0) {
154                     ctx->vec_md = &br_sha384_vtable;
155                     hashsz = br_sha384_SIZE;
156                     cp += 7;
157 #endif
158 #ifdef VE_SHA512_SUPPORT
159                 } else if (strncmp(cp, "sha512=", 7) == 0) {
160                     ctx->vec_md = &br_sha512_vtable;
161                     hashsz = br_sha512_SIZE;
162                     cp += 7;
163 #endif
164                 } else {
165                         ctx->vec_status = VE_FINGERPRINT_UNKNOWN;
166                         ve_error_set("%s: no supported fingerprint", path);
167                 }
168         }
169         *error = ctx->vec_status;
170         ctx->vec_hashsz = hashsz;
171         ctx->vec_want = cp;
172         if (hashsz > 0) {
173                 ctx->vec_md->init(&ctx->vec_ctx.vtable);
174
175                 if (off > 0) {
176                         lseek(fd, 0, SEEK_SET);
177                         vectx_lseek(ctx, off, SEEK_SET);
178                 }
179         }
180         DEBUG_PRINTF(2,
181             ("vectx_open: caller=%s,name='%s',hashsz=%lu,status=%d\n",
182                 caller, path, (unsigned long)ctx->vec_hashsz,
183                 ctx->vec_status));
184         return (ctx);
185
186 enomem:                                 /* unlikely */
187         *error = ENOMEM;
188         free(ctx);
189         return (NULL);
190 }
191
192 /**
193  * @brief
194  * read bytes from file and update hash
195  *
196  * It is critical that all file I/O comes through here.
197  * We keep track of current offset.
198  * We also track what offset we have hashed to,
199  * so we won't replay data if we seek backwards.
200  *
201  * @param[in] pctx
202  *      pointer to ctx
203  *
204  * @param[in] buf
205  *
206  * @param[in] nbytes
207  *
208  * @return bytes read or error.
209  */
210 ssize_t
211 vectx_read(struct vectx *ctx, void *buf, size_t nbytes)
212 {
213         unsigned char *bp = buf;
214         int d;
215         int n;
216         int delta;
217         int x;
218         size_t off;
219
220         if (ctx->vec_hashsz == 0)       /* nothing to do */
221                 return (read(ctx->vec_fd, buf, nbytes));
222
223         off = 0;
224         do {
225                 /*
226                  * Do this in reasonable chunks so
227                  * we don't timeout if doing tftp
228                  */
229                 x = nbytes - off;
230                 x = MIN(PAGE_SIZE, x);
231                 d = n = read(ctx->vec_fd, &bp[off], x);
232                 if (n < 0) {
233                         return (n);
234                 }
235                 if (d > 0) {
236                         /* we may have seeked backwards! */
237                         delta = ctx->vec_hashed - ctx->vec_off;
238                         if (delta > 0) {
239                                 x = MIN(delta, d);
240                                 off += x;
241                                 d -= x;
242                                 ctx->vec_off += x;
243                         }
244                         if (d > 0) {
245                                 ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], d);
246                                 off += d;
247                                 ctx->vec_off += d;
248                                 ctx->vec_hashed += d;
249                         }
250                 }
251         } while (n > 0 && off < nbytes);
252         return (off);
253 }
254
255 /**
256  * @brief
257  * vectx equivalent of lseek
258  *
259  * When seeking forwards we actually call vectx_read
260  * to reach the desired offset.
261  *
262  * We support seeking backwards.
263  *
264  * @param[in] pctx
265  *      pointer to ctx
266  *
267  * @param[in] off
268  *      desired offset
269  *
270  * @param[in] whence
271  *      We try to convert whence to ``SEEK_SET``.
272  *      We do not support ``SEEK_DATA`` or ``SEEK_HOLE``.
273  *
274  * @return offset or error.
275  */
276 off_t
277 vectx_lseek(struct vectx *ctx, off_t off, int whence)
278 {
279         unsigned char buf[PAGE_SIZE];
280         size_t delta;
281         ssize_t n;
282
283         if (ctx->vec_hashsz == 0)       /* nothing to do */
284                 return (lseek(ctx->vec_fd, off, whence));
285
286         /*
287          * Convert whence to SEEK_SET
288          */
289         if (whence == SEEK_END && off <= 0) {
290                 whence = SEEK_SET;
291                 off += ctx->vec_size;
292         } else if (whence == SEEK_CUR) {
293                 whence = SEEK_SET;
294                 off += ctx->vec_off;
295         }
296         if (whence != SEEK_SET ||
297             (size_t)off > ctx->vec_size) {
298                 printf("ERROR: %s: unsupported operation: whence=%d off=%lld -> %lld\n",
299                     __func__, whence, (long long)ctx->vec_off, (long long)off);
300                 return (-1);
301         }
302         if (off < ctx->vec_hashed) {
303                 /* seeking backwards! just do it */
304                 ctx->vec_off = lseek(ctx->vec_fd, off, whence);
305                 return (ctx->vec_off);
306         }
307         n = 0;
308         do {
309                 delta = off - ctx->vec_off;
310                 if (delta > 0) {
311                         delta = MIN(PAGE_SIZE, delta);
312                         n = vectx_read(ctx, buf, delta);
313                         if (n < 0)
314                                 return (n);
315                 }
316         } while (ctx->vec_off < off && n > 0);
317         return (ctx->vec_off);
318 }
319
320 /**
321  * @brief
322  * check that hashes match and cleanup
323  *
324  * We have finished reading file, compare the hash with what
325  * we wanted.
326  *
327  * Be sure to call this before closing the file, since we may
328  * need to seek to the end to ensure hashing is complete.
329  *
330  * @param[in] pctx
331  *      pointer to ctx
332  *
333  * @return 0 or an error.
334  */
335 int
336 vectx_close(struct vectx *ctx, int severity, const char *caller)
337 {
338         int rc;
339
340         if (ctx->vec_hashsz == 0) {
341                 rc = ctx->vec_status;
342         } else {
343 #ifdef VE_PCR_SUPPORT
344                 /*
345                  * Only update pcr with things that must verify
346                  * these tend to be processed in a more deterministic
347                  * order, which makes our pseudo pcr more useful.
348                  */
349                 ve_pcr_updating_set((severity == VE_MUST));
350 #endif
351                 /* make sure we have hashed it all */
352                 vectx_lseek(ctx, 0, SEEK_END);
353                 rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md,
354                     ctx->vec_path, ctx->vec_want, ctx->vec_hashsz);
355         }
356         DEBUG_PRINTF(2,
357             ("vectx_close: caller=%s,name='%s',rc=%d,severity=%d\n",
358                 caller,ctx->vec_path, rc, severity));
359         if (rc == VE_FINGERPRINT_WRONG) {
360                 printf("Unverified: %s\n", ve_error_get());
361 #if !defined(UNIT_TEST) && !defined(DEBUG_VECTX)
362                 /* we are generally called with VE_MUST */
363                 if (severity > VE_WANT)
364                         panic("cannot continue");
365 #endif
366         } else if (severity > VE_WANT) {
367                 printf("%serified %s\n", (rc <= 0) ? "Unv" : "V",
368                     ctx->vec_path);
369         }
370         free(ctx);
371         return ((rc < 0) ? rc : 0);
372 }