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