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 static int 237 open_file(const char *path, struct nameidata *nid) 238 { 239 int flags, rc; 240 241 flags = FREAD; 242 243 pwd_ensure_dirs(); 244 245 NDINIT(nid, LOOKUP, 0, UIO_SYSSPACE, path); 246 rc = vn_open(nid, &flags, 0, NULL); 247 NDFREE_PNBUF(nid); 248 if (rc != 0) 249 return (rc); 250 251 return (0); 252 } 253 254 /* 255 * Read the manifest from location specified in path and verify its digest. 256 */ 257 static char* 258 read_manifest(char *path, unsigned char *digest) 259 { 260 struct nameidata nid; 261 struct vattr va; 262 char *data; 263 ssize_t bytes_read, resid; 264 int rc; 265 266 data = NULL; 267 bytes_read = 0; 268 269 rc = open_file(path, &nid); 270 if (rc != 0) 271 goto fail; 272 273 rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred); 274 if (rc != 0) 275 goto fail; 276 277 data = (char *)malloc(va.va_size + 1, M_VERIEXEC, M_WAITOK); 278 279 while (bytes_read < va.va_size) { 280 rc = vn_rdwr( 281 UIO_READ, nid.ni_vp, data, 282 va.va_size - bytes_read, bytes_read, 283 UIO_SYSSPACE, IO_NODELOCKED, 284 curthread->td_ucred, NOCRED, &resid, curthread); 285 if (rc != 0) 286 goto fail; 287 288 bytes_read = va.va_size - resid; 289 } 290 291 data[bytes_read] = '\0'; 292 293 VOP_UNLOCK(nid.ni_vp); 294 (void)vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread); 295 296 /* 297 * If digest is wrong someone might be trying to fool us. 298 */ 299 if (verify_digest(data, va.va_size, digest)) 300 panic("Manifest hash doesn't match expected value!"); 301 302 return (data); 303 304 fail: 305 if (data != NULL) 306 free(data, M_VERIEXEC); 307 308 return (NULL); 309 } 310 311 /* 312 * Process single line. 313 * First split it into path, digest_type and digest. 314 * Then try to open the file and insert its fingerprint into metadata store. 315 */ 316 static int 317 parse_entry(char *entry, char *prefix) 318 { 319 struct nameidata nid; 320 struct vattr va; 321 char path[MAXPATHLEN]; 322 char *fp_type; 323 unsigned char *digest; 324 int rc, is_exec, flags; 325 326 fp_type = NULL; 327 digest = NULL; 328 flags = 0; 329 330 rc = get_fp(entry, &fp_type, &digest, &flags); 331 if (rc != 0) 332 return (rc); 333 334 rc = hexstring_to_bin(digest); 335 if (rc != 0) 336 return (rc); 337 338 if (strnlen(entry, MAXPATHLEN) == MAXPATHLEN) 339 return (EINVAL); 340 341 /* If the path is not absolute prepend it with a prefix */ 342 if (prefix != NULL && entry[0] != '/') { 343 rc = snprintf(path, MAXPATHLEN, "%s/%s", 344 prefix, entry); 345 if (rc < 0) 346 return (-rc); 347 } else { 348 strcpy(path, entry); 349 } 350 351 rc = open_file(path, &nid); 352 NDFREE_PNBUF(&nid); 353 if (rc != 0) 354 return (rc); 355 356 rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred); 357 if (rc != 0) 358 goto out; 359 360 is_exec = (va.va_mode & VEXEC); 361 362 mtx_lock(&ve_mutex); 363 rc = mac_veriexec_metadata_add_file( 364 is_exec == 0, 365 va.va_fsid, va.va_fileid, va.va_gen, 366 digest, 367 NULL, 0, 368 flags, fp_type, 1); 369 mtx_unlock(&ve_mutex); 370 371 out: 372 VOP_UNLOCK(nid.ni_vp); 373 vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread); 374 return (rc); 375 } 376 377 /* 378 * Look for manifest in env that have beed passed by loader. 379 * This routine should be called right after the rootfs is mounted. 380 */ 381 static int 382 parse_manifest(char *path, unsigned char *hash, char *prefix) 383 { 384 char *data; 385 char *entry; 386 char *next_entry; 387 int rc, success_count; 388 389 data = NULL; 390 success_count = 0; 391 rc = 0; 392 393 data = read_manifest(path, hash); 394 if (data == NULL) { 395 rc = EIO; 396 goto out; 397 } 398 399 entry = data; 400 while (entry != NULL) { 401 next_entry = strchr(entry, '\n'); 402 if (next_entry != NULL) { 403 *next_entry = '\0'; 404 next_entry++; 405 } 406 if (entry[0] == '\n' || entry[0] == '\0') { 407 entry = next_entry; 408 continue; 409 } 410 if ((rc = parse_entry(entry, prefix))) 411 printf("mac_veriexec_parser: Warning: Failed to parse" 412 " entry with rc:%d, entry:\"%s\"\n", rc, entry); 413 else 414 success_count++; 415 416 entry = next_entry; 417 } 418 rc = 0; 419 420 out: 421 if (data != NULL) 422 free(data, M_VERIEXEC); 423 424 if (success_count == 0) 425 rc = EINVAL; 426 427 return (rc); 428 } 429 430 static void 431 parse_manifest_event(void *dummy) 432 { 433 char *manifest_path; 434 char *manifest_prefix; 435 unsigned char *manifest_hash; 436 int rc; 437 438 /* If the envs are not set fail silently */ 439 manifest_path = kern_getenv("veriexec.manifest_path"); 440 if (manifest_path == NULL) 441 return; 442 443 manifest_hash = kern_getenv("veriexec.manifest_hash"); 444 if (manifest_hash == NULL) { 445 freeenv(manifest_path); 446 return; 447 } 448 449 manifest_prefix = kern_getenv("veriexec.manifest_prefix"); 450 451 if (strlen(manifest_hash) != 2 * SHA256_DIGEST_LENGTH) 452 panic("veriexec.manifest_hash has incorrect size"); 453 454 rc = hexstring_to_bin(manifest_hash); 455 if (rc != 0) 456 panic("mac_veriexec: veriexec.loader.manifest_hash" 457 " doesn't contain a hash in hexadecimal form"); 458 459 rc = parse_manifest(manifest_path, manifest_hash, manifest_prefix); 460 if (rc != 0) 461 panic("mac_veriexec: Failed to parse manifest err=%d", rc); 462 463 mtx_lock(&ve_mutex); 464 mac_veriexec_set_state( 465 VERIEXEC_STATE_LOADED | VERIEXEC_STATE_ACTIVE | 466 VERIEXEC_STATE_LOCKED | VERIEXEC_STATE_ENFORCE); 467 mtx_unlock(&ve_mutex); 468 469 freeenv(manifest_path); 470 freeenv(manifest_hash); 471 if (manifest_prefix != NULL) 472 freeenv(manifest_prefix); 473 } 474 475 EVENTHANDLER_DEFINE(mountroot, parse_manifest_event, NULL, 0); 476