img4.h   [plain text]


/*!
 * @header
 * Image4 interfaces. These interfaces encapsulate the basic concepts required
 * for authenticating and validating Image4 manifests as being authoritative.
 * These concepts are:
 *
 * @section Environment
 * An environment is a description of a host comprised of hardware identifiers
 * and policy configurations. For example, the environment of an iPhone may
 * include the following hardware identifiers (among others):
 *
 *     ChipID
 *     A number identifying the chip design.
 *
 *     BoardID
 *     A number identifying the board.
 *
 *     UniqueChipID / ECID
 *     A number uniquely identifying a specific instance of a chip.
 *
 * The environment also includes policy information derived by previous stages
 * of secure boot. Examples of such policy are:
 *
 *     Mix-n-Match Prevention
 *     Whether firmware payloads from multiple, valid secure boot manifests
 *     should be prevented from being executed on the host environment. The
 *     default is true.
 *
 * @section Manifest
 * An Image4 manifest is a set of constraints that describe a host environment.
 * For example, a manifest may have been signed such that it is only valid for a
 * single host environment. In this case, the manifest may include specific
 * values for ChipID, BoardID, UniqueChipID, etc. Such a manifest is said to be
 * personalized for that environment.
 *
 * If an environment meets the constraints in a manifest, that manifest is said
 * to be authoritative over the environment.
 *
 * The manifest also includes one or more objects which may be executed in the
 * environment.
 *
 * @section Object
 * An object is a description of a payload. An object can describe any payload,
 * not just the payload that is in the Image4. An object describes a payload by
 * means of its digest. Examples of objects present in a secure boot manifest
 * are the kernelcache and the static trust cache.
 *
 * If an authoritative manifest accurately describes an object, then that object
 * may be executed in the host environment. The mechanics of execution typically
 * involve mapping its payload into a privileged memory region. For example,
 * when the kernelcache is executed, its payload bytes are mapped into the range
 * of memory associated with supervisor mode.
 *
 * Payload
 * A payload is the raw sequence of bytes that is described by an object. When
 * described via an Image4 object, payloads are first wrapped in Image4 encoding
 * to associate a tag with them. The resulting series of bytes is what is
 * contained in a .im4p file.
 *
 * An Image4 file may only contain a single payload (even though a manifest may
 * describe multiple payloads through multiple objects).
 *
 * Tag
 * A tag is a FourCC which can identify any of the following:
 *
 *     - an object property (e.g. the 'DGST' property)
 *     - a manifest property (e.g. the 'BORD' property)
 *     - a certificate property
 *     - a type of object (e.g. 'krnl')
 *
 * Tags comprised of all-caps are reserved for the Image4 specification.
 *
 * @section Secure Boot Policy
 * Manifests are evaluated with the Secure Boot evaluation policy. Broadly
 * speaking, this policy:
 *
 *     - enforces that manifest identifiers match the host's silicon
 *       identifiers,
 *     - enforces that the epoch of the certificate which signed the manifest is
 *       greater than or equal to the host silicon's epoch
 *     - enforces that the current manifest is the same one that was used in the
 *       previous stage of Secure Boot unless mix-n-match is allowed
 *
 * For manifests which lack a CHMH property, mix-n-match policy is enforced as
 * follows
 *
 *   (1) If the previous stage of Secure Boot disallows mix-n-match and the
 *       manifest does not possess the AMNM entitlement, the hash of the
 *       manifest will be enforced against the hash of the manifest which was
 *       evaluated by the previous stage of Secure Boot.
 *
 *   (2) If the previous stage of Secure Boot allows mix-n-match or the manifest
 *       possesses the AMNM entitlement, the manifest's constraints will be
 *       enforced on the environment, but the manifest will not be expected to
 *       be consistent with the manifest evaluated in the previous stage of
 *       Secure Boot, i.e. the hash of the previous manifest will not be
 *       enforced against the manifest being evaluated.
 *
 *       Enforcement of the manifest's constraints will include the value of the
 *       BNCH tag in the manifest, if any. Therefore the caller should always
 *       provide a nonce value to the implementation via {@link img4_set_nonce}
 *       if this option is used.
 *
 * For manifests which possess a CHMH property, mix-n-match policy is enforced
 * as follows:
 *
 *   (1) If the previous stage of Secure Boot disallows mix-n-match or the
 *       manifest does not possess the AMNM entitlement, the value of the CHMH
 *       property will be enforced against the hash of the manifest which was
 *       evaluated by the previous stage of Secure Boot.
 *
 *   (2) If the previous stage of Secure Boot allows mix-n-match and the
 *       manifest possesses the AMNM entitlement, all of the manifest's
 *       constraints will be enforced on the environment except for the CHMH
 *       constraint, which will be ignored.
 *
 *       Enforcement of the manifest's constraints will include the value of the
 *       BNCH tag in the manifest, if any. Therefore the caller should always
 *       provide a nonce value to the implementation via {@link img4_set_nonce}
 *       if this option is used.
 *
 * The CHMH policy may be expressed as the following truth table:
 *
 * AMNM [manifest]  Verify Manifest Hash [environment]   Enforce CHMH
 *        0                        0                           Y
 *        0                        1                           Y
 *        1                        0                           N
 *        1                        1                           Y
 */


