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 37 /** 38 * @file vectx.c 39 * @brief api to verify file while reading 40 * 41 * This API allows the hash of a file to be computed as it is read. 42 * Key to this is seeking by reading. 43 * 44 * On close an indication of the verification result is returned. 45 */ 46 47 struct vectx { 48 br_hash_compat_context vec_ctx; /* hash ctx */ 49 const br_hash_class *vec_md; /* hash method */ 50 const char *vec_path; /* path we are verifying */ 51 const char *vec_want; /* hash value we want */ 52 off_t vec_off; /* current offset */ 53 size_t vec_size; /* size of path */ 54 size_t vec_hashsz; /* size of hash */ 55 int vec_fd; /* file descriptor */ 56 int vec_status; /* verification status */ 57 }; 58 59 /** 60 * @brief 61 * verify an open file as we read it 62 * 63 * If the file has no fingerprint to match, we will still return a 64 * verification context containing little more than the file 65 * descriptor, and an error code in @c error. 66 * 67 * @param[in] fd 68 * open descriptor 69 * 70 * @param[in] path 71 * pathname to open 72 * 73 * @param[in] off 74 * current offset 75 * 76 * @param[in] stp 77 * pointer to struct stat 78 * 79 * @param[out] error 80 * @li 0 all is good 81 * @li ENOMEM out of memory 82 * @li VE_FINGERPRINT_NONE no entry found 83 * @li VE_FINGERPRINT_UNKNOWN no fingerprint in entry 84 * 85 * @return ctx or NULL on error. 86 * NULL is only returned for non-files or out-of-memory. 87 */ 88 struct vectx * 89 vectx_open(int fd, const char *path, off_t off, struct stat *stp, int *error) 90 { 91 struct vectx *ctx; 92 struct stat st; 93 size_t hashsz; 94 char *cp; 95 96 if (!stp) { 97 if (fstat(fd, &st) == 0) 98 stp = &st; 99 } 100 101 /* we *should* only get called for files */ 102 if (stp && !S_ISREG(stp->st_mode)) { 103 *error = 0; 104 return (NULL); 105 } 106 107 ctx = malloc(sizeof(struct vectx)); 108 if (!ctx) 109 goto enomem; 110 ctx->vec_fd = fd; 111 ctx->vec_path = path; 112 ctx->vec_size = stp->st_size; 113 ctx->vec_off = 0; 114 ctx->vec_want = NULL; 115 ctx->vec_status = 0; 116 hashsz = 0; 117 118 cp = fingerprint_info_lookup(fd, path); 119 if (!cp) { 120 ctx->vec_status = VE_FINGERPRINT_NONE; 121 ve_error_set("%s: no entry", path); 122 } else { 123 if (strncmp(cp, "no_hash", 7) == 0) { 124 ctx->vec_status = VE_FINGERPRINT_IGNORE; 125 hashsz = 0; 126 } else if (strncmp(cp, "sha256=", 7) == 0) { 127 ctx->vec_md = &br_sha256_vtable; 128 hashsz = br_sha256_SIZE; 129 cp += 7; 130 #ifdef VE_SHA1_SUPPORT 131 } else if (strncmp(cp, "sha1=", 5) == 0) { 132 ctx->vec_md = &br_sha1_vtable; 133 hashsz = br_sha1_SIZE; 134 cp += 5; 135 #endif 136 #ifdef VE_SHA384_SUPPORT 137 } else if (strncmp(cp, "sha384=", 7) == 0) { 138 ctx->vec_md = &br_sha384_vtable; 139 hashsz = br_sha384_SIZE; 140 cp += 7; 141 #endif 142 #ifdef VE_SHA512_SUPPORT 143 } else if (strncmp(cp, "sha512=", 7) == 0) { 144 ctx->vec_md = &br_sha512_vtable; 145 hashsz = br_sha512_SIZE; 146 cp += 7; 147 #endif 148 } else { 149 ctx->vec_status = VE_FINGERPRINT_UNKNOWN; 150 ve_error_set("%s: no supported fingerprint", path); 151 } 152 } 153 *error = ctx->vec_status; 154 ctx->vec_hashsz = hashsz; 155 ctx->vec_want = cp; 156 if (hashsz > 0) { 157 ctx->vec_md->init(&ctx->vec_ctx.vtable); 158 159 if (off > 0) { 160 lseek(fd, 0, SEEK_SET); 161 vectx_lseek(ctx, off, SEEK_SET); 162 } 163 } 164 return (ctx); 165 166 enomem: /* unlikely */ 167 *error = ENOMEM; 168 free(ctx); 169 return (NULL); 170 } 171 172 /** 173 * @brief 174 * read bytes from file and update hash 175 * 176 * It is critical that all file I/O comes through here. 177 * We keep track of current offset. 178 * 179 * @param[in] pctx 180 * pointer to ctx 181 * 182 * @param[in] buf 183 * 184 * @param[in] nbytes 185 * 186 * @return bytes read or error. 187 */ 188 ssize_t 189 vectx_read(struct vectx *ctx, void *buf, size_t nbytes) 190 { 191 unsigned char *bp = buf; 192 int n; 193 size_t off; 194 195 if (ctx->vec_hashsz == 0) /* nothing to do */ 196 return (read(ctx->vec_fd, buf, nbytes)); 197 198 off = 0; 199 do { 200 n = read(ctx->vec_fd, &bp[off], nbytes - off); 201 if (n < 0) 202 return (n); 203 if (n > 0) { 204 ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], n); 205 off += n; 206 ctx->vec_off += n; 207 } 208 } while (n > 0 && off < nbytes); 209 return (off); 210 } 211 212 /** 213 * @brief 214 * vectx equivalent of lseek 215 * 216 * We do not actually, seek, but call vectx_read 217 * to reach the desired offset. 218 * 219 * We do not support seeking backwards. 220 * 221 * @param[in] pctx 222 * pointer to ctx 223 * 224 * @param[in] off 225 * desired offset 226 * 227 * @param[in] whence 228 * 229 * @return offset or error. 230 */ 231 off_t 232 vectx_lseek(struct vectx *ctx, off_t off, int whence) 233 { 234 unsigned char buf[PAGE_SIZE]; 235 size_t delta; 236 ssize_t n; 237 238 if (ctx->vec_hashsz == 0) /* nothing to do */ 239 return (lseek(ctx->vec_fd, off, whence)); 240 241 /* 242 * Try to convert whence to SEEK_SET 243 * but we cannot support seeking backwards! 244 * Nor beyond end of file. 245 */ 246 if (whence == SEEK_END && off <= 0) { 247 whence = SEEK_SET; 248 off += ctx->vec_size; 249 } else if (whence == SEEK_CUR && off >= 0) { 250 whence = SEEK_SET; 251 off += ctx->vec_off; 252 } 253 if (whence != SEEK_SET || off < ctx->vec_off || 254 (size_t)off > ctx->vec_size) { 255 printf("ERROR: %s: unsupported operation\n", __func__); 256 return (-1); 257 } 258 n = 0; 259 do { 260 delta = off - ctx->vec_off; 261 if (delta > 0) { 262 delta = MIN(PAGE_SIZE, delta); 263 n = vectx_read(ctx, buf, delta); 264 if (n < 0) 265 return (n); 266 } 267 } while (ctx->vec_off < off && n > 0); 268 return (ctx->vec_off); 269 } 270 271 /** 272 * @brief 273 * check that hashes match and cleanup 274 * 275 * We have finished reading file, compare the hash with what 276 * we wanted. 277 * 278 * @param[in] pctx 279 * pointer to ctx 280 * 281 * @return 0 or an error. 282 */ 283 int 284 vectx_close(struct vectx *ctx) 285 { 286 int rc; 287 288 if (ctx->vec_hashsz == 0) { 289 rc = ctx->vec_status; 290 } else { 291 rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md, 292 ctx->vec_path, ctx->vec_want, ctx->vec_hashsz); 293 } 294 free(ctx); 295 return ((rc < 0) ? rc : 0); 296 } 297