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 37 /** 38 * @file vectx.c 39 * @brief api to verify file while reading 40 * 41 * This API allows the hash of a file to be computed as it is read. 42 * Key to this is seeking by reading. 43 * 44 * On close an indication of the verification result is returned. 45 */ 46 47 struct vectx { 48 br_hash_compat_context vec_ctx; /* hash ctx */ 49 const br_hash_class *vec_md; /* hash method */ 50 const char *vec_path; /* path we are verifying */ 51 const char *vec_want; /* hash value we want */ 52 off_t vec_off; /* current offset */ 53 size_t vec_size; /* size of path */ 54 size_t vec_hashsz; /* size of hash */ 55 int vec_fd; /* file descriptor */ 56 int vec_status; /* verification status */ 57 }; 58 59 /** 60 * @brief 61 * verify an open file as we read it 62 * 63 * If the file has no fingerprint to match, we will still return a 64 * verification context containing little more than the file 65 * descriptor, and an error code in @c error. 66 * 67 * @param[in] fd 68 * open descriptor 69 * 70 * @param[in] path 71 * pathname to open 72 * 73 * @param[in] off 74 * current offset 75 * 76 * @param[in] stp 77 * pointer to struct stat 78 * 79 * @param[out] error 80 * @li 0 all is good 81 * @li ENOMEM out of memory 82 * @li VE_FINGERPRINT_NONE no entry found 83 * @li VE_FINGERPRINT_UNKNOWN no fingerprint in entry 84 * 85 * @return ctx or NULL on error. 86 * NULL is only returned for non-files or out-of-memory. 87 */ 88 struct vectx * 89 vectx_open(int fd, const char *path, off_t off, struct stat *stp, int *error) 90 { 91 struct vectx *ctx; 92 struct stat st; 93 size_t hashsz; 94 char *cp; 95 96 if (!stp) { 97 if (fstat(fd, &st) == 0) 98 stp = &st; 99 } 100 101 /* we *should* only get called for files */ 102 if (stp && !S_ISREG(stp->st_mode)) { 103 *error = 0; 104 return (NULL); 105 } 106 107 ctx = malloc(sizeof(struct vectx)); 108 if (!ctx) 109 goto enomem; 110 ctx->vec_fd = fd; 111 ctx->vec_path = path; 112 ctx->vec_size = stp->st_size; 113 ctx->vec_off = 0; 114 ctx->vec_want = NULL; 115 ctx->vec_status = 0; 116 hashsz = 0; 117 118 cp = fingerprint_info_lookup(fd, path); 119 if (!cp) { 120 ctx->vec_status = VE_FINGERPRINT_NONE; 121 ve_error_set("%s: no entry", path); 122 } else { 123 if (strncmp(cp, "sha256=", 7) == 0) { 124 ctx->vec_md = &br_sha256_vtable; 125 hashsz = br_sha256_SIZE; 126 cp += 7; 127 #ifdef VE_SHA1_SUPPORT 128 } else if (strncmp(cp, "sha1=", 5) == 0) { 129 ctx->vec_md = &br_sha1_vtable; 130 hashsz = br_sha1_SIZE; 131 cp += 5; 132 #endif 133 #ifdef VE_SHA384_SUPPORT 134 } else if (strncmp(cp, "sha384=", 7) == 0) { 135 ctx->vec_md = &br_sha384_vtable; 136 hashsz = br_sha384_SIZE; 137 cp += 7; 138 #endif 139 #ifdef VE_SHA512_SUPPORT 140 } else if (strncmp(cp, "sha512=", 7) == 0) { 141 ctx->vec_md = &br_sha512_vtable; 142 hashsz = br_sha512_SIZE; 143 cp += 7; 144 #endif 145 } else { 146 ctx->vec_status = VE_FINGERPRINT_UNKNOWN; 147 ve_error_set("%s: no supported fingerprint", path); 148 } 149 } 150 *error = ctx->vec_status; 151 ctx->vec_hashsz = hashsz; 152 ctx->vec_want = cp; 153 ctx->vec_md->init(&ctx->vec_ctx.vtable); 154 155 if (hashsz > 0 && off > 0) { 156 lseek(fd, 0, SEEK_SET); 157 vectx_lseek(ctx, off, SEEK_SET); 158 } 159 return (ctx); 160 161 enomem: /* unlikely */ 162 *error = ENOMEM; 163 free(ctx); 164 return (NULL); 165 } 166 167 /** 168 * @brief 169 * read bytes from file and update hash 170 * 171 * It is critical that all file I/O comes through here. 172 * We keep track of current offset. 173 * 174 * @param[in] pctx 175 * pointer to ctx 176 * 177 * @param[in] buf 178 * 179 * @param[in] nbytes 180 * 181 * @return bytes read or error. 182 */ 183 ssize_t 184 vectx_read(struct vectx *ctx, void *buf, size_t nbytes) 185 { 186 unsigned char *bp = buf; 187 int n; 188 size_t off; 189 190 if (ctx->vec_hashsz == 0) /* nothing to do */ 191 return (read(ctx->vec_fd, buf, nbytes)); 192 193 off = 0; 194 do { 195 n = read(ctx->vec_fd, &bp[off], nbytes - off); 196 if (n < 0) 197 return (n); 198 if (n > 0) { 199 ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], n); 200 off += n; 201 ctx->vec_off += n; 202 } 203 } while (n > 0 && off < nbytes); 204 return (off); 205 } 206 207 /** 208 * @brief 209 * vectx equivalent of lseek 210 * 211 * We do not actually, seek, but call vectx_read 212 * to reach the desired offset. 213 * 214 * We do not support seeking backwards. 215 * 216 * @param[in] pctx 217 * pointer to ctx 218 * 219 * @param[in] off 220 * desired offset 221 * 222 * @param[in] whence 223 * 224 * @return offset or error. 225 */ 226 off_t 227 vectx_lseek(struct vectx *ctx, off_t off, int whence) 228 { 229 unsigned char buf[PAGE_SIZE]; 230 size_t delta; 231 ssize_t n; 232 233 if (ctx->vec_hashsz == 0) /* nothing to do */ 234 return (lseek(ctx->vec_fd, off, whence)); 235 236 /* 237 * Try to convert whence to SEEK_SET 238 * but we cannot support seeking backwards! 239 * Nor beyond end of file. 240 */ 241 if (whence == SEEK_END && off <= 0) { 242 whence = SEEK_SET; 243 off += ctx->vec_size; 244 } else if (whence == SEEK_CUR && off >= 0) { 245 whence = SEEK_SET; 246 off += ctx->vec_off; 247 } 248 if (whence != SEEK_SET || off < ctx->vec_off || 249 (size_t)off > ctx->vec_size) { 250 printf("ERROR: %s: unsupported operation\n", __func__); 251 return (-1); 252 } 253 n = 0; 254 do { 255 delta = off - ctx->vec_off; 256 if (delta > 0) { 257 delta = MIN(PAGE_SIZE, delta); 258 n = vectx_read(ctx, buf, delta); 259 if (n < 0) 260 return (n); 261 } 262 } while (ctx->vec_off < off && n > 0); 263 return (ctx->vec_off); 264 } 265 266 /** 267 * @brief 268 * check that hashes match and cleanup 269 * 270 * We have finished reading file, compare the hash with what 271 * we wanted. 272 * 273 * @param[in] pctx 274 * pointer to ctx 275 * 276 * @return 0 or an error. 277 */ 278 int 279 vectx_close(struct vectx *ctx) 280 { 281 int rc; 282 283 if (ctx->vec_hashsz == 0) { 284 rc = ctx->vec_status; 285 } else { 286 rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md, 287 ctx->vec_path, ctx->vec_want, ctx->vec_hashsz); 288 } 289 free(ctx); 290 return ((rc < 0) ? rc : 0); 291 } 292