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