]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libsecureboot/vectx.c
MFV r359442: bmake: import -fno-common fix build back from upstream
[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 n;
215         int delta;
216         int x;
217         size_t off;
218
219         if (ctx->vec_hashsz == 0)       /* nothing to do */
220                 return (read(ctx->vec_fd, buf, nbytes));
221
222         off = 0;
223         do {
224                 n = read(ctx->vec_fd, &bp[off], nbytes - off);
225                 if (n < 0)
226                         return (n);
227                 if (n > 0) {
228                         /* we may have seeked backwards! */
229                         delta = ctx->vec_hashed - ctx->vec_off;
230                         if (delta > 0) {
231                                 x = MIN(delta, n);
232                                 off += x;
233                                 n -= x;
234                                 ctx->vec_off += x;
235                         }
236                         if (n > 0) {
237                                 ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], n);
238                                 off += n;
239                                 ctx->vec_off += n;
240                                 ctx->vec_hashed += n;
241                         }
242                 }
243         } while (n > 0 && off < nbytes);
244         return (off);
245 }
246
247 /**
248  * @brief
249  * vectx equivalent of lseek
250  *
251  * When seeking forwards we actually call vectx_read
252  * to reach the desired offset.
253  *
254  * We support seeking backwards.
255  *
256  * @param[in] pctx
257  *      pointer to ctx
258  *
259  * @param[in] off
260  *      desired offset
261  *
262  * @param[in] whence
263  *      We try to convert whence to ``SEEK_SET``.
264  *      We do not support ``SEEK_DATA`` or ``SEEK_HOLE``.
265  *
266  * @return offset or error.
267  */
268 off_t
269 vectx_lseek(struct vectx *ctx, off_t off, int whence)
270 {
271         unsigned char buf[PAGE_SIZE];
272         size_t delta;
273         ssize_t n;
274
275         if (ctx->vec_hashsz == 0)       /* nothing to do */
276                 return (lseek(ctx->vec_fd, off, whence));
277
278         /*
279          * Convert whence to SEEK_SET
280          */
281         if (whence == SEEK_END && off <= 0) {
282                 whence = SEEK_SET;
283                 off += ctx->vec_size;
284         } else if (whence == SEEK_CUR) {
285                 whence = SEEK_SET;
286                 off += ctx->vec_off;
287         }
288         if (whence != SEEK_SET ||
289             (size_t)off > ctx->vec_size) {
290                 printf("ERROR: %s: unsupported operation: whence=%d off=%lld -> %lld\n",
291                     __func__, whence, (long long)ctx->vec_off, (long long)off);
292                 return (-1);
293         }
294         if (off < ctx->vec_hashed) {
295                 /* seeking backwards! just do it */
296                 ctx->vec_off = lseek(ctx->vec_fd, off, whence);
297                 return (ctx->vec_off);
298         }
299         n = 0;
300         do {
301                 delta = off - ctx->vec_off;
302                 if (delta > 0) {
303                         delta = MIN(PAGE_SIZE, delta);
304                         n = vectx_read(ctx, buf, delta);
305                         if (n < 0)
306                                 return (n);
307                 }
308         } while (ctx->vec_off < off && n > 0);
309         return (ctx->vec_off);
310 }
311
312 /**
313  * @brief
314  * check that hashes match and cleanup
315  *
316  * We have finished reading file, compare the hash with what
317  * we wanted.
318  *
319  * Be sure to call this before closing the file, since we may
320  * need to seek to the end to ensure hashing is complete.
321  *
322  * @param[in] pctx
323  *      pointer to ctx
324  *
325  * @return 0 or an error.
326  */
327 int
328 vectx_close(struct vectx *ctx, int severity, const char *caller)
329 {
330         int rc;
331
332         if (ctx->vec_hashsz == 0) {
333                 rc = ctx->vec_status;
334         } else {
335 #ifdef VE_PCR_SUPPORT
336                 /*
337                  * Only update pcr with things that must verify
338                  * these tend to be processed in a more deterministic
339                  * order, which makes our pseudo pcr more useful.
340                  */
341                 ve_pcr_updating_set((severity == VE_MUST));
342 #endif
343                 /* make sure we have hashed it all */
344                 vectx_lseek(ctx, 0, SEEK_END);
345                 rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md,
346                     ctx->vec_path, ctx->vec_want, ctx->vec_hashsz);
347         }
348         DEBUG_PRINTF(2,
349             ("vectx_close: caller=%s,name='%s',rc=%d,severity=%d\n",
350                 caller,ctx->vec_path, rc, severity));
351         if (rc == VE_FINGERPRINT_WRONG) {
352                 printf("Unverified: %s\n", ve_error_get());
353 #if !defined(UNIT_TEST) && !defined(DEBUG_VECTX)
354                 /* we are generally called with VE_MUST */
355                 if (severity > VE_WANT)
356                         panic("cannot continue");
357 #endif
358         } else if (severity > VE_WANT) {
359                 printf("%serified %s\n", (rc <= 0) ? "Unv" : "V",
360                     ctx->vec_path);
361         }
362         free(ctx);
363         return ((rc < 0) ? rc : 0);
364 }