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 #ifndef _STANDALONE 27 /* Avoid unwanted userlandish components */ 28 #define _KERNEL 29 #include <sys/errno.h> 30 #undef _KERNEL 31 #endif 32 33 #ifdef VECTX_DEBUG 34 static int vectx_debug = VECTX_DEBUG; 35 # define DEBUG_PRINTF(n, x) if (vectx_debug >= n) printf x 36 #endif 37 38 #include "libsecureboot-priv.h" 39 #include <verify_file.h> 40 41 /** 42 * @file vectx.c 43 * @brief api to verify file while reading 44 * 45 * This API allows the hash of a file to be computed as it is read. 46 * Key to this is seeking by reading. 47 * 48 * On close an indication of the verification result is returned. 49 */ 50 51 struct vectx { 52 br_hash_compat_context vec_ctx; /* hash ctx */ 53 const br_hash_class *vec_md; /* hash method */ 54 const char *vec_path; /* path we are verifying */ 55 const char *vec_want; /* hash value we want */ 56 off_t vec_off; /* current offset */ 57 off_t vec_hashed; /* where we have hashed to */ 58 off_t vec_size; /* size of path */ 59 size_t vec_hashsz; /* size of hash */ 60 int vec_fd; /* file descriptor */ 61 int vec_status; /* verification status */ 62 int vec_closing; /* we are closing */ 63 int vec_severity; /* usually VE_MUST */ 64 }; 65 66 67 /** 68 * @brief 69 * verify an open file as we read it 70 * 71 * If the file has no fingerprint to match, we will still return a 72 * verification context containing little more than the file 73 * descriptor, and an error code in @c error. 74 * 75 * @param[in] fd 76 * open descriptor 77 * 78 * @param[in] path 79 * pathname to open 80 * 81 * @param[in] off 82 * current offset 83 * 84 * @param[in] stp 85 * pointer to struct stat 86 * 87 * @param[out] error 88 * @li 0 all is good 89 * @li ENOMEM out of memory 90 * @li VE_FINGERPRINT_NONE no entry found 91 * @li VE_FINGERPRINT_UNKNOWN no fingerprint in entry 92 * 93 * @return ctx or NULL on error. 94 * NULL is only returned for non-files or out-of-memory. 95 */ 96 struct vectx * 97 vectx_open(int fd, const char *path, int severity, 98 off_t off, struct stat *stp, 99 int *error, const char *caller) 100 { 101 struct vectx *ctx; 102 struct stat st; 103 size_t hashsz; 104 char *cp; 105 int rc; 106 107 if (!stp) 108 stp = &st; 109 110 rc = verify_prep(fd, path, off, stp, __func__); 111 if (severity == VE_GUESS) 112 severity = severity_guess(path); 113 114 DEBUG_PRINTF(2, 115 ("vectx_open: caller=%s,fd=%d,name='%s',prep_rc=%d,severity=%d\n", 116 caller, fd, path, rc, severity)); 117 118 switch (rc) { 119 case VE_FINGERPRINT_NONE: 120 case VE_FINGERPRINT_UNKNOWN: 121 if (severity < VE_MUST) 122 break; 123 /* FALLTHROUGH */ 124 case VE_FINGERPRINT_WRONG: 125 *error = rc; 126 return (NULL); 127 } 128 ctx = malloc(sizeof(struct vectx)); 129 if (!ctx) 130 goto enomem; 131 ctx->vec_fd = fd; 132 ctx->vec_path = path; 133 ctx->vec_size = stp->st_size; 134 ctx->vec_off = 0; 135 ctx->vec_hashed = 0; 136 ctx->vec_want = NULL; 137 ctx->vec_severity = severity; 138 ctx->vec_status = 0; 139 ctx->vec_hashsz = hashsz = 0; 140 ctx->vec_closing = 0; 141 142 if (rc == VE_UNVERIFIED_OK) { 143 /* we are not verifying this */ 144 *error = 0; 145 return (ctx); 146 } 147 cp = fingerprint_info_lookup(fd, path); 148 if (!cp) { 149 if (severity < VE_MUST) 150 ctx->vec_status = VE_UNVERIFIED_OK; 151 else { 152 ctx->vec_status = VE_FINGERPRINT_NONE; 153 ve_error_set("%s: no entry", path); 154 } 155 } else { 156 if (strncmp(cp, "no_hash", 7) == 0) { 157 ctx->vec_status = VE_FINGERPRINT_IGNORE; 158 hashsz = 0; 159 } else if (strncmp(cp, "sha256=", 7) == 0) { 160 ctx->vec_md = &br_sha256_vtable; 161 hashsz = br_sha256_SIZE; 162 cp += 7; 163 #ifdef VE_SHA1_SUPPORT 164 } else if (strncmp(cp, "sha1=", 5) == 0) { 165 ctx->vec_md = &br_sha1_vtable; 166 hashsz = br_sha1_SIZE; 167 cp += 5; 168 #endif 169 #ifdef VE_SHA384_SUPPORT 170 } else if (strncmp(cp, "sha384=", 7) == 0) { 171 ctx->vec_md = &br_sha384_vtable; 172 hashsz = br_sha384_SIZE; 173 cp += 7; 174 #endif 175 #ifdef VE_SHA512_SUPPORT 176 } else if (strncmp(cp, "sha512=", 7) == 0) { 177 ctx->vec_md = &br_sha512_vtable; 178 hashsz = br_sha512_SIZE; 179 cp += 7; 180 #endif 181 } else { 182 if (severity < VE_MUST) 183 ctx->vec_status = VE_UNVERIFIED_OK; 184 else { 185 ctx->vec_status = VE_FINGERPRINT_UNKNOWN; 186 ve_error_set("%s: no supported fingerprint", path); 187 } 188 } 189 } 190 *error = ctx->vec_status; 191 ctx->vec_hashsz = hashsz; 192 ctx->vec_want = cp; 193 if (hashsz > 0) { 194 ctx->vec_md->init(&ctx->vec_ctx.vtable); 195 196 if (off > 0) { 197 lseek(fd, 0, SEEK_SET); 198 vectx_lseek(ctx, off, SEEK_SET); 199 } 200 } 201 DEBUG_PRINTF(2, 202 ("vectx_open: caller=%s,name='%s',hashsz=%lu,severity=%d,status=%d\n", 203 caller, path, (unsigned long)ctx->vec_hashsz, 204 severity, ctx->vec_status)); 205 return (ctx); 206 207 enomem: /* unlikely */ 208 *error = ENOMEM; 209 free(ctx); 210 return (NULL); 211 } 212 213 /** 214 * @brief 215 * read bytes from file and update hash 216 * 217 * It is critical that all file I/O comes through here. 218 * We keep track of current offset. 219 * We also track what offset we have hashed to, 220 * so we won't replay data if we seek backwards. 221 * 222 * @param[in] pctx 223 * pointer to ctx 224 * 225 * @param[in] buf 226 * 227 * @param[in] nbytes 228 * 229 * @return bytes read or error. 230 */ 231 ssize_t 232 vectx_read(struct vectx *ctx, void *buf, size_t nbytes) 233 { 234 unsigned char *bp = buf; 235 int d; 236 int n; 237 int delta; 238 int x; 239 size_t off; 240 241 if (ctx->vec_hashsz == 0) /* nothing to do */ 242 return (read(ctx->vec_fd, buf, nbytes)); 243 244 off = 0; 245 do { 246 /* 247 * Do this in reasonable chunks so 248 * we don't timeout if doing tftp 249 */ 250 x = nbytes - off; 251 x = MIN(PAGE_SIZE, x); 252 d = n = read(ctx->vec_fd, &bp[off], x); 253 if (ctx->vec_closing && n < x) { 254 DEBUG_PRINTF(3, 255 ("%s: read %d off=%ld hashed=%ld size=%ld\n", 256 __func__, n, (long)ctx->vec_off, 257 (long)ctx->vec_hashed, (long)ctx->vec_size)); 258 } 259 if (n < 0) { 260 return (n); 261 } 262 if (d > 0) { 263 /* we may have seeked backwards! */ 264 delta = ctx->vec_hashed - ctx->vec_off; 265 if (delta > 0) { 266 x = MIN(delta, d); 267 off += x; 268 d -= x; 269 ctx->vec_off += x; 270 } 271 if (d > 0) { 272 if (ctx->vec_closing && d < PAGE_SIZE) { 273 DEBUG_PRINTF(3, 274 ("%s: update %ld + %d\n", 275 __func__, 276 (long)ctx->vec_hashed, d)); 277 } 278 ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], d); 279 off += d; 280 ctx->vec_off += d; 281 ctx->vec_hashed += d; 282 } 283 } 284 } while (n > 0 && off < nbytes); 285 return (off); 286 } 287 288 /** 289 * @brief 290 * vectx equivalent of lseek 291 * 292 * When seeking forwards we actually call vectx_read 293 * to reach the desired offset. 294 * 295 * We support seeking backwards. 296 * 297 * @param[in] pctx 298 * pointer to ctx 299 * 300 * @param[in] off 301 * desired offset 302 * 303 * @param[in] whence 304 * We try to convert whence to ``SEEK_SET``. 305 * We do not support ``SEEK_DATA`` or ``SEEK_HOLE``. 306 * 307 * @return offset or error. 308 */ 309 off_t 310 vectx_lseek(struct vectx *ctx, off_t off, int whence) 311 { 312 unsigned char buf[PAGE_SIZE]; 313 size_t delta; 314 ssize_t n; 315 316 if (ctx->vec_hashsz == 0) /* nothing to do */ 317 return (lseek(ctx->vec_fd, off, whence)); 318 319 /* 320 * Convert whence to SEEK_SET 321 */ 322 DEBUG_PRINTF(3, 323 ("%s(%s, %ld, %d)\n", __func__, ctx->vec_path, (long)off, whence)); 324 if (whence == SEEK_END && off <= 0) { 325 if (ctx->vec_size < 0) { 326 if (ctx->vec_closing) { 327 /* size unknown - read until EOF */ 328 do { 329 n = vectx_read(ctx, buf, PAGE_SIZE); 330 if (n < 0) 331 return (n); 332 } while (n > 0); 333 return (ctx->vec_off); 334 } 335 } else { 336 if (ctx->vec_closing && ctx->vec_hashed < ctx->vec_size) { 337 DEBUG_PRINTF(3, ("%s: SEEK_END %ld\n", 338 __func__, 339 (long)(ctx->vec_size - ctx->vec_hashed))); 340 } 341 whence = SEEK_SET; 342 off += ctx->vec_size; 343 } 344 } else if (whence == SEEK_CUR) { 345 whence = SEEK_SET; 346 off += ctx->vec_off; 347 } 348 if (whence != SEEK_SET || 349 (off > ctx->vec_size && ctx->vec_size > 0)) { 350 printf("ERROR: %s: unsupported operation: whence=%d off=%ld -> %ld\n", 351 __func__, whence, (long)ctx->vec_off, (long)off); 352 return (-1); 353 } 354 if (off < ctx->vec_hashed) { 355 #ifdef _STANDALONE 356 struct open_file *f = fd2open_file(ctx->vec_fd); 357 358 if (f != NULL && 359 strncmp(f->f_ops->fs_name, "tftp", 4) == 0) { 360 /* we cannot rewind if we've hashed much of the file */ 361 if (ctx->vec_hashed > ctx->vec_size / 5) 362 return (-1); /* refuse! */ 363 } 364 #endif 365 /* seeking backwards! just do it */ 366 ctx->vec_off = lseek(ctx->vec_fd, off, whence); 367 return (ctx->vec_off); 368 } 369 n = 0; 370 do { 371 delta = off - ctx->vec_off; 372 if (delta > 0) { 373 delta = MIN(PAGE_SIZE, delta); 374 n = vectx_read(ctx, buf, delta); 375 if (n < 0) 376 return (n); 377 } 378 } while (ctx->vec_off < off && n > 0); 379 return (ctx->vec_off); 380 } 381 382 /** 383 * @brief 384 * check that hashes match and cleanup 385 * 386 * We have finished reading file, compare the hash with what 387 * we wanted. 388 * 389 * Be sure to call this before closing the file, since we may 390 * need to seek to the end to ensure hashing is complete. 391 * 392 * @param[in] pctx 393 * pointer to ctx 394 * 395 * @return 0 or an error. 396 */ 397 int 398 vectx_close(struct vectx *ctx, const char *caller) 399 { 400 int rc; 401 402 ctx->vec_closing = 1; 403 if (ctx->vec_hashsz == 0) { 404 rc = ctx->vec_status; 405 } else { 406 #ifdef VE_PCR_SUPPORT 407 /* 408 * Only update pcr with things that must verify 409 * these tend to be processed in a more deterministic 410 * order, which makes our pseudo pcr more useful. 411 */ 412 ve_pcr_updating_set((ctx->vec_severity == VE_MUST)); 413 #endif 414 /* make sure we have hashed it all */ 415 vectx_lseek(ctx, 0, SEEK_END); 416 rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md, 417 ctx->vec_path, ctx->vec_want, ctx->vec_hashsz); 418 } 419 DEBUG_PRINTF(2, 420 ("vectx_close: caller=%s,name='%s',severity=%d,rc=%d\n", 421 caller,ctx->vec_path, ctx->vec_severity, rc)); 422 verify_report(ctx->vec_path, ctx->vec_severity, rc, NULL); 423 if (rc == VE_FINGERPRINT_WRONG) { 424 #if !defined(UNIT_TEST) && !defined(DEBUG_VECTX) 425 /* we are generally called with VE_MUST */ 426 if (ctx->vec_severity > VE_WANT) 427 panic("cannot continue"); 428 #endif 429 } 430 free(ctx); 431 return ((rc < 0) ? rc : 0); 432 } 433