2 * Copyright (c) 2019 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/proc_internal.h>
32 #include <sys/systm.h>
33 #include <sys/systm.h>
34 #include <sys/mount_internal.h>
35 #include <sys/filedesc.h>
36 #include <sys/vnode_internal.h>
37 #include <sys/imageboot.h>
38 #include <kern/assert.h>
40 #include <sys/namei.h>
41 #include <sys/fcntl.h>
42 #include <sys/vnode.h>
43 #include <sys/sysproto.h>
45 #include <miscfs/devfs/devfsdefs.h>
46 #include <libkern/crypto/sha2.h>
47 #include <libkern/crypto/rsa.h>
48 #include <libkern/OSKextLibPrivate.h>
50 #include <kern/chunklist.h>
51 #include <kern/kalloc.h>
53 #include <pexpert/pexpert.h>
55 extern int read_file(const char *path
, void **bufp
, size_t *bufszp
); /* implemented in imageboot.c */
56 extern vnode_t
imgboot_get_image_file(const char *path
, off_t
*fsize
, int *errp
); /* implemented in imageboot.c */
58 #define AUTHDBG(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0)
59 #define AUTHPRNT(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0)
60 #define kfree_safe(x) do { if ((x)) { kfree_addr((x)); (x) = NULL; } } while (0)
62 static const char *libkern_path
= "/System/Library/Extensions/System.kext/PlugIns/Libkern.kext/Libkern";
63 static const char *libkern_bundle
= "com.apple.kpi.libkern";
66 * Rev1 chunklist handling
68 const struct chunklist_pubkey rev1_chunklist_pubkeys
[] = {
70 const size_t rev1_chunklist_num_pubkeys
= sizeof(rev1_chunklist_pubkeys
) / sizeof(rev1_chunklist_pubkeys
[0]);
73 key_byteswap(void *_dst
, const void *_src
, size_t len
)
75 uint32_t *dst
__attribute__((align_value(1))) = _dst
;
76 const uint32_t *src
__attribute__((align_value(1))) = _src
;
78 assert(len
% sizeof(uint32_t) == 0);
80 len
= len
/ sizeof(uint32_t);
81 for (size_t i
= 0; i
< len
; i
++) {
82 dst
[len
- i
- 1] = OSSwapInt32(src
[i
]);
87 construct_chunklist_path(const char *root_path
, char **bufp
)
93 path
= kalloc(MAXPATHLEN
);
95 AUTHPRNT("failed to allocate space for chunklist path");
100 len
= strnlen(root_path
, MAXPATHLEN
);
101 if (len
< MAXPATHLEN
&& len
> strlen(".dmg")) {
102 /* correctly terminated string with space for extension */
104 AUTHPRNT("malformed root path");
109 len
= strlcpy(path
, root_path
, MAXPATHLEN
);
110 if (len
>= MAXPATHLEN
) {
111 AUTHPRNT("root path is too long");
116 path
[len
- strlen(".dmg")] = '\0';
117 len
= strlcat(path
, ".chunklist", MAXPATHLEN
);
118 if (len
>= MAXPATHLEN
) {
119 AUTHPRNT("chunklist path is too long");
134 validate_signature(const uint8_t *key_msb
, size_t keylen
, uint8_t *sig_msb
, size_t siglen
, uint8_t *digest
)
137 bool sig_valid
= false;
140 const uint8_t exponent
[] = { 0x01, 0x00, 0x01 };
141 uint8_t *modulus
= kalloc(keylen
);
142 rsa_pub_ctx
*rsa_ctx
= kalloc(sizeof(rsa_pub_ctx
));
143 sig
= kalloc(siglen
);
145 if (modulus
== NULL
|| rsa_ctx
== NULL
|| sig
== NULL
) {
150 bzero(rsa_ctx
, sizeof(rsa_pub_ctx
));
151 key_byteswap(modulus
, key_msb
, keylen
);
152 key_byteswap(sig
, sig_msb
, siglen
);
154 err
= rsa_make_pub(rsa_ctx
,
155 sizeof(exponent
), exponent
,
156 CHUNKLIST_PUBKEY_LEN
, modulus
);
158 AUTHPRNT("rsa_make_pub() failed");
162 err
= rsa_verify_pkcs1v15(rsa_ctx
, CC_DIGEST_OID_SHA256
,
163 SHA256_DIGEST_LENGTH
, digest
,
168 AUTHPRNT("rsa_verify() failed");
179 } else if (sig_valid
== true) {
180 return 0; /* success */
187 validate_root_image(const char *root_path
, void *chunklist
)
190 struct chunklist_hdr
*hdr
= chunklist
;
191 struct chunklist_chunk
*chk
= NULL
;
193 struct vnode
*vp
= NULL
;
196 bool doclose
= false;
200 vfs_context_t ctx
= vfs_context_kernel();
201 kauth_cred_t kerncred
= vfs_context_ucred(ctx
);
202 proc_t p
= vfs_context_proc(ctx
);
204 AUTHDBG("validating root dmg %s", root_path
);
206 vp
= imgboot_get_image_file(root_path
, &fsize
, &err
);
211 if ((err
= VNOP_OPEN(vp
, FREAD
, ctx
)) != 0) {
212 AUTHPRNT("failed to open vnode");
218 * Iterate the chunk list and check each chunk
220 chk
= chunklist
+ hdr
->cl_chunk_offset
;
221 for (ch
= 0; ch
< hdr
->cl_chunk_count
; ch
++) {
225 /* allocate buffer based on first chunk size */
226 buf
= kalloc(chk
->chunk_size
);
231 bufsz
= chk
->chunk_size
;
234 if (chk
->chunk_size
> bufsz
) {
235 AUTHPRNT("chunk size too big");
240 err
= vn_rdwr(UIO_READ
, vp
, (caddr_t
)buf
, chk
->chunk_size
, offset
, UIO_SYSSPACE
, IO_NODELOCKED
, kerncred
, &resid
, p
);
242 AUTHPRNT("vn_rdrw fail (err = %d, resid = %d)", err
, resid
);
247 AUTHPRNT("chunk covered non-existant part of image");
251 /* calculate the SHA256 of this chunk */
252 uint8_t sha_digest
[SHA256_DIGEST_LENGTH
];
254 SHA256_Init(&sha_ctx
);
255 SHA256_Update(&sha_ctx
, buf
, chk
->chunk_size
);
256 SHA256_Final(sha_digest
, &sha_ctx
);
258 /* Check the calculated SHA matches the chunk list */
259 if (bcmp(sha_digest
, chk
->chunk_sha256
, SHA256_DIGEST_LENGTH
) != 0) {
260 AUTHPRNT("SHA mismatch on chunk %lu (offset %lld, size %u)", ch
, offset
, chk
->chunk_size
);
265 if (os_add_overflow(offset
, chk
->chunk_size
, &offset
)) {
272 if (offset
!= fsize
) {
273 AUTHPRNT("chunklist did not cover entire file (offset = %lld, fsize = %lld)", offset
, fsize
);
281 VNOP_CLOSE(vp
, FREAD
, ctx
);
291 static const uuid_t
*
292 getuuidfromheader_safe(const void *buf
, size_t bufsz
, size_t *uuidsz
)
294 const struct uuid_command
*cmd
= NULL
;
295 const kernel_mach_header_t
*mh
= buf
;
297 /* space for the header and at least one load command? */
298 if (bufsz
< sizeof(kernel_mach_header_t
) + sizeof(struct uuid_command
)) {
299 AUTHPRNT("libkern image too small");
303 /* validate the mach header */
304 if (mh
->magic
!= MH_MAGIC_64
|| (mh
->sizeofcmds
> bufsz
- sizeof(kernel_mach_header_t
))) {
305 AUTHPRNT("invalid MachO header");
309 /* iterate the load commands */
310 size_t offset
= sizeof(kernel_mach_header_t
);
311 for (size_t i
= 0; i
< mh
->ncmds
; i
++) {
314 if (cmd
->cmd
== LC_UUID
) {
315 *uuidsz
= sizeof(cmd
->uuid
);
319 if (os_add_overflow(cmd
->cmdsize
, offset
, &offset
) ||
320 offset
> bufsz
- sizeof(struct uuid_command
)) {
329 * Rev2 chunklist handling
331 const struct chunklist_pubkey rev2_chunklist_pubkeys
[] = {
333 const size_t rev2_chunklist_num_pubkeys
= sizeof(rev2_chunklist_pubkeys
) / sizeof(rev2_chunklist_pubkeys
[0]);
335 static const struct efi_guid_t gEfiSignAppleCertTypeGuid
= CHUNKLIST_REV2_SIG_HASH_GUID
;
336 static const struct efi_guid_t gEfiSignCertTypeRsa2048Sha256Guid
= EFI_CERT_TYPE_RSA2048_SHA256
;
339 validate_rev2_certificate(struct rev2_chunklist_certificate
*certificate
)
341 /* Default value of current security epoch MUST be CHUNKLIST_MIN_SECURITY_EPOCH */
342 uint8_t current_security_epoch
= CHUNKLIST_MIN_SECURITY_EPOCH
;
344 /* Certificate.Length must be equal to sizeof(CERTIFICATE) */
345 if (certificate
->length
!= sizeof(struct rev2_chunklist_certificate
)) {
346 AUTHDBG("invalid certificate length");
350 /* Certificate.Revision MUST be equal to 2 */
351 if (certificate
->revision
!= 2) {
352 AUTHDBG("invalid certificate revision");
356 /* Certificate.SecurityEpoch MUST be current or higher */
357 if (PE_parse_boot_argn(CHUNKLIST_SECURITY_EPOCH
, ¤t_security_epoch
, sizeof(current_security_epoch
)) &&
358 certificate
->security_epoch
< current_security_epoch
) {
359 AUTHDBG("invalid certificate security epoch");
363 /* Certificate.CertificateType MUST be equal to WIN_CERT_TYPE_EFI_GUID (0x0EF1) */
364 if (certificate
->certificate_type
!= WIN_CERT_TYPE_EFI_GUID
) {
365 AUTHDBG("invalid certificate type");
369 /* Certificate.CertificateGuid MUST be equal to 45E7BC51-913C-42AC-96A2-10712FFBEBA7 */
370 if (0 != memcmp(&certificate
->certificate_guid
, &gEfiSignAppleCertTypeGuid
, sizeof(struct efi_guid_t
))) {
371 AUTHDBG("invalid certificate GUID");
375 /* Certificate.HashTypeGuid MUST be equal to A7717414-C616-4977-9420-844712A735BF */
376 if (0 != memcmp(&certificate
->hash_type_guid
, &gEfiSignCertTypeRsa2048Sha256Guid
, sizeof(struct efi_guid_t
))) {
377 AUTHDBG("invalid hash type GUID");
385 validate_rev2_chunklist(uint8_t *buffer
, size_t buffer_size
)
387 struct rev2_chunklist_certificate
*certificate
;
388 size_t security_data_offset
;
390 /* Check input parameters to be sane */
391 if (buffer
== NULL
|| buffer_size
== 0) {
392 AUTHDBG("invalid parameter");
396 /* Check for existing signature */
397 if (buffer_size
< sizeof(struct rev2_chunklist_certificate
)) {
398 AUTHDBG("no space for certificate");
402 security_data_offset
= buffer_size
- sizeof(struct rev2_chunklist_certificate
);
403 certificate
= (struct rev2_chunklist_certificate
*)(buffer
+ security_data_offset
);
405 /* Check signature candidate to be a valid rev2 chunklist certificate */
406 if (TRUE
!= validate_rev2_certificate(certificate
)) {
410 /* Check public key to be trusted */
411 for (size_t i
= 0; i
< rev2_chunklist_num_pubkeys
; i
++) {
412 const struct chunklist_pubkey
*key
= &rev2_chunklist_pubkeys
[i
];
413 /* Production keys are always trusted */
414 if (key
->is_production
!= TRUE
) {
415 uint8_t no_rev2_dev
= 0;
416 /* Do not trust rev2 development keys if CHUNKLIST_NO_REV2_DEV is present */
417 if (PE_parse_boot_argn(CHUNKLIST_NO_REV2_DEV
, &no_rev2_dev
, sizeof(no_rev2_dev
))) {
418 AUTHDBG("rev2 development key is not trusted");
423 /* Check certificate public key to be the trusted one */
424 if (0 == memcmp(key
->key
, certificate
->rsa_public_key
, sizeof(certificate
->rsa_public_key
))) {
425 AUTHDBG("certificate public key is trusted");
427 /* Hash everything but signature */
429 SHA256_Init(&hash_ctx
);
430 SHA256_Update(&hash_ctx
, buffer
, security_data_offset
);
432 /* Include Certificate.SecurityEpoch value */
433 SHA256_Update(&hash_ctx
, &certificate
->security_epoch
, sizeof(certificate
->security_epoch
));
435 /* Finalize hashing into the output buffer */
436 uint8_t sha_digest
[SHA256_DIGEST_LENGTH
];
437 SHA256_Final(sha_digest
, &hash_ctx
);
439 /* Validate signature */
440 return validate_signature(certificate
->rsa_public_key
,
441 sizeof(certificate
->rsa_public_key
),
442 certificate
->rsa_signature
,
443 sizeof(certificate
->rsa_signature
),
448 AUTHDBG("certificate public key is not trusted");
453 * Main chunklist validation routine
456 validate_chunklist(void *buf
, size_t len
)
461 size_t chunks_end
= 0;
463 boolean_t valid_sig
= FALSE
;
464 struct chunklist_hdr
*hdr
= buf
;
466 if (len
< sizeof(struct chunklist_hdr
)) {
467 AUTHPRNT("no space for header");
471 /* recognized file format? */
472 if (hdr
->cl_magic
!= CHUNKLIST_MAGIC
||
473 hdr
->cl_file_ver
!= CHUNKLIST_FILE_VERSION_10
||
474 hdr
->cl_chunk_method
!= CHUNKLIST_CHUNK_METHOD_10
) {
475 AUTHPRNT("unrecognized chunklist format");
479 /* determine signature length based on signature method */
480 if (hdr
->cl_sig_method
== CHUNKLIST_SIGNATURE_METHOD_REV1
) {
481 AUTHPRNT("rev1 chunklist");
482 sig_len
= CHUNKLIST_REV1_SIG_LEN
;
483 } else if (hdr
->cl_sig_method
== CHUNKLIST_SIGNATURE_METHOD_REV2
) {
484 AUTHPRNT("rev2 chunklist");
485 sig_len
= CHUNKLIST_REV2_SIG_LEN
;
487 AUTHPRNT("unrecognized chunklist signature method");
491 /* does the chunk list fall within the bounds of the buffer? */
492 if (os_mul_and_add_overflow(hdr
->cl_chunk_count
, sizeof(struct chunklist_chunk
), hdr
->cl_chunk_offset
, &chunks_end
) ||
493 hdr
->cl_chunk_offset
< sizeof(struct chunklist_hdr
) || chunks_end
> len
) {
494 AUTHPRNT("invalid chunk_count (%llu) or chunk_offset (%llu)",
495 hdr
->cl_chunk_count
, hdr
->cl_chunk_offset
);
499 /* does the signature fall within the bounds of the buffer? */
500 if (os_add_overflow(hdr
->cl_sig_offset
, sig_len
, &sig_end
) ||
501 hdr
->cl_sig_offset
< sizeof(struct chunklist_hdr
) ||
502 hdr
->cl_sig_offset
< chunks_end
||
503 hdr
->cl_sig_offset
> len
) {
504 AUTHPRNT("invalid signature offset (%llu)", hdr
->cl_sig_offset
);
509 os_sub_overflow(len
, hdr
->cl_sig_offset
, &sigsz
) ||
511 /* missing or incorrect signature size */
515 /* validate rev1 chunklist */
516 if (hdr
->cl_sig_method
== CHUNKLIST_SIGNATURE_METHOD_REV1
) {
517 /* Do not trust rev1 chunklists if CHUNKLIST_NO_REV1 is present */
519 if (PE_parse_boot_argn(CHUNKLIST_NO_REV1
, &no_rev1
, sizeof(no_rev1
))) {
520 AUTHDBG("rev1 chunklists are not trusted");
524 /* hash the chunklist (excluding the signature) */
525 AUTHDBG("hashing rev1 chunklist");
526 uint8_t sha_digest
[SHA256_DIGEST_LENGTH
];
528 SHA256_Init(&sha_ctx
);
529 SHA256_Update(&sha_ctx
, buf
, hdr
->cl_sig_offset
);
530 SHA256_Final(sha_digest
, &sha_ctx
);
532 AUTHDBG("validating rev1 chunklist signature against rev1 pub keys");
533 for (size_t i
= 0; i
< rev1_chunklist_num_pubkeys
; i
++) {
534 const struct chunklist_pubkey
*key
= &rev1_chunklist_pubkeys
[i
];
535 err
= validate_signature(key
->key
, CHUNKLIST_PUBKEY_LEN
, buf
+ hdr
->cl_sig_offset
, CHUNKLIST_SIGNATURE_LEN
, sha_digest
);
537 AUTHDBG("validated rev1 chunklist signature with rev1 key %lu (prod=%d)", i
, key
->is_production
);
538 valid_sig
= key
->is_production
;
539 #if IMAGEBOOT_ALLOW_DEVKEYS
540 if (!key
->is_production
) {
541 /* allow dev keys in dev builds only */
542 AUTHDBG("*** allowing DEV rev1 key: this will fail in customer builds ***");
550 /* At this point we tried all the keys: nothing went wrong but none of them
551 * signed our chunklist. */
552 AUTHPRNT("rev1 signature did not verify against any known rev1 public key");
553 } else if (hdr
->cl_sig_method
== CHUNKLIST_SIGNATURE_METHOD_REV2
) {
554 AUTHDBG("validating rev2 chunklist signature against rev2 pub keys");
555 err
= validate_rev2_chunklist(buf
, len
);
565 } else if (valid_sig
== TRUE
) {
566 return 0; /* signed, and everything checked out */
573 * Authenticate a given DMG file using chunklist
576 authenticate_root_with_chunklist(const char *root_path
)
578 char *chunklist_path
= NULL
;
579 void *chunklist_buf
= NULL
;
580 size_t chunklist_len
= 32 * 1024 * 1024UL;
583 err
= construct_chunklist_path(root_path
, &chunklist_path
);
585 AUTHPRNT("failed creating chunklist path");
589 AUTHDBG("validating root against chunklist %s", chunklist_path
);
592 * Read and authenticate the chunklist, then validate the root image against
595 AUTHDBG("reading chunklist");
596 err
= read_file(chunklist_path
, &chunklist_buf
, &chunklist_len
);
598 AUTHPRNT("failed to read chunklist");
602 AUTHDBG("validating chunklist");
603 err
= validate_chunklist(chunklist_buf
, chunklist_len
);
605 AUTHPRNT("failed to validate chunklist");
608 AUTHDBG("successfully validated chunklist");
610 AUTHDBG("validating root image against chunklist");
611 err
= validate_root_image(root_path
, chunklist_buf
);
613 AUTHPRNT("failed to validate root image against chunklist (%d)", err
);
617 /* everything checked out - go ahead and mount this */
618 AUTHDBG("root image authenticated");
621 kfree_safe(chunklist_buf
);
622 kfree_safe(chunklist_path
);
627 * Check that the UUID of the libkern currently loaded matches the one on disk.
630 authenticate_root_version_check(void)
634 size_t bufsz
= 4 * 1024 * 1024UL;
636 /* get the UUID of the libkern in /S/L/E */
637 err
= read_file(libkern_path
, &buf
, &bufsz
);
642 unsigned long uuidsz
= 0;
643 const uuid_t
*img_uuid
= getuuidfromheader_safe(buf
, bufsz
, &uuidsz
);
644 if (img_uuid
== NULL
|| uuidsz
!= sizeof(uuid_t
)) {
645 AUTHPRNT("invalid UUID (sz = %lu)", uuidsz
);
650 /* Get the UUID of the loaded libkern */
652 err
= OSKextGetUUIDForName(libkern_bundle
, live_uuid
);
654 AUTHPRNT("could not find loaded libkern");
658 /* ... and compare them */
659 if (bcmp(live_uuid
, img_uuid
, uuidsz
) != 0) {
660 AUTHPRNT("UUID of running libkern does not match %s", libkern_path
);
662 uuid_string_t img_uuid_str
, live_uuid_str
;
663 uuid_unparse(*img_uuid
, img_uuid_str
);
664 uuid_unparse(live_uuid
, live_uuid_str
);
665 AUTHPRNT("loaded libkern UUID = %s", live_uuid_str
);
666 AUTHPRNT("on-disk libkern UUID = %s", img_uuid_str
);