#ifndef __IMG4_H
#define __IMG4_H

#include <os/base.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/cdefs.h>

#if KERNEL
#if !defined(OS_CLOSED_ENUM)
#define OS_CLOSED_ENUM(...) OS_ENUM(__VA_ARGS__)
#endif

#if !defined(OS_OPTIONS)
#define OS_OPTIONS(...) OS_ENUM(__VA_ARGS__)
#endif

#if !defined(OS_CLOSED_OPTIONS)
#define OS_CLOSED_OPTIONS(...) OS_ENUM(__VA_ARGS__)
#endif
#endif

#define __IMG4_INDIRECT 1

/*
 * When used from the pmap layer, this header pulls in the types from libsa,
 * which conflict with the BSD sys/types.h header that we need to pull in. But
 * we only need it for the errno_t typedef and the vnode_t typedef. So when
 * building MACH_KERNEL_PRIVATE, we do two things:
 *
 *     1. Explicitly pull in <sys/_types/_errno_t.h>, so we get errno_t and
 *        nothing else (no transitive #include's)
 *     2. #define _SYS_TYPES_H_ before #includ'ing <sys/kernel_types.h> so that
 *        we don't get the transitive #include of <sys/types.h> but we still get
 *        the definitions we need
 */
#if MACH_KERNEL_PRIVATE
#define _SYS_TYPES_H_ 1
#include <sys/kernel_types.h>
#include <sys/_types/_errno_t.h>
#else
#include <sys/kernel_types.h>
#include <sys/types.h>
#endif

#if !IMG4_PROJECT_BUILD
#include <img4/api.h>
#endif

__BEGIN_DECLS;

/*!
 * @typedef img4_tag_t
 * A type describing an Image4 tag.
 */
IMG4_API_AVAILABLE_20180112
typedef uint32_t img4_tag_t;

/*!
 * @typedef img4_section_t
 * A type describing the sections of an Image4 object.
 *
 * @const IMG4_SECTION_MANIFEST
 * The manifest section.
 *
 * @const IMG4_SECTION_OBJECT
 * The object section.
 *
 * @const IMG4_SECTION_RESTOREINFO
 * The restore info section.
 */
OS_ENUM(img4_section, uint8_t,
	IMG4_SECTION_MANIFEST,
	IMG4_SECTION_OBJECT,
	IMG4_SECTION_RESTOREINFO,
) IMG4_API_AVAILABLE_20180112;

/*!
 * @typedef img4_destructor_t
 * A type describing a destructor routine for an Image4 object.
 *
 * @param ptr
 * A pointer to the buffer to dispose of.
 *
 * @param len
 * The length of the buffer.
 */
IMG4_API_AVAILABLE_20180112
typedef void (*img4_destructor_t)(
	void *ptr,
	size_t len);

