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 n; 215 int delta; 216 int x; 217 size_t off; 218 219 if (ctx->vec_hashsz == 0) /* nothing to do */ 220 return (read(ctx->vec_fd, buf, nbytes)); 221 222 off = 0; 223 do { 224 n = read(ctx->vec_fd, &bp[off], nbytes - off); 225 if (n < 0) 226 return (n); 227 if (n > 0) { 228 /* we may have seeked backwards! */ 229 delta = ctx->vec_hashed - ctx->vec_off; 230 if (delta > 0) { 231 x = MIN(delta, n); 232 off += x; 233 n -= x; 234 ctx->vec_off += x; 235 } 236 if (n > 0) { 237 ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], n); 238 off += n; 239 ctx->vec_off += n; 240 ctx->vec_hashed += n; 241 } 242 } 243 } while (n > 0 && off < nbytes); 244 return (off); 245 } 246 247 /** 248 * @brief 249 * vectx equivalent of lseek 250 * 251 * When seeking forwards we actually call vectx_read 252 * to reach the desired offset. 253 * 254 * We support seeking backwards. 255 * 256 * @param[in] pctx 257 * pointer to ctx 258 * 259 * @param[in] off 260 * desired offset 261 * 262 * @param[in] whence 263 * We try to convert whence to ``SEEK_SET``. 264 * We do not support ``SEEK_DATA`` or ``SEEK_HOLE``. 265 * 266 * @return offset or error. 267 */ 268 off_t 269 vectx_lseek(struct vectx *ctx, off_t off, int whence) 270 { 271 unsigned char buf[PAGE_SIZE]; 272 size_t delta; 273 ssize_t n; 274 275 if (ctx->vec_hashsz == 0) /* nothing to do */ 276 return (lseek(ctx->vec_fd, off, whence)); 277 278 /* 279 * Convert whence to SEEK_SET 280 */ 281 if (whence == SEEK_END && off <= 0) { 282 whence = SEEK_SET; 283 off += ctx->vec_size; 284 } else if (whence == SEEK_CUR) { 285 whence = SEEK_SET; 286 off += ctx->vec_off; 287 } 288 if (whence != SEEK_SET || 289 (size_t)off > ctx->vec_size) { 290 printf("ERROR: %s: unsupported operation: whence=%d off=%lld -> %lld\n", 291 __func__, whence, (long long)ctx->vec_off, (long long)off); 292 return (-1); 293 } 294 if (off < ctx->vec_hashed) { 295 /* seeking backwards! just do it */ 296 ctx->vec_off = lseek(ctx->vec_fd, off, whence); 297 return (ctx->vec_off); 298 } 299 n = 0; 300 do { 301 delta = off - ctx->vec_off; 302 if (delta > 0) { 303 delta = MIN(PAGE_SIZE, delta); 304 n = vectx_read(ctx, buf, delta); 305 if (n < 0) 306 return (n); 307 } 308 } while (ctx->vec_off < off && n > 0); 309 return (ctx->vec_off); 310 } 311 312 /** 313 * @brief 314 * check that hashes match and cleanup 315 * 316 * We have finished reading file, compare the hash with what 317 * we wanted. 318 * 319 * Be sure to call this before closing the file, since we may 320 * need to seek to the end to ensure hashing is complete. 321 * 322 * @param[in] pctx 323 * pointer to ctx 324 * 325 * @return 0 or an error. 326 */ 327 int 328 vectx_close(struct vectx *ctx, int severity, const char *caller) 329 { 330 int rc; 331 332 if (ctx->vec_hashsz == 0) { 333 rc = ctx->vec_status; 334 } else { 335 #ifdef VE_PCR_SUPPORT 336 /* 337 * Only update pcr with things that must verify 338 * these tend to be processed in a more deterministic 339 * order, which makes our pseudo pcr more useful. 340 */ 341 ve_pcr_updating_set((severity == VE_MUST)); 342 #endif 343 /* make sure we have hashed it all */ 344 vectx_lseek(ctx, 0, SEEK_END); 345 rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md, 346 ctx->vec_path, ctx->vec_want, ctx->vec_hashsz); 347 } 348 DEBUG_PRINTF(2, 349 ("vectx_close: caller=%s,name='%s',rc=%d,severity=%d\n", 350 caller,ctx->vec_path, rc, severity)); 351 if (rc == VE_FINGERPRINT_WRONG) { 352 printf("Unverified: %s\n", ve_error_get()); 353 #if !defined(UNIT_TEST) && !defined(DEBUG_VECTX) 354 /* we are generally called with VE_MUST */ 355 if (severity > VE_WANT) 356 panic("cannot continue"); 357 #endif 358 } else if (severity > VE_WANT) { 359 printf("%serified %s\n", (rc <= 0) ? "Unv" : "V", 360 ctx->vec_path); 361 } 362 free(ctx); 363 return ((rc < 0) ? rc : 0); 364 } 365