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