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