xref: /freebsd/lib/libsecureboot/vectx.c (revision 5fff9558a43aaac53da41dc23c250c4e84f6fb02)
1*5fff9558SSimon J. Gerraty /*-
2*5fff9558SSimon J. Gerraty  * Copyright (c) 2018, Juniper Networks, Inc.
3*5fff9558SSimon J. Gerraty  *
4*5fff9558SSimon J. Gerraty  * Redistribution and use in source and binary forms, with or without
5*5fff9558SSimon J. Gerraty  * modification, are permitted provided that the following conditions
6*5fff9558SSimon J. Gerraty  * are met:
7*5fff9558SSimon J. Gerraty  * 1. Redistributions of source code must retain the above copyright
8*5fff9558SSimon J. Gerraty  *    notice, this list of conditions and the following disclaimer.
9*5fff9558SSimon J. Gerraty  * 2. Redistributions in binary form must reproduce the above copyright
10*5fff9558SSimon J. Gerraty  *    notice, this list of conditions and the following disclaimer in the
11*5fff9558SSimon J. Gerraty  *    documentation and/or other materials provided with the distribution.
12*5fff9558SSimon J. Gerraty  *
13*5fff9558SSimon J. Gerraty  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14*5fff9558SSimon J. Gerraty  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15*5fff9558SSimon J. Gerraty  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16*5fff9558SSimon J. Gerraty  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17*5fff9558SSimon J. Gerraty  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18*5fff9558SSimon J. Gerraty  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19*5fff9558SSimon J. Gerraty  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20*5fff9558SSimon J. Gerraty  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21*5fff9558SSimon J. Gerraty  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22*5fff9558SSimon J. Gerraty  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23*5fff9558SSimon J. Gerraty  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24*5fff9558SSimon J. Gerraty  */
25*5fff9558SSimon J. Gerraty #include <sys/cdefs.h>
26*5fff9558SSimon J. Gerraty __FBSDID("$FreeBSD$");
27*5fff9558SSimon J. Gerraty 
28*5fff9558SSimon J. Gerraty #ifndef _STANDALONE
29*5fff9558SSimon J. Gerraty /* Avoid unwanted userlandish components */
30*5fff9558SSimon J. Gerraty #define _KERNEL
31*5fff9558SSimon J. Gerraty #include <sys/errno.h>
32*5fff9558SSimon J. Gerraty #undef _KERNEL
33*5fff9558SSimon J. Gerraty #endif
34*5fff9558SSimon J. Gerraty 
35*5fff9558SSimon J. Gerraty #include "libsecureboot-priv.h"
36*5fff9558SSimon J. Gerraty 
37*5fff9558SSimon J. Gerraty /**
38*5fff9558SSimon J. Gerraty  * @file vectx.c
39*5fff9558SSimon J. Gerraty  * @brief api to verify file while reading
40*5fff9558SSimon J. Gerraty  *
41*5fff9558SSimon J. Gerraty  * This API allows the hash of a file to be computed as it is read.
42*5fff9558SSimon J. Gerraty  * Key to this is seeking by reading.
43*5fff9558SSimon J. Gerraty  *
44*5fff9558SSimon J. Gerraty  * On close an indication of the verification result is returned.
45*5fff9558SSimon J. Gerraty  */
46*5fff9558SSimon J. Gerraty 
47*5fff9558SSimon J. Gerraty struct vectx {
48*5fff9558SSimon J. Gerraty 	br_hash_compat_context vec_ctx;	/* hash ctx */
49*5fff9558SSimon J. Gerraty 	const br_hash_class *vec_md;	/* hash method */
50*5fff9558SSimon J. Gerraty 	const char	*vec_path;	/* path we are verifying */
51*5fff9558SSimon J. Gerraty 	const char	*vec_want;	/* hash value we want */
52*5fff9558SSimon J. Gerraty 	off_t		vec_off;	/* current offset */
53*5fff9558SSimon J. Gerraty 	size_t		vec_size;	/* size of path */
54*5fff9558SSimon J. Gerraty 	size_t		vec_hashsz;	/* size of hash */
55*5fff9558SSimon J. Gerraty 	int		vec_fd;		/* file descriptor */
56*5fff9558SSimon J. Gerraty 	int		vec_status;	/* verification status */
57*5fff9558SSimon J. Gerraty };
58*5fff9558SSimon J. Gerraty 
59*5fff9558SSimon J. Gerraty /**
60*5fff9558SSimon J. Gerraty  * @brief
61*5fff9558SSimon J. Gerraty  * verify an open file as we read it
62*5fff9558SSimon J. Gerraty  *
63*5fff9558SSimon J. Gerraty  * If the file has no fingerprint to match, we will still return a
64*5fff9558SSimon J. Gerraty  * verification context containing little more than the file
65*5fff9558SSimon J. Gerraty  * descriptor, and an error code in @c error.
66*5fff9558SSimon J. Gerraty  *
67*5fff9558SSimon J. Gerraty  * @param[in] fd
68*5fff9558SSimon J. Gerraty  *	open descriptor
69*5fff9558SSimon J. Gerraty  *
70*5fff9558SSimon J. Gerraty  * @param[in] path
71*5fff9558SSimon J. Gerraty  *	pathname to open
72*5fff9558SSimon J. Gerraty  *
73*5fff9558SSimon J. Gerraty  * @param[in] off
74*5fff9558SSimon J. Gerraty  *	current offset
75*5fff9558SSimon J. Gerraty  *
76*5fff9558SSimon J. Gerraty  * @param[in] stp
77*5fff9558SSimon J. Gerraty  *	pointer to struct stat
78*5fff9558SSimon J. Gerraty  *
79*5fff9558SSimon J. Gerraty  * @param[out] error
80*5fff9558SSimon J. Gerraty  *	@li 0 all is good
81*5fff9558SSimon J. Gerraty  *	@li ENOMEM out of memory
82*5fff9558SSimon J. Gerraty  *	@li VE_FINGERPRINT_NONE	no entry found
83*5fff9558SSimon J. Gerraty  *	@li VE_FINGERPRINT_UNKNOWN no fingerprint in entry
84*5fff9558SSimon J. Gerraty  *
85*5fff9558SSimon J. Gerraty  * @return ctx or NULL on error.
86*5fff9558SSimon J. Gerraty  *	NULL is only returned for non-files or out-of-memory.
87*5fff9558SSimon J. Gerraty  */
88*5fff9558SSimon J. Gerraty struct vectx *
89*5fff9558SSimon J. Gerraty vectx_open(int fd, const char *path, off_t off, struct stat *stp, int *error)
90*5fff9558SSimon J. Gerraty {
91*5fff9558SSimon J. Gerraty 	struct vectx *ctx;
92*5fff9558SSimon J. Gerraty 	struct stat st;
93*5fff9558SSimon J. Gerraty 	size_t hashsz;
94*5fff9558SSimon J. Gerraty 	char *cp;
95*5fff9558SSimon J. Gerraty 
96*5fff9558SSimon J. Gerraty 	if (!stp) {
97*5fff9558SSimon J. Gerraty 		if (fstat(fd, &st) == 0)
98*5fff9558SSimon J. Gerraty 			stp = &st;
99*5fff9558SSimon J. Gerraty 	}
100*5fff9558SSimon J. Gerraty 
101*5fff9558SSimon J. Gerraty 	/* we *should* only get called for files */
102*5fff9558SSimon J. Gerraty 	if (stp && !S_ISREG(stp->st_mode)) {
103*5fff9558SSimon J. Gerraty 		*error = 0;
104*5fff9558SSimon J. Gerraty 		return (NULL);
105*5fff9558SSimon J. Gerraty 	}
106*5fff9558SSimon J. Gerraty 
107*5fff9558SSimon J. Gerraty 	ctx = malloc(sizeof(struct vectx));
108*5fff9558SSimon J. Gerraty 	if (!ctx)
109*5fff9558SSimon J. Gerraty 		goto enomem;
110*5fff9558SSimon J. Gerraty 	ctx->vec_fd = fd;
111*5fff9558SSimon J. Gerraty 	ctx->vec_path = path;
112*5fff9558SSimon J. Gerraty 	ctx->vec_size = stp->st_size;
113*5fff9558SSimon J. Gerraty 	ctx->vec_off = 0;
114*5fff9558SSimon J. Gerraty 	ctx->vec_want = NULL;
115*5fff9558SSimon J. Gerraty 	ctx->vec_status = 0;
116*5fff9558SSimon J. Gerraty 	hashsz = 0;
117*5fff9558SSimon J. Gerraty 
118*5fff9558SSimon J. Gerraty 	cp = fingerprint_info_lookup(fd, path);
119*5fff9558SSimon J. Gerraty 	if (!cp) {
120*5fff9558SSimon J. Gerraty 		ctx->vec_status = VE_FINGERPRINT_NONE;
121*5fff9558SSimon J. Gerraty 		ve_error_set("%s: no entry", path);
122*5fff9558SSimon J. Gerraty 	} else {
123*5fff9558SSimon J. Gerraty 		if (strncmp(cp, "sha256=", 7) == 0) {
124*5fff9558SSimon J. Gerraty 			ctx->vec_md = &br_sha256_vtable;
125*5fff9558SSimon J. Gerraty 			hashsz = br_sha256_SIZE;
126*5fff9558SSimon J. Gerraty 			cp += 7;
127*5fff9558SSimon J. Gerraty #ifdef VE_SHA1_SUPPORT
128*5fff9558SSimon J. Gerraty 		} else if (strncmp(cp, "sha1=", 5) == 0) {
129*5fff9558SSimon J. Gerraty 			ctx->vec_md = &br_sha1_vtable;
130*5fff9558SSimon J. Gerraty 			hashsz = br_sha1_SIZE;
131*5fff9558SSimon J. Gerraty 			cp += 5;
132*5fff9558SSimon J. Gerraty #endif
133*5fff9558SSimon J. Gerraty #ifdef VE_SHA384_SUPPORT
134*5fff9558SSimon J. Gerraty 		} else if (strncmp(cp, "sha384=", 7) == 0) {
135*5fff9558SSimon J. Gerraty 		    ctx->vec_md = &br_sha384_vtable;
136*5fff9558SSimon J. Gerraty 		    hashsz = br_sha384_SIZE;
137*5fff9558SSimon J. Gerraty 		    cp += 7;
138*5fff9558SSimon J. Gerraty #endif
139*5fff9558SSimon J. Gerraty #ifdef VE_SHA512_SUPPORT
140*5fff9558SSimon J. Gerraty 		} else if (strncmp(cp, "sha512=", 7) == 0) {
141*5fff9558SSimon J. Gerraty 		    ctx->vec_md = &br_sha512_vtable;
142*5fff9558SSimon J. Gerraty 		    hashsz = br_sha512_SIZE;
143*5fff9558SSimon J. Gerraty 		    cp += 7;
144*5fff9558SSimon J. Gerraty #endif
145*5fff9558SSimon J. Gerraty 		} else {
146*5fff9558SSimon J. Gerraty 			ctx->vec_status = VE_FINGERPRINT_UNKNOWN;
147*5fff9558SSimon J. Gerraty 			ve_error_set("%s: no supported fingerprint", path);
148*5fff9558SSimon J. Gerraty 		}
149*5fff9558SSimon J. Gerraty 	}
150*5fff9558SSimon J. Gerraty 	*error = ctx->vec_status;
151*5fff9558SSimon J. Gerraty 	ctx->vec_hashsz = hashsz;
152*5fff9558SSimon J. Gerraty 	ctx->vec_want = cp;
153*5fff9558SSimon J. Gerraty 	ctx->vec_md->init(&ctx->vec_ctx.vtable);
154*5fff9558SSimon J. Gerraty 
155*5fff9558SSimon J. Gerraty 	if (hashsz > 0 && off > 0) {
156*5fff9558SSimon J. Gerraty 		lseek(fd, 0, SEEK_SET);
157*5fff9558SSimon J. Gerraty 		vectx_lseek(ctx, off, SEEK_SET);
158*5fff9558SSimon J. Gerraty 	}
159*5fff9558SSimon J. Gerraty 	return (ctx);
160*5fff9558SSimon J. Gerraty 
161*5fff9558SSimon J. Gerraty enomem:					/* unlikely */
162*5fff9558SSimon J. Gerraty 	*error = ENOMEM;
163*5fff9558SSimon J. Gerraty 	free(ctx);
164*5fff9558SSimon J. Gerraty 	return (NULL);
165*5fff9558SSimon J. Gerraty }
166*5fff9558SSimon J. Gerraty 
167*5fff9558SSimon J. Gerraty /**
168*5fff9558SSimon J. Gerraty  * @brief
169*5fff9558SSimon J. Gerraty  * read bytes from file and update hash
170*5fff9558SSimon J. Gerraty  *
171*5fff9558SSimon J. Gerraty  * It is critical that all file I/O comes through here.
172*5fff9558SSimon J. Gerraty  * We keep track of current offset.
173*5fff9558SSimon J. Gerraty  *
174*5fff9558SSimon J. Gerraty  * @param[in] pctx
175*5fff9558SSimon J. Gerraty  *	pointer to ctx
176*5fff9558SSimon J. Gerraty  *
177*5fff9558SSimon J. Gerraty  * @param[in] buf
178*5fff9558SSimon J. Gerraty  *
179*5fff9558SSimon J. Gerraty  * @param[in] nbytes
180*5fff9558SSimon J. Gerraty  *
181*5fff9558SSimon J. Gerraty  * @return bytes read or error.
182*5fff9558SSimon J. Gerraty  */
183*5fff9558SSimon J. Gerraty ssize_t
184*5fff9558SSimon J. Gerraty vectx_read(struct vectx *ctx, void *buf, size_t nbytes)
185*5fff9558SSimon J. Gerraty {
186*5fff9558SSimon J. Gerraty 	unsigned char *bp = buf;
187*5fff9558SSimon J. Gerraty 	int n;
188*5fff9558SSimon J. Gerraty 	size_t off;
189*5fff9558SSimon J. Gerraty 
190*5fff9558SSimon J. Gerraty 	if (ctx->vec_hashsz == 0)	/* nothing to do */
191*5fff9558SSimon J. Gerraty 		return (read(ctx->vec_fd, buf, nbytes));
192*5fff9558SSimon J. Gerraty 
193*5fff9558SSimon J. Gerraty 	off = 0;
194*5fff9558SSimon J. Gerraty 	do {
195*5fff9558SSimon J. Gerraty 		n = read(ctx->vec_fd, &bp[off], nbytes - off);
196*5fff9558SSimon J. Gerraty 		if (n < 0)
197*5fff9558SSimon J. Gerraty 			return (n);
198*5fff9558SSimon J. Gerraty 		if (n > 0) {
199*5fff9558SSimon J. Gerraty 			ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], n);
200*5fff9558SSimon J. Gerraty 			off += n;
201*5fff9558SSimon J. Gerraty 			ctx->vec_off += n;
202*5fff9558SSimon J. Gerraty 		}
203*5fff9558SSimon J. Gerraty 	} while (n > 0 && off < nbytes);
204*5fff9558SSimon J. Gerraty 	return (off);
205*5fff9558SSimon J. Gerraty }
206*5fff9558SSimon J. Gerraty 
207*5fff9558SSimon J. Gerraty /**
208*5fff9558SSimon J. Gerraty  * @brief
209*5fff9558SSimon J. Gerraty  * vectx equivalent of lseek
210*5fff9558SSimon J. Gerraty  *
211*5fff9558SSimon J. Gerraty  * We do not actually, seek, but call vectx_read
212*5fff9558SSimon J. Gerraty  * to reach the desired offset.
213*5fff9558SSimon J. Gerraty  *
214*5fff9558SSimon J. Gerraty  * We do not support seeking backwards.
215*5fff9558SSimon J. Gerraty  *
216*5fff9558SSimon J. Gerraty  * @param[in] pctx
217*5fff9558SSimon J. Gerraty  *	pointer to ctx
218*5fff9558SSimon J. Gerraty  *
219*5fff9558SSimon J. Gerraty  * @param[in] off
220*5fff9558SSimon J. Gerraty  *	desired offset
221*5fff9558SSimon J. Gerraty  *
222*5fff9558SSimon J. Gerraty  * @param[in] whence
223*5fff9558SSimon J. Gerraty  *
224*5fff9558SSimon J. Gerraty  * @return offset or error.
225*5fff9558SSimon J. Gerraty  */
226*5fff9558SSimon J. Gerraty off_t
227*5fff9558SSimon J. Gerraty vectx_lseek(struct vectx *ctx, off_t off, int whence)
228*5fff9558SSimon J. Gerraty {
229*5fff9558SSimon J. Gerraty 	unsigned char buf[PAGE_SIZE];
230*5fff9558SSimon J. Gerraty 	size_t delta;
231*5fff9558SSimon J. Gerraty 	ssize_t n;
232*5fff9558SSimon J. Gerraty 
233*5fff9558SSimon J. Gerraty 	if (ctx->vec_hashsz == 0)	/* nothing to do */
234*5fff9558SSimon J. Gerraty 		return (lseek(ctx->vec_fd, off, whence));
235*5fff9558SSimon J. Gerraty 
236*5fff9558SSimon J. Gerraty 	/*
237*5fff9558SSimon J. Gerraty 	 * Try to convert whence to SEEK_SET
238*5fff9558SSimon J. Gerraty 	 * but we cannot support seeking backwards!
239*5fff9558SSimon J. Gerraty 	 * Nor beyond end of file.
240*5fff9558SSimon J. Gerraty 	 */
241*5fff9558SSimon J. Gerraty 	if (whence == SEEK_END && off <= 0) {
242*5fff9558SSimon J. Gerraty 		whence = SEEK_SET;
243*5fff9558SSimon J. Gerraty 		off += ctx->vec_size;
244*5fff9558SSimon J. Gerraty 	} else if (whence == SEEK_CUR && off >= 0) {
245*5fff9558SSimon J. Gerraty 		whence = SEEK_SET;
246*5fff9558SSimon J. Gerraty 		off += ctx->vec_off;
247*5fff9558SSimon J. Gerraty 	}
248*5fff9558SSimon J. Gerraty 	if (whence != SEEK_SET || off < ctx->vec_off ||
249*5fff9558SSimon J. Gerraty 	    (size_t)off > ctx->vec_size) {
250*5fff9558SSimon J. Gerraty 		printf("ERROR: %s: unsupported operation\n",  __func__);
251*5fff9558SSimon J. Gerraty 		return (-1);
252*5fff9558SSimon J. Gerraty 	}
253*5fff9558SSimon J. Gerraty 	n = 0;
254*5fff9558SSimon J. Gerraty 	do {
255*5fff9558SSimon J. Gerraty 		delta = off - ctx->vec_off;
256*5fff9558SSimon J. Gerraty 		if (delta > 0) {
257*5fff9558SSimon J. Gerraty 			delta = MIN(PAGE_SIZE, delta);
258*5fff9558SSimon J. Gerraty 			n = vectx_read(ctx, buf, delta);
259*5fff9558SSimon J. Gerraty 			if (n < 0)
260*5fff9558SSimon J. Gerraty 				return (n);
261*5fff9558SSimon J. Gerraty 		}
262*5fff9558SSimon J. Gerraty 	} while (ctx->vec_off < off && n > 0);
263*5fff9558SSimon J. Gerraty 	return (ctx->vec_off);
264*5fff9558SSimon J. Gerraty }
265*5fff9558SSimon J. Gerraty 
266*5fff9558SSimon J. Gerraty /**
267*5fff9558SSimon J. Gerraty  * @brief
268*5fff9558SSimon J. Gerraty  * check that hashes match and cleanup
269*5fff9558SSimon J. Gerraty  *
270*5fff9558SSimon J. Gerraty  * We have finished reading file, compare the hash with what
271*5fff9558SSimon J. Gerraty  * we wanted.
272*5fff9558SSimon J. Gerraty  *
273*5fff9558SSimon J. Gerraty  * @param[in] pctx
274*5fff9558SSimon J. Gerraty  *	pointer to ctx
275*5fff9558SSimon J. Gerraty  *
276*5fff9558SSimon J. Gerraty  * @return 0 or an error.
277*5fff9558SSimon J. Gerraty  */
278*5fff9558SSimon J. Gerraty int
279*5fff9558SSimon J. Gerraty vectx_close(struct vectx *ctx)
280*5fff9558SSimon J. Gerraty {
281*5fff9558SSimon J. Gerraty 	int rc;
282*5fff9558SSimon J. Gerraty 
283*5fff9558SSimon J. Gerraty 	if (ctx->vec_hashsz == 0) {
284*5fff9558SSimon J. Gerraty 		rc = ctx->vec_status;
285*5fff9558SSimon J. Gerraty 	} else {
286*5fff9558SSimon J. Gerraty 		rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md,
287*5fff9558SSimon J. Gerraty 		    ctx->vec_path, ctx->vec_want, ctx->vec_hashsz);
288*5fff9558SSimon J. Gerraty 	}
289*5fff9558SSimon J. Gerraty 	free(ctx);
290*5fff9558SSimon J. Gerraty 	return ((rc < 0) ? rc : 0);
291*5fff9558SSimon J. Gerraty }
292