1 /*- 2 * Copyright (c) 2019 Stormshield. 3 * Copyright (c) 2019 Semihalf. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/ctype.h> 32 #include <sys/eventhandler.h> 33 #include <sys/fcntl.h> 34 #include <sys/lock.h> 35 #include <sys/module.h> 36 #include <sys/mutex.h> 37 #include <sys/namei.h> 38 #include <sys/proc.h> 39 #include <sys/systm.h> 40 #include <sys/vnode.h> 41 42 #include <crypto/sha2/sha256.h> 43 #include <crypto/sha2/sha384.h> 44 #include <crypto/sha2/sha512.h> 45 46 #include <security/mac_veriexec/mac_veriexec.h> 47 #include <security/mac_veriexec/mac_veriexec_internal.h> 48 49 /* The following are based on sbin/veriexec */ 50 struct fingerprint_type { 51 const char *fp_type; 52 int fp_size; 53 }; 54 55 struct fp_flag { 56 const char *flag_name; 57 int flag; 58 }; 59 60 static const struct fingerprint_type fp_table[] = { 61 {"sha256=", SHA256_DIGEST_LENGTH}, 62 #if MAXFINGERPRINTLEN >= SHA384_DIGEST_LENGTH 63 {"sha384=", SHA384_DIGEST_LENGTH}, 64 #endif 65 #if MAXFINGERPRINTLEN >= SHA512_DIGEST_LENGTH 66 {"sha512=", SHA512_DIGEST_LENGTH}, 67 #endif 68 {NULL, 0} 69 }; 70 71 static const struct fp_flag flags_table[] = { 72 {"indirect", VERIEXEC_INDIRECT}, 73 {"no_ptrace", VERIEXEC_NOTRACE}, 74 {"trusted", VERIEXEC_TRUSTED}, 75 {"no_fips", VERIEXEC_NOFIPS}, 76 {NULL, 0} 77 }; 78 79 extern struct mtx ve_mutex; 80 81 static unsigned char hexchar_to_byte(unsigned char c); 82 static int hexstring_to_bin(unsigned char *buf); 83 84 static int get_flags(const char *entry); 85 static int get_fp(const char *entry, char **type, 86 unsigned char **digest, int *flags); 87 static int verify_digest(const char *data, size_t len, 88 const unsigned char *expected_hash); 89 90 static int open_file(const char *path, struct nameidata *nid); 91 static char *read_manifest(char *path, unsigned char *digest); 92 static int parse_entry(char *entry, char *prefix); 93 static int parse_manifest(char *path, unsigned char *hash, char *prefix); 94 95 static unsigned char 96 hexchar_to_byte(unsigned char c) 97 { 98 99 if (isdigit(c)) 100 return (c - '0'); 101 102 return (isupper(c) ? c - 'A' + 10 : c - 'a' + 10); 103 } 104 105 static int 106 hexstring_to_bin(unsigned char *buf) 107 { 108 size_t i, len; 109 unsigned char byte; 110 111 len = strlen(buf); 112 for (i = 0; i < len / 2; i++) { 113 if (!isxdigit(buf[2 * i]) || !isxdigit(buf[2 * i + 1])) 114 return (EINVAL); 115 116 byte = hexchar_to_byte(buf[2 * i]) << 4; 117 byte += hexchar_to_byte(buf[2 * i + 1]); 118 buf[i] = byte; 119 } 120 return (0); 121 } 122 123 static int 124 get_flags(const char *entry) 125 { 126 int i; 127 int result = 0; 128 129 for (i = 0; flags_table[i].flag_name != NULL; i++) 130 if (strstr(entry, flags_table[i].flag_name) != NULL) 131 result |= flags_table[i].flag; 132 133 return (result); 134 } 135 136 /* 137 * Parse a single line of manifest looking for a digest and its type. 138 * We expect it to be in form of "path shaX=hash". 139 * The line will be split into path, hash type and hash value. 140 */ 141 static int 142 get_fp(const char *entry, char **type, unsigned char **digest, int *flags) 143 { 144 char *delimiter; 145 char *local_digest; 146 char *fp_type; 147 char *prev_fp_type; 148 size_t min_len; 149 int i; 150 151 delimiter = NULL; 152 fp_type = NULL; 153 prev_fp_type = NULL; 154 155 for (i = 0; fp_table[i].fp_type != NULL; i++) { 156 fp_type = strstr(entry, fp_table[i].fp_type); 157 /* Look for the last "shaX=hash" in line */ 158 while (fp_type != NULL) { 159 prev_fp_type = fp_type; 160 fp_type++; 161 fp_type = strstr(fp_type, fp_table[i].fp_type); 162 } 163 fp_type = prev_fp_type; 164 if (fp_type != NULL) { 165 if (fp_type == entry || fp_type[-1] != ' ') 166 return (EINVAL); 167 168 /* 169 * The entry should contain at least 170 * fp_type and digest in hexadecimal form. 171 */ 172 min_len = strlen(fp_table[i].fp_type) + 173 2 * fp_table[i].fp_size; 174 175 if (strnlen(fp_type, min_len) < min_len) 176 return (EINVAL); 177 178 local_digest = &fp_type[strlen(fp_table[i].fp_type)]; 179 delimiter = &local_digest[2 * fp_table[i].fp_size]; 180 181 /* 182 * Make sure that digest is followed by 183 * some kind of delimiter. 184 */ 185 if (*delimiter != '\n' && 186 *delimiter != '\0' && 187 *delimiter != ' ') 188 return (EINVAL); 189 190 /* 191 * Does the entry contain flags we need to parse? 192 */ 193 if (*delimiter == ' ' && flags != NULL) 194 *flags = get_flags(delimiter); 195 196 /* 197 * Split entry into three parts: 198 * path, fp_type and digest. 199 */ 200 local_digest[-1] = '\0'; 201 *delimiter = '\0'; 202 fp_type[-1] = '\0'; 203 break; 204 } 205 } 206 207 if (fp_type == NULL) 208 return (EINVAL); 209 210 if (type != NULL) 211 *type = fp_type; 212 213 if (digest != NULL) 214 *digest = local_digest; 215 216 return (0); 217 } 218 219 /* 220 * Currently we verify manifest using sha256. 221 * In future another env with hash type could be introduced. 222 */ 223 static int 224 verify_digest(const char *data, size_t len, const unsigned char *expected_hash) 225 { 226 SHA256_CTX ctx; 227 unsigned char hash[SHA256_DIGEST_LENGTH]; 228 229 SHA256_Init(&ctx); 230 SHA256_Update(&ctx, data, len); 231 SHA256_Final(hash, &ctx); 232 233 return (memcmp(expected_hash, hash, SHA256_DIGEST_LENGTH)); 234 } 235 236 237 static int 238 open_file(const char *path, struct nameidata *nid) 239 { 240 int flags, rc; 241 242 flags = FREAD; 243 244 pwd_ensure_dirs(); 245 246 NDINIT(nid, LOOKUP, 0, UIO_SYSSPACE, path, curthread); 247 rc = vn_open(nid, &flags, 0, NULL); 248 NDFREE(nid, NDF_ONLY_PNBUF); 249 if (rc != 0) 250 return (rc); 251 252 return (0); 253 } 254 255 /* 256 * Read the manifest from location specified in path and verify its digest. 257 */ 258 static char* 259 read_manifest(char *path, unsigned char *digest) 260 { 261 struct nameidata nid; 262 struct vattr va; 263 char *data; 264 ssize_t bytes_read, resid; 265 int rc; 266 267 data = NULL; 268 bytes_read = 0; 269 270 rc = open_file(path, &nid); 271 if (rc != 0) 272 goto fail; 273 274 rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred); 275 if (rc != 0) 276 goto fail; 277 278 data = (char *)malloc(va.va_size + 1, M_VERIEXEC, M_WAITOK); 279 280 while (bytes_read < va.va_size) { 281 rc = vn_rdwr( 282 UIO_READ, nid.ni_vp, data, 283 va.va_size - bytes_read, bytes_read, 284 UIO_SYSSPACE, IO_NODELOCKED, 285 curthread->td_ucred, NOCRED, &resid, curthread); 286 if (rc != 0) 287 goto fail; 288 289 bytes_read = va.va_size - resid; 290 } 291 292 data[bytes_read] = '\0'; 293 294 VOP_UNLOCK(nid.ni_vp); 295 (void)vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread); 296 297 /* 298 * If digest is wrong someone might be trying to fool us. 299 */ 300 if (verify_digest(data, va.va_size, digest)) 301 panic("Manifest hash doesn't match expected value!"); 302 303 return (data); 304 305 fail: 306 if (data != NULL) 307 free(data, M_VERIEXEC); 308 309 return (NULL); 310 } 311 312 /* 313 * Process single line. 314 * First split it into path, digest_type and digest. 315 * Then try to open the file and insert its fingerprint into metadata store. 316 */ 317 static int 318 parse_entry(char *entry, char *prefix) 319 { 320 struct nameidata nid; 321 struct vattr va; 322 char path[MAXPATHLEN]; 323 char *fp_type; 324 unsigned char *digest; 325 int rc, is_exec, flags; 326 327 fp_type = NULL; 328 digest = NULL; 329 flags = 0; 330 331 rc = get_fp(entry, &fp_type, &digest, &flags); 332 if (rc != 0) 333 return (rc); 334 335 rc = hexstring_to_bin(digest); 336 if (rc != 0) 337 return (rc); 338 339 if (strnlen(entry, MAXPATHLEN) == MAXPATHLEN) 340 return (EINVAL); 341 342 /* If the path is not absolute prepend it with a prefix */ 343 if (prefix != NULL && entry[0] != '/') { 344 rc = snprintf(path, MAXPATHLEN, "%s/%s", 345 prefix, entry); 346 if (rc < 0) 347 return (-rc); 348 } else { 349 strcpy(path, entry); 350 } 351 352 rc = open_file(path, &nid); 353 NDFREE(&nid, NDF_ONLY_PNBUF); 354 if (rc != 0) 355 return (rc); 356 357 rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred); 358 if (rc != 0) 359 goto out; 360 361 is_exec = (va.va_mode & VEXEC); 362 363 mtx_lock(&ve_mutex); 364 rc = mac_veriexec_metadata_add_file( 365 is_exec == 0, 366 va.va_fsid, va.va_fileid, va.va_gen, 367 digest, 368 NULL, 0, 369 flags, fp_type, 1); 370 mtx_unlock(&ve_mutex); 371 372 out: 373 VOP_UNLOCK(nid.ni_vp); 374 vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread); 375 return (rc); 376 } 377 378 /* 379 * Look for manifest in env that have beed passed by loader. 380 * This routine should be called right after the rootfs is mounted. 381 */ 382 static int 383 parse_manifest(char *path, unsigned char *hash, char *prefix) 384 { 385 char *data; 386 char *entry; 387 char *next_entry; 388 int rc, success_count; 389 390 data = NULL; 391 success_count = 0; 392 rc = 0; 393 394 data = read_manifest(path, hash); 395 if (data == NULL) { 396 rc = EIO; 397 goto out; 398 } 399 400 entry = data; 401 while (entry != NULL) { 402 next_entry = strchr(entry, '\n'); 403 if (next_entry != NULL) { 404 *next_entry = '\0'; 405 next_entry++; 406 } 407 if (entry[0] == '\n' || entry[0] == '\0') { 408 entry = next_entry; 409 continue; 410 } 411 if ((rc = parse_entry(entry, prefix))) 412 printf("mac_veriexec_parser: Warning: Failed to parse" 413 " entry with rc:%d, entry:\"%s\"\n", rc, entry); 414 else 415 success_count++; 416 417 entry = next_entry; 418 } 419 rc = 0; 420 421 out: 422 if (data != NULL) 423 free(data, M_VERIEXEC); 424 425 if (success_count == 0) 426 rc = EINVAL; 427 428 return (rc); 429 } 430 431 static void 432 parse_manifest_event(void *dummy) 433 { 434 char *manifest_path; 435 char *manifest_prefix; 436 unsigned char *manifest_hash; 437 int rc; 438 439 /* If the envs are not set fail silently */ 440 manifest_path = kern_getenv("veriexec.manifest_path"); 441 if (manifest_path == NULL) 442 return; 443 444 manifest_hash = kern_getenv("veriexec.manifest_hash"); 445 if (manifest_hash == NULL) { 446 freeenv(manifest_path); 447 return; 448 } 449 450 manifest_prefix = kern_getenv("veriexec.manifest_prefix"); 451 452 if (strlen(manifest_hash) != 2 * SHA256_DIGEST_LENGTH) 453 panic("veriexec.manifest_hash has incorrect size"); 454 455 rc = hexstring_to_bin(manifest_hash); 456 if (rc != 0) 457 panic("mac_veriexec: veriexec.loader.manifest_hash" 458 " doesn't contain a hash in hexadecimal form"); 459 460 rc = parse_manifest(manifest_path, manifest_hash, manifest_prefix); 461 if (rc != 0) 462 panic("mac_veriexec: Failed to parse manifest err=%d", rc); 463 464 mtx_lock(&ve_mutex); 465 mac_veriexec_set_state( 466 VERIEXEC_STATE_LOADED | VERIEXEC_STATE_ACTIVE | 467 VERIEXEC_STATE_LOCKED | VERIEXEC_STATE_ENFORCE); 468 mtx_unlock(&ve_mutex); 469 470 freeenv(manifest_path); 471 freeenv(manifest_hash); 472 if (manifest_prefix != NULL) 473 freeenv(manifest_prefix); 474 } 475 476 EVENTHANDLER_DEFINE(mountroot, parse_manifest_event, NULL, 0); 477