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