xref: /freebsd/lib/libsecureboot/vectx.c (revision 7cc42f6d)
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 }
373