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