/*!
 * @typedef img4_flags_t
 * A flagset modifying the behavior of an {@link img4_t}.
 *
 * @const I4F_INIT
 * No flags set. This value is suitable for initialization purposes.
 *
 * @const I4F_TRUST_MANIFEST
 * Causes the implementation to bypass trust evaluation for the manifest, i.e.
 * it will not verify that a manifest has been signed by Apple before trusting
 * it.
 *
 * This option is for testing purposes only and is not respected on the RELEASE
 * variant of the implementation.
 *
 * @const I4F_FORCE_MIXNMATCH
 * Causes the implementation to bypass mix-n-match policy evaluation and always
 * allow mix-n-match, irrespective of the previous boot stage's conclusion or
 * manifest policy. This also allows replay of manifests whose personalization
 * has been invalidated by rolling the nonce.
 *
 * This option is for testing purposes only and is not respected on the RELEASE
 * variant of the implementation.
 *
 * @const I4F_FIRST_STAGE
 * Indicates that the manifest being evaluated is the first link in the secure
 * boot chain. This causes the implementation to enforce the manifest directly
 * on the environment rather than requiring that a previous stage has already
 * done so by way of checking the previous stage's boot manifest hash. In effect
 * this disables the mix-n-match enforcement policy.
 *
 * The critical difference between this flag and {@link I4F_FORCE_MIXNMATCH} is
 * that this flag will cause the entire manifest to be enforced on the
 * environment, including the anti-replay token in BNCH.
 * {@link I4F_FORCE_MIXNMATCH} will ignore the nonce.
 *
 * It is illegal to use a manifest which possesses a CHMH tag as a first-stage
 * manifest.
 */
OS_CLOSED_OPTIONS(img4_flags, uint64_t,
	I4F_INIT = 0,
	I4F_TRUST_MANIFEST = (1 << 0),
	I4F_FORCE_MIXNMATCH = (1 << 1),
	I4F_FIRST_STAGE = (1 << 2),
) IMG4_API_AVAILABLE_20180112;

typedef char _img4_opaque_data_64[696];

typedef char _img4_opaque_data_32[520];

/*!
 * @typedef img4_t
 * An opaque structure representing Image4 data. The Image4 data must contain a
 * manifest and may optionally contain a payload. Neither this type nor the APIs
 * APIs which manipulate it are thread-safe.
 */
IMG4_API_AVAILABLE_20180112
typedef struct _img4 {
#if __ILP64__ || __LP64__
	_img4_opaque_data_64 __opaque;
#else
	_img4_opaque_data_32 __opaque;
#endif
} img4_t;

typedef char _img4_payload_opaque_data_64[504];

#if __ARM_ARCH_7A__ || __ARM_ARCH_7S__ || __ARM_ARCH_7K__ || \
		__ARM64_ARCH_8_32__ || __i386__
typedef char _img4_payload_opaque_data_32[328];
#else
typedef char _img4_payload_opaque_data_32[332];
#endif

/*!
 * @typedef img4_payload_t
 * An opaque structure describing Image4 payload data. Neither this type nor the
 * APIs which manipulate it are thread-safe.
 */
IMG4_API_AVAILABLE_20180112
typedef struct _img4_payload {
#if __ILP64__ || __LP64__
	_img4_payload_opaque_data_64 __opaque;
#else
	_img4_payload_opaque_data_32 __opaque;
#endif
} img4_payload_t;

#if !IMG4_PROJECT_BUILD
#include <img4/environment.h>
#include <img4/nonce.h>
#include <img4/payload.h>
#endif

#if IMG4_TAPI
#include "environment.h"
#include "nonce.h"
#include "payload.h"
#endif

/*!
 * @function img4_init
 * Initializes an Image4.
 *
 * @param i4
 * A pointer to the storage to initialize.
 *
 * @param flags
 * Flags to modify initialization.
 *
 * @param bytes
 * The Image4 data from which to initialize. If a destructor is provided,
 * control of this buffer transfers to the Image4.
 *
 * @param len
 * The length of the Image4 data.
 *
 * @param destructor
 * A destructor for the Image4 data. May be NULL if the buffer does not require
 * explicit deallocation (e.g. because the buffer is stack data).
 *
 * @result
 * Upon success, zero is returned. The implementation may also return one of the
 * following error codes directly:
 *
 *     [EILSEQ]     The data is not valid Image4 data
 *     [EFTYPE]     The data does not contain an Image4 manifest
 *
 * @discussion
 * The bytes given to this routine must represent an Image4 manifest. They may
 * optionally also represent an Image4 payload.
 */
