X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_codesigning/lib/codedirectory.h diff --git a/Security/libsecurity_codesigning/lib/codedirectory.h b/Security/libsecurity_codesigning/lib/codedirectory.h new file mode 100644 index 00000000..352870b4 --- /dev/null +++ b/Security/libsecurity_codesigning/lib/codedirectory.h @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2006-2012,2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// codedirectory - format and operations for code signing "code directory" structures +// +// A CodeDirectory is the top level object describing a particular instance +// of (static) code. It contains hashes of other objects that further describe +// parts of that code; these hashes hold the various pieces together. +// +// This means that if you reliably ascertain the contents of a CodeDirectory, +// you can verify the integrity of the entire code object it represents - the +// CodeDirectory can stand as a proxy for that code. +// +// Code signatures usually use CMS to sign the CodeDirectory to form full +// signature blobs; ad-hoc signatures simply record the SHA-1 hash of the +// CodeDirectory directly. The SHA-1 of the CodeDirectory is also widely +// used as concordance for a particular code instance - in essence, for +// different processes (or a process and the kernel) to "compare notes" +// to make sure they refer to the same code. +// +#ifndef _H_CODEDIRECTORY +#define _H_CODEDIRECTORY + +#include +#include +#include +#include +#include + + +namespace Security { +namespace CodeSigning { + + +// +// Conventional string names for various code signature components. +// Depending on storage, these may end up as filenames, extended attribute names, etc. +// +#define kSecCS_CODEDIRECTORYFILE "CodeDirectory" // CodeDirectory +#define kSecCS_SIGNATUREFILE "CodeSignature" // CMS Signature +#define kSecCS_REQUIREMENTSFILE "CodeRequirements" // internal requirements +#define kSecCS_RESOURCEDIRFILE "CodeResources" // resource directory +#define kSecCS_APPLICATIONFILE "CodeApplication" // application-specific resource +#define kSecCS_ENTITLEMENTFILE "CodeEntitlements" // entitlement configuration + + +// +// Special hash slot values. In a CodeDirectory, these show up at negative slot +// indices. This enumeration is also used widely in various internal APIs, and as +// type values in embedded SuperBlobs. +// +// How to add a new special slot type: +// 1. Add the new name at the end of the primary or virtual slot array (below). +// 2a. For slots representing existing code pieces, follow the ball for cdInfoSlot. +// 2b. For slots representing global signature components, follow the ball for cdResourceDirSlot. +// 2c. For slots representing per-architecture signature components, follow the ball for cdEntitlementSlot. +// ("Follow the ball" -> Global search for that name and do likewise.) +// +enum { + // + // Primary slot numbers. + // These values are potentially present in the CodeDirectory hash array + // under their negative values. They are also used in APIs and SuperBlobs. + // Note that zero must not be used for these (it's page 0 of the main code array), + // and it is important to assign contiguous (very) small values for them. + // + cdInfoSlot = 1, // Info.plist + cdRequirementsSlot = 2, // internal requirements + cdResourceDirSlot = 3, // resource directory + cdApplicationSlot = 4, // Application specific slot + cdEntitlementSlot = 5, // embedded entitlement configuration + // (add further primary slot numbers here) + + cdSlotCount, // total number of special slots (+1 for slot 0) + cdSlotMax = cdSlotCount - 1, // highest special slot number (as a positive number) + + // + // Virtual slot numbers. + // These values are NOT used in the CodeDirectory hash array. They are used as + // internal API identifiers and as types in SuperBlobs. + // Zero is okay to use here; and we assign that to the CodeDirectory itself so + // it shows up first in (properly sorted) SuperBlob indices. The rest of the + // numbers is set Far Away so the primary slot set can expand safely. + // It's okay to have large gaps in these assignments. + // + cdCodeDirectorySlot = 0, // CodeDirectory + cdSignatureSlot = 0x10000, // CMS signature + cdIdentificationSlot, // identification blob + // (add further virtual slot numbers here) +}; + + +// +// Special hash slot attributes. +// This is a central description of attributes of each slot. +// Various places in Code Signing pick up those attributes and act accordingly. +// +enum { + cdComponentPerArchitecture = 1, // slot value differs for each Mach-O architecture + cdComponentIsBlob = 2, // slot value is a Blob (need not be BlobWrapped) +}; + + +// +// A CodeDirectory is a typed Blob describing the secured pieces of a program. +// This structure describes the common header and provides access to the variable-size +// elements packed after it. For help in constructing a CodeDirectory, use the nested +// Builder class. +// +// At the heart of a CodeDirectory lies a packed array of hash digests. +// The array's zero-index element is at offset hashOffset, and the array covers +// elements in the range [-nSpecialSlots .. nCodeSlots-1]. Non-negative indices +// denote pages of the main executable. Negative indices indicate "special" hashes, +// each of a different thing (see cd*Slot constants above). +// Special slots that are in range but not present are zeroed out. Unallocated special +// slots are also presumed absent; this is not an error. (Thus the range of special +// slots can be extended at will.) +// +// HOW TO MANAGE COMPATIBILITY: +// Each CodeDirectory has a format (compatibility) version. Two constants control +// versioning: +// * currentVersion is the version used for newly created CodeDirectories. +// * compatibilityLimit is the highest version the code will accept as compatible. +// Test for version < currentVersion to detect old formats that may need special +// handling; this is done in checkIntegrity(). The current code rejects versions +// below earliestVersion. +// Break backward compatibility by rejecting versions that are unsuitable. +// Accept currentVersion < version <= compatibilityLimit as versions newer than +// those understood by this code but engineered (by newer code) to be backward +// compatible. Reject version > compatibilityLimit as incomprehensible gibberish. +// +// When creating a new version, increment currentVersion. When adding new fixed fields, +// just append them; the flex fields will shift to make room. To add new flex fields, +// add a fixed field containing the new field's offset and add suitable computations +// to the Builder to place the new data (right) before the hash array. Remember to check +// for offset in-range in checkIntegrity(). Older code will then simply ignore your +// new fields on load/read. +// Add flag bits to the existing flags field to add features that step outside +// of the linear versioning stream. Leave the 'spare' fields alone unless you need +// something extraordinarily weird - they're meant to be the final escape when everything +// else fails. +// As you create new versions, consider moving the compatibilityLimit out to open up +// new room for backward compatibility. +// To break backward compatibility intentionally, move currentVersion beyond the +// old compatibilityLimit (and move compatibilityLimit further out). +// +class CodeDirectory: public Blob { +public: + Endian version; // compatibility version + Endian flags; // setup and mode flags + Endian hashOffset; // offset of hash slot element at index zero + Endian identOffset; // offset of identifier string + Endian nSpecialSlots; // number of special hash slots + Endian nCodeSlots; // number of ordinary (code) hash slots + Endian codeLimit; // limit to main image signature range + uint8_t hashSize; // size of each hash digest (bytes) + uint8_t hashType; // type of hash (kSecCodeSignatureHash* constants) + uint8_t spare1; // unused (must be zero) + uint8_t pageSize; // log2(page size in bytes); 0 => infinite + Endian spare2; // unused (must be zero) + Endian scatterOffset; // offset of optional scatter vector (zero if absent) + Endian teamIDOffset; // offset of optional teamID string + + // works with the version field; see comments above + static const uint32_t currentVersion = 0x20200; // "version 2.2" + static const uint32_t compatibilityLimit = 0x2F000; // "version 3 with wiggle room" + + static const uint32_t earliestVersion = 0x20001; // earliest supported version + static const uint32_t supportsScatter = 0x20100; // first version to support scatter option + static const uint32_t supportsTeamID = 0x20200; // first version to support team ID option + + void checkIntegrity() const; // throws if inconsistent or unsupported version + + typedef uint32_t HashAlgorithm; // types of internal glue hashes + typedef int Slot; // slot index (negative for special slots) + typedef unsigned int SpecialSlot; // positive special slot index (not for code slots) + + const char *identifier() const { return at(identOffset); } + char *identifier() { return at(identOffset); } + + // main hash array access + SpecialSlot maxSpecialSlot() const; + + unsigned char *operator [] (Slot slot) + { + assert(slot >= int(-nSpecialSlots) && slot < int(nCodeSlots)); + return at(hashOffset) + hashSize * slot; + } + + const unsigned char *operator [] (Slot slot) const + { + assert(slot >= int(-nSpecialSlots) && slot < int(nCodeSlots)); + return at(hashOffset) + hashSize * slot; + } + + // + // The main page hash array can be "scattered" across the code file + // by specifying an array of Scatter elements, terminated with an + // element whose count field is zero. + // The scatter vector is optional; if absent, the hash array covers + // a single contiguous range of pages. CodeDirectory versions below + // supportsScatter never have scatter vectors (they lack the scatterOffset field). + // + struct Scatter { + Endian count; // number of pages; zero for sentinel (only) + Endian base; // first page number + Endian targetOffset; // byte offset in target + Endian spare; // reserved (must be zero) + }; + Scatter *scatterVector() // first scatter vector element (NULL if none) + { return (version >= supportsScatter && scatterOffset) ? at(scatterOffset) : NULL; } + const Scatter *scatterVector() const + { return (version >= supportsScatter && scatterOffset) ? at(scatterOffset) : NULL; } + + const char *teamID() const { return version >= supportsTeamID && teamIDOffset ? at(teamIDOffset) : NULL; } + char *teamID() { return version >= supportsTeamID && teamIDOffset ? at(teamIDOffset) : NULL; } + +public: + bool validateSlot(const void *data, size_t size, Slot slot) const; // validate memory buffer against page slot + bool validateSlot(UnixPlusPlus::FileDesc fd, size_t size, Slot slot) const; // read and validate file + bool slotIsPresent(Slot slot) const; + + class Builder; + +public: + static DynamicHash *hashFor(HashAlgorithm hashType); // create a DynamicHash subclass for (hashType) digests + DynamicHash *getHash() const { return hashFor(this->hashType); } // make one for me + + std::string hexHash(const unsigned char *hash) const; // encode any canonical-type hash as a hex string + +protected: + static size_t generateHash(DynamicHash *hash, UnixPlusPlus::FileDesc fd, Hashing::Byte *digest, size_t limit = 0); // hash to count or end of file + static size_t generateHash(DynamicHash *hash, const void *data, size_t length, Hashing::Byte *digest); // hash data buffer + +public: + // + // Information about SpecialSlots. + // This specifies meta-data about slots themselves; + // it does not work with the contents of hash slots. + // + static const char *canonicalSlotName(SpecialSlot slot); + static unsigned slotAttributes(SpecialSlot slot); + IFDEBUG(static const char * const debugSlotName[]); + +public: + // + // Canonical screening code. Requires a fully formed CodeDirectory. + // + std::string screeningCode() const; +}; + + +} // CodeSigning +} // Security + + +#endif //_H_CODEDIRECTORY