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