#if !XNU_KERNEL_PRIVATE
IMG4_API_AVAILABLE_20180112
OS_EXPORT OS_WARN_RESULT OS_NONNULL1 OS_NONNULL3
errno_t
img4_init(img4_t *i4, img4_flags_t flags, const uint8_t *bytes, size_t len,
		img4_destructor_t destructor);
#else
#define img4_init(...) (img4if->i4if_init(__VA_ARGS__))
#endif

/*!
 * @function img4_set_nonce
 * Sets the anti-reply token to be used during manifest enforcement. This value
 * will be compared against the value of the manifest's BNCH property.
 *
 * @param i4
 * The Image4 to modify.
 *
 * @param bytes
 * The bytes which comprise the anti-replay token.
 *
 * @param len
 * The length of the anti-replay token.
 *
 * @discussion
 * If a nonce is not set prior to a call to either
 * {@link img4_get_trusted_payload} or
 * {@link img4_get_trusted_external_payload}, the implementation will act as
 * though there is no nonce in the environment. Therefore, any manifests which
 * have a BNCH property constraint will fail to validate.
 */
#if !XNU_KERNEL_PRIVATE
IMG4_API_AVAILABLE_20180112
OS_EXPORT OS_NONNULL1 OS_NONNULL2
void
img4_set_nonce(img4_t *i4, const void *bytes, size_t len);
#else
#define img4_set_nonce(...) (img4if->i4if_set_nonce(__VA_ARGS__))
#endif

/*!
 * @function img4_set_nonce_domain
 * Sets the nonce domain to be consulted for the anti-replay token during
 * manifest enforcement.
 *
 * @param i4
 * The Image4 to modify.
 *
 * @param nd
 * The nonce domain to use for anti-replay.
 *
 * @discussion
 * See discussion for {@link img4_set_nonce}.
 */
#if !XNU_KERNEL_PRIVATE
IMG4_API_AVAILABLE_20181106
OS_EXPORT OS_NONNULL1 OS_NONNULL2
void
img4_set_nonce_domain(img4_t *i4, const img4_nonce_domain_t *nd);
#else
#define img4_set_nonce_domain(...) \
		(img4if->i4if_v1.set_nonce_domain(__VA_ARGS__))
#endif

/*!
 * @function img4_get_trusted_payload
 * Obtains the trusted payload bytes from the Image4.
 *
 * @param i4
 * The Image4 to query.
 *
 * @param tag
 * The tag for the payload to obtain.
 *
 * @param env
 * The environment against which to validate the Image4.
 *
 * @param bytes
 * A pointer to the storage where the pointer to the payload buffer will be
 * written on success.
 *
 * @param len
 * A pointer to the storage where the length of the payload buffer will be
 * written on success.
 *
 * @result
 * Upon success, zero is returned. The implementation may also return one of the
 * following error codes directly:
 *
 *     [ENOENT]     The Image4 does not contain a payload for the specified tag
 *     [EAUTH]      The Image4 manifest was not authentic
 *     [EACCES]     The environment given does not satisfy the manifest
 *                  constraints
 *     [ESTALE]     The nonce specified is not valid
 *     [EACCES]     The environment and manifest do not agree on a digest
 *                  algorithm
 *     [EILSEQ]     The payload for the given tag does not match its description
 *                  in the manifest
 *     [EIO]        The payload could not be fetched
 *
 * @discussion
 * This routine will perform the following validation:
 *
 *     1. Validate that the Image4 manifest is authentic (i.e. was signed by
 *        Apple)
 *     2. Validate that the given environment satisfies the constraints in the
 *        manifest
 *     3. Validate that the measurement of the payload for the given tag matches
 *        the measurement in the manifest
 *
 * If any one of these validation checks fails, the payload is considered
 * untrustworthy and is not returned.
 */
#if !XNU_KERNEL_PRIVATE
IMG4_API_AVAILABLE_20180112
OS_EXPORT OS_WARN_RESULT OS_NONNULL1 OS_NONNULL3 OS_NONNULL4 OS_NONNULL5
errno_t
img4_get_trusted_payload(img4_t *i4, img4_tag_t tag,
		const img4_environment_t *env, const uint8_t **bytes, size_t *len);
#else
#define img4_get_trusted_payload(...) \
		(img4if->i4if_get_trusted_payload(__VA_ARGS__))
#endif

/*!
 * @function img4_get_trusted_external_payload
 * Obtains the trusted payload bytes from the external Image4 payload after
 * validating them against the object description in the Image4's manifest.
 *
 * @param i4
 * The Image4 to query.
 *
 * @param payload
 * The payload to validate.
 *
 * @param env
 * The environment against which to validate the Image4.
 *
 * @param bytes
 * A pointer to the storage where the pointer to the payload buffer will be
 * written on success.
 *
 * If the payload objects was initialized with
 * {@link img4_payload_init_with_vnode_4xnu}, this parameter should be NULL, as
 * there will be no in-memory buffer to return.
 *
 * @param len
 * A pointer to the storage where the length of the payload buffer will be
 * written on success.
 *
 * If the payload objects was initialized with
 * {@link img4_payload_init_with_vnode_4xnu}, this parameter should be NULL, as
 * there will be no in-memory buffer to return.
 *
 * @result
 * Upon success, zero is returned. The implementation may also return one of the
 * following error codes directly:
 *
 *     [ENOENT]     The Image4 does not contain an object describing the given
 *                  payload
 *     [EAUTH]      The Image4 manifest was not authentic
 *     [EACCES]     The environment given does not satisfy the manifest
 *                  constraints
 *     [ESTALE]     The nonce specified is not valid
 *     [EACCES]     The environment and manifest do not agree on a digest
 *                  algorithm
 *     [EILSEQ]     The payload for the given tag does not match its description
 *                  in the manifest
 *     [EIO]        The payload could not be fetched
 *     [EIO]        The payload was initialized with
 *                  {@link img4_payload_init_with_vnode_4xnu}, and reading from
 *                  the vnode stalled repeatedly beyond the implementation's
 *                  tolerance
 *
 * If the payload was initialized with
 * {@link img4_payload_init_with_vnode_4xnu}, any error returned by
 * {@link vnode_getattr} or {@link vn_rdwr} may be returned.
 *
 * If the payload was initialized with
 * {@link img4_payload_init_with_fd_4MSM}, any error returned by stat(2),
 * read(2), or malloc(3) may be returned.
 *
 * Otherwise, an error from the underlying Image4 implementation will be
 * returned.
 *
 * @discussion
 * This routine performs the same validation steps as
 * {@link img4_get_trusted_payload} and has the same caveats.
 */
#if !XNU_KERNEL_PRIVATE
IMG4_API_AVAILABLE_20180112
OS_EXPORT OS_WARN_RESULT OS_NONNULL1 OS_NONNULL2 OS_NONNULL3
errno_t
img4_get_trusted_external_payload(img4_t *i4, img4_payload_t *payload,
		const img4_environment_t *env, const uint8_t **bytes, size_t *len);
#else
#define img4_get_trusted_external_payload(...) \
		(img4if->i4if_get_trusted_external_payload(__VA_ARGS__))
#endif

/*!
 * @function img4_destroy
 * Destroys an Image4 and disposes of associated resources.
 *
 * @param i4
 * The Image4 to destroy.
 *
 * @discussion
 * The destructor passed to {@link img4_init} is called as a result of this
 * routine, if any was set.
 */
#if !XNU_KERNEL_PRIVATE
IMG4_API_AVAILABLE_20180112
OS_EXPORT OS_NONNULL1
void
img4_destroy(img4_t *i4);
#else
#define img4_destroy(...) (img4if->i4if_destroy(__VA_ARGS__))
#endif

__END_DECLS;

#endif // __IMG4_H