From 7d31e9289af373b98c8a36838bd41fab5ad01b44 Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 29 Aug 2007 22:27:19 +0000 Subject: [PATCH] libsecurity_codesigning-32568.tar.gz --- lib/CSCommon.h | 226 +++ lib/Code.cpp | 223 +++ lib/Code.h | 78 + lib/CodeSigner.cpp | 208 +++ lib/CodeSigner.h | 85 + lib/CodeSigning.h | 37 + lib/Requirements.cpp | 72 + lib/Requirements.h | 61 + lib/SecCode.cpp | 242 +++ lib/SecCode.h | 388 +++++ lib/SecCodeHost.cpp | 117 ++ lib/SecCodeHost.h | 232 +++ lib/SecCodeHostLib.c | 109 ++ lib/SecCodeHostLib.h | 103 ++ lib/SecCodeSigner.cpp | 94 ++ lib/SecCodeSigner.h | 120 ++ lib/SecIntegrity.cpp | 26 + lib/SecIntegrity.h | 49 + lib/SecIntegrityLib.c | 24 + lib/SecIntegrityLib.h | 55 + lib/SecRequirement.cpp | 162 ++ lib/SecRequirement.h | 172 ++ lib/SecStaticCode.cpp | 167 ++ lib/SecStaticCode.h | 124 ++ lib/StaticCode.cpp | 973 ++++++++++++ lib/StaticCode.h | 182 +++ lib/antlrplugin.cpp | 124 ++ lib/antlrplugin.h | 74 + lib/bundlediskrep.cpp | 272 ++++ lib/bundlediskrep.h | 113 ++ lib/cdbuilder.cpp | 173 ++ lib/cdbuilder.h | 78 + lib/cfmdiskrep.cpp | 216 +++ lib/cfmdiskrep.h | 111 ++ lib/cfmunge.cpp | 597 +++++++ lib/cfmunge.h | 136 ++ lib/codedirectory.cpp | 240 +++ lib/codedirectory.h | 233 +++ lib/cs.cpp | 46 + lib/cs.h | 142 ++ lib/cscdefs.c | 4 + lib/cscdefs.h | 89 ++ lib/cserror.cpp | 79 + lib/cserror.h | 65 + lib/csgeneric.cpp | 154 ++ lib/csgeneric.h | 79 + lib/cskernel.cpp | 137 ++ lib/cskernel.h | 83 + lib/csprocess.cpp | 51 + lib/csprocess.h | 66 + lib/csutilities.cpp | 60 + lib/csutilities.h | 52 + lib/diskrep.cpp | 181 +++ lib/diskrep.h | 131 ++ lib/filediskrep.cpp | 160 ++ lib/filediskrep.h | 91 ++ lib/foreigndiskrep.cpp | 185 +++ lib/foreigndiskrep.h | 92 ++ lib/kerneldiskrep.cpp | 81 + lib/kerneldiskrep.h | 62 + lib/macho++.cpp | 449 ++++++ lib/macho++.h | 158 ++ lib/machorep.cpp | 289 ++++ lib/machorep.h | 94 ++ lib/renum.cpp | 80 + lib/renum.h | 58 + lib/reqdumper.cpp | 287 ++++ lib/reqdumper.h | 100 ++ lib/reqinterp.cpp | 360 +++++ lib/reqinterp.h | 76 + lib/reqmaker.cpp | 154 ++ lib/reqmaker.h | 103 ++ lib/reqparser.cpp | 125 ++ lib/reqparser.h | 66 + lib/reqreader.cpp | 90 ++ lib/reqreader.h | 86 + lib/requirement.cpp | 159 ++ lib/requirement.h | 180 +++ lib/resources.cpp | 200 +++ lib/resources.h | 109 ++ lib/security_codesigning.exp | 93 ++ lib/sigblob.cpp | 55 + lib/sigblob.h | 66 + lib/signer.cpp | 284 ++++ lib/signer.h | 76 + lib/signerutils.cpp | 290 ++++ lib/signerutils.h | 235 +++ lib/singlediskrep.cpp | 115 ++ lib/singlediskrep.h | 87 + .../project.pbxproj | 1407 +++++++++++++++++ req/cfm.ireqs | 4 + req/ppc.ireqs | 4 + req/workaround.ireqs | 1 + requirements.grammar | 399 +++++ 94 files changed, 15125 insertions(+) create mode 100644 lib/CSCommon.h create mode 100644 lib/Code.cpp create mode 100644 lib/Code.h create mode 100644 lib/CodeSigner.cpp create mode 100644 lib/CodeSigner.h create mode 100644 lib/CodeSigning.h create mode 100644 lib/Requirements.cpp create mode 100644 lib/Requirements.h create mode 100644 lib/SecCode.cpp create mode 100644 lib/SecCode.h create mode 100644 lib/SecCodeHost.cpp create mode 100644 lib/SecCodeHost.h create mode 100644 lib/SecCodeHostLib.c create mode 100644 lib/SecCodeHostLib.h create mode 100644 lib/SecCodeSigner.cpp create mode 100644 lib/SecCodeSigner.h create mode 100644 lib/SecIntegrity.cpp create mode 100644 lib/SecIntegrity.h create mode 100644 lib/SecIntegrityLib.c create mode 100644 lib/SecIntegrityLib.h create mode 100644 lib/SecRequirement.cpp create mode 100644 lib/SecRequirement.h create mode 100644 lib/SecStaticCode.cpp create mode 100644 lib/SecStaticCode.h create mode 100644 lib/StaticCode.cpp create mode 100644 lib/StaticCode.h create mode 100644 lib/antlrplugin.cpp create mode 100644 lib/antlrplugin.h create mode 100644 lib/bundlediskrep.cpp create mode 100644 lib/bundlediskrep.h create mode 100644 lib/cdbuilder.cpp create mode 100644 lib/cdbuilder.h create mode 100644 lib/cfmdiskrep.cpp create mode 100644 lib/cfmdiskrep.h create mode 100644 lib/cfmunge.cpp create mode 100644 lib/cfmunge.h create mode 100644 lib/codedirectory.cpp create mode 100644 lib/codedirectory.h create mode 100644 lib/cs.cpp create mode 100644 lib/cs.h create mode 100644 lib/cscdefs.c create mode 100644 lib/cscdefs.h create mode 100644 lib/cserror.cpp create mode 100644 lib/cserror.h create mode 100644 lib/csgeneric.cpp create mode 100644 lib/csgeneric.h create mode 100644 lib/cskernel.cpp create mode 100644 lib/cskernel.h create mode 100644 lib/csprocess.cpp create mode 100644 lib/csprocess.h create mode 100644 lib/csutilities.cpp create mode 100644 lib/csutilities.h create mode 100644 lib/diskrep.cpp create mode 100644 lib/diskrep.h create mode 100644 lib/filediskrep.cpp create mode 100644 lib/filediskrep.h create mode 100644 lib/foreigndiskrep.cpp create mode 100644 lib/foreigndiskrep.h create mode 100644 lib/kerneldiskrep.cpp create mode 100644 lib/kerneldiskrep.h create mode 100644 lib/macho++.cpp create mode 100644 lib/macho++.h create mode 100644 lib/machorep.cpp create mode 100644 lib/machorep.h create mode 100644 lib/renum.cpp create mode 100644 lib/renum.h create mode 100644 lib/reqdumper.cpp create mode 100644 lib/reqdumper.h create mode 100644 lib/reqinterp.cpp create mode 100644 lib/reqinterp.h create mode 100644 lib/reqmaker.cpp create mode 100644 lib/reqmaker.h create mode 100644 lib/reqparser.cpp create mode 100644 lib/reqparser.h create mode 100644 lib/reqreader.cpp create mode 100644 lib/reqreader.h create mode 100644 lib/requirement.cpp create mode 100644 lib/requirement.h create mode 100644 lib/resources.cpp create mode 100644 lib/resources.h create mode 100644 lib/security_codesigning.exp create mode 100644 lib/sigblob.cpp create mode 100644 lib/sigblob.h create mode 100644 lib/signer.cpp create mode 100644 lib/signer.h create mode 100644 lib/signerutils.cpp create mode 100644 lib/signerutils.h create mode 100644 lib/singlediskrep.cpp create mode 100644 lib/singlediskrep.h create mode 100644 libsecurity_codesigning.xcodeproj/project.pbxproj create mode 100644 req/cfm.ireqs create mode 100644 req/ppc.ireqs create mode 100644 req/workaround.ireqs create mode 100644 requirements.grammar diff --git a/lib/CSCommon.h b/lib/CSCommon.h new file mode 100644 index 0000000..b1fd186 --- /dev/null +++ b/lib/CSCommon.h @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +/*! + @header CSCommon + CSCommon is the common header of all Code Signing API headers. + It defines types, constants, and error codes. +*/ +#ifndef _H_CSCOMMON +#define _H_CSCOMMON + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + + +/* + Code Signing specific OSStatus codes. + [Assigned range 0xFFFE_FAxx]. +*/ +enum { + errSecCSUnimplemented = -67072, /* unimplemented code signing feature */ + errSecCSInvalidObjectRef, /* invalid API object reference */ + errSecCSInvalidFlags, /* invalid or inapprpopriate API flag(s) specified */ + errSecCSObjectRequired, /* a required pointer argument was NULL */ + errSecCSStaticCodeNotFound, /* cannot find code object on disk */ + errSecCSUnsupportedGuestAttributes, /* cannot locate guests using this attribute set */ + errSecCSInvalidAttributeValues, /* given attribute values are invalid */ + errSecCSNoSuchCode, /* host has no guest with the requested attributes */ + errSecCSMultipleGuests, /* host has multiple guests with this attribute value */ + errSecCSGuestInvalid, /* code identity has been invalidated */ + errSecCSUnsigned, /* code object is not signed */ + errSecCSSignatureFailed, /* code or signature modified */ + errSecCSSignatureNotVerifiable, /* signature cannot be read, e.g., due to a filesystem that maps root to an unprivileged user */ + errSecCSSignatureUnsupported, /* unsupported type or version of signature */ + errSecCSBadDictionaryFormat, /* a required plist file or resource is malformed */ + errSecCSResourcesNotSealed, /* resources are not sealed by signature */ + errSecCSResourcesNotFound, /* cannot find sealed resources in code */ + errSecCSResourcesInvalid, /* the sealed resource directory is invalid */ + errSecCSBadResource, /* a sealed resource is missing or invalid */ + errSecCSResourceRulesInvalid, /* invalid resource selection rule(s) */ + errSecCSReqInvalid, /* invalid or corrupted code requirement(s) */ + errSecCSReqUnsupported, /* unsupported type or version of code requirement(s) */ + errSecCSReqFailed, /* failed to satisfy code requirement(s) */ + errSecCSBadObjectFormat, /* object file format invalid or unsuitable */ + errSecCSInternalError, /* internal error in Code Signing subsystem */ + errSecCSHostReject, /* code rejected its host */ + errSecCSNotAHost, /* this code is not a host */ + errSecCSSignatureInvalid, /* invalid format for signature */ + errSecCSHostProtocolRelativePath, /* host protocol violation - absolute guest path required */ + errSecCSHostProtocolContradiction, /* host protocol violation - contradictory hosting modes */ + errSecCSHostProtocolDedicationError, /* host protocol violation - operation not allowed with/for a dedicated guest */ + errSecCSHostProtocolNotProxy, /* host protocol violation - proxy hosting not engaged */ + errSecCSHostProtocolStateError, /* host protocol violation - invalid guest state change request */ + errSecCSHostProtocolUnrelated, /* host protocol violation - the given guest is not a guest of the given host */ +}; + + +/* + * Code Signing specific CFError "user info" keys. + * In calls that can return CFErrorRef indications, if a CFErrorRef is actually + * returned, its "user info" dictionary will contain some (but not all) of the + * following keys to more closely describe the circumstances of the failure. + */ +extern const CFStringRef kSecCFErrorPattern; /* CFStringRef: invalid resource selection pattern encountered */ +extern const CFStringRef kSecCFErrorResourceSeal; /* CFTypeRef: invalid component in resource seal (CodeResources) */ +extern const CFStringRef kSecCFErrorResourceAdded; /* CFURLRef: unsealed resource found */ +extern const CFStringRef kSecCFErrorResourceAltered; /* CFURLRef: modified resource found */ +extern const CFStringRef kSecCFErrorResourceMissing; /* CFURLRef: sealed (non-optional) resource missing */ +extern const CFStringRef kSecCFErrorInfoPlist; /* CFTypeRef: Info.plist dictionary or component found invalid */ +extern const CFStringRef kSecCFErrorGuestAttributes; /* CFTypeRef: Guest attribute set of element not accepted */ +extern const CFStringRef kSecCFErrorRequirementSyntax; /* CFStringRef: compilation error for Requirement source */ + + +/*! + @typedef SecCodeRef + This is the type of a reference to running code. + + In many (but not all) calls, this can be passed to a SecStaticCodeRef + argument, which performs an implicit SecCodeCopyStaticCode call and + operates on the result. +*/ +typedef struct __SecCode *SecCodeRef; /* running code */ + +/*! + @typedef SecStaticCodeRef + This is the type of a reference to static code on disk. +*/ +typedef struct __SecCode const *SecStaticCodeRef; /* code on disk */ + +/*! + @typedef SecRequirementRef + This is the type of a reference to a code requirement. +*/ +typedef struct __SecRequirement *SecRequirementRef; /* code requirement */ + + +/*! + @typedef SecGuestRef + An abstract handle to identify a particular Guest in the context of its Host. + + Guest handles are assigned by the host at will, with kSecNoGuest (zero) being + reserved as the null value). They can be reused for new children if desired. +*/ +typedef u_int32_t SecGuestRef; + +enum { + kSecNoGuest = 0, /* not a valid SecGuestRef */ +}; + + +/*! + @typddef SecCSFlags + This is the type of flags arguments to Code Signing API calls. + It provides a bit mask of request and option flags. All of the bits in these + masks are reserved to Apple; if you set any bits not defined in these headers, + the behavior is generally undefined. + + This list describes the flags that are shared among several Code Signing API calls. + Flags that only apply to one call are defined and documented with that call. + Global flags are assigned from high order down (31 -> 0); call-specific flags + are assigned from the bottom up (0 -> 31). + + @constant kSecCSDefaultFlags + When passed to a flags argument throughout, indicates that default behavior + is desired. Do not mix with other flags values. + @constant kSecCSConsiderExpiration + When passed to a call that performs code validation, requests that code signatures + made by expired certificates be rejected. By default, expiration of participating + certificates is not automatic grounds for rejection. +*/ +typedef uint32_t SecCSFlags; + +enum { + kSecCSDefaultFlags = 0, /* no particular flags (default behavior) */ + + kSecCSConsiderExpiration = 1 << 31, /* consider expired certificates invalid */ +}; + + +/*! + @typedef SecCodeSignatureFlags + This is the type of option flags that can be embedded in a code signature + during signing, and that govern the use of the signature thereafter. + Some of these flags can be set through the codesign(1) command's --options + argument; some are set implicitly based on signing circumstances; and all + can be set with the kSecCodeSignerFlags item of a signing information dictionary. + + @constant kSecCodeSignatureHost + Indicates that the code may act as a host that controls and supervises guest + code. If this flag is not set in a code signature, the code is never considered + eligible to be a host, and any attempt to act like one will be ignored. + @constant kSecCodeSignatureAdhoc + The code has been sealed without a signing identity. No identity may be retrieved + from it, and any code requirement placing restrictions on the signing identity + will fail. This flag is set by the code signing API and cannot be set explicitly. + @constant kSecCodeSignatureForceHard + Implicitly set the "hard" status bit for the code when it starts running. + This bit indicates that the code prefers to be denied access to a resource + if gaining such access would cause its invalidation. Since the hard bit is + sticky, setting this option bit guarantees that the code will always have + it set. + @constant kSecCodeSignatureForceKill + Implicitly set the "kill" status bit for the code when it starts running. + This bit indicates that the code wishes to be terminated with prejudice if + it is ever invalidated. Since the kill bit is sticky, setting this option bit + guarantees that the code will always be valid, since it will die immediately + if it becomes invalid. + @constant kSecCodeSignatureForceExpiration + Forces the kSecCSConsiderExpiration on all validations of the code. + */ +typedef uint32_t SecCodeSignatureFlags; + +enum { + kSecCodeSignatureHost = 0x0001, /* may host guest code */ + kSecCodeSignatureAdhoc = 0x0002, /* must be used without signer */ + kSecCodeSignatureForceHard = 0x0100, /* always set HARD mode on launch */ + kSecCodeSignatureForceKill = 0x0200, /* always set KILL mode on launch */ + kSecCodeSignatureForceExpiration = 0x0400, /* force certificat expiration checks */ +}; + + +/*! + @typedef SecRequirementType + An enumeration indicating different types of internal requirements for code. +*/ +typedef uint32_t SecRequirementType; + +enum { + kSecHostRequirementType = 1, /* what hosts may run us */ + kSecGuestRequirementType = 2, /* what guests we may run */ + kSecDesignatedRequirementType = 3, /* designated requirement */ + kSecLibraryRequirementType = 4, /* what libraries we may link against */ + kSecInvalidRequirementType, /* invalid type of Requirement (must be last) */ + kSecRequirementTypeCount = kSecInvalidRequirementType /* number of valid requirement types */ +}; + + +#ifdef __cplusplus +} +#endif + +#endif //_H_CSCOMMON diff --git a/lib/Code.cpp b/lib/Code.cpp new file mode 100644 index 0000000..d1ad41e --- /dev/null +++ b/lib/Code.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// Code - SecCode API objects +// +#include "Code.h" +#include "StaticCode.h" +#include +#include "cskernel.h" +#include "cfmunge.h" +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// Construction +// +SecCode::SecCode(SecCode *host) + : mHost(host) +{ +} + + +// +// Clean up a SecCode object +// +SecCode::~SecCode() throw() +{ +} + + +// +// Yield the host Code +// +SecCode *SecCode::host() const +{ + return mHost; +} + + +// +// Yield the static code. This is cached. +// The caller does not own the object returned; it lives (at least) as long +// as the SecCode it was derived from. +// +SecStaticCode *SecCode::staticCode() +{ + if (!mStaticCode) { + mStaticCode.take(this->getStaticCode()); + secdebug("seccode", "%p got static=%p", this, mStaticCode.get()); + } + assert(mStaticCode); + return mStaticCode; +} + + +// +// By default, we have no guests +// +SecCode *SecCode::locateGuest(CFDictionaryRef) +{ + return NULL; +} + + +// +// By default, we map ourselves to disk using our host's mapping facility. +// (This is currently only overridden in the root-of-trust (kernel) implementation.) +// The caller owns the object returned. +// +SecStaticCode *SecCode::getStaticCode() +{ + return host()->mapGuestToStatic(this); +} + + +// +// The default implementation cannot map guests to disk +// +SecStaticCode *SecCode::mapGuestToStatic(SecCode *guest) +{ + MacOSError::throwMe(errSecCSNoSuchCode); +} + + +// +// Master validation function. +// +// This is the most important function in all of Code Signing. It performs +// dynamic validation on running code. Despite its simple structure, it does +// everything that's needed to establish whether a Code is currently valid... +// with a little help from StaticCode, format drivers, type drivers, and so on. +// +// This function validates internal requirements in the hosting chain. It does +// not validate external requirements - the caller needs to do that with a separate call. +// +static const uint8_t interim_hosting_default_requirement[] = { + // anchor apple and (identifier com.apple.translate or identifier com.apple.LaunchCFMApp) + 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, + 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, + 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x63, 0x6f, 0x6d, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x43, 0x46, 0x4d, 0x41, + 0x70, 0x70, 0x00, 0x00, +}; + +void SecCode::checkValidity(SecCSFlags flags) +{ + if (this->isRoot()) { + // the root-of-trust is valid by definition + secdebug("validator", "%p root of trust is presumed valid", this); + return; + } + secdebug("validator", "%p begin validating %s", + this, this->staticCode()->mainExecutablePath().c_str()); + + // + // Do not reorder the operations below without thorough cogitation. There are + // interesting dependencies and significant performance issues. + // + // For the most part, failure of (secure) identity will cause exceptions to be + // thrown, and success is indicated by survival. If you make it to the end, + // you have won the validity race. (Good rat.) + // + + // check my host first, recursively + this->host()->checkValidity(flags); + + SecStaticCode *myDisk = this->staticCode(); + SecStaticCode *hostDisk = this->host()->staticCode(); + + // check my static state + myDisk->validateDirectory(); + + // check my own dynamic state + if (!(this->host()->getGuestStatus(this) & CS_VALID)) + MacOSError::throwMe(errSecCSGuestInvalid); + + // check host/guest constraints + if (!this->host()->isRoot()) { // not hosted by root of trust + myDisk->validateRequirements(kSecHostRequirementType, hostDisk, errSecCSHostReject); + hostDisk->validateRequirements(kSecGuestRequirementType, myDisk); + } + + secdebug("validator", "%p validation successful", this); +} + + +// +// By default, we track no validity for guests (we don't have any) +// +uint32_t SecCode::getGuestStatus(SecCode *guest) +{ + MacOSError::throwMe(errSecCSNoSuchCode); +} + + +// +// Given a bag of attribute values, automagically come up with a SecCode +// without any other information. +// This is meant to be the "just do what makes sense" generic call, for callers +// who don't want to engage in the fascinating dance of manual guest enumeration. +// +// Note that we expect the logic embedded here to change over time (in backward +// compatible fashion, one hopes), and that it's all right to use heuristics here +// as long as it's done sensibly. +// +// Be warned that the present logic is quite a bit ad-hoc, and will likely not +// handle arbitrary combinations of proxy hosting, dynamic hosting, and dedicated +// hosting all that well. +// +SecCode *SecCode::autoLocateGuest(CFDictionaryRef attributes, SecCSFlags flags) +{ + // main logic: we need a pid, and we'll take a canonical guest id as an option + int pid = 0; + if (!cfscan(attributes, "{%O=%d}", kSecGuestAttributePid, &pid)) + CSError::throwMe(errSecCSUnsupportedGuestAttributes, kSecCFErrorGuestAttributes, attributes); + if (SecCode *process = + KernelCode::active()->locateGuest(CFTemp("{%O=%d}", kSecGuestAttributePid, pid))) { + SecPointer code; + code.take(process); // locateGuest gave us a retained object + if (code->staticCode()->flag(kSecCodeSignatureHost)) { + // might be a code host. Let's find out + CFRef rest = CFDictionaryCreateMutableCopy(NULL, 0, attributes); + CFDictionaryRemoveValue(rest, kSecGuestAttributePid); + if (SecCode *guest = code->locateGuest(rest)) + return guest; + } + if (!CFDictionaryGetValue(attributes, kSecGuestAttributeCanonical)) { + // only "soft" attributes, and no hosting is happening. Return the (non-)host itself + return code.yield(); + } + } + MacOSError::throwMe(errSecCSNoSuchCode); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/Code.h b/lib/Code.h new file mode 100644 index 0000000..fca2dfd --- /dev/null +++ b/lib/Code.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// Code - SecCode API objects +// +#ifndef _H_CODE +#define _H_CODE + +#include "cs.h" +#include "Requirements.h" +#include + +namespace Security { +namespace CodeSigning { + + +class SecStaticCode; + + +// +// A SecCode object represents running code in the system. It must be subclassed +// to implement a particular notion of code. +// +class SecCode : public SecCFObject { + NOCOPY(SecCode) +public: + SECCFFUNCTIONS(SecCode, SecCodeRef, errSecCSInvalidObjectRef, gCFObjects().Code) + + SecCode(SecCode *host); + virtual ~SecCode() throw(); + + SecCode *host() const; + bool isRoot() const { return host() == NULL; } + SecStaticCode *staticCode(); // cached. Result lives as long as this SecCode + + // primary virtual drivers. Caller owns the result + virtual SecStaticCode *getStaticCode(); + virtual SecCode *locateGuest(CFDictionaryRef attributes); + virtual SecStaticCode *mapGuestToStatic(SecCode *guest); + + void checkValidity(SecCSFlags flags); + virtual uint32_t getGuestStatus(SecCode *guest); + +public: + // perform "autolocation" (root-based heuristic). Caller owns the result + static SecCode *autoLocateGuest(CFDictionaryRef attributes, SecCSFlags flags); + +private: + SecPointer mHost; + SecPointer mStaticCode; +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_CODE diff --git a/lib/CodeSigner.cpp b/lib/CodeSigner.cpp new file mode 100644 index 0000000..6514c24 --- /dev/null +++ b/lib/CodeSigner.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// CodeSigner - SecCodeSigner API objects +// +#include "CodeSigner.h" +#include "signer.h" +#include "reqparser.h" +#include "renum.h" +#include +#include +#include + +namespace Security { + +__SEC_CFTYPE(SecIdentity) + +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// The allocation helper +// +static const char helperName[] = "codesign_allocate"; +static const char helperPath[] = "/usr/bin/codesign_allocate"; +static const size_t csAlign = 16; + + +// +// A helper for parsing out a CFDictionary signing-data specification +// +class SecCodeSigner::Parser : CFDictionary { +public: + Parser(SecCodeSigner &signer, CFDictionaryRef parameters); + + bool getBool(CFStringRef key) const + { + if (CFBooleanRef flag = get(key)) + return flag == kCFBooleanTrue; + else + return false; + } +}; + + +// +// Construct a SecCodeSigner +// +SecCodeSigner::SecCodeSigner() + : mRequirements(NULL) +{ + secdebug("signer", "%p created", this); +} + + +// +// Clean up a SecCodeSigner +// +SecCodeSigner::~SecCodeSigner() throw() +{ + secdebug("signer", "%p destroyed", this); + ::free((Requirements *)mRequirements); +} + + +// +// Parse an input parameter dictionary and set ready-to-use parameters +// +void SecCodeSigner::parameters(CFDictionaryRef paramDict) +{ + secdebug("signer", "%p loading %d parameters", this, int(CFDictionaryGetCount(paramDict))); + Parser(*this, paramDict); + if (!valid()) + MacOSError::throwMe(errSecCSInvalidObjectRef); +} + + +// +// Sign code +// +void SecCodeSigner::sign(SecStaticCode *code, SecCSFlags flags) +{ + if (!valid()) + MacOSError::throwMe(errSecCSInvalidObjectRef); + secdebug("signer", "%p will sign %p (flags 0x%x)", this, code, flags); + Signer(*this, code).sign(flags); + code->resetValidity(); +} + + +// +// ReturnDetachedSignature is called by writers or editors that try to return +// detached signature data (rather than annotate the target). +// +void SecCodeSigner::returnDetachedSignature(BlobCore *blob) +{ + assert(mDetached); + if (CFGetTypeID(mDetached) == CFURLGetTypeID()) { + // URL to destination file + AutoFileDesc fd(cfString(CFURLRef(mDetached.get())), O_WRONLY | O_CREAT | O_TRUNC); + fd.writeAll(*blob); + } else if (CFGetTypeID(mDetached) == CFDataGetTypeID()) { + CFDataAppendBytes(CFMutableDataRef(mDetached.get()), + (const UInt8 *)blob, blob->length()); + } else + assert(false); +} + + +// +// The actual parsing operation is done in the Parser class. +// +// Note that we need to copy or retain all incoming data. The caller has no requirement +// to keep the parameters dictionary around. +// +SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters) + : CFDictionary(parameters, errSecCSBadDictionaryFormat) +{ + // the signer may be an identity or null + if (CFTypeRef signer = get(kSecCodeSignerIdentity)) + if (CFGetTypeID(signer) == SecIdentityGetTypeID() || signer == kCFNull) + state.mSigner = SecIdentityRef(signer); + else + MacOSError::throwMe(errSecCSInvalidObjectRef); + else + MacOSError::throwMe(errSecCSInvalidObjectRef); + + // the flags need some augmentation + if (CFNumberRef flags = get(kSecCodeSignerFlags)) { + state.mCdFlagsGiven = true; + state.mCdFlags = cfNumber(flags); + } else + state.mCdFlagsGiven = false; + + if (CFNumberRef cmsSize = get(CFSTR("cmssize"))) + state.mCMSSize = cfNumber(cmsSize); + else + state.mCMSSize = 5000; // likely big enough + + // signing time can be a CFDateRef or null + if (CFTypeRef time = get(kSecCodeSignerSigningTime)) + if (CFGetTypeID(time) == CFDateGetTypeID() || time == kCFNull) + state.mSigningTime = CFDateRef(time); + else + MacOSError::throwMe(errSecCSInvalidObjectRef); + + if (CFStringRef ident = get(kSecCodeSignerIdentifier)) + state.mIdentifier = cfString(ident); + + if (CFStringRef prefix = get(kSecCodeSignerIdentifierPrefix)) + state.mIdentifierPrefix = cfString(prefix); + + // requirements can be binary or string (to be compiled) + if (CFTypeRef reqs = get(kSecCodeSignerRequirements)) { + if (CFGetTypeID(reqs) == CFDataGetTypeID()) { // binary form + const Requirements *rp = (const Requirements *)CFDataGetBytePtr(CFDataRef(reqs)); + state.mRequirements = rp->clone(); + } else if (CFGetTypeID(reqs) == CFStringGetTypeID()) { // text form + state.mRequirements = parseRequirements(cfString(CFStringRef(reqs))); + } else + MacOSError::throwMe(errSecCSInvalidObjectRef); + } else + state.mRequirements = NULL; + + state.mNoMachO = getBool(CFSTR("no-macho")); + + state.mPageSize = get(kSecCodeSignerPageSize); + + // detached can be (destination) file URL or (mutable) Data to be appended-to + if (state.mDetached = get(kSecCodeSignerDetached)) { + if (CFGetTypeID(state.mDetached) != CFURLGetTypeID() + && CFGetTypeID(state.mDetached) != CFDataGetTypeID()) + MacOSError::throwMe(errSecCSInvalidObjectRef); + } + + state.mDryRun = getBool(kSecCodeSignerDryRun); + + state.mResourceRules = get(kSecCodeSignerResourceRules); + + state.mApplicationData = get(kSecCodeSignerApplicationData); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/CodeSigner.h b/lib/CodeSigner.h new file mode 100644 index 0000000..150d528 --- /dev/null +++ b/lib/CodeSigner.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// CodeSigner - SecCodeSigner API objects +// +#ifndef _H_CODESIGNER +#define _H_CODESIGNER + +#include "cs.h" +#include "StaticCode.h" +#include "cdbuilder.h" +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// A SecCode object represents running code in the system. It must be subclassed +// to implement a particular notion of code. +// +class SecCodeSigner : public SecCFObject { + NOCOPY(SecCodeSigner) +public: + SECCFFUNCTIONS(SecCodeSigner, SecCodeSignerRef, errSecCSInvalidObjectRef, gCFObjects().CodeSigner) + + SecCodeSigner(); + virtual ~SecCodeSigner() throw(); + + void parameters(CFDictionaryRef args); // parse and set parameters + bool valid() const { return mSigner; } + + void sign(SecStaticCode *code, SecCSFlags flags); + + void returnDetachedSignature(BlobCore *blob); + +public: + class Parser; + class Signer; + +private: + // parsed parameter set + CFRef mSigner; // signing identity + CFRef mDetached; // detached-signing information (NULL => attached) + CFRef mResourceRules; // explicit resource collection rules (override) + CFRef mSigningTime; // signing time desired (kCFNull for none) + CFRef mApplicationData; // contents of application slot + const Requirements *mRequirements; // internal code requirements + size_t mCMSSize; // size estimate for CMS blob + uint32_t mCdFlags; // CodeDirectory flags + bool mCdFlagsGiven; // CodeDirectory flags were specified + std::string mIdentifier; // unique identifier override + std::string mIdentifierPrefix; // prefix for un-dotted default identifiers + bool mNoMachO; // override to perform non-Mach-O signing + bool mDryRun; // dry run (do not change target) + CFRef mPageSize; // main executable page size +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_CODESIGNER diff --git a/lib/CodeSigning.h b/lib/CodeSigning.h new file mode 100644 index 0000000..a33e9e7 --- /dev/null +++ b/lib/CodeSigning.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#ifndef _H_CODESIGNING +#define _H_CODESIGNING + + +/*! + @header CodeSigning + This header file includes all the headers that are needed to use + the client interface to Code Signing. + It does not include headers for the other Code Signing related interfaces. + */ +#include +#include +#include + +#endif //_H_CODESIGNING diff --git a/lib/Requirements.cpp b/lib/Requirements.cpp new file mode 100644 index 0000000..e6b05a2 --- /dev/null +++ b/lib/Requirements.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// Requirements - SecRequirement API objects +// +#include "Requirements.h" + +namespace Security { +namespace CodeSigning { + + +// +// Create from a Requirement blob in memory +// +SecRequirement::SecRequirement(const void *data, size_t length) + : mReq(NULL) +{ + const Requirement *req = (const Requirement *)data; + if (!req->validateBlob(length)) + MacOSError::throwMe(errSecCSReqInvalid); + mReq = req->clone(); +} + + +// +// Create from a genuine Requirement object +// +SecRequirement::SecRequirement(const Requirement *req, bool transferOwnership) + : mReq(NULL) +{ + if (!req->validateBlob()) + MacOSError::throwMe(errSecCSReqInvalid); + + if (transferOwnership) + mReq = req; + else + mReq = req->clone(); +} + + +// +// Clean up a SecRequirement object +// +SecRequirement::~SecRequirement() throw() +{ + ::free((void *)mReq); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/Requirements.h b/lib/Requirements.h new file mode 100644 index 0000000..dd3996d --- /dev/null +++ b/lib/Requirements.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// Requirements - SecRequirement API objects +// +#ifndef _H_REQUIREMENTS +#define _H_REQUIREMENTS + +#include "cs.h" +#include "requirement.h" + +namespace Security { +namespace CodeSigning { + + +// +// A SecRequirement object acts as the API representation for a code +// requirement. All its semantics are within the Requirement object it holds. +// The SecRequirement just manages the API appearances. +// +class SecRequirement : public SecCFObject { + NOCOPY(SecRequirement) +public: + SECCFFUNCTIONS(SecRequirement, SecRequirementRef, errSecCSInvalidObjectRef, gCFObjects().Requirement) + + SecRequirement(const void *data, size_t length); + SecRequirement(const Requirement *req, bool transferOwnership = false); + virtual ~SecRequirement() throw(); + + const Requirement *requirement() const { return mReq; } + +private: + const Requirement *mReq; +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_REQUIREMENTS diff --git a/lib/SecCode.cpp b/lib/SecCode.cpp new file mode 100644 index 0000000..9eaa69f --- /dev/null +++ b/lib/SecCode.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// SecCode - API frame for SecCode objects. +// +// Note that some SecCode* functions take SecStaticCodeRef arguments in order to +// accept either static or dynamic code references, operating on the respective +// StaticCode. Those functions are in SecStaticCode.cpp, not here, despite their name. +// +#include "cs.h" +#include "Code.h" +#include "cskernel.h" +#include +#include + +using namespace CodeSigning; + + +// +// CFError user info keys +// +const CFStringRef kSecCFErrorPattern = CFSTR("SecCSPattern"); +const CFStringRef kSecCFErrorResourceSeal = CFSTR("SecCSResourceSeal"); +const CFStringRef kSecCFErrorResourceAdded = CFSTR("SecCSResourceAdded"); +const CFStringRef kSecCFErrorResourceAltered = CFSTR("SecCSResourceAltered"); +const CFStringRef kSecCFErrorResourceMissing = CFSTR("SecCSResourceMissing"); +const CFStringRef kSecCFErrorInfoPlist = CFSTR("SecCSInfoPlist"); +const CFStringRef kSecCFErrorGuestAttributes = CFSTR("SecCSGuestAttributes"); +const CFStringRef kSecCFErrorRequirementSyntax = CFSTR("SecRequirementSyntax"); + +// +// CF-standard type code functions +// +CFTypeID SecCodeGetTypeID(void) +{ + BEGIN_CSAPI + return gCFObjects().Code.typeID; + END_CSAPI1(_kCFRuntimeNotATypeID) +} + + +// +// Get the root of trust Code +// +SecCodeRef SecGetRootCode(SecCSFlags flags) +{ + BEGIN_CSAPI + + checkFlags(flags); + return KernelCode::active()->handle(); + + END_CSAPI1(NULL) +} + + +// +// Get a reference to the calling code. +// +OSStatus SecCodeCopySelf(SecCSFlags flags, SecCodeRef *selfRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + CFRef attributes = makeCFMutableDictionary(1, + kSecGuestAttributePid, CFTempNumber(getpid()).get()); + Required(selfRef) = SecCode::autoLocateGuest(attributes, flags)->handle(false); + + END_CSAPI +} + + +// +// Get the StaticCode for an Code +// +OSStatus SecCodeCopyStaticCode(SecCodeRef codeRef, SecCSFlags flags, SecStaticCodeRef *staticCodeRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + SecPointer staticCode = SecCode::required(codeRef)->staticCode(); + Required(staticCodeRef) = staticCode ? staticCode->handle() : NULL; + + END_CSAPI +} + + +// +// Get the host for an Code +// +OSStatus SecCodeCopyHost(SecCodeRef guestRef, SecCSFlags flags, SecCodeRef *hostRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + SecPointer host = SecCode::required(guestRef)->host(); + Required(hostRef) = host ? host->handle() : NULL; + + END_CSAPI +} + + +// +// Find a guest by attribute(s) +// +const CFStringRef kSecGuestAttributePid = CFSTR("pid"); +const CFStringRef kSecGuestAttributeCanonical = CFSTR("canonical"); +const CFStringRef kSecGuestAttributeMachPort = CFSTR("mach-port"); + +OSStatus SecCodeCopyGuestWithAttributes(SecCodeRef hostRef, + CFDictionaryRef attributes, SecCSFlags flags, SecCodeRef *guestRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + if (hostRef) { + if (SecCode *guest = SecCode::required(hostRef)->locateGuest(attributes)) + Required(guestRef) = guest->handle(false); + else + return errSecCSNoSuchCode; + } else + Required(guestRef) = SecCode::autoLocateGuest(attributes, flags)->handle(false); + + END_CSAPI +} + + +// +// Shorthand for getting the SecCodeRef for a UNIX process +// +OSStatus SecCodeCreateWithPID(pid_t pid, SecCSFlags flags, SecCodeRef *processRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + if (SecCode *guest = KernelCode::active()->locateGuest(CFTemp("{%O=%d}", kSecGuestAttributePid, pid))) + Required(processRef) = guest->handle(false); + else + return errSecCSNoSuchCode; + + END_CSAPI +} + + +// +// Check validity of an Code +// +OSStatus SecCodeCheckValidity(SecCodeRef codeRef, SecCSFlags flags, + SecRequirementRef requirementRef) +{ + return SecCodeCheckValidityWithErrors(codeRef, flags, requirementRef, NULL); +} + +OSStatus SecCodeCheckValidityWithErrors(SecCodeRef codeRef, SecCSFlags flags, + SecRequirementRef requirementRef, CFErrorRef *errors) +{ + BEGIN_CSAPI + + checkFlags(flags, + kSecCSConsiderExpiration); + SecPointer code = SecCode::required(codeRef); + code->checkValidity(flags); + if (const SecRequirement *req = SecRequirement::optional(requirementRef)) + code->staticCode()->validateRequirements(req->requirement(), errSecCSReqFailed); + + END_CSAPI_ERRORS +} + + +// +// Collect suitably laundered information about the code signature of a SecStaticCode +// and return it as a CFDictionary. +// +// This API contracts to return a few pieces of information even for unsigned +// code. This means that a SecStaticCodeRef is usable as a basic indentifier +// (i.e. handle) for any code out there. +// +const CFStringRef kSecCodeInfoCertificates = CFSTR("certificates"); +const CFStringRef kSecCodeInfoChangedFiles = CFSTR("changed-files"); +const CFStringRef kSecCodeInfoCMS = CFSTR("cms"); +const CFStringRef kSecCodeInfoDesignatedRequirement = CFSTR("designated-requirement"); +const CFStringRef kSecCodeInfoTime = CFSTR("signing-time"); +const CFStringRef kSecCodeInfoFormat = CFSTR("format"); +const CFStringRef kSecCodeInfoIdentifier = CFSTR("identifier"); +const CFStringRef kSecCodeInfoImplicitDesignatedRequirement = CFSTR("implicit-requirement"); +const CFStringRef kSecCodeInfoMainExecutable = CFSTR("main-executable"); +const CFStringRef kSecCodeInfoPList = CFSTR("info-plist"); +const CFStringRef kSecCodeInfoRequirements = CFSTR("requirements"); +const CFStringRef kSecCodeInfoStatus = CFSTR("status"); +const CFStringRef kSecCodeInfoTrust = CFSTR("trust"); + +OSStatus SecCodeCopySigningInformation(SecStaticCodeRef codeRef, SecCSFlags flags, + CFDictionaryRef *infoRef) +{ + BEGIN_CSAPI + + checkFlags(flags, + kSecCSInternalInformation + | kSecCSSigningInformation + | kSecCSRequirementInformation + | kSecCSDynamicInformation + | kSecCSContentInformation); + + SecPointer code = SecStaticCode::requiredStatic(codeRef); + CFRef info = code->signingInformation(flags); + + if (flags & kSecCSDynamicInformation) + if (SecPointer dcode = SecStaticCode::optionalDynamic(codeRef)) { + uint32_t status; + if (SecPointer host = dcode->host()) + status = host->getGuestStatus(dcode); + else + status = CS_VALID; // root of trust, presumed valid + info = cfmake("{+%O,%O=%u}", info.get(), + kSecCodeInfoStatus, status); + } + + Required(infoRef) = info.yield(); + + END_CSAPI +} + diff --git a/lib/SecCode.h b/lib/SecCode.h new file mode 100644 index 0000000..471de30 --- /dev/null +++ b/lib/SecCode.h @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +/*! + @header SecCode + SecCode represents separately indentified running code in the system. + In addition to UNIX processes, this can also include (with suitable support) + scripts, applets, widgets, etc. +*/ +#ifndef _H_SECCODE +#define _H_SECCODE + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + +/*! + @function SecCodeGetTypeID + Returns the type identifier of all SecCode instances. +*/ +CFTypeID SecCodeGetTypeID(void); + + +/*! + @function SecGetRootCode + Obtains a SecCode object for the running code that hosts the entire system. + This object usually represents the system kernel. It is always considered + valid and is implicitly trusted by everyone. + + This object has no host. + + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + + @result An object reference to the root Code. This call should never fail. + If this call returns NULL, Code Signing is unusable. +*/ +SecCodeRef SecGetRootCode(SecCSFlags flags); + + +/*! + @function SecCodeCopySelf + Obtains a SecCode object for the code making the call. + The calling code is determined in a way that is subject to modification over + time, but obeys the following rules. If it is a UNIX process, its process id (pid) + is always used. If it is an active code host that has a dedicated guest, such a guest + is always preferred. If it is a host that has called SecHostSelectGuest, such selection + is considered until revoked. + + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. + */ +OSStatus SecCodeCopySelf(SecCSFlags flags, SecCodeRef *self); + + + +/*! + @function SecCodeCopyStaticCode + Given a SecCode object, locate its origin in the file system and return + a SecStaticCode object representing it. + + The link established by this call is generally reliable but is NOT guaranteed + to be secure. + + Many API functions taking SecStaticCodeRef arguments will also directly + accept a SecCodeRef and apply this translation implicitly, operating on + its result or returning its error code if any. Each of these functions + calls out that behavior in its documentation. + + @param code A valid SecCode object reference representing code running + on the system. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param staticCode On successful return, a SecStaticCode object reference representing + the file system origin of the given SecCode. On error, unchanged. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecCodeCopyStaticCode(SecCodeRef code, SecCSFlags flags, SecStaticCodeRef *staticCode); + + +/*! + @function SecCodeCopyHost + Given a SecCode object, identify the (different) SecCode object that acts + as its host. A SecCode's host acts as a supervisor and controller, + and is the ultimate authority on the its dynamic validity and status. + The host relationship is securely established (absent reported errors). + + @param code A valid SecCode object reference representing code running + on the system. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param host On successful return, a SecCode object reference identifying + the code's host. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecCodeCopyHost(SecCodeRef guest, SecCSFlags flags, SecCodeRef *host); + + +/*! + @function SecCodeCopyGuestWithAttributes + Asks a SecCode object acting as a Code Signing host to identify one of + its guests by the type and value of specific attribute(s). Different hosts + support different types and combinations of attributes. + + The methods a host uses to identify, separate, and control its guests + are specific to each type of host. This call provides a generic abstraction layer + that allows uniform interrogation of all hosts. A SecCode that does not + act as a host will always return errSecCSNoSuchCode. A SecCode that does + support hosting may return itself to signify that the attribute refers to + itself rather than one of its hosts. + + @param host A valid SecCode object reference representing code running + on the system that acts as a Code Signing host. As a special case, passing + NULL indicates that the Code Signing root of trust should be used as a starting + point. Currently, that is the system kernel. + @param attributes A CFDictionary containing one or more attribute selector + values. Each selector has a CFString key and associated CFTypeRef value. + The key name identifies the attribute being selected; the associated value, + whose type depends on the the key name, selects a particular value or other + constraint on that attribute. Each host only supports particular combinations + of keys and values, and errors will be returned if any unsupported set is requested. + As a special case, NULL is taken to mean an empty attribute set. + Also note that some hosts that support hosting chains (guests being hosts) + may return sub-guests in this call. In other words, do not assume that + a SecCodeRef returned by this call is an immediate guest of the queried host + (though it will be a proximate guest, i.e. a guest's guest some way down). + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param guest On successful return, a SecCode object reference identifying + the particular guest of the host that owns the attribute value specified. + This argument will not be changed if the call fails (does not return noErr). + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. In particular: + @error errSecCSUnsupportedGuestAttributes The host does not support the attribute + type given by attributeType. + @error errSecCSInvalidAttributeValues The type of value given for a guest + attribute is not supported by the host. + @error errSecCSNoSuchCode The host has no guest with the attribute value given + by attributeValue, even though the value is of a supported type. This may also + be returned if the host code does not currently act as a Code Signing host. + @error errSecCSNotAHost The putative host cannot, in fact, act as a code + host. (It is missing the kSecCodeSignatureHost option flag in its code + signature.) + @error errSecCSMultipleGuests The attributes specified do not uniquely identify + a guest (the specification is ambiguous). +*/ +extern const CFStringRef kSecGuestAttributePid; +extern const CFStringRef kSecGuestAttributeCanonical; +extern const CFStringRef kSecGuestAttributeMachPort; + +OSStatus SecCodeCopyGuestWithAttributes(SecCodeRef host, + CFDictionaryRef attributes, SecCSFlags flags, SecCodeRef *guest); + + +/*! + @function SecCodeCreateWithPID + Asks the kernel to return a SecCode object for a process identified + by a UNIX process id (pid). This is a shorthand for asking SecGetRootCode() + for a guest whose "pid" attribute has the given pid value. + + @param pid A process id for an existing UNIX process on the system. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param process On successful return, a SecCode object reference identifying + the requesteed process. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecCodeCreateWithPID(pid_t pid, SecCSFlags flags, SecCodeRef *process); + + +/*! + @function SecCodeCheckValidity + Performs dynamic validation of the given SecCode object. The call obtains and + verifies the signature on the code object. It checks the validity of only those + sealed components required to establish identity. It checks the SecCode's + dynamic validity status as reported by its host. It ensures that the SecCode's + host is in turn valid. Finally, it validates the code against a SecRequirement + if one is given. The call succeeds if all these conditions are satisfactory. + It fails otherwise. + + This call is secure against attempts to modify the file system source of the + SecCode. + + @param code The code object to be validated. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param requirement An optional code requirement specifying additional conditions + the code object must satisfy to be considered valid. If NULL, no additional + requirements are imposed. + @param errors An optional pointer to a CFErrorRef variable. If the call fails + (and something other than noErr is returned), and this argument is non-NULL, + a CFErrorRef is stored there further describing the nature and circumstances + of the failure. The caller must CFRelease() this error object when done with it. + @result If validation passes, noErr. If validation fails, an OSStatus value + documented in CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecCodeCheckValidity(SecCodeRef code, SecCSFlags flags, + SecRequirementRef requirement); + +OSStatus SecCodeCheckValidityWithErrors(SecCodeRef code, SecCSFlags flags, + SecRequirementRef requirement, CFErrorRef *errors); + + +/*! + @function SecCodeCopyPath + For a given Code or StaticCode object, returns a URL to a location on disk where the + code object can be found. For single files, the URL points to that file. + For bundles, it points to the directory containing the entire bundle. + + This returns the same URL as the kSecCodeInfoMainExecutable key returned + by SecCodeCopySigningInformation. + + @param code The Code or StaticCode object to be located. For a Code + argument, its StaticCode is processed as per SecCodeCopyStaticCode. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param path On successful return, contains a CFURL identifying the location + on disk of the staticCode object. + @result On success, noErr. On error, an OSStatus value + documented in CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecCodeCopyPath(SecStaticCodeRef staticCode, SecCSFlags flags, + CFURLRef *path); + + +/*! + @function SecCodeCopyDesignatedRequirement + For a given Code or StaticCode object, determines its Designated Code Requirement. + The Designated Requirement is the SecRequirement that the code believes + should be used to properly identify it in the future. + + If the SecCode contains an explicit Designated Requirement, a copy of that + is returned. If it does not, a SecRequirement is implicitly constructed from + its signing authority and its embedded unique identifier. No Designated + Requirement can be obtained from code that is unsigned. Code that is modified + after signature, improperly signed, or has become invalid, may or may not yield + a Designated Requirement. This call does not validate the SecStaticCode argument. + + @param code The Code or StaticCode object to be interrogated. For a Code + argument, its StaticCode is processed as per SecCodeCopyStaticCode. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param requirement On successful return, contains a copy of a SecRequirement + object representing the code's Designated Requirement. On error, unchanged. + @result On success, noErr. On error, an OSStatus value + documented in CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecCodeCopyDesignatedRequirement(SecStaticCodeRef code, SecCSFlags flags, + SecRequirementRef *requirement); + + +/* + @function SecCodeCopySigningInformation + For a given Code or StaticCode object, extract various pieces of information + from its code signature and return them in the form of a CFDictionary. The amount + and detail level of the data is controlled by the flags passed to the call. + + If the code exists but is not signed at all, this call will succeed and return + a dictionary that does NOT contain the kSecCodeInfoIdentifier key. This is the + recommended way to check quickly whether a code is signed. + + If the signing data for the code is corrupt or invalid, this call may fail or it + may return partial data. To ensure that only valid data is returned (and errors + are raised for invalid data), you must successfully call one of the CheckValidity + functions on the code before calling CopySigningInformation. + + @param code The Code or StaticCode object to be interrogated. For a Code + argument, its StaticCode is processed as per SecCodeCopyStaticCode. + @param flags Optional flags. Use any or all of the kSecCS*Information flags + to select what information to return. A generic set of entries is returned + regardless; you may specify kSecCSDefaultFlags for just those. + @param information A CFDictionary containing information about the code is stored + here on successful completion. The contents of the dictionary depend on + the flags passed. Regardless of flags, the kSecCodeInfoIdentifier key is always present + if the code is signed, and absent if the code is entirely unsigned. + @result On success, noErr. On error, an OSStatus value + documented in CSCommon.h or certain other Security framework headers. + + @constant kSecCSSigningInformation Return cryptographic signing information, + including the certificate chain and CMS data. + @constant kSecCSRequirementInformation Return information about internal code + requirements embedded in the code. + @constant kSecCSInternalInformation Return internal code signing information. + This information is for use by Apple, and is subject to change. + It will not be further documented here. + @constant kSecCSDynamicInformation Return dynamic validity information about + the Code. The subject code must be a SecCodeRef (not a SecStaticCodeRef). + @constant kSecCSContentInformation Return more information about the file system + contents making up the signed code on disk. It is not generally advisable to + make use of this information, but some utilities (such as software-update + tools) may find it useful. + */ +enum { + kSecCSInternalInformation = 1 << 0, + kSecCSSigningInformation = 1 << 1, + kSecCSRequirementInformation = 1 << 2, + kSecCSDynamicInformation = 1 << 3, + kSecCSContentInformation = 1 << 4 +}; + + /* flag required to get this value */ +extern const CFStringRef kSecCodeInfoCertificates; /* Signing */ +extern const CFStringRef kSecCodeInfoChangedFiles; /* Content */ +extern const CFStringRef kSecCodeInfoCMS; /* Signing */ +extern const CFStringRef kSecCodeInfoTime; /* Signing */ +extern const CFStringRef kSecCodeInfoDesignatedRequirement; /* Requirement */ +extern const CFStringRef kSecCodeInfoFormat; /* generic */ +extern const CFStringRef kSecCodeInfoIdentifier; /* generic */ +extern const CFStringRef kSecCodeInfoImplicitDesignatedRequirement; /* Requirement */ +extern const CFStringRef kSecCodeInfoMainExecutable; /* generic */ +extern const CFStringRef kSecCodeInfoPList; /* generic */ +extern const CFStringRef kSecCodeInfoRequirements; /* Requirement */ +extern const CFStringRef kSecCodeInfoStatus; /* Dynamic */ +extern const CFStringRef kSecCodeInfoTrust; /* Signing */ + +OSStatus SecCodeCopySigningInformation(SecStaticCodeRef code, SecCSFlags flags, + CFDictionaryRef *information); + + +/* + @function SecCodeSetDetachedSignature + For a given Code or StaticCode object, explicitly specify the detached signature + data used to verify it. + This call unconditionally overrides any signature embedded in the Code and any + previously specified detached signature; only the signature data specified here + will be used from now on for this Code object. If NULL data is specified, this + call reverts to using any embedded signature. + Any call to this function voids all cached validations for the Code object. + Validations will be performed again as needed in the future. This call does not, + by itself, perform or trigger any validations. + Please note that it is possible to have multiple Code objects for the same static + or dynamic code entity in the system. This function only attaches signature data + to the particular SecStaticCodeRef involved. It is your responsibility to understand + the object graph and pick the right one(s). + + @param code A Code or StaticCode object whose signature information is to be changed. + @param signature A CFDataRef containing the signature data to be used for validating + the given Code. This must be exactly the data previously generated as a detached + signature by the SecCodeSignerAddSignature API or the codesign(1) command with + the -D/--detached option. + If signature is NULL, discards any previously set signature data and reverts + to using the embedded signature, if any. If not NULL, the data is retained and used + for future validation operations. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + */ +OSStatus SecCodeSetDetachedSignature(SecStaticCodeRef code, CFDataRef signature, + SecCSFlags flags); + + +/* + @function SecCodeMapMemory + For a given Code or StaticCode object, ask the kernel to accept the signing information + currently attached to it in the caller and use it to validate memory page-ins against it, + updating dynamic validity state accordingly. This change affects all processes that have + the main executable of this code mapped. + + @param code A Code or StaticCode object representing the signed code whose main executable + should be subject to page-in validation. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + */ +OSStatus SecCodeMapMemory(SecStaticCodeRef code, SecCSFlags flags); + + +#ifdef __cplusplus +} +#endif + +#endif //_H_SECCODE diff --git a/lib/SecCodeHost.cpp b/lib/SecCodeHost.cpp new file mode 100644 index 0000000..fce2403 --- /dev/null +++ b/lib/SecCodeHost.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// SecCodeHost - Host Code API +// +#include "cs.h" +#include "SecCodeHost.h" +#include +#include +#include + +using namespace CodeSigning; + + +// +// Munge a CFDictionary into a CssmData representing its plist +// +class DictData : public CFRef { +public: + DictData(CFDictionaryRef dict) : CFRef(makeCFData(dict)) { } + + operator CssmData() const + { + if (*this) + return CssmData::wrap(CFDataGetBytePtr(*this), CFDataGetLength(*this)); + else + return CssmData(); + } +}; + + +OSStatus SecHostCreateGuest(SecGuestRef host, + uint32_t status, CFURLRef path, CFDictionaryRef attributes, + SecCSFlags flags, SecGuestRef *newGuest) +{ + BEGIN_CSAPI + + checkFlags(flags, kSecCSDedicatedHost); + Required(newGuest) = SecurityServer::ClientSession().createGuest(host, + status, cfString(path).c_str(), DictData(attributes), flags); + + END_CSAPI +} + +OSStatus SecHostRemoveGuest(SecGuestRef host, SecGuestRef guest, SecCSFlags flags) +{ + BEGIN_CSAPI + + checkFlags(flags); + SecurityServer::ClientSession().removeGuest(host, guest); + + END_CSAPI +} + +OSStatus SecHostSelectGuest(SecGuestRef guestRef, SecCSFlags flags) +{ + BEGIN_CSAPI + + checkFlags(flags); + SecurityServer::ClientSession().selectGuest(guestRef); + + END_CSAPI +} + + +OSStatus SecHostSelectedGuest(SecCSFlags flags, SecGuestRef *guestRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + Required(guestRef) = SecurityServer::ClientSession().selectedGuest(); + + END_CSAPI +} + +OSStatus SecHostSetGuestStatus(SecGuestRef guestRef, + uint32_t status, CFDictionaryRef attributes, + SecCSFlags flags) +{ + BEGIN_CSAPI + + checkFlags(flags); + SecurityServer::ClientSession().setGuestStatus(guestRef, status, DictData(attributes)); + + END_CSAPI +} + +OSStatus SecHostSetHostingPort(mach_port_t hostingPort, SecCSFlags flags) +{ + BEGIN_CSAPI + + checkFlags(flags); + SecurityServer::ClientSession().registerHosting(hostingPort, flags); + + END_CSAPI +} diff --git a/lib/SecCodeHost.h b/lib/SecCodeHost.h new file mode 100644 index 0000000..fc9c83d --- /dev/null +++ b/lib/SecCodeHost.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +/*! + @header SecCodeHost + This header provides the hosting API for Code Signing. These are calls + that are (only) made by code that is hosting guests. + In the context of Code Signing, a Host is code that creates and manages other + codes from which it defends its own integrity. As part of that duty, it maintains + state for each of its children, and answers questions about them. + + A Host is externally represented by a SecCodeRef (it is a SecCode object). + So is a Guest. There is no specific API object to represent Hosts or Guests. + Within the Hosting API, guests are identified by simple numeric handles that + are unique and valid only in the context of their specific host. + + The functions in this API always apply to the Host making the API calls. + They cannot be used to (directly) interrogate another host. +*/ +#ifndef _H_SECCODEHOST +#define _H_SECCODEHOST + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + @header SecCodeHost + This header describes the Code Signing Hosting API. These are calls made + by code that wishes to become a Host in the Code Signing Host/Guest infrastructure. + Hosting allows the caller to establish separate, independent code identities + (SecCodeRefs) for parts of itself, usually because it is loading and managing + code in the form of scripts, plugins, etc. + + The Hosting API does not directly connect to the Code Signing Client APIs. + Certain calls in the client API will cause internal queries to hosts about their + guests. The Host side of these queries is managed through this API. The results + will eventually be delivered to client API callers in appropriate form. + + If code never calls any of the Hosting API functions, it is deemed to not have + guests and not act as a Host. This is the default and requires no action. + + Hosting operates in one of two modes, dynamic or proxy. Whichever mode is first + engaged prevails for the lifetime of the caller. There is no way to switch between + the two, and calling an API belonging to the opposite mode will fail. + + In dynamic hosting mode, the caller provides a Mach port that receives direct + queries about its guests. Dynamic mode is engaged by calling SecHostSetHostingPort. + + In proxy hosting mode, the caller provides information about its guests as + guests are created, removed, or change status. The system caches this information + and answers queries about guests from this pool of information. The caller is not + directly involved in answering such queries, and has no way to intervene. +*/ + + +/*! + @function SecHostCreateGuest + Create a new Guest and describe its initial properties. + + This call activates Hosting Proxy Mode. From here on, the system will record + guest information provided through SecHostCreateGuest, SecHostSetGuestStatus, and + SecHostRemoveGuest, and report hosting status to callers directly. This mode + is incompatible with dynamic host mode as established by a call to SecHostSetHostingPort. + + @param host Pass kSecNoGuest to create a guest of the process itself. + To create a guest of another guest (extending the hosting chain), pass the SecGuestRef + of the guest to act as the new guest's host. If host has a dedicated guest, + it will be deemed to be be the actual host, recursively. + @param status The Code Signing status word for the new guest. These are combinations + of the CS_* flags in . Note that the proxy will enforce the rules for + the stickiness of these bits. In particular, if you don't pass the CS_VALID bit during + creation, your new guest will be born invalid and will never have a valid identity. + @param path The canonical path to the guest's code on disk. This is the path you would + pass to SecStaticCodeCreateWithPath to make a static code object reference. You must + use an absolute path. + @param attributes An optional CFDictionaryRef containing attributes that can be used + to locate this particular guest among all of the caller's guests. The "canonical" + attribute is automatically added for the value of guestRef. If you pass NULL, + no other attributes are established for the guest. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior, or + a combination of the flags defined below for special features. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. + @param newGuest Upon successful creation of the new guest, the new SecGuestRef + that should be used to identify the new guest from here on. + + @constant kSecCSDedicatedHost Declares dedicated hosting for the given host. + In dedicated hosting, the host has exactly one guest (the one this call is + introducing), and the host will spend all of its time from here on running + that guest (or on its behalf). This declaration is irreversable for the lifetime + of the host. Note that this is a declaration about the given host, and is not + binding upon other hosts on either side of the hosting chain, though they in turn + may declare dedicated hosting if desired. + It is invalid to declare dedicated hosting if other guests have already been + introduced for this host, and it is invalid to introduce additional guests + for this host after this call. +*/ +enum { + kSecCSDedicatedHost = 1 << 0, +}; + +OSStatus SecHostCreateGuest(SecGuestRef host, + uint32_t status, CFURLRef path, CFDictionaryRef attributes, + SecCSFlags flags, SecGuestRef *newGuest); + + +/*! + @function SecHostRemoveGuest + Announce that the guest with the given guestRef has permanently disappeared. + It removes all memory of the guest from the hosting system. You cannot remove + a dedicated guest. + + @param host The SecGuestRef that was used to create guest. You cannot specify + a proximate host (host of a host) here. However, the substitution for dedicated + guests described for SecHostCreateGuest also takes place here. + @param guest The handle for a Guest previously created with SecHostCreateGuest + that has not previously been destroyed. This guest is to be destroyed now. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecHostRemoveGuest(SecGuestRef host, SecGuestRef guest, SecCSFlags flags); + + +/*! + @function SecHostSelectGuest + Tell the Code Signing host subsystem that the calling thread will now act + on behalf of the given Guest. This must be a valid Guest previously created + with SecHostCreateGuest. + + @param guestRef The handle for a Guest previously created with SecHostCreateGuest + on whose behalf this thread will act from now on. This setting will be remembered + until it is changed (or the thread terminates). + To indicate that the thread will act on behalf of the Host itself (rather than + any Guest), pass kSecNoGuest. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecHostSelectGuest(SecGuestRef guestRef, SecCSFlags flags); + + +/*! + @function SecHostSelectedGuest + Retrieve the handle for the Guest currently selected for the calling thread. + + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param guestRef Will be assigned the SecGuestRef currently in effect for + the calling thread. If no Guest is active on this thread (i.e. the thread + is acting for the Host), the return value is kSecNoGuest. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecHostSelectedGuest(SecCSFlags flags, SecGuestRef *guestRef); + + +/*! + @function SecHostSetGuestStatus + Updates the status of a particular guest. + + @param guestRef The handle for a Guest previously created with SecHostCreateGuest + on whose behalf this thread will act from now on. This setting will be remembered + until it is changed (or the thread terminates). + @param status The new Code Signing status word for the guest. The proxy enforces + the restrictions on changes to guest status; in particular, the CS_VALID bit can only + be cleared, and the CS_HARD and CS_KILL flags can only be set. Pass the previous + guest status to indicate that no change is desired. + @param attributes An optional dictionary containing attributes to be used to distinguish + this guest from all guests of the caller. If given, it completely replaces the attributes + specified earlier. If NULL, previously established attributes are retained. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. + */ +OSStatus SecHostSetGuestStatus(SecGuestRef guestRef, + uint32_t status, CFDictionaryRef attributes, + SecCSFlags flags); + + +/*! + @function SecHostSetHostingPort + Tells the Code Signing Hosting subsystem that the calling code will directly respond + to hosting inquiries over the given port. + + This API should be the first hosting API call made. With it, the calling code takes + direct responsibility for answering questions about its guests using the hosting IPC + services. The SecHostCreateGuest, SecHostDestroyGuest and SecHostSetGuestStatus calls + are not valid after this. The SecHostSelectGuest and SecHostSelectedGuest calls will + still work, and will use whatever SecGuestRefs the caller has assigned in its internal + data structures. + + This call cannot be undone; once it is made, record-and-forward facilities are + disabled for the lifetime of the calling code. + + @param hostingPort A Mach message port with send rights. This port will be recorded + and handed to parties interested in querying the host about its children. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. + */ +OSStatus SecHostSetHostingPort(mach_port_t hostingPort, SecCSFlags flags); + + +#ifdef __cplusplus +} +#endif + +#endif //_H_SECCODEHOST diff --git a/lib/SecCodeHostLib.c b/lib/SecCodeHostLib.c new file mode 100644 index 0000000..a79c85d --- /dev/null +++ b/lib/SecCodeHostLib.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2007 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@ + */ +//#include +#include "SecCodeHostLib.h" +#include +#include +#include +#include + + +// +// Global state +// +mach_port_t gServerPort; +SecCSFlags gInitFlags; + + +// +// Framing macros and facilities +// +#define UCSP_ARGS gServerPort, mig_get_reply_port(), &securitydCreds, &rcode +#define ATTRDATA(attr) (void *)(attr), (attr) ? strlen((attr)) : 0 + +#define CALL(func) \ + security_token_t securitydCreds; \ + CSSM_RETURN rcode; \ + if (KERN_SUCCESS != func) \ + return errSecCSInternalError; \ + if (securitydCreds.val[0] != 0) \ + return CSSM_ERRCODE_VERIFICATION_FAILURE; \ + return rcode + + + +// +// Mandatory initialization call +// +OSStatus SecHostLibInit(SecCSFlags flags) +{ + if (gServerPort != MACH_PORT_NULL) // re-initialization attempt + return errSecCSInternalError; + + mach_port_t bootstrapPort; + if (KERN_SUCCESS != task_get_bootstrap_port(mach_task_self(), &bootstrapPort)) + return errSecCSInternalError; + static char serverName[BOOTSTRAP_MAX_NAME_LEN] = SECURITYSERVER_BOOTSTRAP_NAME; + if (KERN_SUCCESS != bootstrap_look_up(bootstrapPort, + serverName, &gServerPort)) + return errSecCSInternalError; + + ClientSetupInfo info = { 0x1234, SSPROTOVERSION }; + CALL(ucsp_client_setup(UCSP_ARGS, mach_task_self(), info, "?:unspecified")); +} + + +// +// Guest creation. +// At this time, this ONLY supports the creation of (one) dedicated guest. +// +OSStatus SecHostLibCreateGuest(SecGuestRef host, + uint32_t status, const char *path, const char *attributeXML, + SecCSFlags flags, SecGuestRef *newGuest) +{ + if (flags != kSecCSDedicatedHost) + return errSecCSInvalidFlags; + + CALL(ucsp_client_createGuest(UCSP_ARGS, host, status, path, + ATTRDATA(attributeXML), flags, newGuest)); +} + + +// +// Update the status of a guest. +// +OSStatus SecHostLibSetGuestStatus(SecGuestRef guestRef, + uint32_t status, const char *attributeXML, + SecCSFlags flags) +{ + CALL(ucsp_client_setGuestStatus(UCSP_ARGS, guestRef, status, ATTRDATA(attributeXML))); +} + + +// +// Enable dynamic hosting mode. +// +OSStatus SecHostSetHostingPort(mach_port_t hostingPort, SecCSFlags flags) +{ + CALL(ucsp_client_registerHosting(UCSP_ARGS, hostingPort, flags)); +} diff --git a/lib/SecCodeHostLib.h b/lib/SecCodeHostLib.h new file mode 100644 index 0000000..65fa720 --- /dev/null +++ b/lib/SecCodeHostLib.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2007 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@ + */ + +/*! + @header SecCodeHostLib + This header provides a subset of the hosting API for Code Signing. + This subset functionality is implemented as a static library written + entirely in C, and depends on nothing except the system library and the + C runtime. It is thus suitable to be used by low-level libraries and + other such system facilities. On the other hand, it does not provide the + full functionality of . + + This file is documented as a delta to , which + you should consult as a baseline. +*/ +#ifndef _H_SECCODEHOSTLIB +#define _H_SECCODEHOSTLIB + +//#include +#include "SecCodeHost.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/*! + @function SecHostLibInit + This function must be called first to use the SecCodeHostLib facility. + */ +OSStatus SecHostLibInit(SecCSFlags flags); + + +/*! + @function SecHostLibCreateGuest + This function declares a code host, engages hosting proxy services for it, + and creates a guest with given attributes and state. + + NOTE: This version of the function currently only supports dedicated hosting. + If you do not pass the kSecCSDedicatedHost flag, the call will fail. + */ +OSStatus SecHostLibCreateGuest(SecGuestRef host, + uint32_t status, const char *path, const char *attributeXML, + SecCSFlags flags, SecGuestRef *newGuest); + + +/*! + @function SecHostLibSetGuestStatus + This function can change the state or attributes (or both) of a given guest. + It performs all the work of SecHostSetGuestStatus. + */ +OSStatus SecHostLibSetGuestStatus(SecGuestRef guestRef, + uint32_t status, const char *attributeXML, + SecCSFlags flags); + + +/*! + @function SecHostLibSetHostingPort + Register a Mach port to receive hosting queries on. This enables (and locks) + dynamic hosting mode, and is incompatible with all proxy-mode calls. + You still must call SecHostLibInit first. + */ +OSStatus SecHostSetHostingPort(mach_port_t hostingPort, SecCSFlags flags); + + +/* + Functionality from SecCodeHost.h that is genuinely missing here: + +OSStatus SecHostRemoveGuest(SecGuestRef host, SecGuestRef guest, SecCSFlags flags); + +OSStatus SecHostSelectGuest(SecGuestRef guestRef, SecCSFlags flags); + +OSStatus SecHostSelectedGuest(SecCSFlags flags, SecGuestRef *guestRef); + +*/ + + +#ifdef __cplusplus +} +#endif + +#endif //_H_SECCODEHOSTLIB diff --git a/lib/SecCodeSigner.cpp b/lib/SecCodeSigner.cpp new file mode 100644 index 0000000..b6d3b45 --- /dev/null +++ b/lib/SecCodeSigner.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// SecCode - API frame for SecCode objects. +// +// Note that some SecCode* functions take SecStaticCodeRef arguments in order to +// accept either static or dynamic code references, operating on the respective +// StaticCode. Those functions are in SecStaticCode.cpp, not here, despite their name. +// +#include "cs.h" +#include "CodeSigner.h" +#include "cskernel.h" + +using namespace CodeSigning; + + +// +// Parameter keys +// +const CFStringRef kSecCodeSignerApplicationData = CFSTR("application-specific"); +const CFStringRef kSecCodeSignerDetached = CFSTR("detached"); +const CFStringRef kSecCodeSignerDryRun = CFSTR("dryrun"); +const CFStringRef kSecCodeSignerFlags = CFSTR("flags"); +const CFStringRef kSecCodeSignerIdentifier = CFSTR("identifier"); +const CFStringRef kSecCodeSignerIdentifierPrefix = CFSTR("identifier-prefix"); +const CFStringRef kSecCodeSignerIdentity = CFSTR("signer"); +const CFStringRef kSecCodeSignerPageSize = CFSTR("pagesize"); +const CFStringRef kSecCodeSignerRequirements = CFSTR("requirements"); +const CFStringRef kSecCodeSignerResourceRules = CFSTR("resource-rules"); +const CFStringRef kSecCodeSignerSigningTime = CFSTR("signing-time"); + + +// +// CF-standard type code functions +// +CFTypeID SecCodeSignerGetTypeID(void) +{ + BEGIN_CSAPI + return gCFObjects().CodeSigner.typeID; + END_CSAPI1(_kCFRuntimeNotATypeID) +} + + +// +// Create a signer object +// +OSStatus SecCodeSignerCreate(CFDictionaryRef parameters, SecCSFlags flags, + SecCodeSignerRef *signerRef) +{ + BEGIN_CSAPI + SecPointer signer = new SecCodeSigner; + signer->parameters(parameters); + Required(signerRef) = signer->handle(); + END_CSAPI +} + + +// +// Generate a signature +// +OSStatus SecCodeSignerAddSignature(SecCodeSignerRef signerRef, + SecStaticCodeRef codeRef, SecCSFlags flags) +{ + return SecCodeSignerAddSignatureWithErrors(signerRef, codeRef, flags, NULL); +} + +OSStatus SecCodeSignerAddSignatureWithErrors(SecCodeSignerRef signerRef, + SecStaticCodeRef codeRef, SecCSFlags flags, CFErrorRef *errors) +{ + BEGIN_CSAPI + SecCodeSigner::required(signerRef)->sign(SecStaticCode::required(codeRef), flags); + END_CSAPI_ERRORS +} diff --git a/lib/SecCodeSigner.h b/lib/SecCodeSigner.h new file mode 100644 index 0000000..5d72ce4 --- /dev/null +++ b/lib/SecCodeSigner.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +/*! + @header SecCodeSigner + SecCodeSigner represents an object that signs code. +*/ +#ifndef _H_SECCODESIGNER +#define _H_SECCODESIGNER + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*! + @typedef SecCodeSignerRef + This is the type of a reference to a code requirement. +*/ +typedef struct __SecCodeSigner *SecCodeSignerRef; /* code signing object */ + + +/*! + @function SecCodeGetTypeID + Returns the type identifier of all SecCode instances. +*/ +CFTypeID SecCodeSignerGetTypeID(void); + + +/*! + @constant kSecCodeSignerIdentity A SecIdentityRef describing the signing identity + to use for signing code. This is a mandatory parameter. + @constant kSecCodeSignerDetached If present, a detached code signature is generated. + If absent, code signature data is written into the target code (which must + be writable). The value is a CFURL identifying the file that is replaced with + the detached signature data. + @constant kSecCodeSignerRequirements Internal code requirements. + @constant kSecCodeSignerFlags Signature flags. + */ +extern const CFStringRef kSecCodeSignerApplicationData; +extern const CFStringRef kSecCodeSignerDetached; +extern const CFStringRef kSecCodeSignerDryRun; +extern const CFStringRef kSecCodeSignerFlags; +extern const CFStringRef kSecCodeSignerIdentifier; +extern const CFStringRef kSecCodeSignerIdentifierPrefix; +extern const CFStringRef kSecCodeSignerIdentity; +extern const CFStringRef kSecCodeSignerPageSize; +extern const CFStringRef kSecCodeSignerRequirements; +extern const CFStringRef kSecCodeSignerResourceRules; +extern const CFStringRef kSecCodeSignerSigningTime; + + +/*! + @function SecCodeSignerCreate + Create a (new) SecCodeSigner object to be used for signing code. + + @param parameters An optional CFDictionary containing parameters that influence + signing operations with the newly created SecCodeSigner. If NULL, defaults + are applied to all parameters; note however that some parameters do not have + useful defaults, and will need to be set before signing is attempted. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param staticCode On successful return, a SecStaticCode object reference representing + the file system origin of the given SecCode. On error, unchanged. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecCodeSignerCreate(CFDictionaryRef parameters, SecCSFlags flags, + SecCodeSignerRef *signer); + + +/*! + @function SecCodeSignerAddSignature + Create a code signature and add it to the StaticCode object being signed. + + @param signer A SecCodeSigner object containing all the information required + to sign code. + @param code A valid SecStaticCode object reference representing code files + on disk. This code will be signed, and will ordinarily be modified to contain + the resulting signature data. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param errors An optional pointer to a CFErrorRef variable. If the call fails + (and something other than noErr is returned), and this argument is non-NULL, + a CFErrorRef is stored there further describing the nature and circumstances + of the failure. The caller must CFRelease() this error object when done with it. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecCodeSignerAddSignature(SecCodeSignerRef signer, + SecStaticCodeRef code, SecCSFlags flags); + +OSStatus SecCodeSignerAddSignatureWithErrors(SecCodeSignerRef signer, + SecStaticCodeRef code, SecCSFlags flags, CFErrorRef *errors); + + +#ifdef __cplusplus +} +#endif + +#endif //_H_SECCODESIGNER diff --git a/lib/SecIntegrity.cpp b/lib/SecIntegrity.cpp new file mode 100644 index 0000000..5bbb603 --- /dev/null +++ b/lib/SecIntegrity.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2007 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@ + */ +#include +#include "SecIntegrity.h" + + diff --git a/lib/SecIntegrity.h b/lib/SecIntegrity.h new file mode 100644 index 0000000..c8fb069 --- /dev/null +++ b/lib/SecIntegrity.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2007 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@ + */ + +/*! + @header SecIntegrity + This header provides functionality that allows a program to use its own + code signature to extend trust to parts of its bundle that it covers. + + This file does not help you verify code signatures themselves; use SecCode.h + for that. It is reasonable to use SecCode.h and SecIntegrity.h together. +*/ +#ifndef _H_SECINTEGRITY +#define _H_SECINTEGRITY + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + + + +#ifdef __cplusplus +} +#endif + +#endif //_H_SECINTEGRITY diff --git a/lib/SecIntegrityLib.c b/lib/SecIntegrityLib.c new file mode 100644 index 0000000..bab8ccc --- /dev/null +++ b/lib/SecIntegrityLib.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2007 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@ + */ +//#include +#include "SecIntegrityLib.h" diff --git a/lib/SecIntegrityLib.h b/lib/SecIntegrityLib.h new file mode 100644 index 0000000..b0960c6 --- /dev/null +++ b/lib/SecIntegrityLib.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2007 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@ + */ + +/*! + @header SecIntegrityLib + This header provides a subset of the code-integrity API for Code Signing. + This subset functionality is implemented as a static library written + entirely in C, and depends on nothing except the system library and the + C runtime. It is thus suitable to be used by low-level libraries and + other such system facilities. On the other hand, it does not provide the + full functionality of . + + This file is documented as a delta to , which + you should consult as a baseline. +*/ +#ifndef _H_SECINTEGRITYLIB +#define _H_SECINTEGRITYLIB + +#include "SecIntegrity.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + This file is not yet published. + */ + + +#ifdef __cplusplus +} +#endif + +#endif //_H_SECINTEGRITYLIB diff --git a/lib/SecRequirement.cpp b/lib/SecRequirement.cpp new file mode 100644 index 0000000..0b6616d --- /dev/null +++ b/lib/SecRequirement.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// SecRequirement - API frame for SecRequirement objects +// +#include "cs.h" +#include "Requirements.h" +#include "reqparser.h" +#include "reqmaker.h" +#include "reqdumper.h" +#include +#include + +using namespace CodeSigning; + + +// +// CF-standard type code function +// +CFTypeID SecRequirementGetTypeID(void) +{ + BEGIN_CSAPI + return gCFObjects().Requirement.typeID; + END_CSAPI1(_kCFRuntimeNotATypeID) +} + + +// +// Create a Requirement from data +// +OSStatus SecRequirementCreateWithData(CFDataRef data, SecCSFlags flags, + SecRequirementRef *requirementRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + Required(requirementRef) = (new SecRequirement(CFDataGetBytePtr(data), CFDataGetLength(data)))->handle(); + + END_CSAPI +} + + +// +// Create a Requirement from data in a file +// +OSStatus SecRequirementCreateWithResource(CFURLRef resource, SecCSFlags flags, + SecRequirementRef *requirementRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + CFRef data = cfLoadFile(resource); + Required(requirementRef) = + (new SecRequirement(CFDataGetBytePtr(data), CFDataGetLength(data)))->handle(); + + END_CSAPI +} + + +// +// Create a Requirement from source text (compiling it) +// +OSStatus SecRequirementCreateWithString(CFStringRef text, SecCSFlags flags, + SecRequirementRef *requirementRef) +{ + return SecRequirementCreateWithStringAndErrors(text, flags, NULL, requirementRef); +} + +OSStatus SecRequirementCreateWithStringAndErrors(CFStringRef text, SecCSFlags flags, + CFErrorRef *errors, SecRequirementRef *requirementRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + Required(requirementRef) = (new SecRequirement(parseRequirement(cfString(text))))->handle(); + + END_CSAPI_ERRORS +} + + +// +// Create a Requirement group. +// This is the canonical point where "application group" is defined. +// +OSStatus SecRequirementCreateGroup(CFStringRef groupName, SecCertificateRef anchorRef, + SecCSFlags flags, SecRequirementRef *requirementRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + Requirement::Maker maker; + maker.put(opAnd); // both of... + maker.infoKey("Application-Group", cfString(groupName)); + if (anchorRef) { + CSSM_DATA certData; + MacOSError::check(SecCertificateGetData(anchorRef, &certData)); + maker.anchor(0, certData.Data, certData.Length); + } else { + maker.anchor(); // canonical Apple anchor + } + Required(requirementRef) = (new SecRequirement(maker.make(), true))->handle(); + + secdebug("codesign", "created group requirement for %s", cfString(groupName).c_str()); + + END_CSAPI +} + + +// +// Extract the stable binary from from a SecRequirementRef +// +OSStatus SecRequirementCopyData(SecRequirementRef requirementRef, SecCSFlags flags, + CFDataRef *data) +{ + BEGIN_CSAPI + + const Requirement *req = SecRequirement::required(requirementRef)->requirement(); + checkFlags(flags); + Required(data); + *data = makeCFData(*req); + + END_CSAPI +} + + +// +// Generate source form for a SecRequirement (decompile/disassemble) +// +OSStatus SecRequirementCopyString(SecRequirementRef requirementRef, SecCSFlags flags, + CFStringRef *text) +{ + BEGIN_CSAPI + + const Requirement *req = SecRequirement::required(requirementRef)->requirement(); + checkFlags(flags); + Required(text); + *text = makeCFString(Dumper::dump(req)); + + END_CSAPI +} + diff --git a/lib/SecRequirement.h b/lib/SecRequirement.h new file mode 100644 index 0000000..358b1b2 --- /dev/null +++ b/lib/SecRequirement.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +/*! + @header SecRequirement + SecRequirement represents a condition or constraint (a "Code Requirement") + that code must satisfy to be considered valid for some purpose. + SecRequirement itself does not understand or care WHY such a constraint + is appropriate or useful; it is purely a tool for formulating, recording, + and evaluating it. + + Code Requirements are usually stored and retrieved in the form of a variable-length + binary Blob that can be encapsulated as a CFDataRef and safely stored in various + data structures. They can be formulated in a text form that can be compiled + into binary form and decompiled back into text form without loss of functionality + (though comments and formatting are not preserved). +*/ +#ifndef _H_SECREQUIREMENT +#define _H_SECREQUIREMENT + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + + +/*! + @function SecRequirementGetTypeID + Returns the type identifier of all SecRequirement instances. +*/ +CFTypeID SecRequirementGetTypeID(void); + + +/*! + @function SecRequirementCreateWithData + Create a SecRequirement object from binary form. + This is the effective inverse of SecRequirementCopyData. + + @param data A binary blob obtained earlier from a valid SecRequirement object + using the SecRequirementCopyData call. This is the only publicly supported + way to get such a data blob. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param requirement On successful return, contains a reference to a SecRequirement + object that behaves identically to the one the data blob was obtained from. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecRequirementCreateWithData(CFDataRef data, SecCSFlags flags, + SecRequirementRef *requirement); + +/*! + @function SecRequirementCreateWithResource + Create a SecRequirement object from binary form obtained from a file. + This call is functionally equivalent to reading the entire contents of a file + into a CFDataRef and then calling SecRequirementCreateWithData with that. + + @param resource A CFURL identifying a file containing a (binary) requirement blob. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param requirement On successful return, contains a reference to a SecRequirement + object that behaves identically to the one the data blob was obtained from. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecRequirementCreateWithResource(CFURLRef resource, SecCSFlags flags, + SecRequirementRef *requirement); + +/*! + @function SecRequirementCreateWithString + Create a SecRequirement object by compiling a valid text representation + of a requirement. + + @param text A CFString containing the text form of a (single) Code Requirement. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param requirement On successful return, contains a reference to a SecRequirement + object that implements the conditions described in text. + @param errors An optional pointer to a CFErrorRef variable. If the call fails + (and something other than noErr is returned), and this argument is non-NULL, + a CFErrorRef is stored there further describing the nature and circumstances + of the failure. The caller must CFRelease() this error object when done with it. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecRequirementCreateWithString(CFStringRef text, SecCSFlags flags, + SecRequirementRef *requirement); + +OSStatus SecRequirementCreateWithStringAndErrors(CFStringRef text, SecCSFlags flags, + CFErrorRef *errors, SecRequirementRef *requirement); + +/*! + @function SecRequirementCreateGroup + Create a SecRequirement object that represents membership in a developer-defined + application group. Group membership is defined by an entry in the code's + Info.plist, and sealed to a particular signing authority. + + @param groupName A CFString containing the name of the desired application group. + @param anchor A reference to a digital certificate representing the signing + authority that asserts group membership. If NULL, indicates Apple's authority. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param requirement On successful return, contains a reference to a SecRequirement + object that requires group membership to pass validation. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecRequirementCreateGroup(CFStringRef groupName, SecCertificateRef anchor, + SecCSFlags flags, SecRequirementRef *requirement); + +/*! + @function SecRequirementCopyData + Extracts a stable, persistent binary form of a SecRequirement. + This is the effective inverse of SecRequirementCreateWithData. + + @param requirement A valid SecRequirement object. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param data On successful return, contains a reference to a CFData object + containing a binary blob that can be fed to SecRequirementCreateWithData + to recreate a SecRequirement object with identical behavior. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecRequirementCopyData(SecRequirementRef requirement, SecCSFlags flags, + CFDataRef *data); + + +/*! + @function SecRequirementCopyString + Converts a SecRequirement object into text form. + This is the effective inverse of SecRequirementCreateWithString. + + Repeated application of this function may produce text that differs in + formatting, may contain different source comments, and may perform its + validation functions in different order. However, it is guaranteed that + recompiling the text using SecRequirementCreateWithString will produce a + SecRequirement object that behaves identically to the one you start with. + + @param requirement A valid SecRequirement object. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param text On successful return, contains a reference to a CFString object + containing a text representation of the requirement. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecRequirementCopyString(SecRequirementRef requirement, SecCSFlags flags, + CFStringRef *text); + + +#ifdef __cplusplus +} +#endif + +#endif //_H_SECREQUIREMENT diff --git a/lib/SecStaticCode.cpp b/lib/SecStaticCode.cpp new file mode 100644 index 0000000..51dd2a4 --- /dev/null +++ b/lib/SecStaticCode.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// SecStaticCode - API frame for SecStaticCode objects +// +#include "cs.h" +#include "StaticCode.h" +#include + +using namespace CodeSigning; + + +// +// CF-standard type code function +// +CFTypeID SecStaticCodeGetTypeID(void) +{ + BEGIN_CSAPI + return gCFObjects().StaticCode.typeID; + END_CSAPI1(_kCFRuntimeNotATypeID) +} + + +// +// Create an StaticCode directly from disk path. +// +OSStatus SecStaticCodeCreateWithPath(CFURLRef path, SecCSFlags flags, SecStaticCodeRef *staticCodeRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + Required(staticCodeRef) = (new SecStaticCode(DiskRep::bestGuess(cfString(path).c_str())))->handle(); + + END_CSAPI +} + + +// +// Check static validity of a StaticCode +// +OSStatus SecStaticCodeCheckValidity(SecStaticCodeRef staticCodeRef, SecCSFlags flags, + SecRequirementRef requirementRef) +{ + return SecStaticCodeCheckValidityWithErrors(staticCodeRef, flags, requirementRef, NULL); +} + +OSStatus SecStaticCodeCheckValidityWithErrors(SecStaticCodeRef staticCodeRef, SecCSFlags flags, + SecRequirementRef requirementRef, CFErrorRef *errors) +{ + BEGIN_CSAPI + + checkFlags(flags, + kSecCSCheckAllArchitectures + | kSecCSDoNotValidateExecutable + | kSecCSDoNotValidateResources + | kSecCSConsiderExpiration); + + SecPointer code = SecStaticCode::requiredStatic(staticCodeRef); + code->validateDirectory(); + if (!(flags & kSecCSDoNotValidateExecutable)) + code->validateExecutable(); + if (!(flags & kSecCSDoNotValidateResources)) + code->validateResources(); + if (const SecRequirement *req = SecRequirement::optional(requirementRef)) + code->validateRequirements(req->requirement(), errSecCSReqFailed); + + END_CSAPI_ERRORS +} + + +// +// ==================================================================================== +// +// The following API functions are called SecCode* but accept both SecCodeRef and +// SecStaticCodeRef arguments, operating on the implied SecStaticCodeRef as appropriate. +// Hence they're here, rather than in SecCode.cpp. +// + + +// +// Retrieve location information for an StaticCode. +// +OSStatus SecCodeCopyPath(SecStaticCodeRef staticCodeRef, SecCSFlags flags, CFURLRef *path) +{ + BEGIN_CSAPI + + checkFlags(flags); + SecPointer staticCode = SecStaticCode::requiredStatic(staticCodeRef); + Required(path) = staticCode->canonicalPath(); + + END_CSAPI +} + + +// +// Fetch or make up a designated requirement +// +OSStatus SecCodeCopyDesignatedRequirement(SecStaticCodeRef staticCodeRef, SecCSFlags flags, + SecRequirementRef *requirementRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + const Requirement *req = + SecStaticCode::requiredStatic(staticCodeRef)->designatedRequirement(); + Required(requirementRef) = (new SecRequirement(req))->handle(); + + END_CSAPI +} + + +// +// Record for future use a detached code signature. +// +OSStatus SecCodeSetDetachedSignature(SecStaticCodeRef codeRef, CFDataRef signature, + SecCSFlags flags) +{ + BEGIN_CSAPI + + checkFlags(flags); + SecPointer code = SecStaticCode::requiredStatic(codeRef); + + code->detachedSignature(signature); + code->resetValidity(); + + END_CSAPI +} + + +// +// Attach a code signature to a kernel memory mapping for page-in validation. +// +OSStatus SecCodeMapMemory(SecStaticCodeRef codeRef, SecCSFlags flags) +{ + BEGIN_CSAPI + + checkFlags(flags); + SecPointer code = SecStaticCode::requiredStatic(codeRef); + if (const CodeDirectory *cd = code->codeDirectory(false)) { + fsignatures args = { code->diskRep()->signingBase(), (void *)cd, cd->length() }; + UnixError::check(::fcntl(code->diskRep()->fd(), F_ADDSIGS, &args)); + } else + MacOSError::throwMe(errSecCSUnsigned); + + END_CSAPI +} diff --git a/lib/SecStaticCode.h b/lib/SecStaticCode.h new file mode 100644 index 0000000..8e3b04b --- /dev/null +++ b/lib/SecStaticCode.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +/*! + @header SecStaticCode + SecStaticCode represents the Code Signing identity of code in the file system. + This includes applications, tools, frameworks, plugins, scripts, and so on. + + Normally, each SecCode has a specific SecStaticCode that holds its static signing + data. Informally, that is the SecStaticCode the SecCode "was made from". There is + however no viable link in the other direction - given a SecStaticCode, it is not + possible to find, enumerate, or control any SecCode that originated from it. +*/ +#ifndef _H_SECSTATICCODE +#define _H_SECSTATICCODE + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + +/*! + @function SecStaticCodeGetTypeID + Returns the type identifier of all SecStaticCode instances. +*/ +CFTypeID SecStaticCodeGetTypeID(void); + + +/*! + @function SecStaticCodeCreateWithPath + Given a path to a file system object, create a SecStaticCode object representing + the code at that location, if possible. Such a SecStaticCode is not inherently + linked to running code in the system. + + It is possible to create a SecStaticCode object from an unsigned code object. + Most uses of such an object will return the errSecCSUnsigned error. + + @param path A path to a location in the file system. Only file:// URLs are + currently supported. For bundles, pass a URL to the root directory of the + bundle. For single files, pass a URL to the file. If you pass a URL to the + main executable of a bundle, the bundle as a whole will be generally recognized. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + @param staticCode On successful return, contains a reference to the StaticCode object + representing the code at path. Unchanged on error. + @result Upon success, noErr. Upon error, an OSStatus value documented in + CSCommon.h or certain other Security framework headers. +*/ +OSStatus SecStaticCodeCreateWithPath(CFURLRef path, SecCSFlags flags, SecStaticCodeRef *staticCode); + + +/*! + @function SecStaticCodeCheckValidity + Performs static validation on the given SecStaticCode object. The call obtains and + verifies the signature on the code object. It checks the validity of all + sealed components (including resources, if any). It validates the code against + a SecRequirement if one is given. The call succeeds if all these conditions + are satisfactory. It fails otherwise. + + This call is only secure if the code is not subject to concurrent modification, + and the outcome is only valid as long as the code is unmodified thereafter. + + @param staticCode The code object to be validated. + @param flags Optional flags. Pass kSecCSDefaultFlags for standard behavior. + + @constant kSecCSCheckAllArchitectures + For multi-architecture (universal) Mach-O programs, validate all architectures + included. By default, only the native architecture is validated. + @constant kSecCSNoDnotValidateExecutable + Do not validate the contents of the main executable. This is normally done. + @constant kSecCSNoNotValidateResources + Do not validate the presence and contents of all bundle resources (if any). + By default, a mismatch in any bundle resource causes validation to fail. + + @param requirement On optional code requirement specifying additional conditions + the staticCode object must satisfy to be considered valid. If NULL, no additional + requirements are imposed. + @param errors An optional pointer to a CFErrorRef variable. If the call fails + (and something other than noErr is returned), and this argument is non-NULL, + a CFErrorRef is stored there further describing the nature and circumstances + of the failure. The caller must CFRelease() this error object when done with it. + @result If validation passes, noErr. If validation fails, an OSStatus value + documented in CSCommon.h or certain other Security framework headers. +*/ +enum { + kSecCSCheckAllArchitectures = 1 << 0, + kSecCSDoNotValidateExecutable = 1 << 1, + kSecCSDoNotValidateResources = 1 << 2, + kSecCSBasicValidateOnly = kSecCSDoNotValidateExecutable | kSecCSDoNotValidateResources +}; + +OSStatus SecStaticCodeCheckValidity(SecStaticCodeRef staticCode, SecCSFlags flags, + SecRequirementRef requirement); + +OSStatus SecStaticCodeCheckValidityWithErrors(SecStaticCodeRef staticCode, SecCSFlags flags, + SecRequirementRef requirement, CFErrorRef *errors); + + +#ifdef __cplusplus +} +#endif + +#endif //_H_SECSTATICCODE diff --git a/lib/StaticCode.cpp b/lib/StaticCode.cpp new file mode 100644 index 0000000..78d7208 --- /dev/null +++ b/lib/StaticCode.cpp @@ -0,0 +1,973 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// StaticCode - SecStaticCode API objects +// +#include "StaticCode.h" +#include "Code.h" +#include "reqmaker.h" +#include "reqdumper.h" +#include "sigblob.h" +#include "resources.h" +#include "renum.h" +#include "csutilities.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// We use a DetachedRep to interpose (filter) the genuine DiskRep representing +// the code on disk, *if* a detached signature was set on this object. In this +// situation, mRep will point to a (2 element) chain of DiskReps. +// +// This is a neat way of dealing with the (unusual) detached-signature case +// without disturbing things unduly. Consider DetachedDiskRep to be closely +// married to SecStaticCode; it's unlikely to work right if you use it elsewhere. +// +class DetachedRep : public DiskRep { +public: + DetachedRep(CFDataRef sig, DiskRep *orig); + + const RefPointer original; // underlying representation + + DiskRep *base() { return original; } + CFDataRef component(CodeDirectory::SpecialSlot slot); + std::string mainExecutablePath() { return original->mainExecutablePath(); } + CFURLRef canonicalPath() { return original->canonicalPath(); } + std::string recommendedIdentifier() { return original->recommendedIdentifier(); } + std::string resourcesRootPath() { return original->resourcesRootPath(); } + CFDictionaryRef defaultResourceRules() { return original->defaultResourceRules(); } + Universal *mainExecutableImage() { return original->mainExecutableImage(); } + size_t signingBase() { return original->signingBase(); } + size_t signingLimit() { return original->signingLimit(); } + std::string format() { return original->format(); } + FileDesc &fd() { return original->fd(); } + void flush() { return original->flush(); } + +private: + CFCopyRef mSignature; + const EmbeddedSignatureBlob *mArch; // current architecture; points into mSignature + const EmbeddedSignatureBlob *mGlobal; // shared elements; points into mSignature +}; + + +// +// Construct a SecStaticCode object given a disk representation object +// +SecStaticCode::SecStaticCode(DiskRep *rep) + : mRep(rep), + mValidated(false), mExecutableValidated(false), + mDesignatedReq(NULL), mGotResourceBase(false), mEvalDetails(NULL) +{ +} + + +// +// Clean up a SecStaticCode object +// +SecStaticCode::~SecStaticCode() throw() +{ + ::free(const_cast(mDesignatedReq)); +} + + +// +// Attach a detached signature. +// +void SecStaticCode::detachedSignature(CFDataRef sigData) +{ + if (sigData) + mRep = new DetachedRep(sigData, mRep->base()); + else + mRep = mRep->base(); +} + + +// +// Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs +// (if possible). +// +SecStaticCode *SecStaticCode::requiredStatic(SecStaticCodeRef ref) +{ + SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef); + if (SecStaticCode *scode = dynamic_cast(object)) + return scode; + else if (SecCode *code = dynamic_cast(object)) + return code->staticCode(); + else // neither (a SecSomethingElse) + MacOSError::throwMe(errSecCSInvalidObjectRef); +} + +SecCode *SecStaticCode::optionalDynamic(SecStaticCodeRef ref) +{ + SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef); + if (dynamic_cast(object)) + return NULL; + else if (SecCode *code = dynamic_cast(object)) + return code; + else // neither (a SecSomethingElse) + MacOSError::throwMe(errSecCSInvalidObjectRef); +} + + +// +// Void all cached validity data. +// +// We also throw out cached components, because the new signature data may have +// a different idea of what components should be present. We could reconcile the +// cached data instead, if performance seems to be impacted. +// +void SecStaticCode::resetValidity() +{ + secdebug("staticCode", "%p resetting validity status", this); + mValidated = false; + mExecutableValidated = false; + mDir = NULL; + mSignature = NULL; + for (unsigned n = 0; n < cdSlotCount; n++) + mCache[n] = NULL; + mInfoDict = NULL; + mResourceDict = NULL; + mDesignatedReq = NULL; + mTrust = NULL; + mCertChain = NULL; + mEvalDetails = NULL; + mRep->flush(); +} + + +// +// Retrieve a sealed component by special slot index. +// If the CodeDirectory has already been validated, validate against that. +// Otherwise, retrieve the component without validation (but cache it). Validation +// will go through the cache and validate all cached components. +// +CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot) +{ + assert(slot <= cdSlotMax); + + CFRef &cache = mCache[slot]; + if (!cache) { + if (CFRef data = mRep->component(slot)) { + if (validated()) // if the directory has been validated... + if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), // ... and it's no good + CFDataGetLength(data), -slot)) + MacOSError::throwMe(errSecCSSignatureFailed); // ... then bail + cache = data; // it's okay, cache it + } else { // absent, mark so + if (validated()) // if directory has been validated... + if (codeDirectory()->slotIsPresent(-slot)) // ... and the slot is NOT missing + MacOSError::throwMe(errSecCSSignatureFailed); // was supposed to be there + cache = CFDataRef(kCFNull); // white lie + } + } + return (cache == CFDataRef(kCFNull)) ? NULL : cache.get(); +} + + +// +// Get the CodeDirectory. +// Throws (if check==true) or returns NULL (check==false) if there is none. +// Always throws if the CodeDirectory exists but is invalid. +// NEVER validates against the signature. +// +const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */) +{ + if (!mDir) { + if (mDir.take(mRep->codeDirectory())) { + const CodeDirectory *dir = reinterpret_cast(CFDataGetBytePtr(mDir)); + dir->checkVersion(); + } + } + if (mDir) + return reinterpret_cast(CFDataGetBytePtr(mDir)); + if (check) + MacOSError::throwMe(errSecCSUnsigned); + return NULL; +} + + +// +// Return the CMS signature blob; NULL if none found. +// +CFDataRef SecStaticCode::signature() +{ + if (!mSignature) + mSignature.take(mRep->signature()); + if (mSignature) + return mSignature; + MacOSError::throwMe(errSecCSUnsigned); +} + + +// +// Verify the signature on the CodeDirectory. +// If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy. +// Any outcome (successful or not) is cached for the lifetime of the StaticCode. +// +void SecStaticCode::validateDirectory() +{ + // echo previous outcome, if any + if (!validated()) + try { + // perform validation (or die trying) + secdebug("staticCode", "%p validating directory", this); + mValidationExpired = verifySignature(); + component(cdInfoSlot); // force load of Info Dictionary (if any) + for (CodeDirectory::SpecialSlot slot = codeDirectory()->nSpecialSlots; + slot >= 1; --slot) + if (mCache[slot]) // if we already loaded that resource... + validateComponent(slot); // ... then check it now + mValidated = true; // we've done the deed... + mValidationResult = noErr; // ... and it was good + } catch (const CommonError &err) { + mValidated = true; + mValidationResult = err.osStatus(); + throw; + } catch (...) { + secdebug("staticCode", "%p validation threw non-common exception", this); + mValidated = true; + mValidationResult = errSecCSInternalError; + throw; + } + assert(validated()); + if (mValidationResult == noErr) { + if (mValidationExpired) + if ((apiFlags() & kSecCSConsiderExpiration) + || (codeDirectory()->flags & kSecCodeSignatureForceExpiration)) + MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED); + } else + MacOSError::throwMe(mValidationResult); +} + + +// +// Get the (signed) signing date from the code signature. +// Sadly, we need to validate the signature to get the date (as a side benefit). +// This means that you can't get the signing time for invalidly signed code. +// +// We could run the decoder "almost to" verification to avoid this, but there seems +// little practical point to such a duplication of effort. +// +CFAbsoluteTime SecStaticCode::signingTime() +{ + validateDirectory(); + return mSigningTime; +} + + +// +// Verify the CMS signature on the CodeDirectory. +// This performs the cryptographic tango. It returns if the signature is valid, +// or throws if it is not. As a side effect, a successful return sets up the +// cached certificate chain for future use. +// +bool SecStaticCode::verifySignature() +{ + // ad-hoc signed code is considered validly signed by definition + if (flag(kSecCodeSignatureAdhoc)) { + secdebug("staticCode", "%p considered verified since it is ad-hoc", this); + return false; + } + + // decode CMS and extract SecTrust for verification + secdebug("staticCode", "%p verifying signature", this); + CFRef cms; + MacOSError::check(CMSDecoderCreate(&cms.aref())); // create decoder + CFDataRef sig = this->signature(); + MacOSError::check(CMSDecoderUpdateMessage(cms, CFDataGetBytePtr(sig), CFDataGetLength(sig))); + this->codeDirectory(); // load CodeDirectory (sets mDir) + MacOSError::check(CMSDecoderSetDetachedContent(cms, mDir)); + MacOSError::check(CMSDecoderFinalizeMessage(cms)); + MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray())); + CMSSignerStatus status; + MacOSError::check(CMSDecoderCopySignerStatus(cms, 0, verificationPolicy(), + false, &status, &mTrust.aref(), NULL)); + + // get signing date (we've got the decoder handle right here) + mSigningTime = 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-) + SecCmsMessageRef cmsg; + MacOSError::check(CMSDecoderGetCmsMessage(cms, &cmsg)); + SecCmsSignedDataRef signedData = NULL; + int numContentInfos = SecCmsMessageContentLevelCount(cmsg); + for(int dex = 0; !signedData && dex < numContentInfos; dex++) { + SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex); + SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); + switch(tag) { + case SEC_OID_PKCS7_SIGNED_DATA: + if (SecCmsSignedDataRef signedData = SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci))) + if (SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, 0)) + SecCmsSignerInfoGetSigningTime(signerInfo, &mSigningTime); + break; + default: + break; + } + } + + // set up the environment for SecTrust + MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); // no anchors + CSSM_APPLE_TP_ACTION_DATA actionData = { + CSSM_APPLE_TP_ACTION_VERSION, // version of data structure + CSSM_TP_ACTION_IMPLICIT_ANCHORS // action flags + }; + + for (;;) { + MacOSError::check(SecTrustSetParameters(mTrust, + CSSM_TP_ACTION_DEFAULT, CFTempData(&actionData, sizeof(actionData)))); + + // evaluate trust and extract results + SecTrustResultType trustResult; + MacOSError::check(SecTrustEvaluate(mTrust, &trustResult)); + MacOSError::check(SecTrustGetResult(mTrust, &trustResult, &mCertChain.aref(), &mEvalDetails)); + secdebug("staticCode", "%p verification result=%d chain=%ld", + this, trustResult, mCertChain ? CFArrayGetCount(mCertChain) : -1); + switch (trustResult) { + case kSecTrustResultProceed: + case kSecTrustResultConfirm: + case kSecTrustResultUnspecified: + break; // success + case kSecTrustResultDeny: + MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY); // user reject + case kSecTrustResultInvalid: + assert(false); // should never happen + MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED); + case kSecTrustResultRecoverableTrustFailure: + case kSecTrustResultFatalTrustFailure: + case kSecTrustResultOtherError: + { + OSStatus result; + MacOSError::check(SecTrustGetCssmResultCode(mTrust, &result)); + if (result == CSSMERR_TP_CERT_EXPIRED && !(actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED)) { + secdebug("staticCode", "expired certificate(s); retrying validation"); + actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED; + continue; // retry validation + } + MacOSError::throwMe(result); + } + } + return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED; + } +} + + +// +// Return the TP policy used for signature verification. +// This policy object is cached and reused. +// +SecPolicyRef SecStaticCode::verificationPolicy() +{ + if (!mPolicy) + MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, + &CSSMOID_APPLE_TP_CODE_SIGNING, &mPolicy.aref())); + return mPolicy; +} + + +// +// Validate a particular sealed, cached resource against its (special) CodeDirectory slot. +// The resource must already have been placed in the cache. +// This does NOT perform basic validation. +// +void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot) +{ + CFDataRef data = mCache[slot]; + assert(data); // must be cached + if (data == CFDataRef(kCFNull)) { + if (codeDirectory()->slotIsPresent(-slot)) // was supposed to be there... + MacOSError::throwMe(errSecCSSignatureFailed); // ... and is missing + } else { + if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot)) + MacOSError::throwMe(errSecCSSignatureFailed); + } +} + + +// +// Perform static validation of the main executable. +// This reads the main executable from disk and validates it against the +// CodeDirectory code slot array. +// Note that this is NOT an in-memory validation, and is thus potentially +// subject to timing attacks. +// +void SecStaticCode::validateExecutable() +{ + secdebug("staticCode", "%p performing static main exec validate of %s", + this, mainExecutablePath().c_str()); + const CodeDirectory *cd = this->codeDirectory(); + if (!cd) + MacOSError::throwMe(errSecCSUnsigned); + AutoFileDesc fd(mainExecutablePath(), O_RDONLY); + fd.fcntl(F_NOCACHE, true); // turn off page caching (one-pass) + if (Universal *fat = mRep->mainExecutableImage()) + fd.seek(fat->archOffset()); + size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0; + size_t remaining = cd->codeLimit; + for (size_t slot = 0; slot < cd->nCodeSlots; ++slot) { + size_t size = min(remaining, pageSize); + if (!cd->validateSlot(fd, size, slot)) { + secdebug("staticCode", "%p failed static validation of code page %zd", this, slot); + mExecutableValidated = true; // we tried + mExecutableValid = false; // it failed + MacOSError::throwMe(errSecCSSignatureFailed); + } + remaining -= size; + } + secdebug("staticCode", "%p validated full executable (%d pages)", + this, int(cd->nCodeSlots)); + mExecutableValidated = true; // we tried + mExecutableValid = true; // it worked +} + + +// +// Perform static validation of sealed resources. +// +// This performs a whole-code static resource scan and effectively +// computes a concordance between what's on disk and what's in the ResourceDirectory. +// Any unsanctioned difference causes an error. +// +void SecStaticCode::validateResources() +{ + // sanity first + CFDictionaryRef sealedResources = resourceDictionary(); + if (this->resourceBase()) // disk has resources + if (sealedResources) + /* go to work below */; + else + MacOSError::throwMe(errSecCSResourcesNotFound); + else // disk has no resources + if (sealedResources) + MacOSError::throwMe(errSecCSResourcesNotFound); + else + return; // no resources, not sealed - fine (no work) + + // found resources, and they are sealed + CFDictionaryRef rules = cfget(sealedResources, "rules"); + CFDictionaryRef files = cfget(sealedResources, "files"); + secdebug("staticCode", "%p verifying %d sealed resources", + this, int(CFDictionaryGetCount(files))); + + // make a shallow copy of the ResourceDirectory so we can "check off" what we find + CFRef resourceMap = CFDictionaryCreateMutableCopy(NULL, + CFDictionaryGetCount(files), files); + if (!resourceMap) + CFError::throwMe(); + + // scan through the resources on disk, checking each against the resourceDirectory + CollectingContext ctx(*this); // collect all failures in here + ResourceBuilder resources(cfString(this->resourceBase()), rules); + string path; + ResourceBuilder::Rule *rule; + + while (resources.next(path, rule)) { + if (CFDataRef value = resource(path, ctx)) + CFRelease(value); + CFDictionaryRemoveValue(resourceMap, CFTempString(path)); + secdebug("staticCode", "%p validated %s", this, path.c_str()); + } + + if (CFDictionaryGetCount(resourceMap) > 0) { + secdebug("staticCode", "%p sealed resource(s) not found in code", this); + CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, &ctx); + } + + // now check for any errors found in the reporting context + if (ctx) + ctx.throwMe(); + + secdebug("staticCode", "%p sealed resources okay", this); +} + + +void SecStaticCode::checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context) +{ + CollectingContext *ctx = static_cast(context); + ResourceSeal seal(value); + if (!seal.optional()) + if (key && CFGetTypeID(key) == CFStringGetTypeID()) { + ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, + CFTempURL(CFStringRef(key), false, ctx->code.resourceBase())); + } else + ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceSeal, key); +} + + +// +// Load, validate, cache, and return CFDictionary forms of sealed resources. +// +CFDictionaryRef SecStaticCode::infoDictionary() +{ + if (!mInfoDict) { + mInfoDict.take(getDictionary(cdInfoSlot)); + secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict.get()); + } + return mInfoDict; +} + +CFDictionaryRef SecStaticCode::resourceDictionary() +{ + if (mResourceDict) // cached + return mResourceDict; + if (CFRef dict = getDictionary(cdResourceDirSlot)) + if (cfscan(dict, "{rules=%Dn,files=%Dn}")) { + secdebug("staticCode", "%p loaded ResourceDict %p", + this, mResourceDict.get()); + return mResourceDict = dict; + } + // bad format + return NULL; +} + + +// +// Load and cache the resource directory base. +// Note that the base is optional for each DiskRep. +// +CFURLRef SecStaticCode::resourceBase() +{ + if (!mGotResourceBase) { + string base = mRep->resourcesRootPath(); + if (!base.empty()) + mResourceBase.take(makeCFURL(base, true)); + mGotResourceBase = true; + } + return mResourceBase; +} + + +// +// Load a component, validate it, convert it to a CFDictionary, and return that. +// This will force load and validation, which means that it will perform basic +// validation if it hasn't been done yet. +// +CFDictionaryRef SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot) +{ + validateDirectory(); + if (CFDataRef infoData = component(slot)) { + validateComponent(slot); + if (CFDictionaryRef dict = makeCFDictionaryFrom(infoData)) + return dict; + else + MacOSError::throwMe(errSecCSBadDictionaryFormat); + } + return NULL; +} + + +// +// Load, validate, and return a sealed resource. +// The resource data (loaded in to memory as a blob) is returned and becomes +// the responsibility of the caller; it is NOT cached by SecStaticCode. +// +// A resource that is not sealed will not be returned, and an error will be thrown. +// A missing resource will cause an error unless it's marked optional in the Directory. +// Under no circumstances will a corrupt resource be returned. +// NULL will only be returned for a resource that is neither sealed nor present +// (or that is sealed, absent, and marked optional). +// If the ResourceDictionary itself is not sealed, this function will always fail. +// +// There is currently no interface for partial retrieval of the resource data. +// (Since the ResourceDirectory does not currently support segmentation, all the +// data would have to be read anyway, but it could be read into a reusable buffer.) +// +CFDataRef SecStaticCode::resource(string path, ValidationContext &ctx) +{ + if (CFDictionaryRef rdict = resourceDictionary()) { + if (CFTypeRef file = cfget(rdict, "files.%s", path.c_str())) { + ResourceSeal seal = file; + if (!resourceBase()) // no resources in DiskRep + MacOSError::throwMe(errSecCSResourcesNotFound); + CFRef fullpath = makeCFURL(path, false, resourceBase()); + if (CFRef data = cfLoadFile(fullpath)) { + SHA1 hasher; + hasher(CFDataGetBytePtr(data), CFDataGetLength(data)); + if (hasher.verify(seal.hash())) + return data.yield(); // good + else + ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered + } else { + if (!seal.optional()) + ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing + else + return NULL; // validly missing + } + } else + ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase())); + return NULL; + } else + MacOSError::throwMe(errSecCSResourcesNotSealed); +} + +CFDataRef SecStaticCode::resource(string path) +{ + ValidationContext ctx; + return resource(path, ctx); +} + + +// +// Test a CodeDirectory flag. +// Returns false if there is no CodeDirectory. +// May throw if the CodeDirectory is present but somehow invalid. +// +bool SecStaticCode::flag(uint32_t tested) +{ + if (const CodeDirectory *cd = this->codeDirectory(false)) + return cd->flags & tested; + else + return false; +} + + +// +// Retrieve the full SuperBlob containing all internal requirements. +// +const Requirements *SecStaticCode::internalRequirements() +{ + if (CFDataRef req = component(cdRequirementsSlot)) + return (const Requirements *)CFDataGetBytePtr(req); + else + return NULL; +} + + +// +// Retrieve a particular internal requirement by type. +// +const Requirement *SecStaticCode::internalRequirement(SecRequirementType type) +{ + if (const Requirements *reqs = internalRequirements()) + return reqs->find(type); + else + return NULL; +} + + +// +// Return the Designated Requirement. This can be either explicit in the +// Internal Requirements resource, or implicitly generated. +// +const Requirement *SecStaticCode::designatedRequirement() +{ + if (const Requirement *req = internalRequirement(kSecDesignatedRequirementType)) { + return req; // explicit in signing data + } else { + if (!mDesignatedReq) + mDesignatedReq = defaultDesignatedRequirement(); + return mDesignatedReq; + } +} + + +// +// Generate the default (implicit) Designated Requirement for this StaticCode. +// This is a heuristic of sorts, and may change over time (for the better, we hope). +// +// The current logic is this: +// * If the code is ad-hoc signed, use the CodeDirectory hash directory. +// * Otherwise, use the form anchor (anchor) and identifier (CodeDirectory identifier). +// ** If the root CA is Apple's, we use the "anchor apple" construct. Otherwise, +// we default to the leaf (directly signing) certificate. +// +const Requirement *SecStaticCode::defaultDesignatedRequirement() +{ + validateDirectory(); // need the cert chain + Requirement::Maker maker; + + // if this is an ad-hoc (unsigned) object, return a cdhash requirement + if (flag(kSecCodeSignatureAdhoc)) { + SHA1 hash; + hash(codeDirectory(), codeDirectory()->length()); + SHA1::Digest digest; + hash.finish(digest); + maker.cdhash(digest); + } else { + // always require the identifier + maker.put(opAnd); + maker.ident(codeDirectory()->identifier()); + + SHA1::Digest anchorHash; + hashOfCertificate(cert(Requirement::anchorCert), anchorHash); + if (!memcmp(anchorHash, Requirement::appleAnchorHash(), SHA1::digestLength) +#if defined(TEST_APPLE_ANCHOR) + || !memcmp(anchorHash, Requirement::testAppleAnchorHash(), SHA1::digestLength) +#endif + ) { + maker.anchor(); // canonical Apple anchor + } else { + // we don't know anything more, so we'll (conservatively) pick the leaf + SHA1::Digest leafHash; + hashOfCertificate(cert(Requirement::leafCert), leafHash); + maker.anchor(Requirement::leafCert, leafHash); + } + } + + return maker(); +} + + +// +// Validate a SecStaticCode against the internal requirement of a particular type. +// +void SecStaticCode::validateRequirements(SecRequirementType type, SecStaticCode *target, + OSStatus nullError /* = noErr */) +{ + secdebug("staticCode", "%p validating %s requirements for %p", + this, Requirement::typeNames[type], target); + if (const Requirement *req = internalRequirement(type)) + target->validateRequirements(req, nullError ? nullError : errSecCSReqFailed); + else if (nullError) { + secdebug("staticCode", "%p NULL validate for %s prohibited", + this, Requirement::typeNames[type]); + MacOSError::throwMe(nullError); + } else + secdebug("staticCode", "%p NULL validate (no requirements for %s)", + this, Requirement::typeNames[type]); +} + + +// +// Validate this StaticCode against an external Requirement +// +void SecStaticCode::validateRequirements(const Requirement *req, OSStatus failure) +{ + assert(req); + validateDirectory(); + req->validate(Requirement::Context(mCertChain, infoDictionary(), codeDirectory()), failure); +} + + +// +// Retrieve one certificate from the cert chain. +// Positive and negative indices can be used: +// [ leaf, intermed-1, ..., intermed-n, anchor ] +// 0 1 ... -2 -1 +// Returns NULL if unavailable for any reason. +// +SecCertificateRef SecStaticCode::cert(int ix) +{ + validateDirectory(); // need cert chain + if (mCertChain) { + if (ix < 0) + ix += CFArrayGetCount(mCertChain); + if (CFTypeRef element = CFArrayGetValueAtIndex(mCertChain, ix)) + return SecCertificateRef(element); + } + return NULL; +} + +CFArrayRef SecStaticCode::certificates() +{ + validateDirectory(); // need cert chain + return mCertChain; +} + + +// +// Gather API-official information about this StaticCode. +// +// This method lives in the twilight between the API and internal layers, +// since it generates API objects (Sec*Refs) for return. +// +CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags) +{ + // + // Start with the pieces that we return even for unsigned code. + // This makes Sec[Static]CodeRefs useful as API-level replacements + // of our internal OSXCode objects. + // + CFRef dict = makeCFMutableDictionary(1, + kSecCodeInfoMainExecutable, CFTempURL(this->mainExecutablePath()).get() + ); + + // + // If we're not signed, this is all you get + // + if (!this->isSigned()) + return dict.yield(); + + + // + // Now add the generic attributes that we always include + // + CFDictionaryAddValue(dict, kSecCodeInfoIdentifier, CFTempString(this->identifier())); + CFDictionaryAddValue(dict, kSecCodeInfoFormat, CFTempString(this->format())); + if (CFDictionaryRef info = infoDictionary()) + CFDictionaryAddValue(dict, kSecCodeInfoPList, info); + + // + // kSecCSSigningInformation adds information about signing certificates and chains + // + if (flags & kSecCSSigningInformation) { + if (CFArrayRef certs = certificates()) + CFDictionaryAddValue(dict, kSecCodeInfoCertificates, certs); + if (CFDataRef sig = signature()) + CFDictionaryAddValue(dict, kSecCodeInfoCMS, sig); + if (mTrust) + CFDictionaryAddValue(dict, kSecCodeInfoTrust, mTrust); + if (CFAbsoluteTime time = signingTime()) + if (CFRef date = CFDateCreate(NULL, time)) + CFDictionaryAddValue(dict, kSecCodeInfoTime, date); + } + + // + // kSecCSRequirementInformation adds information on requirements + // + if (flags & kSecCSRequirementInformation) { + if (const Requirements *reqs = internalRequirements()) { + CFDictionaryAddValue(dict, kSecCodeInfoRequirements, + CFTempString(Dumper::dump(reqs))); + } + const Requirement *ddreq = defaultDesignatedRequirement(); + CFRef ddreqRef = (new SecRequirement(ddreq))->handle(); + const Requirement *dreq = designatedRequirement(); + if (dreq == ddreq) { + CFDictionaryAddValue(dict, kSecCodeInfoDesignatedRequirement, ddreqRef); + CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, ddreqRef); + } else { + CFDictionaryAddValue(dict, kSecCodeInfoDesignatedRequirement, + CFRef((new SecRequirement(dreq))->handle())); + CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, ddreqRef); + } + } + + // + // kSecCSInternalInformation adds internal information meant to be for Apple internal + // use (SPI), and not guaranteed to be stable. Primarily, this is data we want + // to reliably transmit through the API wall so that code outside the Security.framework + // can use it without having to play nasty tricks to get it. + // + if (flags & kSecCSInternalInformation) { + if (mDir) + CFDictionaryAddValue(dict, CFSTR("CodeDirectory"), mDir); + CFDictionaryAddValue(dict, CFSTR("CodeOffset"), CFTempNumber(mRep->signingBase())); + } + + + // + // kSecCSContentInformation adds more information about the physical layout + // of the signed code. This is (only) useful for packaging or patching-oriented + // applications. + // + if (flags & kSecCSContentInformation) + if (CFRef files = mRep->modifiedFiles()) + CFDictionaryAddValue(dict, kSecCodeInfoChangedFiles, files); + + return dict.yield(); +} + + +// +// Resource validation contexts. +// The default context simply throws a CSError, rudely terminating the operation. +// +SecStaticCode::ValidationContext::~ValidationContext() +{ /* virtual */ } + +void SecStaticCode::ValidationContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value) +{ + CSError::throwMe(rc, type, value); +} + +void SecStaticCode::CollectingContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value) +{ + if (mStatus == noErr) + mStatus = rc; // record first failure for eventual error return + if (type) { + if (!mCollection) + mCollection.take(makeCFMutableDictionary(0)); + CFMutableArrayRef element = CFMutableArrayRef(CFDictionaryGetValue(mCollection, type)); + if (!element) { + element = makeCFMutableArray(0); + if (!element) + CFError::throwMe(); + CFDictionaryAddValue(mCollection, type, element); + CFRelease(element); + } + CFArrayAppendValue(element, value); + } +} + +void SecStaticCode::CollectingContext::throwMe() +{ + assert(mStatus != noErr); + throw CSError(mStatus, mCollection.yield()); +} + + +// +// DetachedRep construction +// +DetachedRep::DetachedRep(CFDataRef sig, DiskRep *orig) + : original(orig), mSignature(sig) +{ + const BlobCore *sigBlob = reinterpret_cast(CFDataGetBytePtr(sig)); + if (sigBlob->is()) { // architecture-less + mArch = EmbeddedSignatureBlob::specific(sigBlob); + mGlobal = NULL; + return; + } else if (sigBlob->is()) { // architecture collection + const DetachedSignatureBlob *dsblob = DetachedSignatureBlob::specific(sigBlob); + if (Universal *fat = orig->mainExecutableImage()) { + if (const BlobCore *blob = dsblob->find(fat->bestNativeArch().cpuType())) { + mArch = EmbeddedSignatureBlob::specific(blob); + mGlobal = EmbeddedSignatureBlob::specific(dsblob->find(0)); + return; + } else + secdebug("staticcode", "detached signature missing architecture %s", + fat->bestNativeArch().name()); + } else + secdebug("staticcode", "detached signature requires Mach-O binary"); + } else + secdebug("staticcode", "detached signature bad magic 0x%x", sigBlob->magic()); + MacOSError::throwMe(errSecCSSignatureInvalid); +} + +CFDataRef DetachedRep::component(CodeDirectory::SpecialSlot slot) +{ + if (CFDataRef result = mArch->component(slot)) + return result; + if (mGlobal) + if (CFDataRef result = mGlobal->component(slot)) + return result; + return original->component(slot); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/StaticCode.h b/lib/StaticCode.h new file mode 100644 index 0000000..49d7d51 --- /dev/null +++ b/lib/StaticCode.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// StaticCode - SecStaticCode API objects +// +#ifndef _H_STATICCODE +#define _H_STATICCODE + +#include "cs.h" +#include "Requirements.h" +#include "requirement.h" +#include "diskrep.h" +#include "codedirectory.h" +#include +#include + +namespace Security { +namespace CodeSigning { + + +class SecCode; + + +// +// A SecStaticCode object represents the file system version of some code. +// There's a lot of pieces to this, and we'll bring them all into +// memory here (lazily) and let you fondle them with ease. +// +// Note that concrete knowledge of where stuff is stored resides in the DiskRep +// object we hold. DiskReps allocate, retrieve, and return data to us. We are +// responsible for interpreting, caching, and validating them. +// +class SecStaticCode : public SecCFObject { + NOCOPY(SecStaticCode) + +protected: + class ValidationContext { + public: + virtual ~ValidationContext(); + virtual void reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value); + }; + + class CollectingContext : public ValidationContext { + public: + CollectingContext(SecStaticCode &c) : code(c), mStatus(noErr) { } + void reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value); + + operator OSStatus () const { return mStatus; } + void throwMe() __attribute__((noreturn)); + + SecStaticCode &code; + + private: + CFRef mCollection; + OSStatus mStatus; + }; + +public: + SECCFFUNCTIONS(SecStaticCode, SecStaticCodeRef, + errSecCSInvalidObjectRef, gCFObjects().StaticCode) + + // implicitly convert SecCodeRefs to their SecStaticCodeRefs + static SecStaticCode *requiredStatic(SecStaticCodeRef ref); // convert SecCodeRef + static SecCode *optionalDynamic(SecStaticCodeRef ref); // extract SecCodeRef or NULL + + SecStaticCode(DiskRep *rep); + virtual ~SecStaticCode() throw(); + + void detachedSignature(CFDataRef sig); + + const CodeDirectory *codeDirectory(bool check = true); + CFDataRef signature(); + CFAbsoluteTime signingTime(); + bool isSigned() { return codeDirectory(false) != NULL; } + DiskRep *diskRep() { return mRep; } + std::string mainExecutablePath() { return mRep->mainExecutablePath(); } + CFURLRef canonicalPath() const { return mRep->canonicalPath(); } + std::string identifier() { return codeDirectory()->identifier(); } + std::string format() const { return mRep->format(); } + CFDataRef component(CodeDirectory::SpecialSlot slot); + CFDictionaryRef infoDictionary(); + + CFDictionaryRef resourceDictionary(); + CFURLRef resourceBase(); + CFDataRef resource(std::string path); + CFDataRef resource(std::string path, ValidationContext &ctx); + + bool flag(uint32_t tested); + + void resetValidity(); + + bool validated() const { return mValidated; } + bool valid() const + { assert(validated()); return mValidated && (mValidationResult == noErr); } + + void validateDirectory(); + void validateComponent(CodeDirectory::SpecialSlot slot); + void validateResources(); + void validateExecutable(); + + const Requirements *internalRequirements(); + const Requirement *internalRequirement(SecRequirementType type); + const Requirement *designatedRequirement(); + const Requirement *defaultDesignatedRequirement(); + + void validateRequirements(SecRequirementType type, SecStaticCode *target, + OSStatus nullError = noErr); + void validateRequirements(const Requirement *req, OSStatus failure); + + SecCertificateRef cert(int ix); // get a cert from the cert chain + CFArrayRef certificates(); // get the entire certificate chain + + CFDictionaryRef signingInformation(SecCSFlags flags); // information-gathering API + +protected: + CFDictionaryRef getDictionary(CodeDirectory::SpecialSlot slot); + bool verifySignature(); + SecPolicyRef verificationPolicy(); + + static void checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context); + +private: + RefPointer mRep; // on-disk representation + + // master validation state + bool mValidated; // core validation was attempted + OSStatus mValidationResult; // outcome of core validation + bool mValidationExpired; // outcome had expired certificates + + // static executable validation state (nested within mValidated/mValid) + bool mExecutableValidated; // tried to validate executable file + bool mExecutableValid; // outcome if mExecutableValidated + + // cached contents + CFRef mDir; // code directory data + CFRef mSignature; // CMS signature data + CFAbsoluteTime mSigningTime; // (signed) signing time + CFRef mCache[cdSlotCount]; // NULL => not tried, kCFNull => absent, other => present + + // alternative cache forms (storage may depend on cached contents above) + CFRef mInfoDict; // derived from mCache slot + CFRef mResourceDict; // derived from mCache slot + const Requirement *mDesignatedReq; // cached designated req if we made one up + + bool mGotResourceBase; // asked mRep for resourceBasePath + CFRef mResourceBase; // URL form of resource base directory + + // signature verification outcome (mTrust == NULL => not done yet) + CFRef mTrust; // outcome of crypto validation (valid or not) + CFRef mCertChain; + CSSM_TP_APPLE_EVIDENCE_INFO *mEvalDetails; + + // cached verification policy + CFRef mPolicy; +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_STATICCODE diff --git a/lib/antlrplugin.cpp b/lib/antlrplugin.cpp new file mode 100644 index 0000000..fc38e53 --- /dev/null +++ b/lib/antlrplugin.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2007 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@ + */ + +#include "antlrplugin.h" +#include "cserror.h" +#include "RequirementLexer.hpp" +#include "RequirementParser.hpp" +#include + + +namespace Security { +namespace CodeSigning { + +namespace Parser = Security_CodeSigning; + + +// +// Lexer input adapters +// +class StdioInputStream : public antlr::InputBuffer { +public: + StdioInputStream(FILE *fp) : mFile(fp) { } + int getChar() { return fgetc(mFile); } + +private: + FILE *mFile; +}; + +class StringInputStream : public antlr::InputBuffer { +public: + StringInputStream(const string &s) : mInput(s), mPos(mInput.begin()) { } + int getChar() { return (mPos == mInput.end()) ? EOF : *mPos++; } + +private: + string mInput; + string::const_iterator mPos; +}; + + +// +// Generic parser driver +// +template +const Result *parse(Source source, Result *(Parser::RequirementParser::*rule)(), std::string &errors) +{ + Input input(source); + Parser::RequirementLexer lexer(input); + Parser::RequirementParser parser(lexer); + try { + const Result *result = (parser.*rule)(); + errors = parser.errors; + if (errors.empty()) + return result; + else + ::free((void *)result); + } catch (const antlr::TokenStreamException &ex) { + errors = ex.toString() + "\n"; + } + return NULL; // signal failure +} + + +// +// Hook up each supported parsing action to the plugin interface +// +const Requirement *fileRequirement(FILE *source, string &errors) +{ return parse(source, &Parser::RequirementParser::requirement, errors); } + +const Requirement *stringRequirement(string source, string &errors) +{ return parse(source, &Parser::RequirementParser::requirement, errors); } + +const Requirements *fileRequirements(FILE *source, string &errors) +{ return parse(source, &Parser::RequirementParser::requirementSet, errors); } + +const Requirements *stringRequirements(string source, string &errors) +{ return parse(source, &Parser::RequirementParser::requirementSet, errors); } + +const BlobCore *fileGeneric(FILE *source, string &errors) +{ return parse(source, &Parser::RequirementParser::autosense, errors); } + +const BlobCore *stringGeneric(string source, string &errors) +{ return parse(source, &Parser::RequirementParser::autosense, errors); } + + +// +// Basic plugin hookup +// +static AntlrPlugin plugin = { + fileRequirement, + fileRequirements, + fileGeneric, + stringRequirement, + stringRequirements, + stringGeneric +}; + +AntlrPlugin *findAntlrPlugin() +{ + return &plugin; +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/antlrplugin.h b/lib/antlrplugin.h new file mode 100644 index 0000000..2d11139 --- /dev/null +++ b/lib/antlrplugin.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// Plugin interface for internal Security plug-ins +// +#ifndef _H_ANTLRPLUGIN +#define _H_ANTLRPLUGIN + +#include +#include "requirement.h" +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// The plugin proxy. +// +// During loading, one instance of this object will be created by the plugin +// and returned through the (one and only) dynamically-linked method of the plugin. +// All further interaction then proceeds through methods of this object. +// +// +class AntlrPlugin { +public: + typedef const Requirement *FileRequirement(std::FILE *source, std::string &errors); + FileRequirement *fileRequirement; + typedef const Requirements *FileRequirements(std::FILE *source, std::string &errors); + FileRequirements *fileRequirements; + typedef const BlobCore *FileGeneric(std::FILE *source, std::string &errors); + FileGeneric *fileGeneric; + typedef const Requirement *StringRequirement(std::string source, std::string &errors); + StringRequirement *stringRequirement; + typedef const Requirements *StringRequirements(std::string source, std::string &errors); + StringRequirements *stringRequirements; + typedef const BlobCore *StringGeneric(std::string source, std::string &errors); + StringGeneric *stringGeneric; +}; + +extern "C" { + AntlrPlugin *findAntlrPlugin(); + typedef AntlrPlugin *FindAntlrPlugin(); +} + +#define FINDANTLRPLUGIN "findAntlrPlugin" + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_ANTLRPLUGIN diff --git a/lib/bundlediskrep.cpp b/lib/bundlediskrep.cpp new file mode 100644 index 0000000..ad4d819 --- /dev/null +++ b/lib/bundlediskrep.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2006-2007 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@ + */ +#include "bundlediskrep.h" +#include +#include +#include + + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// We make a CFBundleRef immediately, but everything else is lazy +// +BundleDiskRep::BundleDiskRep(const char *path) + : mBundle(_CFBundleCreateIfLooksLikeBundle(NULL, CFTempURL(path))) +{ + if (!mBundle) + MacOSError::throwMe(errSecCSBadObjectFormat); + mExecRep = DiskRep::bestFileGuess(this->mainExecutablePath()); +} + +BundleDiskRep::BundleDiskRep(CFBundleRef ref) +{ + mBundle = ref; // retains + mExecRep = DiskRep::bestFileGuess(this->mainExecutablePath()); +} + + +// +// Create a path to a bundle signing resource, by name. +// Note that these are stored in the bundle's Content directory, +// not its Resources directory. +// +string BundleDiskRep::resourcePath(const char *name) +{ + if (mResourcePath.empty()) + mResourcePath = cfString(CFBundleCopySupportFilesDirectoryURL(mBundle), true); + return mResourcePath + "/" + name; +} + + +// +// Load the data for a signing resource, by URL. +// +CFDataRef BundleDiskRep::resourceData(CFURLRef url) +{ + CFDataRef data; + SInt32 error; + if (CFURLCreateDataAndPropertiesFromResource(NULL, url, + &data, NULL, NULL, &error)) { + return data; + } else { + secdebug("bundlerep", "failed to fetch %s error=%d", + cfString(url).c_str(), int(error)); + return NULL; + } +} + + +// +// Load and return a component, by slot number. +// Info.plist components come from the bundle, always (we don't look +// for Mach-O embedded versions). +// Everything else comes from the embedded blobs of a Mach-O image, or from +// files located in the Contents directory of the bundle. +// +CFDataRef BundleDiskRep::component(CodeDirectory::SpecialSlot slot) +{ + switch (slot) { + // the Info.plist comes from the magic CFBundle-indicated place and ONLY from there + case cdInfoSlot: + if (CFRef info = _CFBundleCopyInfoPlistURL(mBundle)) + return resourceData(info); + else + return NULL; + // by default, we take components from the executable image or files + default: + if (CFDataRef data = mExecRep->component(slot)) + return data; + // falling through + // but the following always come from files + case cdResourceDirSlot: + if (const char *name = CodeDirectory::canonicalSlotName(slot)) + return resourceData(name); + else + return NULL; + } +} + + +// +// Various aspects of our DiskRep personality. +// +CFURLRef BundleDiskRep::canonicalPath() +{ + return CFBundleCopyBundleURL(mBundle); +} + +string BundleDiskRep::recommendedIdentifier() +{ + if (CFStringRef identifier = CFBundleGetIdentifier(mBundle)) + return cfString(identifier); + if (CFDictionaryRef infoDict = CFBundleGetInfoDictionary(mBundle)) + if (CFStringRef identifier = CFStringRef(CFDictionaryGetValue(infoDict, kCFBundleNameKey))) + return cfString(identifier); + + // fall back to using the $(basename) of the canonical path. Drop any .app suffix + string path = cfString(this->canonicalPath()); + if (path.substr(path.size() - 4) == ".app") + path = path.substr(0, path.size() - 4); + string::size_type p = path.rfind('/'); + if (p == string::npos) + return path; + else + return path.substr(p+1); +} + +string BundleDiskRep::mainExecutablePath() +{ + if (CFURLRef exec = CFBundleCopyExecutableURL(mBundle)) + return cfString(exec, true); + else + MacOSError::throwMe(errSecCSBadObjectFormat); +} + +string BundleDiskRep::resourcesRootPath() +{ + return cfString(CFBundleCopySupportFilesDirectoryURL(mBundle), true); +} + +CFDictionaryRef BundleDiskRep::defaultResourceRules() +{ + return cfmake("{rules={" + "'^version.plist$' = #T" + "'^Resources/' = #T" + "'^Resources/.*\\.lproj/' = {optional=#T, weight=1000}" + "'^Resources/.*\\.lproj/locversion.plist$' = {omit=#T, weight=1100}" + "}}"); +} + +const Requirements *BundleDiskRep::defaultRequirements(const Architecture *arch) +{ + return mExecRep->defaultRequirements(arch); +} + + +Universal *BundleDiskRep::mainExecutableImage() +{ + return mExecRep->mainExecutableImage(); +} + +size_t BundleDiskRep::pageSize() +{ + return mExecRep->pageSize(); +} + +size_t BundleDiskRep::signingBase() +{ + return mExecRep->signingBase(); +} + +size_t BundleDiskRep::signingLimit() +{ + return mExecRep->signingLimit(); +} + +string BundleDiskRep::format() +{ + return string("bundle with ") + mExecRep->format(); +} + +CFArrayRef BundleDiskRep::modifiedFiles() +{ + CFMutableArrayRef files = CFArrayCreateMutableCopy(NULL, 0, mExecRep->modifiedFiles()); + checkModifiedFile(files, cdCodeDirectorySlot); + checkModifiedFile(files, cdSignatureSlot); + checkModifiedFile(files, cdResourceDirSlot); + return files; +} + +void BundleDiskRep::checkModifiedFile(CFMutableArrayRef files, CodeDirectory::SpecialSlot slot) +{ + if (CFDataRef data = mExecRep->component(slot)) // provided by executable file + CFRelease(data); + else if (const char *resourceName = CodeDirectory::canonicalSlotName(slot)) // bundle file + CFArrayAppendValue(files, CFTempURL(resourcePath(resourceName))); + else + /* we don't have that one */; +} + +FileDesc &BundleDiskRep::fd() +{ + return mExecRep->fd(); +} + +void BundleDiskRep::flush() +{ + mExecRep->flush(); +} + + +// +// Writers +// +DiskRep::Writer *BundleDiskRep::writer() +{ + return new Writer(this); +} + +BundleDiskRep::Writer::Writer(BundleDiskRep *r) + : rep(r) +{ + execWriter = rep->mExecRep->writer(); +} + + +// +// Write a component. +// Note that this isn't concerned with Mach-O writing; this is handled at +// a much higher level. If we're called, we write to a file in the Bundle's contents directory. +// +void BundleDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) +{ + switch (slot) { + default: + if (!execWriter->attribute(writerLastResort)) // willing to take the data... + return execWriter->component(slot, data); // ... so hand it through + // execWriter doesn't want the data; store it as a resource file (below) + case cdResourceDirSlot: + case cdRequirementsSlot: + // the resource directory always goes into a bundle file + if (const char *name = CodeDirectory::canonicalSlotName(slot)) { + AutoFileDesc fd(rep->resourcePath(name), O_WRONLY | O_CREAT | O_TRUNC); + fd.writeAll(CFDataGetBytePtr(data), CFDataGetLength(data)); + } else + MacOSError::throwMe(errSecCSBadObjectFormat); + } +} + + +void BundleDiskRep::Writer::flush() +{ + execWriter->flush(); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/bundlediskrep.h b/lib/bundlediskrep.h new file mode 100644 index 0000000..4aae953 --- /dev/null +++ b/lib/bundlediskrep.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// bundlediskrep - bundle directory disk representation +// +#ifndef _H_BUNDLEDISKREP +#define _H_BUNDLEDISKREP + +#include "diskrep.h" +#include "machorep.h" + +namespace Security { +namespace CodeSigning { + + +// +// A BundleDiskRep represents a standard Mac OS X bundle on disk. +// The bundle is expected to have an Info.plist, and a "main executable file" +// of some sort (as indicated therein). +// The BundleDiskRep stores the necessary components in the main executable +// if it is in Mach-O format, or in Contents files if not. +// This DiskRep supports resource sealing. +// +class BundleDiskRep : public DiskRep { +public: + BundleDiskRep(const char *path); + BundleDiskRep(CFBundleRef ref); + + CFDataRef component(CodeDirectory::SpecialSlot slot); + std::string mainExecutablePath(); + CFURLRef canonicalPath(); + std::string recommendedIdentifier(); + std::string resourcesRootPath(); + CFDictionaryRef defaultResourceRules(); + const Requirements *defaultRequirements(const Architecture *arch); + Universal *mainExecutableImage(); + size_t pageSize(); + size_t signingBase(); + size_t signingLimit(); + std::string format(); + CFArrayRef modifiedFiles(); + UnixPlusPlus::FileDesc &fd(); + void flush(); + + CFBundleRef bundle() const { return mBundle; } + +public: + Writer *writer(); + class Writer; + friend class Writer; + +protected: + CFDataRef resourceData(CFURLRef url); + CFDataRef resourceData(const char *name) { return resourceData(CFTempURL(resourcePath(name))); } + + std::string resourcePath(const char *name); + +private: + void checkModifiedFile(CFMutableArrayRef files, CodeDirectory::SpecialSlot slot); + +private: + CFRef mBundle; + std::string mResourcePath; + RefPointer mExecRep; +}; + + +// +// Writers +// +// +class BundleDiskRep::Writer : public DiskRep::Writer { + friend class BundleDiskRep; +public: + Writer(BundleDiskRep *r); + + void component(CodeDirectory::SpecialSlot slot, CFDataRef data); + void flush(); + +protected: + DiskRep *execRep() { return rep->mExecRep; } + +protected: + RefPointer rep; + RefPointer execWriter; +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_BUNDLEDISKREP diff --git a/lib/cdbuilder.cpp b/lib/cdbuilder.cpp new file mode 100644 index 0000000..eac0a42 --- /dev/null +++ b/lib/cdbuilder.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// cdbuilder - constructor for CodeDirectories +// +#include "cdbuilder.h" +#include +#include + +using namespace UnixPlusPlus; +using LowLevelMemoryUtilities::alignUp; + + +namespace Security { +namespace CodeSigning { + + +// +// Create an (empty) builder +// +CodeDirectory::Builder::Builder() + : mFlags(0), mSpecialSlots(0), mCodeSlots(0), mDir(NULL) +{ + memset(mSpecial, 0, sizeof(mSpecial)); +} + + +// +// Set the source of the main executable (i.e. the code pages) +// +void CodeDirectory::Builder::executable(string path, + size_t pagesize, size_t offset, size_t length) +{ + mExec.close(); // any previously opened one + mExec.open(path); + mPageSize = pagesize; + mExecOffset = offset; + mExecLength = length; +} + +void CodeDirectory::Builder::reopen(string path, size_t offset, size_t length) +{ + assert(mExec); // already called executable() + mExec.close(); + mExec.open(path); + mExecOffset = offset; + mExecLength = length; +} + + +// +// Set the source for one special slot +// +void CodeDirectory::Builder::special(size_t slot, CFDataRef data) +{ + assert(slot <= cdSlotMax); + Hash hash; + hash(CFDataGetBytePtr(data), CFDataGetLength(data)); + hash.finish(mSpecial[slot]); + if (slot >= mSpecialSlots) + mSpecialSlots = slot; +} + + +size_t CodeDirectory::Builder::size() +{ + assert(mExec); // must have called executable() + if (mExecLength == 0) + mExecLength = mExec.fileSize() - mExecOffset; + + // how many code pages? + if (mPageSize == 0) { // indefinite - one page + mCodeSlots = (mExecLength > 0); + } else { // finite - calculate from file size + mCodeSlots = (mExecLength + mPageSize - 1) / mPageSize; // round up + } + + size_t offset = sizeof(CodeDirectory); + offset += mIdentifier.size() + 1; // size of identifier (with null byte) + offset += (mCodeSlots + mSpecialSlots) * Hash::digestLength; // hash vector + return offset; +} + + +// +// Take everything added to date and wrap it up in a shiny new CodeDirectory. +// +// Note that this doesn't include or generate the signature. You're free to +// modify the result. But this function determines the memory layout, so the +// sizes and counts should be correct on entry. +// +CodeDirectory *CodeDirectory::Builder::build() +{ + assert(mExec); // must have (successfully) called executable() + + // size and allocate + size_t identLength = mIdentifier.size() + 1; + size_t total = size(); + if (!(mDir = (CodeDirectory *)calloc(1, total))) // initialize to zero + UnixError::throwMe(ENOMEM); + + // fill header + mDir->initialize(total); + mDir->version = currentVersion; + mDir->flags = mFlags; + mDir->nSpecialSlots = mSpecialSlots; + mDir->nCodeSlots = mCodeSlots; + mDir->codeLimit = mExecLength; + mDir->hashSize = Hash::digestLength; + mDir->hashType = cdHashTypeDefault; + if (mPageSize) { + int pglog; + assert(frexp(mPageSize, &pglog) == 0.5); // must be power of 2 + frexp(mPageSize, &pglog); + assert(pglog < 256); + mDir->pageSize = pglog - 1; + } else + mDir->pageSize = 0; // means infinite page size + + // locate and fill flex fields + size_t offset = sizeof(CodeDirectory); + mDir->identOffset = offset; + memcpy(mDir->identifier(), mIdentifier.c_str(), identLength); + offset += identLength; + + // (add new flexibly-allocated fields here) + + mDir->hashOffset = offset + mSpecialSlots * Hash::digestLength; + offset += (mSpecialSlots + mCodeSlots) * Hash::digestLength; + assert(offset == total); // matches allocated size + + // fill special slots + memset((*mDir)[-mSpecialSlots], 0, mDir->hashSize * mSpecialSlots); + for (size_t slot = 1; slot <= mSpecialSlots; ++slot) + memcpy((*mDir)[-slot], &mSpecial[slot], Hash::digestLength); + + // fill code slots + mExec.seek(mExecOffset); + size_t remaining = mExecLength; + for (unsigned int slot = 0; slot < mCodeSlots; ++slot) { + size_t thisPage = min(mPageSize, remaining); + hash(mExec, (*mDir)[slot], thisPage); + remaining -= thisPage; + } + + // all done. Pass ownership to caller + return mDir; +} + + +} // CodeSigning +} // Security diff --git a/lib/cdbuilder.h b/lib/cdbuilder.h new file mode 100644 index 0000000..01c0a67 --- /dev/null +++ b/lib/cdbuilder.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// cdbuilder - constructor for CodeDirectories +// +#ifndef _H_CDBUILDER +#define _H_CDBUILDER + +#include "codedirectory.h" + + +namespace Security { +namespace CodeSigning { + + +// +// Builder can construct CodeDirectories from pieces: +// Builder builder; +// builder.variousSetters(withSuitableData); +// CodeDirectory *result = builder.build(); +// Builder is not reusable. +// +class CodeDirectory::Builder { +public: + Builder(); + + void executable(string path, size_t pagesize, size_t offset, size_t length); + void reopen(string path, size_t offset, size_t length); + + void special(size_t slot, CFDataRef data); + void identifier(const std::string &code) { mIdentifier = code; } + void flags(uint32_t f) { mFlags = f; } + + size_t size(); // calculate size + CodeDirectory *build(); // build CodeDirectory and return it + +private: + Hash::SDigest mSpecial[cdSlotCount]; // special slot hashes + UnixPlusPlus::AutoFileDesc mExec; // main executable file + size_t mExecOffset; // starting offset in mExec + size_t mExecLength; // total bytes of file to sign + size_t mPageSize; // page size of executable (bytes) + uint32_t mFlags; // CodeDirectory flags + std::string mIdentifier; // canonical identifier + + size_t mSpecialSlots; // highest special slot set + size_t mCodeSlots; // number of code pages (slots) + + CodeDirectory *mDir; // what we're building +}; + + +} // CodeSigning +} // Security + + +#endif //_H_CDBUILDER diff --git a/lib/cfmdiskrep.cpp b/lib/cfmdiskrep.cpp new file mode 100644 index 0000000..a74c0e0 --- /dev/null +++ b/lib/cfmdiskrep.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// cfmdiskrep - single-file CFM (PEF) executable disk representation +// +#include "cfmdiskrep.h" +#include + + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// Everything's lazy in here +// +CFMDiskRep::CFMDiskRep(const char *path) + : SingleDiskRep(path), mTriedRead(false) +{ +} + +CFMDiskRep::~CFMDiskRep() +{ + if (mTriedRead) + delete mSigningData; +} + + +// +// CFM filter heuristic +// +bool CFMDiskRep::candidiate(FileDesc &fd) +{ + static const char magicMarker[] = "Joy!peffpwpc"; + static const size_t magicLength = 12; + char marker[magicLength]; + return fd.read(marker, magicLength, 0) == magicLength + && !memcmp(marker, magicMarker, magicLength); +} + + +// +// Extract and return a component by slot number. +// If we have a Mach-O binary, use embedded components. +// Otherwise, look for and return the extended attribute, if any. +// +CFDataRef CFMDiskRep::component(CodeDirectory::SpecialSlot slot) +{ + if (!mTriedRead) + readSigningData(); + if (mSigningData) + return mSigningData->component(slot); + else + return NULL; +} + + +// +// In Mac OS X, a CFM binary must always be managed by the LaunchCFMApp +// system tool. Thus, we recommend that this be required as a host. +// +static const uint8_t cfm_ireqs[] = { // host => anchor apple and identifier com.apple.LaunchCFMApp + 0xfa, 0xde, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x14, 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, + 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, + 0x43, 0x46, 0x4d, 0x41, 0x70, 0x70, 0x00, 0x00, +}; + +const Requirements *CFMDiskRep::defaultRequirements(const Architecture *) +{ + return ((const Requirements *)cfm_ireqs)->clone(); // need to pass ownership +} + + +// +// Default to system-paged signing +// +size_t CFMDiskRep::pageSize() +{ + return segmentedPageSize; +} + + +// +// The signing limit is the start of the signature if present, +// or the end of the file otherwise. +// +size_t CFMDiskRep::signingLimit() +{ + readSigningData(); + if (mSigningData) + return mSigningOffset; + else + return fd().fileSize(); +} + + +// +// Various other aspects of our DiskRep personality. +// +string CFMDiskRep::format() +{ + return "CFM/PEF binary"; +} + + +// +// Discard cached information +// +void CFMDiskRep::flush() +{ + mTriedRead = false; + ::free(mSigningData); +} + + +// +// Locate, read, and cache embedded signing data from the CFM binary. +// +void CFMDiskRep::readSigningData() +{ + if (!mTriedRead) { // try it once + mSigningData = NULL; // preset failure + mTriedRead = true; // we've tried (and perhaps failed) + + FileDesc &fd = this->fd(); + fd.seek(fd.fileSize() - sizeof(Sentinel)); + Sentinel sentinel; + if (fd.read(&sentinel, sizeof(sentinel), fd.fileSize() - sizeof(Sentinel)) == sizeof(Sentinel)) + if (sentinel.magic == EmbeddedSignatureBlob::typeMagic) { + mSigningOffset = sentinel.offset; + fd.seek(mSigningOffset); + mSigningData = EmbeddedSignatureBlob::readBlob(fd); + if (mSigningData) + secdebug("cfmrep", "%zd signing bytes in %d blob(s) from %s(CFM)", + mSigningData->length(), mSigningData->count(), + mainExecutablePath().c_str()); + else + secdebug("cfmrep", "failed to read signing bytes from %s(CFM)", + mainExecutablePath().c_str()); + } + } +} + + +// +// CFMDiskRep::Writers +// +DiskRep::Writer *CFMDiskRep::writer() +{ + return new Writer(this); +} + +CFMDiskRep::Writer::~Writer() +{ + delete mSigningData; +} + + +// +// Write a component. +// Note that this isn't concerned with Mach-O writing; this is handled at +// a much higher level. If we're called, it's extended attribute time. +// +void CFMDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) +{ + EmbeddedSignatureBlob::Maker::component(slot, data); +} + + +// +// Append the superblob we built to the CFM binary. +// Note: Aligning the signing blob to a 16-byte boundary is not strictly necessary, +// but it's what the Mach-O case does, and it probably improves performance a bit. +// +void CFMDiskRep::Writer::flush() +{ + delete mSigningData; // ditch previous blob just in case + mSigningData = Maker::make(); // assemble new signature SuperBlob + size_t start = LowLevelMemoryUtilities::alignUp(rep->signingLimit(), 16); + Sentinel sentinel; + sentinel.magic = EmbeddedSignatureBlob::typeMagic; + sentinel.offset = start; + AutoFileDesc fd(rep->path(), O_RDWR); + fd.seek(start); + fd.writeAll(mSigningData, mSigningData->length()); + fd.writeAll(&sentinel, sizeof(sentinel)); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/cfmdiskrep.h b/lib/cfmdiskrep.h new file mode 100644 index 0000000..8eeaa2b --- /dev/null +++ b/lib/cfmdiskrep.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// cfmdiskrep - single-file CFM (PEF) executable disk representation +// +#ifndef _H_CFMDISKREP +#define _H_CFMDISKREP + +#include "singlediskrep.h" +#include "sigblob.h" +#include "signerutils.h" +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// A CFMDiskRep represents a single code file on disk containing a CFM (PEF) +// binary. It is considered self-contained, and does not depend on any other +// files in the system (even if it may be part of a larger bundle etc.) +// +// CFM is considered a legacy format that is not generated by Apple, but still +// supported for backward compatibility. This DiskRep supports writing signing +// data into the executable, using a simple back-of-file frame. It does not +// support embedded Info.plists or any other fancy stuff. +// +// This DiskRep does not support resource sealing. +// +class CFMDiskRep : public SingleDiskRep { +public: + CFMDiskRep(const char *path); + ~CFMDiskRep(); + + CFDataRef component(CodeDirectory::SpecialSlot slot); + const Requirements *defaultRequirements(const Architecture *arch); + size_t pageSize(); + size_t signingLimit(); + std::string format(); + void flush(); + + static bool candidiate(UnixPlusPlus::FileDesc &fd); // could this reasonably be a CFM code? + +public: + // + // Signing sticks this structure at the very end of the file + // + struct Sentinel { + Endian magic; // EmbeddedSignatureBlob::magic() + Endian offset; // file absolute offset of EmbeddedSignatureBlob + }; + +public: + DiskRep::Writer *writer(); + class Writer; + friend class Writer; + +protected: + void readSigningData(); // read and cache signing data + +private: + bool mTriedRead; // tried to get signing data + size_t mSigningOffset; // where we found the signing data + EmbeddedSignatureBlob *mSigningData; // cached signing data +}; + + +// +// The write side of a FileDiskRep +// +class CFMDiskRep::Writer : public DiskRep::Writer, private EmbeddedSignatureBlob::Maker { + friend class CFMDiskRep; +public: + Writer(CFMDiskRep *r) : rep(r), mSigningData(NULL) { } + ~Writer(); + + void component(CodeDirectory::SpecialSlot slot, CFDataRef data); + virtual void flush(); + +protected: + RefPointer rep; + EmbeddedSignatureBlob *mSigningData; +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_CFMDISKREP diff --git a/lib/cfmunge.cpp b/lib/cfmunge.cpp new file mode 100644 index 0000000..214cac5 --- /dev/null +++ b/lib/cfmunge.cpp @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2006-2007 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@ + */ +// +// CoreFoundation building and parsing functions. +// +// These classes provide a printf/scanf-like interface to nested data structures +// of the Property List Subset of CoreFoundation. +// +#include "cfmunge.h" +#include +#include + +namespace Security { + + +// +// Format codes for consistency +// +#define F_ARRAY 'A' +#define F_BOOLEAN 'B' +#define F_DATA 'X' +#define F_DICTIONARY 'D' +#define F_OBJECT 'O' +#define F_STRING 'S' +#define F_NUMBER 'N' + + +// +// Initialize a CFMunge. We start out with the default CFAllocator, and +// we do not throw errors. +// +CFMunge::CFMunge(const char *fmt, va_list arg) + : format(fmt), allocator(NULL), error(noErr) +{ + va_copy(args, arg); +} + +CFMunge::~CFMunge() +{ + va_end(args); +} + + +// +// Skip whitespace and other fluff and deliver the next significant character. +// +char CFMunge::next() +{ + while (*format && (isspace(*format) || *format == ',')) ++format; + return *format; +} + + +// +// Locate and consume an optional character +// +bool CFMunge::next(char c) +{ + if (next() == c) { + ++format; + return true; + } else + return false; +} + + +// +// Process @? parameter specifications. +// The @ operator is used for side effects, and does not return a value. +// +bool CFMunge::parameter() +{ + switch (*++format) { + case 'A': + ++format; + allocator = va_arg(args, CFAllocatorRef); + return true; + case 'E': + ++format; + error = va_arg(args, OSStatus); + return true; + default: + return false; + } +} + + +// +// The top constructor. +// +CFTypeRef CFMake::make() +{ + while (next() == '@') + parameter(); + switch (next()) { + case '\0': + return NULL; + case '{': + return makedictionary(); + case '[': + return makearray(); + case '\'': + return makestring(); + case '%': + return makeformat(); + case '#': + return makespecial(); + case ']': + case '}': + assert(false); // unexpected + return NULL; // error + default: + if (isdigit(*format)) + return makenumber(); + else if (isalpha(*format)) + return makestring(); + else { + assert(false); + return NULL; + } + } +} + + +CFTypeRef CFMake::makeformat() +{ + ++format; + switch (*format++) { + case 'b': // blob (pointer, length) + { + const void *data = va_arg(args, const void *); + size_t length = va_arg(args, size_t); + return CFDataCreate(allocator, (const UInt8 *)data, length); + } + case F_BOOLEAN: // boolean (with int promotion) + return va_arg(args, int) ? kCFBooleanTrue : kCFBooleanFalse; + case 'd': + return makeCFNumber(va_arg(args, int)); + case 's': + return CFStringCreateWithCString(allocator, va_arg(args, const char *), + kCFStringEncodingUTF8); + case F_OBJECT: + return CFRetain(va_arg(args, CFTypeRef)); + case 'u': + return makeCFNumber(va_arg(args, unsigned int)); + default: + assert(false); + return NULL; + } +} + + +CFTypeRef CFMake::makespecial() +{ + ++format; + switch (*format++) { + case 'N': + return kCFNull; + case 't': + case 'T': + return kCFBooleanTrue; + case 'f': + case 'F': + return kCFBooleanFalse; + default: + assert(false); + return NULL; + } +} + + +CFTypeRef CFMake::makenumber() +{ + double value = strtod(format, (char **)&format); + return CFNumberCreate(allocator, kCFNumberDoubleType, &value); +} + + +// +// Embedded strings can either be alphanumeric (only), or delimited with single quotes ''. +// No escapes are processed within such quotes. If you want arbitrary string values, use %s. +// +CFTypeRef CFMake::makestring() +{ + const char *start, *end; + if (*format == '\'') { + start = ++format; // next quote + if (!(end = strchr(format, '\''))) { + assert(false); + return NULL; + } + format = end + 1; + } else { + start = format; + for (end = start + 1; isalnum(*end); ++end) ; + format = end; + } + return CFStringCreateWithBytes(allocator, + (const UInt8 *)start, end - start, + kCFStringEncodingUTF8, false); +} + + +// +// Construct a CFDictionary +// +CFTypeRef CFMake::makedictionary() +{ + ++format; // next '{' + next('!'); // indicates mutable (currently always true) + CFMutableDictionaryRef dict; + if (next('+')) { // {+%O, => copy dictionary argument, then proceed + if (next('%') && next('O')) { + CFDictionaryRef source = va_arg(args, CFDictionaryRef); + dict = CFDictionaryCreateMutableCopy(allocator, NULL, source); + if (next('}')) + return dict; + } else + return NULL; // bad syntax + } else + dict = CFDictionaryCreateMutable(allocator, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (add(dict)) + return dict; + else { + CFRelease(dict); + return NULL; + } +} + +CFDictionaryRef CFMake::add(CFMutableDictionaryRef dict) +{ + while (next() != '}') { + CFTypeRef key = make(); + if (key == NULL) + return NULL; + if (!next('=')) { + CFRelease(key); + return NULL; + } + if (CFTypeRef value = make()) { + CFDictionaryAddValue(dict, key, value); + CFRelease(key); + CFRelease(value); + } else { + CFRelease(key); + return NULL; + } + } + ++format; + return dict; +} + + +CFDictionaryRef CFMake::addto(CFMutableDictionaryRef dict) +{ + if (next('{')) + return add(dict); + else { + assert(false); + return NULL; + } +} + + +// +// Construct a CFArray +// +CFTypeRef CFMake::makearray() +{ + ++format; // next '[' + next('!'); // indicates mutable (currently always) + CFMutableArrayRef array = makeCFMutableArray(0); + while (next() != ']') { + CFTypeRef value = make(); + if (value == NULL) { + CFRelease(array); + return NULL; + } + CFArrayAppendValue(array, value); + CFRelease(value); + } + ++format; + return array; +} + + +// +// A CFScan processes its format by parsing through an existing CF object +// structure, matching and extracting values as directed. Note that CFScan +// is a structure (tree) scanner rather than a linear parser, and will happily +// parse out a subset of the input object graph. +// +class CFScan : public CFMake { +public: + CFScan(const char *format, va_list args) + : CFMake(format, args), suppress(false) { } + + bool scan(CFTypeRef obj); + CFTypeRef dictpath(CFTypeRef obj); + +protected: + bool scandictionary(CFDictionaryRef obj); + bool scanarray(CFArrayRef obj); + bool scanformat(CFTypeRef obj); + + enum Typescan { fail = -1, more = 0, done = 1 }; + Typescan typescan(CFTypeRef obj, CFTypeID type); + + template + bool CFScan::scannumber(CFTypeRef obj); + + template + void store(Type value); + + bool suppress; // output suppression +}; + + +// +// Master scan function +// +bool CFScan::scan(CFTypeRef obj) +{ + while (next() == '@') + parameter(); + switch (next()) { + case '\0': + return true; // done, okay + case '{': + if (obj && CFGetTypeID(obj) != CFDictionaryGetTypeID()) + return false; + return scandictionary(CFDictionaryRef(obj)); + case '[': + if (obj && CFGetTypeID(obj) != CFArrayGetTypeID()) + return false; + return scanarray(CFArrayRef(obj)); + case '%': // return this value in some form + return scanformat(obj); + case '=': // match value + { + ++format; + CFTypeRef match = make(); + bool rc = CFEqual(obj, match); + CFRelease(match); + return rc; + } + case ']': + case '}': + assert(false); // unexpected + return false; + default: + assert(false); + return false; + } +} + + +// +// Primitive type-match helper. +// Ensures the object has the CF runtime type required, and processes +// the %?o format (return CFTypeRef) and %?n format (ignore value). +// +CFScan::Typescan CFScan::typescan(CFTypeRef obj, CFTypeID type) +{ + if (obj && CFGetTypeID(obj) != type) + return fail; + switch (*++format) { + case F_OBJECT: // return CFTypeRef + ++format; + store(obj); + return done; + case 'n': // suppress assignment + ++format; + return done; + default: + return more; + } +} + + +// +// Store a value into the next varargs slot, unless output suppression is on. +// +template +void CFScan::store(Type value) +{ + if (!suppress) + *va_arg(args, Type *) = value; +} + + +// +// Convert a CFNumber to an external numeric form +// +template +bool CFScan::scannumber(CFTypeRef obj) +{ + ++format; // consume format code + if (!obj) + return true; // suppressed, okay + if (CFGetTypeID(obj) != CFNumberGetTypeID()) + return false; + store(cfNumber(CFNumberRef(obj))); + return true; +} + + +// +// Process % scan forms. +// This delivers the object value, scanf-style, somehow. +// +bool CFScan::scanformat(CFTypeRef obj) +{ + switch (*++format) { + case F_OBJECT: + store(obj); + return true; + case F_ARRAY: // %a* + return typescan(obj, CFArrayGetTypeID()) == done; + case F_BOOLEAN: + if (Typescan rc = typescan(obj, CFBooleanGetTypeID())) + return rc == done; + switch (*format) { + case 'f': // %Bf - two arguments (value, &variable) + { + unsigned flag = va_arg(args, unsigned); + unsigned *value = va_arg(args, unsigned *); + if (obj == kCFBooleanTrue && !suppress) + *value |= flag; + return true; + } + default: // %b - CFBoolean as int boolean + store(obj == kCFBooleanTrue); + return true; + } + case F_DICTIONARY: + return typescan(obj, CFDictionaryGetTypeID()) == done; + case 'd': // %d - int + return scannumber(obj); + case F_NUMBER: + return typescan(obj, CFNumberGetTypeID()) == done; + case F_STRING: + case 's': + if (Typescan rc = typescan(obj, CFStringGetTypeID())) + return rc == done; + // %s + store(cfString(CFStringRef(obj))); + return true; + case 'u': + return scannumber(obj); + case F_DATA: + return typescan(obj, CFDataGetTypeID()) == done; + default: + assert(false); + return false; + } +} + + +bool CFScan::scandictionary(CFDictionaryRef obj) +{ + ++format; // skip '{' + while (next() != '}') { + bool optional = next('?'); + if (CFTypeRef key = make()) { + bool oldSuppress = suppress; + CFTypeRef elem = obj ? CFDictionaryGetValue(obj, key) : NULL; + if (elem || optional) { + suppress |= (elem == NULL); + if (next('=')) { + if (scan(elem)) { + suppress = oldSuppress; // restore + CFRelease(key); + continue; + } + } + } + CFRelease(key); + return false; + } else { + assert(false); // bad format + return false; + } + } + return true; +} + + +bool CFScan::scanarray(CFArrayRef obj) +{ + ++format; // skip '[' + CFIndex length = CFArrayGetCount(obj); + for (int pos = 0; pos < length; ++pos) { + if (next() == ']') + return true; + if (!scan(CFArrayGetValueAtIndex(obj, pos))) + return false; + } + return false; // array length exceeded +} + + +// +// Run down a "dictionary path", validating heavily. +// +CFTypeRef CFScan::dictpath(CFTypeRef obj) +{ + while (next()) { // while we've got more text + next('.'); // optional + if (obj == NULL || CFGetTypeID(obj) != CFDictionaryGetTypeID()) + return NULL; + CFTypeRef key = make(); + obj = CFDictionaryGetValue(CFDictionaryRef(obj), key); + CFRelease(key); + } + return obj; +} + + +// +// The public functions +// +CFTypeRef cfmake(const char *format, ...) +{ + va_list args; + va_start(args, format); + CFTypeRef result = CFMake(format, args).make(); + va_end(args); + return result; +} + +CFTypeRef vcfmake(const char *format, va_list args) +{ + return CFMake(format, args).make(); +} + +CFDictionaryRef cfadd(CFMutableDictionaryRef dict, const char *format, ...) +{ + va_list args; + va_start(args, format); + CFDictionaryRef result = CFMake(format, args).addto(dict); + va_end(args); + return result; +} + + +bool cfscan(CFTypeRef obj, const char *format, ...) +{ + va_list args; + va_start(args, format); + bool result = vcfscan(obj, format, args); + va_end(args); + return result; +} + +bool vcfscan(CFTypeRef obj, const char *format, va_list args) +{ + return CFScan(format, args).scan(obj); +} + + +CFTypeRef cfget(CFTypeRef obj, const char *format, ...) +{ + va_list args; + va_start(args, format); + CFTypeRef result = vcfget(obj, format, args); + va_end(args); + return result; +} + +CFTypeRef vcfget(CFTypeRef obj, const char *format, va_list args) +{ + return CFScan(format, args).dictpath(obj); +} + +} // end namespace Security diff --git a/lib/cfmunge.h b/lib/cfmunge.h new file mode 100644 index 0000000..2af591b --- /dev/null +++ b/lib/cfmunge.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2000-2004 Apple Computer, 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@ + */ +// +// CoreFoundation building and parsing functions +// +#ifndef _H_CFMUNGE +#define _H_CFMUNGE + +#include +#include +#include + +namespace Security { + + +// +// Common interface to Mungers. +// A CFMunge provides a one-pass, non-resettable scan through a format string, +// performing various actions on the way. +// +class CFMunge { +public: + CFMunge(const char *fmt, va_list arg); + ~CFMunge(); + +protected: + char next(); + bool next(char c); + + bool parameter(); + +protected: + const char *format; + va_list args; + CFAllocatorRef allocator; + OSStatus error; +}; + + +// +// A CFMake is a CFMunge for making CF data structures. +// +class CFMake : public CFMunge { +public: + CFMake(const char *fmt, va_list arg) : CFMunge(fmt, arg) { } + + CFTypeRef make(); + CFDictionaryRef addto(CFMutableDictionaryRef dict); + +protected: + CFTypeRef makedictionary(); + CFTypeRef makearray(); + CFTypeRef makenumber(); + CFTypeRef makestring(); + CFTypeRef makeformat(); + CFTypeRef makespecial(); + + CFDictionaryRef add(CFMutableDictionaryRef dict); +}; + + +// +// Make a CF object following a general recipee +// +CFTypeRef cfmake(const char *format, ...); +CFTypeRef vcfmake(const char *format, va_list args); + +template +CFType cfmake(const char *format, ...) +{ + va_list args; + va_start(args, format); + CFType result = CFType(vcfmake(format, args)); + va_end(args); + return result; +} + +CFDictionaryRef cfadd(CFMutableDictionaryRef dict, const char *format, ...); + + +// +// Parse out parts of a CF object following a general recipe. +// Cfscan returns false on error; cfget throws. +// +bool cfscan(CFTypeRef source, const char *format, ...); +bool vcfscan(CFTypeRef source, const char *format, va_list args); + +CFTypeRef cfget(CFTypeRef source, const char *format, ...); +CFTypeRef vcfget(CFTypeRef source, const char *format, va_list args); + +template +CFType cfget(CFTypeRef source, const char *format, ...) +{ + va_list args; + va_start(args, format); + CFType result = CFType(vcfget(source, format, args)); + va_end(args); + return CFTraits::check(result) ? result : NULL; +} + +template +class CFTemp : public CFRef { +public: + CFTemp(const char *format, ...) + { + va_list args; + va_start(args, format); + this->take(CFType(vcfmake(format, args))); + va_end(args); + } +}; + + +} // end namespace Security + +#endif //_H_CFMUNGE diff --git a/lib/codedirectory.cpp b/lib/codedirectory.cpp new file mode 100644 index 0000000..f9ad55d --- /dev/null +++ b/lib/codedirectory.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2006 Apple Computer, 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 +// +#include "codedirectory.h" +#include "CSCommon.h" + +using namespace UnixPlusPlus; + + +namespace Security { +namespace CodeSigning { + + +// +// Canonical filesystem names for select slot numbers. +// These are variously used for filenames, extended attribute names, etc. +// to get some consistency in naming. These are for storing signing-related +// data; they have no bearing on the actual hash slots in the CodeDirectory. +// +const char *CodeDirectory::canonicalSlotName(SpecialSlot slot) +{ + switch (slot) { + case cdRequirementsSlot: + return kSecCS_REQUIREMENTSFILE; + case cdResourceDirSlot: + return kSecCS_RESOURCEDIRFILE; + case cdCodeDirectorySlot: + return kSecCS_CODEDIRECTORYFILE; + case cdSignatureSlot: + return kSecCS_SIGNATUREFILE; + case cdApplicationSlot: + return kSecCS_APPLICATIONFILE; + default: + return NULL; + } +} + + +// +// Canonical attributes of SpecialSlots. +// +unsigned CodeDirectory::slotAttributes(SpecialSlot slot) +{ + switch (slot) { + case cdRequirementsSlot: + return cdComponentIsBlob; // global + case cdCodeDirectorySlot: + return cdComponentPerArchitecture | cdComponentIsBlob; + case cdSignatureSlot: + return cdComponentPerArchitecture; // raw + default: + return 0; // global, raw + } +} + + +// +// Symbolic names for code directory special slots. +// These are only used for debug output. They are not API-official. +// Needs to be coordinated with the cd*Slot enumeration in codedirectory.h. +// +#if !defined(NDEBUG) +const char * const CodeDirectory::debugSlotName[] = { + "codedirectory", + "info", + "requirements", + "resources", + "application" +}; +#endif //NDEBUG + + +// +// Check the version of this CodeDirectory for basic sanity. +// Throws if the directory is corrupted or out of versioning bounds. +// Returns if the version is usable (perhaps with degraded features due to +// compatibility hacks). +// +void CodeDirectory::checkVersion() const +{ + if (!this->validateBlob()) + MacOSError::throwMe(errSecCSSignatureInvalid); // busted + if (version > compatibilityLimit) + MacOSError::throwMe(errSecCSSignatureUnsupported); // too new - no clue + if (version > currentVersion) + secdebug("codedir", "%p version 0x%x newer than current 0x%x", + this, uint32_t(version), currentVersion); +} + + +// +// Validate a slot against data in memory. +// +bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot) const +{ + secdebug("codedir", "%p validating slot %d", this, int(slot)); + Hash::Byte digest[Hash::digestLength]; + hash(data, length, digest); + return memcmp(digest, (*this)[slot], Hash::digestLength) == 0; +} + + +// +// Validate a slot against the contents of an open file. At most 'length' bytes +// will be read from the file. +// +bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot) const +{ + Hash::Digest digest; + hash(fd, digest, length); + return memcmp(digest, (*this)[slot], Hash::digestLength) == 0; +} + + +// +// Check whether a particular slot is present. +// Absense is indicated by either a zero hash, or by lying outside +// the slot range. +// +bool CodeDirectory::slotIsPresent(Slot slot) const +{ + if (slot >= -Slot(nSpecialSlots) && slot < Slot(nCodeSlots)) { + const Hash::Byte *digest = (*this)[slot]; + for (unsigned n = 0; n < Hash::digestLength; n++) + if (digest[n]) + return true; // non-zero digest => present + } + return false; // absent +} + + +// +// Hash the next limit bytes of a file and return the digest. +// If the file is shorter, hash as much as you can. +// Limit==0 means unlimited (to end of file). +// Return how many bytes were actually hashed. +// Throw on any errors. +// +size_t CodeDirectory::hash(FileDesc fd, Hash::Byte *digest, size_t limit) +{ + IFDEBUG(size_t hpos = fd.position()); + IFDEBUG(size_t hlimit = limit); + unsigned char buffer[4096]; + Hash hash; + size_t total = 0; + for (;;) { + size_t size = sizeof(buffer); + if (limit && limit < size) + size = limit; + size_t got = fd.read(buffer, size); + total += got; + if (fd.atEnd()) + break; + hash(buffer, got); + if (limit && (limit -= got) == 0) + break; + } + hash.finish(digest); + secdebug("cdhash", "fd %d %zd@0x%zx => %2x.%2x.%2x...", + fd.fd(), hpos, hlimit, digest[0], digest[1], digest[2]); + return total; +} + + +// +// Ditto, but hash a memory buffer instead. +// +size_t CodeDirectory::hash(const void *data, size_t length, Hash::Byte *digest) +{ + Hash hash; + hash(data, length); + hash.finish(digest); + return length; +} + + +// +// Canonical text form for user-settable code directory flags +// +const CodeDirectory::FlagItem CodeDirectory::flagItems[] = { + { "host", kSecCodeSignatureHost, true }, + { "adhoc", kSecCodeSignatureAdhoc, false }, + { "hard", kSecCodeSignatureForceHard, true }, + { "kill", kSecCodeSignatureForceKill, true }, + { "expires", kSecCodeSignatureForceExpiration, true }, + { NULL } +}; + + +// +// Parse a canonical text description of code flags, in the form +// flag,...,flag +// where each flag can be a prefix of a known flag name. +// Internally set flags are not accepted. +// +uint32_t CodeDirectory::textFlags(std::string text) +{ + uint32_t flags = 0; + for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) { + string word = (comma == string::npos) ? text : text.substr(0, comma); + const CodeDirectory::FlagItem *item; + for (item = CodeDirectory::flagItems; item->name; item++) + if (item->external && !strncmp(word.c_str(), item->name, word.size())) { + flags |= item->value; + break; + } + if (!item) // not found + MacOSError::throwMe(errSecCSInvalidFlags); + if (comma == string::npos) // last word + break; + } + return flags; +} + + +} // CodeSigning +} // Security diff --git a/lib/codedirectory.h b/lib/codedirectory.h new file mode 100644 index 0000000..38cedc2 --- /dev/null +++ b/lib/codedirectory.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2006 Apple Computer, 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 a contiguous binary blob containing a two-tiered +// hash of (much of) the contents of a real StaticCode object. +// It consists of a header followed by an array of hash vector elements. +// +// This structure is meant to be self-describing, binary stable, and endian independent. +// +#ifndef _H_CODEDIRECTORY +#define _H_CODEDIRECTORY + +#include +#include +#include +#include + + +namespace Security { +namespace CodeSigning { + + +// +// Types of hashes supported. +// Actually, right now, only SHA1 is really supported. +// +enum { + cdHashTypeSHA1 = 1, + cdHashTypeSHA256 = 2, + + cdHashTypeDefault = cdHashTypeSHA1 +}; + + +// +// 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 + + +// +// 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. +// +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's good 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 + // (add further primary slot numbers here) + + cdSlotCount, // total number of special slots (+1 for slot 0) + cdSlotMax = cdSlotCount - 1, // highest special slot number + + // + // Virtual slot numbers. + // These values are NOT used in the CodeDirectory hash array. The are used as + // internal API identifiers and as types in in-image 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 gaps in these assignments. + // + cdCodeDirectorySlot = 0, // CodeDirectory + cdSignatureSlot = 0x10000, // CMS signature + // (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. +// +// The hashes are stored as an array of digests. The array covers 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. Three 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. The current code rejects those; add backward cases to checkVersion(). +// 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. 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 { + typedef SHA1 Hash; +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 in bytes + uint8_t hashType; // type of hash (cdHashType* constants) + uint8_t spare1; // unused (must be zero) + uint8_t pageSize; // log2(page size in bytes); 0 => infinite + Endian spare2; // unused (must be zero) + + // works with the version field; see comments above + static const uint32_t currentVersion = 0x20001; // "version 2" + static const uint32_t compatibilityLimit = 0x2F000; // "version 3 with wiggle room" + + void checkVersion() const; // throws if not compatible with this code + + 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); } + + 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; + } + + bool validateSlot(const void *data, size_t size, Slot slot) const; + bool validateSlot(UnixPlusPlus::FileDesc fd, size_t size, Slot slot) const; + bool slotIsPresent(Slot slot) const; + + class Builder; + +protected: + static size_t hash(UnixPlusPlus::FileDesc fd, Hash::Byte *digest, size_t limit = 0); + static size_t hash(const void *data, size_t length, Hash::Byte *digest); + +public: + // + // Information about SpecialSlots + // + static const char *canonicalSlotName(SpecialSlot slot); + static unsigned slotAttributes(SpecialSlot slot); + IFDEBUG(static const char * const debugSlotName[]); + +public: + // + // Canonical text forms for (only) the user-settable flags + // + struct FlagItem { + const char *name; + uint32_t value; + bool external; + }; + static const FlagItem flagItems[]; // terminated with NULL item + + static uint32_t textFlags(std::string text); +}; + + +} // CodeSigning +} // Security + + +#endif //_H_CODEDIRECTORY diff --git a/lib/cs.cpp b/lib/cs.cpp new file mode 100644 index 0000000..f55a48e --- /dev/null +++ b/lib/cs.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// cs.h - code signing core header +// +#include "cs.h" +#include "cfmunge.h" + +namespace Security { +namespace CodeSigning { + + +ModuleNexus gCFObjects; + +CFObjects::CFObjects() + : Code("SecCode", false), + StaticCode("SecStaticCode", false), + Requirement("SecRequirements", false), + CodeSigner("SecCodeSigner", false) +{ +} + + +} // CodeSigning +} // Security diff --git a/lib/cs.h b/lib/cs.h new file mode 100644 index 0000000..d7957fc --- /dev/null +++ b/lib/cs.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// cs.h - code signing core header +// +#ifndef _H_CS +#define _H_CS + +#include "cserror.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Security { +namespace CodeSigning { + + +// +// API per-thread globals +// +struct PerThread { + SecCSFlags flags; // flags of pending API call +}; + + +// +// API globals +// +struct CFObjects { + CFObjects(); + CFClass Code; + CFClass StaticCode; + CFClass Requirement; + CFClass CodeSigner; + + ThreadNexus perThread; + + SecCSFlags &flags() { return perThread().flags; } +}; + +extern ModuleNexus gCFObjects; + +static inline SecCSFlags apiFlags() { return gCFObjects().flags(); } + + +// +// Code Signing API brackets +// +#define BEGIN_CSAPI \ + try { + +#define END_CSAPI \ + } \ + catch (const UnixError &err) { \ + switch (err.error) { \ + case ENOEXEC: return errSecCSBadObjectFormat; \ + default: return err.osStatus(); \ + }} \ + catch (const MacOSError &err) { return err.osStatus(); } \ + catch (const CommonError &err) { return SecKeychainErrFromOSStatus(err.osStatus()); } \ + catch (const std::bad_alloc &) { return memFullErr; } \ + catch (...) { return internalComponentErr; } \ + return noErr; + +#define END_CSAPI_ERRORS \ + } \ + catch (const CSError &err) { return err.cfError(errors); } \ + catch (const UnixError &err) { \ + switch (err.error) { \ + case ENOEXEC: return CSError::cfError(errors, errSecCSBadObjectFormat); \ + default: return CSError::cfError(errors, err.osStatus()); \ + }} \ + catch (const MacOSError &err) { return CSError::cfError(errors, err.osStatus()); } \ + catch (const CommonError &err) { return CSError::cfError(errors, SecKeychainErrFromOSStatus(err.osStatus())); } \ + catch (const std::bad_alloc &) { return CSError::cfError(errors, memFullErr); } \ + catch (...) { return CSError::cfError(errors, internalComponentErr); } \ + return noErr; + +#define END_CSAPI1(bad) } catch (...) { return bad; } + + +// +// A version of Required +// +template +static inline T &Required(T *ptr) +{ + if (ptr == NULL) + MacOSError::throwMe(errSecCSObjectRequired); + return *ptr; +} + +static inline void Required(const void *ptr) +{ + if (ptr == NULL) + MacOSError::throwMe(errSecCSObjectRequired); +} + + +// +// Check flags against a validity mask +// +static inline void checkFlags(SecCSFlags flags, SecCSFlags acceptable = 0) +{ + if (flags & ~acceptable) + MacOSError::throwMe(errSecCSInvalidFlags); + gCFObjects().flags() = flags; +} + + +} // CodeSigning +} // Security + +#endif //_H_CS diff --git a/lib/cscdefs.c b/lib/cscdefs.c new file mode 100644 index 0000000..401c1a6 --- /dev/null +++ b/lib/cscdefs.c @@ -0,0 +1,4 @@ +#include +#include +#include +#include "cscdefs.h" diff --git a/lib/cscdefs.h b/lib/cscdefs.h new file mode 100644 index 0000000..4c3b998 --- /dev/null +++ b/lib/cscdefs.h @@ -0,0 +1,89 @@ +/* + * Magic numbers used by Code Signing + */ +enum { + CSMAGIC_REQUIREMENT = 0xfade0c00, /* single Requirement blob */ + CSMAGIC_REQUIREMENTS = 0xfade0c01, /* Requirements vector (internal requirements) */ + CSMAGIC_CODEDIRECTORY = 0xfade0c02, /* CodeDirectory blob */ + CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */ + CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */ + + CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */ +}; + + +/* + * Structure of an embedded-signature SuperBlob + */ +typedef struct __BlobIndex { + uint32_t type; /* type of entry */ + uint32_t offset; /* offset of entry */ +} CS_BlobIndex; + +typedef struct __SuperBlob { + uint32_t magic; /* magic number */ + uint32_t length; /* total length of SuperBlob */ + uint32_t count; /* number of index entries following */ + CS_BlobIndex index[]; /* (count) entries */ + /* followed by Blobs in no particular order as indicated by offsets in index */ +} CS_SuperBlob; + + +/* + * C form of a CodeDirectory. + */ +typedef struct __CodeDirectory { + uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */ + uint32_t length; /* total length of CodeDirectory blob */ + uint32_t version; /* compatibility version */ + uint32_t flags; /* setup and mode flags */ + uint32_t hashOffset; /* offset of hash slot element at index zero */ + uint32_t identOffset; /* offset of identifier string */ + uint32_t nSpecialSlots; /* number of special hash slots */ + uint32_t nCodeSlots; /* number of ordinary (code) hash slots */ + uint32_t codeLimit; /* limit to main image signature range */ + uint8_t hashSize; /* size of each hash in bytes */ + uint8_t hashType; /* type of hash (cdHashType* constants) */ + uint8_t spare1; /* unused (must be zero) */ + uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */ + uint32_t spare2; /* unused (must be zero) */ + /* followed by dynamic content as located by offset fields above */ +} CS_CodeDirectory; + + +/* + * Sample code to locate the CodeDirectory from an embedded signature blob + */ +static inline const CS_CodeDirectory *findCodeDirectory(const CS_SuperBlob *embedded) +{ + if (embedded && ntohl(embedded->magic) == CSMAGIC_EMBEDDED_SIGNATURE) { + const CS_BlobIndex *limit = &embedded->index[ntohl(embedded->count)]; + const CS_BlobIndex *p; + for (p = embedded->index; p < limit; ++p) + if (ntohl(p->type) == CSSLOT_CODEDIRECTORY) { + const unsigned char *base = (const unsigned char *)embedded; + const CS_CodeDirectory *cd = (const CS_CodeDirectory *)(base + ntohl(p->offset)); + if (cd->magic == CSMAGIC_CODEDIRECTORY) + return cd; + else + break; + } + } + // not found + return NULL; +} + + +/* + * Locating a page hash + */ +static inline const unsigned char *hashes(const CS_CodeDirectory *cd, unsigned page) +{ + const unsigned char *base = (const unsigned char *)cd; + assert(page < ntohl(cd->nCodeSlots)); + + // "20" below is the size of a SHA-1 hash. There's got to be a constant for that + // where you get your SHA-1 functions from. This can also be had as cd->hashSize + // (but the constant is marginally faster, I suppose) + return base + ntohl(cd->hashOffset) + page * 20; +} diff --git a/lib/cserror.cpp b/lib/cserror.cpp new file mode 100644 index 0000000..8f3dba0 --- /dev/null +++ b/lib/cserror.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// cserror.h - extended-diagnostics Code Signing errors +// +#include "cs.h" +#include "cfmunge.h" + +namespace Security { +namespace CodeSigning { + + +// +// We need a nothrow destructor +// +CSError::~CSError() throw () +{ } + + +// +// Create and throw various forms of CSError +// +void CSError::throwMe(OSStatus rc) +{ + throw CSError(rc); +} + +void CSError::throwMe(OSStatus rc, CFDictionaryRef dict) +{ + throw CSError(rc, dict); +} + +void CSError::throwMe(OSStatus rc, CFStringRef key, CFTypeRef value) +{ + throw CSError(rc, cfmake("{%O=%O}", key, value)); +} + + +// +// Convert exception-carried error information to CFError form +// +OSStatus CSError::cfError(CFErrorRef *errors) const +{ + if (errors) // errors argument was specified + *errors = CFErrorCreate(NULL, kCFErrorDomainOSStatus, this->osStatus(), this->infoDict()); + return this->osStatus(); +} + +OSStatus CSError::cfError(CFErrorRef *errors, OSStatus rc) +{ + if (errors) + *errors = CFErrorCreate(NULL, kCFErrorDomainOSStatus, rc, NULL); + return rc; +} + + +} // CodeSigning +} // Security diff --git a/lib/cserror.h b/lib/cserror.h new file mode 100644 index 0000000..e872b50 --- /dev/null +++ b/lib/cserror.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// cserror.h - extended-diagnostics Code Signing errors +// +#ifndef _H_CSERRORS +#define _H_CSERRORS + +#include +#include + + +namespace Security { +namespace CodeSigning { + + +// +// Special tailored exceptions to transmit additional error information +// +class CSError : public MacOSError { +public: + CSError(OSStatus rc) : MacOSError(rc) { } + CSError(OSStatus rc, CFDictionaryRef dict) : MacOSError(rc), mInfoDict(dict) { } // takes dict + ~CSError() throw (); + + static void throwMe(OSStatus rc) __attribute__((noreturn)); + static void throwMe(OSStatus rc, CFDictionaryRef info) __attribute__ ((noreturn)); // takes dict + static void throwMe(OSStatus rc, CFStringRef key, CFTypeRef value) __attribute__((noreturn)); + + CFDictionaryRef infoDict() const { return mInfoDict; } + +public: + OSStatus cfError(CFErrorRef *errors) const; + static OSStatus cfError(CFErrorRef *errors, OSStatus rc); + +private: + CFRef mInfoDict; +}; + + +} // CodeSigning +} // Security + +#endif //_H_CSERRORS diff --git a/lib/csgeneric.cpp b/lib/csgeneric.cpp new file mode 100644 index 0000000..0ea453e --- /dev/null +++ b/lib/csgeneric.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// csgeneric - generic Code representative +// +#include "csgeneric.h" +#include "cs.h" +#include "StaticCode.h" +#include +#include + +namespace Security { +namespace CodeSigning { + +using MachPlusPlus::Port; + + +// +// Common call-out code for cshosting RPC service +// +#define CALL(host, name, args...) \ + OSStatus result; \ + if (cshosting_client_ ## name (host, mig_get_reply_port(), &result, args)) \ + MacOSError::throwMe(errSecCSNotAHost); \ + MacOSError::check(result); + + +// +// Construct a running process representation +// +GenericCode::GenericCode(SecCode *host, SecGuestRef guestRef) + : SecCode(host), mGuestRef(guestRef) +{ +} + + +// +// Identify a guest by attribute set, and return a new GenericCode representing it. +// This uses cshosting RPCs to ask the host (or its proxy). +// +SecCode *GenericCode::locateGuest(CFDictionaryRef attributes) +{ + if (Port host = hostingPort()) { + CFRef attrData; + void *attrPtr = NULL; size_t attrLength = 0; + if (attributes) { + attrData = CFPropertyListCreateXMLData(NULL, attributes); + attrPtr = (void *)CFDataGetBytePtr(attrData); + attrLength = CFDataGetLength(attrData); + } + GuestChain guestPath; + mach_msg_type_number_t guestPathLength; + mach_port_t subport; + CALL(host, findGuest, guestRef(), attrPtr, attrLength, + &guestPath, &guestPathLength, &subport); + secdebug("genericcode", "%p found guest chain length=%d", + this, guestPathLength); + SecPointer code = this; + for (unsigned n = 0; n < guestPathLength; n++) + code = new GenericCode(code, guestPath[n]); + return code.yield(); + } else + return NULL; // not found, no error +} + + +// +// Map a guest to its StaticCode. +// This uses cshosting RPCs to ask the host (or its proxy). +// +SecStaticCode *GenericCode::mapGuestToStatic(SecCode *guest) +{ + if (Port host = hostingPort()) { + char path[MAXPATHLEN]; + CALL(host, guestPath, safe_cast(guest)->guestRef(), path); + return (new GenericStaticCode(DiskRep::bestGuess(path)))->retain(); + } else + MacOSError::throwMe(errSecCSNotAHost); +} + + +// +// Get the Code Signing Status Word for a Code. +// This uses cshosting RPCs to ask the host (or its proxy). +// +uint32_t GenericCode::getGuestStatus(SecCode *guest) +{ + if (Port host = hostingPort()) { + uint32_t status; + CALL(host, guestStatus, safe_cast(guest)->guestRef(), &status); + return status; + } else + MacOSError::throwMe(errSecCSNotAHost); +} + + +// +// Return the Hosting Port for this Code. +// May return MACH_PORT_NULL if the code is not a code host. +// Throws if we can't get the hosting port for some reason (and can't positively +// determine that there is none). +// +// Note that we do NOT cache negative outcomes. Being a host is a dynamic property, +// and this Code may not have commenced hosting operations yet. For non- (or not-yet-)hosts +// we simply return NULL. +// +Port GenericCode::hostingPort() +{ + if (!mHostingPort) { + if (staticCode()->codeDirectory()->flags & kSecCodeSignatureHost) + mHostingPort = getHostingPort(); + } + return mHostingPort; +} + + +// +// A pure GenericHost has no idea where to get a hosting port from. +// This method must be overridden to get one. +// However, we do handle a contiguous chain of GenericCodes by deferring +// to our next-higher host for it. +// +mach_port_t GenericCode::getHostingPort() +{ + if (GenericCode *genericHost = dynamic_cast(host())) + return genericHost->getHostingPort(); + else + MacOSError::throwMe(errSecCSNotAHost); +} + + +} // CodeSigning +} // Security diff --git a/lib/csgeneric.h b/lib/csgeneric.h new file mode 100644 index 0000000..c848639 --- /dev/null +++ b/lib/csgeneric.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// csgeneric - generic Code representative +// +#ifndef _H_CSGENERIC +#define _H_CSGENERIC + +#include "Code.h" +#include +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// A SecCode that represents "generic" code. +// Generic code is, well, generic. It doesn't have any real resources that define it, +// and so it's defined, de facto, by its host. The Code Signing subsystem has no special +// knowledge as to its nature, and so it just asks the host about everything. The asking +// is done via the cshosting Mach RPC protocol, which can be implemented by hosts in whichever +// way they find reasonable. This code doesn't care, as long as someone is answering. +// +// It is all right to subclass GenericCode to inherit access to the cshosting protocol. +// +class GenericCode : public SecCode { +public: + GenericCode(SecCode *host, SecGuestRef guestRef = kSecNoGuest); + + SecCode *locateGuest(CFDictionaryRef attributes); + SecStaticCode *mapGuestToStatic(SecCode *guest); + uint32_t getGuestStatus(SecCode *guest); + + SecGuestRef guestRef() const { return mGuestRef; } + +protected: + MachPlusPlus::Port hostingPort(); + + virtual mach_port_t getHostingPort(); + +private: + MachPlusPlus::Port mHostingPort; // cached hosting port for this Code + SecGuestRef mGuestRef; // guest reference +}; + + +// +// We don't need a GenericCode variant of SecStaticCode +// +typedef SecStaticCode GenericStaticCode; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_CSGENERIC diff --git a/lib/cskernel.cpp b/lib/cskernel.cpp new file mode 100644 index 0000000..bd39355 --- /dev/null +++ b/lib/cskernel.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// cskernel - Kernel implementation of the Code Signing Host Interface +// +#include "cskernel.h" +#include "csprocess.h" +#include "kerneldiskrep.h" +#include +#include +#include // MAXPATHLEN + +namespace Security { +namespace CodeSigning { + + +// +// The running-kernel singletons +// +ModuleNexus KernelCode::globals; + +KernelCode::Globals::Globals() +{ + code = new KernelCode; + staticCode = new KernelStaticCode; +} + +KernelCode::KernelCode() + : SecCode(NULL) +{ +} + +KernelStaticCode::KernelStaticCode() + : SecStaticCode(new KernelDiskRep()) +{ +} + + +// +// We locate a guest (process) by invoking a kernel service. +// The only attributes supported are ("pid", pid_t). +// (We could also support task ports if we liked, but those can be translated +// to pids by the caller without trouble.) +// +SecCode *KernelCode::locateGuest(CFDictionaryRef attributes) +{ + if (CFTypeRef attr = CFDictionaryGetValue(attributes, kSecGuestAttributePid)) { + if (CFDictionaryGetCount(attributes) != 1) + MacOSError::throwMe(errSecCSUnsupportedGuestAttributes); // had more + if (CFGetTypeID(attr) == CFNumberGetTypeID()) + return (new ProcessCode(cfNumber(CFNumberRef(attr))))->retain(); + MacOSError::throwMe(errSecCSInvalidAttributeValues); + } else + MacOSError::throwMe(errSecCSUnsupportedGuestAttributes); +} + + +// +// We map guests to disk by calling a kernel service. +// +SecStaticCode *KernelCode::mapGuestToStatic(SecCode *iguest) +{ + if (ProcessCode *guest = dynamic_cast(iguest)) { + char path[2 * MAXPATHLEN]; // reasonable upper limit + if (::proc_pidpath(guest->pid(), path, sizeof(path))) + return (new ProcessStaticCode(DiskRep::bestGuess(path)))->retain(); + else + UnixError::throwMe(); + } + MacOSError::throwMe(errSecCSNoSuchCode); +} + + +// +// We obtain the guest's status by asking the kernel +// +uint32_t KernelCode::getGuestStatus(SecCode *iguest) +{ + if (ProcessCode *guest = dynamic_cast(iguest)) { + uint32_t pFlags; + if (::csops(guest->pid(), CS_OPS_STATUS, &pFlags, 0) == -1) { + secdebug("kcode", "cannot get guest status of %p(%d) errno=%d", + guest, guest->pid(), errno); + switch (errno) { + case ESRCH: + MacOSError::throwMe(errSecCSNoSuchCode); + default: + UnixError::throwMe(); + } + } + secdebug("kcode", "guest %p(%d) kernel status 0x%x", guest, guest->pid(), pFlags); + +#if defined(USERSPACE_VALIDATION) + // Former static substitute for dynamic kernel validation of executable pages. + // This is now done in the kernel's page-in path. + guest->staticCode()->validateExecutable(); +#endif //USERSPACE_VALIDATION + + return pFlags; + } else + MacOSError::throwMe(errSecCSNoSuchCode); +} + + +// +// The StaticCode for the running kernel is explicit. +// We can't ask our own host for it, naturally. +// +SecStaticCode *KernelCode::getStaticCode() +{ + return globals().staticCode->retain(); +} + + +} // CodeSigning +} // Security diff --git a/lib/cskernel.h b/lib/cskernel.h new file mode 100644 index 0000000..b5e272e --- /dev/null +++ b/lib/cskernel.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// cskernel - Kernel implementation of the Code Signing Host Interface +// +#ifndef _H_CSKERNEL +#define _H_CSKERNEL + +#include "Code.h" +#include "StaticCode.h" +#include + +namespace Security { +namespace CodeSigning { + + +// +// The nominal StaticCode representing the kernel on disk. +// This is barely used, since we don't validate the kernel (it's the root of trust) +// and we don't activate new kernels at runtime. +// +class KernelStaticCode : public SecStaticCode { +public: + KernelStaticCode(); + +private: +}; + + +// +// A SecCode that represents the system's running kernel. +// We usually only have one of those in the system at one time. :-) +// +class KernelCode : public SecCode { +public: + KernelCode(); + + SecCode *locateGuest(CFDictionaryRef attributes); + SecStaticCode *mapGuestToStatic(SecCode *guest); + uint32_t getGuestStatus(SecCode *guest); + + static KernelCode *active() { return globals().code; } + +public: + struct Globals { + Globals(); + SecPointer code; + SecPointer staticCode; + }; + static ModuleNexus globals; + +protected: + SecStaticCode *getStaticCode(); + +private: +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_CSKERNEL diff --git a/lib/csprocess.cpp b/lib/csprocess.cpp new file mode 100644 index 0000000..0d14392 --- /dev/null +++ b/lib/csprocess.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// csprocess - UNIX process implementation of the Code Signing Host Interface +// +#include "csprocess.h" +#include "cskernel.h" +#include + +namespace Security { +namespace CodeSigning { + + +// +// Construct a running process representation +// +ProcessCode::ProcessCode(pid_t pid) + : GenericCode(KernelCode::active()), mPid(pid) +{ +} + + +mach_port_t ProcessCode::getHostingPort() +{ + return SecurityServer::ClientSession().hostingPort(pid()); +} + + +} // CodeSigning +} // Security diff --git a/lib/csprocess.h b/lib/csprocess.h new file mode 100644 index 0000000..7fd6efc --- /dev/null +++ b/lib/csprocess.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// csprocess - UNIX process implementation of the Code Signing Host Interface +// +#ifndef _H_CSPROCESS +#define _H_CSPROCESS + +#include "csgeneric.h" +#include + +namespace Security { +namespace CodeSigning { + + +// +// A SecCode that represents a running UNIX process. +// Processes are identified by pid. +// +// ProcessCode inherits GenericCode's access to the cshosting Mach protocol to +// deal with guests. +// +class ProcessCode : public GenericCode { +public: + ProcessCode(pid_t pid); + + pid_t pid() const { return mPid; } + + mach_port_t getHostingPort(); + +private: + pid_t mPid; +}; + + +// +// We don't need a GenericCode variant of ProcessCode +// +typedef SecStaticCode ProcessStaticCode; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_CSPROCESS diff --git a/lib/csutilities.cpp b/lib/csutilities.cpp new file mode 100644 index 0000000..f818b06 --- /dev/null +++ b/lib/csutilities.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// csutilities - miscellaneous utilities for the code signing implementation +// +#include "csutilities.h" +#include +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// Calculate the canonical hash of a certificate, given its raw (DER) data. +// +void hashOfCertificate(const void *certData, size_t certLength, SHA1::Digest digest) +{ + SHA1 hasher; + hasher(certData, certLength); + hasher.finish(digest); +} + + +// +// Ditto, given a SecCertificateRef +// +void hashOfCertificate(SecCertificateRef cert, SHA1::Digest digest) +{ + assert(cert); + CSSM_DATA certData; + MacOSError::check(SecCertificateGetData(cert, &certData)); + hashOfCertificate(certData.Data, certData.Length, digest); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/csutilities.h b/lib/csutilities.h new file mode 100644 index 0000000..ec3b7d9 --- /dev/null +++ b/lib/csutilities.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// csutilities - miscellaneous utilities for the code signing implementation +// +// This is a collection of odds and ends that wouldn't fit anywhere else. +// The common theme is that the contents are otherwise naturally homeless. +// +#ifndef _H_CSUTILITIES +#define _H_CSUTILITIES + +#include +#include + + +namespace Security { +namespace CodeSigning { + + +// +// Calculate canonical hashes of certificate. +// This is simply defined as (always) the SHA1 hash of the DER. +// +void hashOfCertificate(const void *certData, size_t certLength, SHA1::Digest digest); +void hashOfCertificate(SecCertificateRef cert, SHA1::Digest digest); + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_CSUTILITIES diff --git a/lib/diskrep.cpp b/lib/diskrep.cpp new file mode 100644 index 0000000..0a28490 --- /dev/null +++ b/lib/diskrep.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// diskrep - disk representations of code +// +#include "diskrep.h" +#include +#include + +// specific disk representations created by the bestGuess() function +#include "filediskrep.h" +#include "bundlediskrep.h" +#include "cfmdiskrep.h" +#include "foreigndiskrep.h" + + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// Abstract features +// +DiskRep::DiskRep() +{ +} + +DiskRep::~DiskRep() +{ /* virtual */ } + + +// +// Normal DiskReps are their own base. +// +DiskRep *DiskRep::base() +{ + return this; +} + + +// +// By default, DiskReps are read-only. +// +DiskRep::Writer *DiskRep::writer() +{ + MacOSError::throwMe(errSecCSBadObjectFormat); +} + + +// +// Given a file system path, come up with the most likely correct +// disk representation for what's there. +// This is, strictly speaking, a heuristic that could be fooled - there's +// no fool-proof rule for figuring this out. But we'd expect this to work +// fine in ordinary use. If you happen to know what you're looking at +// (say, a bundle), then just create the suitable subclass of DiskRep directly. +// That's quite legal. +// +DiskRep *DiskRep::bestGuess(const char *path) +{ + struct stat st; + if (::stat(path, &st)) + UnixError::throwMe(); + + // if it's a directory, assume it's a bundle + if ((st.st_mode & S_IFMT) == S_IFDIR) // directory - assume bundle + return new BundleDiskRep(path); + + // see if it's the main executable of a recognized bundle + if (CFRef pathURL = makeCFURL(path)) + if (CFRef bundle = _CFBundleCreateWithExecutableURLIfLooksLikeBundle(NULL, pathURL)) + return new BundleDiskRep(bundle); + + // follow the file choosing rules + return bestFileGuess(path); +} + + +DiskRep *DiskRep::bestFileGuess(const char *path) +{ + AutoFileDesc fd(path, O_RDONLY); + if (MachORep::candidiate(fd)) + return new MachORep(path); + if (CFMDiskRep::candidiate(fd)) + return new CFMDiskRep(path); + if (ForeignDiskRep::candidate(fd)) + return new ForeignDiskRep(path); + + return new FileDiskRep(path); +} + + +// +// Default behaviors of DiskRep +// +string DiskRep::resourcesRootPath() +{ + return ""; // has no resources directory +} + +CFDictionaryRef DiskRep::defaultResourceRules() +{ + return NULL; // none +} + +const Requirements *DiskRep::defaultRequirements(const Architecture *) +{ + return NULL; // none +} + +Universal *DiskRep::mainExecutableImage() +{ + return NULL; // no Mach-O executable +} + +size_t DiskRep::pageSize() +{ + return monolithicPageSize; // unpaged (monolithic) +} + +size_t DiskRep::signingBase() +{ + return 0; // whole file (start at beginning) +} + +CFArrayRef DiskRep::modifiedFiles() +{ + // by default, claim (just) the main executable modified + CFRef mainURL = makeCFURL(mainExecutablePath()); + return makeCFArray(1, mainURL.get()); +} + +void DiskRep::flush() +{ + // nothing cached +} + + +// +// Writers +// +DiskRep::Writer::Writer(uint32_t attrs) + : mArch(CPU_TYPE_ANY), mAttributes(attrs) +{ +} + +DiskRep::Writer::~Writer() +{ /* virtual */ } + +uint32_t DiskRep::Writer::attributes() const +{ return mAttributes; } + +void DiskRep::Writer::flush() +{ /* do nothing */ } + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/diskrep.h b/lib/diskrep.h new file mode 100644 index 0000000..001d1a5 --- /dev/null +++ b/lib/diskrep.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// diskrep - disk representations of code +// +#ifndef _H_DISKREP +#define _H_DISKREP + +#include "cs.h" +#include "codedirectory.h" +#include "requirement.h" +#include "macho++.h" // for class Architecture +#include +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// DiskRep is an abstract interface to code somewhere located by +// a file system path. It presents the ability to read and write +// Code Signing-related information about such code without exposing +// the details of the storage locations or formats. +// +class DiskRep : public RefCount { +public: + DiskRep(); + virtual ~DiskRep(); + virtual DiskRep *base(); + virtual CFDataRef component(CodeDirectory::SpecialSlot slot) = 0; // fetch component + virtual std::string mainExecutablePath() = 0; // path to main executable + virtual CFURLRef canonicalPath() = 0; // path to whole code + virtual std::string recommendedIdentifier() = 0; // default identifier + virtual std::string resourcesRootPath(); // resource directory if any + virtual CFDictionaryRef defaultResourceRules(); // default resource rules + virtual const Requirements *defaultRequirements(const Architecture *arch); // default internal requirements + virtual Universal *mainExecutableImage(); // binary if Mach-O/Universal + virtual size_t pageSize(); // default main executable page size + virtual size_t signingBase(); // start offset of signed area in main executable + virtual size_t signingLimit() = 0; // size of signed area in main executable + virtual std::string format() = 0; // human-readable type string + virtual CFArrayRef modifiedFiles(); // list of files modified by signing + virtual UnixPlusPlus::FileDesc &fd() = 0; // a cached fd for main executable file + virtual void flush(); // flush caches (refetch as needed) + + bool mainExecutableIsMachO() { return mainExecutableImage() != NULL; } + + // shorthands + CFDataRef codeDirectory() { return component(cdCodeDirectorySlot); } + CFDataRef signature() { return component(cdSignatureSlot); } + +public: + class Writer; + virtual Writer *writer(); + +public: + static DiskRep *bestGuess(const char *path); // canonical heuristic, any path + static DiskRep *bestFileGuess(const char *path); // canonical heuristic, single file only + + static DiskRep *bestGuess(const std::string &path) { return bestGuess(path.c_str()); } + static DiskRep *bestFileGuess(const std::string &path) { return bestFileGuess(path.c_str()); } + + +public: + static const size_t segmentedPageSize = 4096; // default page size for system-paged signatures + static const size_t monolithicPageSize = 0; // default page size for non-Mach-O executables +}; + + +// +// Write-access objects. +// At this layer they are quite abstract, carrying just the functionality needed +// for the signing machinery to place data wherever it should go. Each DiskRep subclass +// that supports writing signing data to a place inside the code needs to implement +// a subclass of Writer and return an instance in the DiskRep::writer() method when asked. +// +class DiskRep::Writer : public RefCount { +public: + Writer(uint32_t attrs = 0); + virtual ~Writer(); + virtual void component(CodeDirectory::SpecialSlot slot, CFDataRef data) = 0; + virtual uint32_t attributes() const; + virtual void flush(); + + bool attribute(uint32_t attr) const { return mAttributes & attr; } + + void signature(CFDataRef data) { component(cdSignatureSlot, data); } + void codeDirectory(const CodeDirectory *cd) + { component(cdCodeDirectorySlot, CFTempData(cd->data(), cd->length())); } + +private: + Architecture mArch; + uint32_t mAttributes; +}; + +// +// Writer attributes. Defaults should be off-bits. +// +enum { + writerLastResort = 0x0001, // prefers not to store attributes itself + writerNoGlobal = 0x0002, // has only per-architecture storage +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_DISKREP diff --git a/lib/filediskrep.cpp b/lib/filediskrep.cpp new file mode 100644 index 0000000..6906c74 --- /dev/null +++ b/lib/filediskrep.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2006-2007 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@ + */ +#include "filediskrep.h" +#include "StaticCode.h" +#include "macho++.h" // may perhaps move into security_utilities... +#include + + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// Everything's lazy in here +// +FileDiskRep::FileDiskRep(const char *path) + : SingleDiskRep(path) +{ +} + + +// +// Produce an extended attribute name from a canonical slot name +// +string FileDiskRep::attrName(const char *name) +{ + static const char prefix[] = "com.apple.cs."; + return string(prefix) + name; +} + + +// +// Retrieve an extended attribute by name +// +CFDataRef FileDiskRep::getAttribute(const char *name) +{ + string aname = attrName(name); + ssize_t length = fd().getAttrLength(aname); + if (length < 0) + return NULL; // no such attribute + CFMallocData buffer(length); + fd().getAttr(aname, buffer, length); + return buffer; +} + + +// +// Extract and return a component by slot number. +// If we have a Mach-O binary, use embedded components. +// Otherwise, look for and return the extended attribute, if any. +// +CFDataRef FileDiskRep::component(CodeDirectory::SpecialSlot slot) +{ + if (const char *name = CodeDirectory::canonicalSlotName(slot)) + return getAttribute(name); + else + return NULL; +} + + +// +// Generate a suggested set of internal requirements. +// We don't really have to say much. However, if we encounter a file that +// starts with the magic "#!" script marker, we do suggest that this should +// be a valid host if we can reasonably make out what that is. +// +const Requirements *FileDiskRep::defaultRequirements(const Architecture *) +{ + // read start of file + char buffer[256]; + size_t length = fd().read(buffer, sizeof(buffer)); + if (length > 3 && buffer[0] == '#' && buffer[1] == '!' && buffer[2] == '/') { + // isolate (full) path element in #!/full/path -some -other -stuff + if (length == sizeof(buffer)) + length--; + buffer[length] = '\0'; + char *path = buffer + 2; + path[strcspn(path, " \t\n\r\f")] = '\0'; + secdebug("filediskrep", "looks like a script for %s", path); + if (path[1]) + try { + // find path on disk, get designated requirement (if signed) + if (RefPointer rep = DiskRep::bestFileGuess(path)) + if (SecPointer code = new SecStaticCode(rep)) + if (const Requirement *req = code->designatedRequirement()) { + // package up as host requirement and return that + Requirements::Maker maker; + maker.add(kSecHostRequirementType, req->clone()); + secdebug("filediskrep", "made a scripting host requirement"); + return maker.make(); + } + } catch (...) { + secdebug("filediskrep", "exception getting host requirement (ignored)"); + } + } + return NULL; +} + + +string FileDiskRep::format() +{ + return "generic"; +} + + +// +// FileDiskRep::Writers +// +DiskRep::Writer *FileDiskRep::writer() +{ + return new Writer(this); +} + + +// +// Write a component. +// Note that this isn't concerned with Mach-O writing; this is handled at +// a much higher level. If we're called, it's extended attribute time. +// +void FileDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) +{ + fd().setAttr(attrName(CodeDirectory::canonicalSlotName(slot)), + CFDataGetBytePtr(data), CFDataGetLength(data)); +} + + +// +// We are NOT the preferred store for components because our approach +// (extended attributes) suffers from some serious limitations. +// +bool FileDiskRep::Writer::preferredStore() +{ + return false; +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/filediskrep.h b/lib/filediskrep.h new file mode 100644 index 0000000..e84b455 --- /dev/null +++ b/lib/filediskrep.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// filediskrep - single-file executable disk representation +// +#ifndef _H_FILEDISKREP +#define _H_FILEDISKREP + +#include "singlediskrep.h" +#include "machorep.h" +#include + +namespace Security { +namespace CodeSigning { + + +// +// A FileDiskRep represents a single code file on disk. We assume nothing about +// the format or contents of the file and impose no structure on it, other than +// assuming that all relevant code is contained in the file's data bytes. +// By default, we seal the entire file data as a single page. +// +// This is the ultimate fallback disk format. It is used if no other pattern +// applies. As such it is important that we do not introduce any assumptions +// here. Know that you do not know what any of the file means. +// +// FileDiskrep stores components in extended file attributes, one attribute +// per component. Note that this imposes size limitations on component size +// that may well be prohibitive in some applications. +// +// This DiskRep does not support resource sealing. +// +class FileDiskRep : public SingleDiskRep { +public: + FileDiskRep(const char *path); + + CFDataRef component(CodeDirectory::SpecialSlot slot); + const Requirements *defaultRequirements(const Architecture *arch); + std::string format(); + +public: + DiskRep::Writer *writer(); + class Writer; + friend class Writer; + +protected: + CFDataRef getAttribute(const char *name); + static std::string attrName(const char *name); +}; + + +// +// The write side of a FileDiskRep +// +class FileDiskRep::Writer : public SingleDiskRep::Writer { + friend class FileDiskRep; +public: + void component(CodeDirectory::SpecialSlot slot, CFDataRef data); + bool preferredStore(); + +protected: + Writer(FileDiskRep *r) : SingleDiskRep::Writer(r, writerLastResort) { } + RefPointer rep; +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_FILEDISKREP diff --git a/lib/foreigndiskrep.cpp b/lib/foreigndiskrep.cpp new file mode 100644 index 0000000..89ac70f --- /dev/null +++ b/lib/foreigndiskrep.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// foreigndiskrep - foreign executable disk representation +// +#include "foreigndiskrep.h" +#include + + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// Everything's lazy in here +// +ForeignDiskRep::ForeignDiskRep(const char *path) + : SingleDiskRep(path), mTriedRead(false) +{ +} + +ForeignDiskRep::~ForeignDiskRep() +{ + if (mTriedRead) + delete mSigningData; +} + + +// +// Foreign filter heuristic +// +bool ForeignDiskRep::candidate(FileDesc &fd) +{ + static const char magicMarker[] = "MZ\0\0\0\0\0\0\0\0\0\0PE\0\0 \b"; + static const size_t magicLength = 18; + char marker[magicLength]; + return fd.read(marker, magicLength, 0) == magicLength + && !memcmp(marker, magicMarker, magicLength); +} + + +// +// Extract and return a component by slot number. +// If we have a Mach-O binary, use embedded components. +// Otherwise, look for and return the extended attribute, if any. +// +CFDataRef ForeignDiskRep::component(CodeDirectory::SpecialSlot slot) +{ + if (!mTriedRead) + readSigningData(); + if (mSigningData) + return mSigningData->component(slot); + else + return NULL; +} + + +// +// Default to system-paged signing +// +size_t ForeignDiskRep::pageSize() +{ + return segmentedPageSize; +} + + +// +// Various other aspects of our DiskRep personality. +// +string ForeignDiskRep::format() +{ + return "foreign binary"; +} + + +// +// Discard cached information +// +void ForeignDiskRep::flush() +{ + mTriedRead = false; + ::free(mSigningData); +} + + +// +// Locate, read, and cache embedded signing data from the foreign binary. +// +void ForeignDiskRep::readSigningData() +{ + if (!mTriedRead) { // try it once + mSigningData = NULL; // preset failure + mTriedRead = true; // we've tried (and perhaps failed) + + AutoFileDesc fd(cspath(), O_RDONLY); + mSigningData = EmbeddedSignatureBlob::readBlob(fd); + if (mSigningData) + secdebug("foreignrep", "%zd signing bytes in %d blob(s) from %s(foreign)", + mSigningData->length(), mSigningData->count(), + mainExecutablePath().c_str()); + else + secdebug("foreignrep", "failed to read signing bytes from %s(foreign)", + mainExecutablePath().c_str()); + } +} + + +// +// Generate the path to the (default) sidecar file +// This is generated as /path/to/.CS.execfilename. +// We're assuming that we're only dealing with absolute paths here. +// +string ForeignDiskRep::cspath() +{ + string p = this->path(); + string::size_type slash = p.rfind('/'); + assert(slash != string::npos); + return p.substr(0, slash+1) + ".CS." + p.substr(slash+1); // => /path/to/.CS.executable +} + + +// +// ForeignDiskRep::Writers +// +DiskRep::Writer *ForeignDiskRep::writer() +{ + return new Writer(this); +} + +ForeignDiskRep::Writer::~Writer() +{ + delete mSigningData; +} + + +// +// Write a component. +// Note that this isn't concerned with Mach-O writing; this is handled at +// a much higher level. If we're called, it's extended attribute time. +// +void ForeignDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) +{ + EmbeddedSignatureBlob::Maker::component(slot, data); +} + + +// +// Append the superblob we built to the foreign binary. +// Note: Aligning the signing blob to a 16-byte boundary is not strictly necessary, +// but it's what the Mach-O case does, and it probably improves performance a bit. +// +void ForeignDiskRep::Writer::flush() +{ + delete mSigningData; // ditch previous blob just in case + mSigningData = Maker::make(); // assemble new signature SuperBlob + AutoFileDesc fd(rep->cspath(), O_WRONLY | O_CREAT | O_TRUNC, 0666); + fd.writeAll(mSigningData, mSigningData->length()); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/foreigndiskrep.h b/lib/foreigndiskrep.h new file mode 100644 index 0000000..9a8dd17 --- /dev/null +++ b/lib/foreigndiskrep.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// foreigndiskrep - foreign executable disk representation +// +#ifndef _H_FOREIGNDISKREP +#define _H_FOREIGNDISKREP + +#include "singlediskrep.h" +#include "sigblob.h" +#include "signerutils.h" +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// +// +class ForeignDiskRep : public SingleDiskRep { +public: + ForeignDiskRep(const char *path); + ~ForeignDiskRep(); + + CFDataRef component(CodeDirectory::SpecialSlot slot); + size_t pageSize(); + std::string format(); + void flush(); + + static bool candidate(UnixPlusPlus::FileDesc &fd); // could this reasonably be a CFM code? + +public: + DiskRep::Writer *writer(); + class Writer; + friend class Writer; + +protected: + void readSigningData(); // read and cache signing data + string cspath(); // path to sidecar + +private: + bool mTriedRead; // tried to get signing data + size_t mSigningOffset; // where we found the signing data + EmbeddedSignatureBlob *mSigningData; // cached signing data +}; + + +// +// The write side of a FileDiskRep +// +class ForeignDiskRep::Writer : public DiskRep::Writer, private EmbeddedSignatureBlob::Maker { + friend class ForeignDiskRep; +public: + Writer(ForeignDiskRep *r) : rep(r), mSigningData(NULL) { } + ~Writer(); + + void component(CodeDirectory::SpecialSlot slot, CFDataRef data); + virtual void flush(); + +protected: + RefPointer rep; + EmbeddedSignatureBlob *mSigningData; +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_FOREIGNDISKREP diff --git a/lib/kerneldiskrep.cpp b/lib/kerneldiskrep.cpp new file mode 100644 index 0000000..35b5ad7 --- /dev/null +++ b/lib/kerneldiskrep.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include "kerneldiskrep.h" + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// Everything about the kernel is pretty much fixed, so there's +// no state to maintain. +// +KernelDiskRep::KernelDiskRep() +{ +} + + +// +// We can't pull any resources from the kernel. +// And we know where it all is. +// +CFDataRef KernelDiskRep::component(CodeDirectory::SpecialSlot slot) +{ + return NULL; +} + +CFURLRef KernelDiskRep::canonicalPath() +{ + return makeCFURL("/mach_kernel"); +} + +string KernelDiskRep::recommendedIdentifier() +{ + return "@@kernel@@"; //@@@ use $(uname -whatever)? +} + +size_t KernelDiskRep::signingLimit() +{ + return 0; // don't bother +} + +string KernelDiskRep::format() +{ + return "system kernel"; +} + +UnixPlusPlus::FileDesc &KernelDiskRep::fd() +{ + UnixError::throwMe(EINVAL); // don't have one +} + +string KernelDiskRep::mainExecutablePath() +{ + return "/mach_kernel"; +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/kerneldiskrep.h b/lib/kerneldiskrep.h new file mode 100644 index 0000000..973971c --- /dev/null +++ b/lib/kerneldiskrep.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// kerneldiskrep - the kernel's own disk representation. +// +// This is a very special case. +// It's here primarily so we don't have to add special cases for the kernel +// all over the higher layers. +// +#ifndef _H_KERNELDISKREP +#define _H_KERNELDISKREP + +#include "diskrep.h" + +namespace Security { +namespace CodeSigning { + + +// +// A KernelDiskRep represents a (the) kernel on disk. +// It has no write support, so we can't sign the kernel, +// which is fine since we unconditionally trust it anyway. +// +class KernelDiskRep : public DiskRep { +public: + KernelDiskRep(); + + CFDataRef component(CodeDirectory::SpecialSlot slot); + std::string mainExecutablePath(); + CFURLRef canonicalPath(); + std::string recommendedIdentifier(); + size_t signingLimit(); + std::string format(); + UnixPlusPlus::FileDesc &fd(); +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_KERNELDISKREP diff --git a/lib/macho++.cpp b/lib/macho++.cpp new file mode 100644 index 0000000..9c5572b --- /dev/null +++ b/lib/macho++.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// macho++ - Mach-O object file helpers +// +#include "macho++.h" +#include + +namespace Security { + + +// +// Architecture values +// +Architecture::Architecture(const fat_arch &arch) + : pair(ntohl(arch.cputype), ntohl(arch.cpusubtype)) +{ +} + + +// +// The local architecture (on demand; cached) +// +struct LocalArch { + const NXArchInfo *arch; + LocalArch() { arch = NXGetLocalArchInfo(); } +}; +static ModuleNexus localArch; + +Architecture Architecture::local() +{ + const NXArchInfo &local = *localArch().arch; + return Architecture(local.cputype, local.cpusubtype); +} + + +// +// Translate between names and numbers +// +const char *Architecture::name() const +{ + if (const NXArchInfo *info = NXGetArchInfoFromCpuType(cpuType(), cpuSubtype())) + return info->name; + else + return NULL; +} + + +// +// Create a MachO object from an open file and a starting offset. +// We load (only) the header and load commands into memory at that time. +// +MachO::MachO(FileDesc fd, size_t offset, size_t length) + : FileDesc(fd), mOffset(offset), mLength(length ? length : (fd.fileSize() - offset)) +{ + size_t size = fd.read(&mHeader, sizeof(mHeader), mOffset); + if (size != sizeof(mHeader)) + UnixError::throwMe(ENOEXEC); + switch (mHeader.magic) { + case MH_MAGIC: + mFlip = false; + m64 = false; + break; + case MH_CIGAM: + mFlip = true; + m64 = false; + break; + case MH_MAGIC_64: + mFlip = false; + m64 = true; + break; + case MH_CIGAM_64: + mFlip = true; + m64 = true; + break; + default: + UnixError::throwMe(ENOEXEC); + } + + size_t cmdSize = flip(mHeader.sizeofcmds); + size_t cmdStart = m64 ? sizeof(mach_header_64) : sizeof(mach_header); + mCommands = (load_command *)malloc(cmdSize); + if (!mCommands) + UnixError::throwMe(); + if (fd.read(mCommands, cmdSize, cmdStart + mOffset) != cmdSize) + UnixError::throwMe(ENOEXEC); + mEndCommands = LowLevelMemoryUtilities::increment(mCommands, cmdSize); + secdebug("macho", "%p created fd=%d offset=0x%zx size=0x%zx %s%s %d command(s)", + this, this->fd(), mOffset, mLength, mFlip ? " flipped" : "", m64 ? " 64-bit" : "", + flip(mHeader.ncmds)); +} + + +// +// Destroy a MachO. +// Note that we don't close the file descriptor. +// +MachO::~MachO() +{ + secdebug("macho", "%p destroyed", this); + ::free(mCommands); +} + + +// +// Return various header fields +// +Architecture MachO::architecture() const +{ + return Architecture(flip(mHeader.cputype), flip(mHeader.cpusubtype)); +} + +uint32_t MachO::type() const +{ + return flip(mHeader.filetype); +} + +uint32_t MachO::flags() const +{ + return flip(mHeader.flags); +} + + +// +// Iterate through load commands +// +const load_command *MachO::nextCommand(const load_command *command) const +{ + using LowLevelMemoryUtilities::increment; + command = increment(command, flip(command->cmdsize)); + return (command < mEndCommands) ? command : NULL; +} + + +// +// Locate a segment command, by name +// +const segment_command *MachO::findSegment(const char *segname) const +{ + for (const load_command *command = loadCommands(); command; command = nextCommand(command)) { + if (flip(command->cmd) == LC_SEGMENT) { + const segment_command *seg = reinterpret_cast(command); + if (!strcmp(seg->segname, segname)) + return seg; + } + } + return NULL; +} + +const section *MachO::findSection(const char *segname, const char *sectname) const +{ + using LowLevelMemoryUtilities::increment; + if (const segment_command *seg = findSegment(segname)) { + if (is64()) { + const segment_command_64 *seg64 = reinterpret_cast(seg); + const section_64 *sect = increment(seg64 + 1, 0); + for (unsigned n = flip(seg64->nsects); n > 0; n--, sect++) { + if (!strcmp(sect->sectname, sectname)) + return reinterpret_cast(sect); + } + } else { + const section *sect = increment(seg + 1, 0); + for (unsigned n = flip(seg->nsects); n > 0; n--, sect++) { + if (!strcmp(sect->sectname, sectname)) + return sect; + } + } + } + return NULL; +} + + +// +// Figure out where the Code Signing information starts in the Mach-O binary image. +// The code signature is at the end of the file, and identified +// by a specially-named section. So its starting offset is also the end +// of the signable part. +// Note that the offset returned is relative to the start of the Mach-O image. +// Returns zero if not found (usually indicating that the binary was not signed). +// +const linkedit_data_command *MachO::findCodeSignature() const +{ + for (const load_command *cmd = loadCommands(); cmd; cmd = nextCommand(cmd)) + if (flip(cmd->cmd) == LC_CODE_SIGNATURE) + return reinterpret_cast(cmd); + return NULL; // not found +} + +size_t MachO::signingOffset() const +{ + if (const linkedit_data_command *lec = findCodeSignature()) + return flip(lec->dataoff); + else + return 0; +} + +size_t MachO::signingLength() const +{ + if (const linkedit_data_command *lec = findCodeSignature()) + return flip(lec->datasize); + else + return 0; +} + + +// +// Return the signing-limit length for this Mach-O binary image. +// This is the signingOffset if present, or the full length if not. +// +size_t MachO::signingExtent() const +{ + if (size_t offset = signingOffset()) + return offset; + else + return length(); +} + + +// +// I/O operations +// +void MachO::seek(size_t offset) +{ + FileDesc::seek(mOffset + offset); +} + +CFDataRef MachO::dataAt(size_t offset, size_t size) +{ + CFMallocData buffer(size); + if (this->read(buffer, size, mOffset + offset) != size) + UnixError::throwMe(); + return buffer; +} + + +// +// Fat (aka universal) file wrappers +// +Universal::Universal(FileDesc fd) + : FileDesc(fd) +{ + //@@@ we could save a read by using a heuristically sized combined buffer here + fat_header header; // note how in the thin-file case, we'll reinterpret the header below + if (fd.read(&header, sizeof(header), 0) != sizeof(header)) + UnixError::throwMe(ENOEXEC); + switch (header.magic) { + case FAT_MAGIC: + case FAT_CIGAM: + { + mArchCount = ntohl(header.nfat_arch); + size_t archSize = sizeof(fat_arch) * mArchCount; + mArchList = (fat_arch *)malloc(archSize); + if (!mArchList) + UnixError::throwMe(); + if (fd.read(mArchList, archSize, sizeof(header)) != archSize) { + ::free(mArchList); + UnixError::throwMe(ENOEXEC); + } + secdebug("macho", "%p is a fat file with %d architectures", + this, mArchCount); + break; + } + case MH_MAGIC: + case MH_MAGIC_64: + mArchList = NULL; + mArchCount = 0; + mThinArch = Architecture(reinterpret_cast(header).cputype); + secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name()); + break; + case MH_CIGAM: + case MH_CIGAM_64: + mArchList = NULL; + mArchCount = 0; + mThinArch = Architecture(ntohl(reinterpret_cast(header).cputype)); + secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name()); + break; + default: + UnixError::throwMe(ENOEXEC); + } +} + +Universal::~Universal() +{ + ::free(mArchList); +} + + +// +// Get the "local" architecture from the fat file +// Throws ENOEXEC if not found. +// +MachO *Universal::architecture() const +{ + if (isUniversal()) + return findImage(bestNativeArch()); + else + return new MachO(*this); +} + +size_t Universal::archOffset() const +{ + if (isUniversal()) + return ntohl(findArch(bestNativeArch())->offset); + else + return 0; +} + + +// +// Get the specified architecture from the fat file +// Throws ENOEXEC if not found. +// +MachO *Universal::architecture(const Architecture &arch) const +{ + if (isUniversal()) + return findImage(arch); + else if (arch == mThinArch) + return new MachO(*this); + else + UnixError::throwMe(ENOEXEC); +} + +size_t Universal::archOffset(const Architecture &arch) const +{ + if (isUniversal()) + return ntohl(findArch(arch)->offset); + else if (arch == mThinArch) + return 0; + else + UnixError::throwMe(ENOEXEC); +} + + +// +// Locate an architecture from the fat file's list. +// Throws ENOEXEC if not found. +// +const fat_arch *Universal::findArch(const Architecture &target) const +{ + assert(isUniversal()); + const fat_arch *end = mArchList + mArchCount; + for (const fat_arch *arch = mArchList; arch < end; ++arch) + if (cpu_type_t(ntohl(arch->cputype)) == target.cpuType()) //@@@ ignoring subarch + return arch; + UnixError::throwMe(ENOEXEC); // not found +} + +MachO *Universal::findImage(const Architecture &target) const +{ + const fat_arch *arch = findArch(target); + return new MachO(*this, ntohl(arch->offset), ntohl(arch->size)); +} + + +// +// Find the best-matching architecture for this fat file. +// We pick the native architecture if it's available. +// If it contains exactly one architecture, we take that. +// Otherwise, we throw. +// +Architecture Universal::bestNativeArch() const +{ + if (isUniversal()) { + const cpu_type_t native = Architecture::local().cpuType(); + const fat_arch *end = mArchList + mArchCount; + for (const fat_arch *arch = mArchList; arch < end; ++arch) + if (cpu_type_t(ntohl(arch->cputype)) == native) // ignoring subarch + return Architecture(native); + if (mArchCount == 1) + return Architecture(ntohl(mArchList->cputype)); + UnixError::throwMe(ENOEXEC); + } else + return mThinArch; +} + + +// +// List all architectures from the fat file's list. +// +void Universal::architectures(Architectures &archs) +{ + if (isUniversal()) { + for (unsigned n = 0; n < mArchCount; n++) + archs.insert(mArchList[n]); + } else { + auto_ptr macho(architecture()); + archs.insert(macho->architecture()); + } +} + + +// +// Quickly guess the Mach-O type of a file. +// Returns type zero if the file isn't Mach-O or Universal. +// Does not reposition the file. +// +uint32_t Universal::typeOf(FileDesc fd) +{ + mach_header header; + if (fd.read(&header, sizeof(header), 0) != sizeof(header)) + return false; + for (;;) { + switch (header.magic) { + case MH_MAGIC: + case MH_MAGIC_64: + return header.filetype; + break; + case MH_CIGAM: + case MH_CIGAM_64: + return flip(header.filetype); + break; + case FAT_MAGIC: + case FAT_CIGAM: + { + const fat_arch *arch1 = + LowLevelMemoryUtilities::increment(&header, sizeof(fat_header)); + if (fd.read(&header, sizeof(header), ntohl(arch1->offset)) != sizeof(header)) + return 0; + continue; + } + default: + return 0; + } + } +} + + +} // Security diff --git a/lib/macho++.h b/lib/macho++.h new file mode 100644 index 0000000..3c02c5f --- /dev/null +++ b/lib/macho++.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// macho++ - Mach-O object file helpers +// +#ifndef _H_MACHOPLUSPLUS +#define _H_MACHOPLUSPLUS + +#include +#include +#include +#include +#include +#include +#include + +namespace Security { + + +// +// An architecture specification. +// Simply a pair or (cpu type, cpu subtype), really. +// +class Architecture : public std::pair { +public: + Architecture() { } + explicit Architecture(cpu_type_t type, cpu_subtype_t sub = 0) + : std::pair(type, sub) { } + Architecture(const fat_arch &archInFile); + + cpu_type_t cpuType() const { return this->first; } + cpu_subtype_t cpuSubtype() const { return this->second; } + const char *name() const; + +public: + friend bool operator == (const Architecture &a1, const Architecture &a2) + { return a1.cpuType() == a2.cpuType(); } + + friend bool operator < (const Architecture &a1, const Architecture &a2) + { return a1.cpuType() < a2.cpuType(); } + +public: + static Architecture local(); +}; + + +// +// A Mach-O formatted file segment. +// +class MachO : public UnixPlusPlus::FileDesc { +public: + MachO(FileDesc fd, size_t offset = 0, size_t length = 0); + ~MachO(); + + size_t offset() const { return mOffset; } + size_t length() const { return mLength; } + + template + T flip(T value) const + { return mFlip ? Security::flip(value) : value; } + + bool isFlipped() const { return mFlip; } + bool is64() const { return m64; } + + Architecture architecture() const; + uint32_t type() const; + uint32_t flags() const; + + const load_command *loadCommands() const { return mCommands; } + const load_command *nextCommand(const load_command *command) const; + + const segment_command *findSegment(const char *segname) const; + const section *findSection(const char *segname, const char *sectname) const; + + const linkedit_data_command *findCodeSignature() const; + + size_t signingOffset() const; // starting offset of CS section, or 0 if none + size_t signingLength() const; // length of CS section, or 0 if none + size_t signingExtent() const; // signingOffset, or file length if none + + void seek(size_t offset); // relative to start of image + CFDataRef dataAt(size_t offset, size_t size); + +private: + size_t mOffset; // starting file offset + size_t mLength; // Mach-O file length + bool m64; // is 64-bit + bool mFlip; // wrong byte order (flip all integers) + mach_header mHeader; // Mach-O header + load_command *mCommands; // load commands + load_command *mEndCommands; // end of load commands +}; + + +// +// A Universal object represents a Mach-O binary image (whole) file. +// It can represent a true Universal (aka "Fat") file with multiple +// architectures; but it will also represent a single Mach-O ("thin") +// binary and make you believe it's a Universal with just one architecture. +// +class Universal : public UnixPlusPlus::FileDesc { +public: + Universal(FileDesc fd); + ~Universal(); + + // return a genuine MachO object for the given architecture + MachO *architecture() const; // native + MachO *architecture(const Architecture &arch) const; // given + + // return (just) the starting offset of an architecture + size_t archOffset() const; // native + size_t archOffset(const Architecture &arch) const; // given + + // return a set of architectures contained + typedef std::set Architectures; + void architectures(Architectures &archs); + + bool isUniversal() const { return mArchList; } + Architecture bestNativeArch() const; + +public: + static uint32_t typeOf(FileDesc fd); + +private: + const fat_arch *findArch(const Architecture &arch) const; + MachO *findImage(const Architecture &arch) const; + +private: + fat_arch *mArchList; // architectures (NULL if thin file) + unsigned mArchCount; // number of architectures (if fat) + Architecture mThinArch; // single architecture (if thin) +}; + + +} // end namespace Security + +#endif // !_H_MACHOPLUSPLUS diff --git a/lib/machorep.cpp b/lib/machorep.cpp new file mode 100644 index 0000000..dae7c25 --- /dev/null +++ b/lib/machorep.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// machorep - DiskRep mix-in for handling Mach-O main executables +// +#include "machorep.h" + + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// Object management. +// We open the main executable lazily, so nothing much happens on construction. +// +MachORep::MachORep(const char *path) + : SingleDiskRep(path), mSigningData(NULL) +{ + mExecutable = new Universal(fd()); +} + +MachORep::~MachORep() +{ + delete mExecutable; + ::free(mSigningData); +} + + +// +// Sniffer function for "plausible Mach-O binary" +// +bool MachORep::candidiate(FileDesc &fd) +{ + switch (Universal::typeOf(fd)) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_DYLINKER: + case MH_BUNDLE: + case MH_PRELOAD: + return true; // dynamic image; supported + case MH_OBJECT: + return false; // maybe later... + default: + return false; // not Mach-O (or too exotic) + } +} + + +// +// For Mach-O binaries that are of PowerPC architecture, we recommend +// allowing the Rosetta translator as a host. Otherwise, no suggestions. +// +static const uint8_t ppc_ireqs[] = { // host => anchor apple and identifier com.apple.translate + 0xfa, 0xde, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x14, 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, + 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, + 0x61, 0x74, 0x65, 0x00, +}; + +const Requirements *MachORep::defaultRequirements(const Architecture *arch) +{ + assert(arch); // enforced by signing infrastructure + if (arch->cpuType() == CPU_TYPE_POWERPC) + return ((const Requirements *)ppc_ireqs)->clone(); // need to pass ownership + else + return NULL; +} + + +// +// Obtain, cache, and return a Universal reference to the main executable, +// IF the main executable is a Mach-O binary (or fat version thereof). +// Returns NULL if the main executable can't be opened as such. +// +Universal *MachORep::mainExecutableImage() +{ + if (!mExecutable) + mExecutable = new Universal(fd()); + return mExecutable; +} + + +// +// Default to system page size for segmented (paged) signatures +// +size_t MachORep::pageSize() +{ + return segmentedPageSize; +} + + +// +// Signing base is the start of the Mach-O architecture we're using +// +size_t MachORep::signingBase() +{ + return mainExecutableImage()->archOffset(); +} + + +// +// Retrieve a component from the executable. +// This reads the entire signing SuperBlob when first called for an executable, +// and then caches it for further use. +// Note that we could read individual components directly off disk and only cache +// the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected +// to cache the pieces anyway. +// +CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot) +{ + switch (slot) { + case cdInfoSlot: + return infoPlist(); + default: + return embeddedComponent(slot); + } +} + + +// Retrieve a component from the embedded signature SuperBlob (if present). +// This reads the entire signing SuperBlob when first called for an executable, +// and then caches it for further use. +// Note that we could read individual components directly off disk and only cache +// the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected +// to cache the pieces anyway. But it's not clear that the resulting multiple I/O +// calls wouldn't be slower in the end. +// +CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot) +{ + if (!mSigningData) // fetch and cache + try { + auto_ptr macho(mainExecutableImage()->architecture()); + if (macho.get()) + if (size_t offset = macho->signingOffset()) { + macho->seek(offset); + mSigningData = EmbeddedSignatureBlob::readBlob(macho->fd()); + if (mSigningData) + secdebug("machorep", "%zd signing bytes in %d blob(s) from %s(%s)", + mSigningData->length(), mSigningData->count(), + mainExecutablePath().c_str(), macho->architecture().name()); + else + secdebug("machorep", "failed to read signing bytes from %s(%s)", + mainExecutablePath().c_str(), macho->architecture().name()); + } + } catch (...) { + secdebug("machorep", "exception reading Mach-O from universal"); + } + if (mSigningData) + return mSigningData->component(slot); + + // not found + return NULL; +} + + +// +// Extract an embedded Info.plist from the file. +// Returns NULL if none is found. +// +CFDataRef MachORep::infoPlist() +{ + CFRef info; + try { + auto_ptr macho(mainExecutableImage()->architecture()); + if (const section *sect = macho->findSection("__TEXT", "__info_plist")) { + if (macho->is64()) { + const section_64 *sect64 = reinterpret_cast(sect); + info = macho->dataAt(macho->flip(sect64->offset), macho->flip(sect64->size)); + } else { + info = macho->dataAt(macho->flip(sect->offset), macho->flip(sect->size)); + } + } + } catch (...) { + secdebug("machorep", "exception reading embedded Info.plist"); + } + return info.yield(); +} + + +// +// Return a recommended unique identifier. +// If our file has an embedded Info.plist, use the CFBundleIdentifier from that. +// Otherwise, use the default. +// +string MachORep::recommendedIdentifier() +{ + if (CFDataRef info = infoPlist()) { + if (CFDictionaryRef dict = makeCFDictionaryFrom(info)) { + CFStringRef code = CFStringRef(CFDictionaryGetValue(dict, kCFBundleIdentifierKey)); + if (code && CFGetTypeID(code) != CFStringGetTypeID()) + MacOSError::throwMe(errSecCSBadDictionaryFormat); + if (code) + return cfString(code); + } else + MacOSError::throwMe(errSecCSBadDictionaryFormat); + } + + // ah well. Use the default + return SingleDiskRep::recommendedIdentifier(); +} + + +// +// Provide a (vaguely) human readable characterization of this code +// +string MachORep::format() +{ + if (Universal *fat = mainExecutableImage()) { + Universal::Architectures archs; + fat->architectures(archs); + if (fat->isUniversal()) { + string s = "Mach-O universal ("; + for (Universal::Architectures::const_iterator it = archs.begin(); + it != archs.end(); ++it) { + if (it != archs.begin()) + s += " "; + s += it->name(); + } + return s + ")"; + } else { + assert(archs.size() == 1); + return string("Mach-O thin (") + archs.begin()->name() + ")"; + } + } else + return "not Mach-O"; // (you don't usually show that one to the user) +} + + +// +// Flush cached data +// +void MachORep::flush() +{ + delete mExecutable; + mExecutable = NULL; + ::free(mSigningData); + mSigningData = NULL; + SingleDiskRep::flush(); +} + + +// +// FileDiskRep::Writers +// +DiskRep::Writer *MachORep::writer() +{ + return new Writer(this); +} + + +// +// Write a component. +// MachORep::Writers don't write to components directly; the signing code uses special +// knowledge of the Mach-O format to build embedded signatures and blasts them directly +// to disk. Thus this implementation will never be called (and, if called, will simply fail). +// +void MachORep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) +{ + MacOSError::throwMe(errSecCSInternalError); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/machorep.h b/lib/machorep.h new file mode 100644 index 0000000..4b05cf8 --- /dev/null +++ b/lib/machorep.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// machorep - DiskRep mix-in for handling Mach-O main executables +// +#ifndef _H_MACHOREP +#define _H_MACHOREP + +#include "singlediskrep.h" +#include "sigblob.h" +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// MachORep is a mix-in class that supports reading +// Code Signing resources from the main executable. +// +// It does not have write support (for writing signatures); +// writing multi-architecture binaries is complicated enough +// that it's driven directly from the signing code, with no +// abstractions to get in the way. +// +class MachORep : public SingleDiskRep { +public: + MachORep(const char *path); + virtual ~MachORep(); + + CFDataRef component(CodeDirectory::SpecialSlot slot); + std::string recommendedIdentifier(); + const Requirements *defaultRequirements(const Architecture *arch); + Universal *mainExecutableImage(); + size_t pageSize(); + size_t signingBase(); + std::string format(); + + void flush(); // flush cache + + static bool candidiate(UnixPlusPlus::FileDesc &fd); + +public: + DiskRep::Writer *writer(); + class Writer; + friend class Writer; + +protected: + CFDataRef embeddedComponent(CodeDirectory::SpecialSlot slot); + CFDataRef infoPlist(); + +private: + Universal *mExecutable; // cached Mach-O/Universal reference to mainExecutablePath() + EmbeddedSignatureBlob *mSigningData; // cached signing data from current architecture +}; + + +// +// The write side of a FileDiskRep +// +class MachORep::Writer : public SingleDiskRep::Writer { + friend class FileDiskRep; +public: + Writer(MachORep *r) : SingleDiskRep::Writer(r, writerNoGlobal) { } + void component(CodeDirectory::SpecialSlot slot, CFDataRef data); +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_MACHOREP diff --git a/lib/renum.cpp b/lib/renum.cpp new file mode 100644 index 0000000..8ab499e --- /dev/null +++ b/lib/renum.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// renum - enumerator for code (usually bundle) resources +// +#include "renum.h" +#include +#include + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +ResourceEnumerator::ResourceEnumerator(string path) + : mPath(path) +{ + assert(!mPath.empty()); + const char * paths[2] = { path.c_str(), NULL }; + mFTS = fts_open((char * const *)paths, FTS_PHYSICAL, NULL); + if (!mFTS) + UnixError::throwMe(); +} + +ResourceEnumerator::~ResourceEnumerator() +{ + checkError(fts_close(mFTS)); +} + + +FTSENT *ResourceEnumerator::next(string &path) +{ + while (FTSENT *ent = fts_read(mFTS)) { + switch (ent->fts_info) { + case FTS_F: + path = ent->fts_path + mPath.size() + 1; // skip prefix + "/" + return ent; + case FTS_D: + secdebug("rdirenum", "entering %s", ent->fts_path); + break; + case FTS_DP: + secdebug("rdirenum", "leaving %s", ent->fts_path); + break; + case FTS_SL: + secdebug("rdirenum", "symlink ignored: %s", ent->fts_path); + break; + default: + secdebug("rdirenum", "type %d (errno %d): %s", + ent->fts_info, ent->fts_errno, ent->fts_path); + break; + } + } + return NULL; +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/renum.h b/lib/renum.h new file mode 100644 index 0000000..1d40983 --- /dev/null +++ b/lib/renum.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// renum - enumerator for code (usually bundle) resources +// +#ifndef _H_RENUM +#define _H_RENUM + +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// A ResourceEnumerator front-ends FTS to scan out relevant resource +// directory entries (ignoring irrelevant ones). +// It also returns canonical resource paths (relative to the resource directory). +// +class ResourceEnumerator { +public: + ResourceEnumerator(std::string path); + ~ResourceEnumerator(); + + FTSENT *next(std::string &path); + +private: + std::string mPath; + FTS *mFTS; +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_RENUM diff --git a/lib/reqdumper.cpp b/lib/reqdumper.cpp new file mode 100644 index 0000000..33867de --- /dev/null +++ b/lib/reqdumper.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// reqdumper - Requirement un-parsing (disassembly) +// +#include "reqdumper.h" +#include + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// Printf to established output channel +// +void Dumper::print(const char *format, ...) +{ + char buffer[256]; + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + mOutput += buffer; +} + + +// +// Dump the underlying Requirement program +// +void Dumper::dump() +{ + this->expr(); + + // remove any initial space + if (mOutput[0] == ' ') + mOutput = mOutput.substr(1); +} + + +// +// Dump an entire Requirements set, using temporary Dumper objects. +// +// This detects single Requirement inputs and dumps them successfully (using +// single-requirement syntax). No indication of error is returned in this case. +// +string Dumper::dump(const Requirements *reqs, bool debug /* = false */) +{ + if (!reqs) { + return "# no requirement(s)"; + } else if (reqs->magic() == Requirement::typeMagic) { // single requirement + return dump((const Requirement *)reqs) + "\n"; + } else { + string result; + for (unsigned n = 0; n < reqs->count(); n++) { + char prefix[200]; + if (reqs->type(n) < kSecRequirementTypeCount) + snprintf(prefix, sizeof(prefix), + "%s => ", Requirement::typeNames[reqs->type(n)]); + else + snprintf(prefix, sizeof(prefix), "/*unknown type*/ %d => ", reqs->type(n)); + Dumper dumper(reqs->blob(n), debug); + dumper.expr(); + result += prefix + dumper.value() + "\n"; + } + return result; + } +} + +string Dumper::dump(const Requirement *req, bool debug /* = false */) +{ + Dumper dumper(req, debug); + try { + dumper.dump(); + return dumper; + } catch (const CommonError &err) { + if (debug) { + char errstr[80]; + snprintf(errstr, sizeof(errstr), " !! error %ld !!", err.osStatus()); + return dumper.value() + errstr; + } + throw; + } +} + +string Dumper::dump(const BlobCore *req, bool debug /* = false */) +{ + switch (req->magic()) { + case Requirement::typeMagic: + return dump(static_cast(req), debug); + break; + case Requirements::typeMagic: + return dump(static_cast(req), debug); + break; + default: + return "invalid data type"; + } +} + + +// +// Element dumpers. Output accumulates in internal buffer. +// +void Dumper::expr(SyntaxLevel level) +{ + if (mDebug) + print("/*@0x%x*/", pc()); + ExprOp op = ExprOp(get()); + switch (op & ~opFlagMask) { + case opFalse: + print("never"); + break; + case opTrue: + print("always"); + break; + case opIdent: + print("identifier "); + data(); + break; + case opAppleAnchor: + print("anchor apple"); + break; + case opAnchorHash: + print("anchor"); certSlot(); print(" = "); hashData(); + break; + case opInfoKeyValue: + if (mDebug) + print("/*legacy*/"); + print("info["); dotString(); print("] = "); data(); + break; + case opAnd: + if (level < slAnd) + print("("); + expr(slAnd); + print(" and "); + expr(slAnd); + if (level < slAnd) + print(")"); + break; + case opOr: + if (level < slOr) + print("("); + expr(slOr); + print(" or "); + expr(slOr); + if (level < slOr) + print(")"); + break; + case opNot: + print("! "); + expr(slPrimary); + break; + case opCDHash: + print(" cdhash "); + hashData(); + break; + case opInfoKeyField: + print("info["); dotString(); print("]"); match(); + break; + case opCertField: + print("certificate"); certSlot(); print("["); dotString(); print("]"); match(); + break; + case opTrustedCert: + print("certificate"); certSlot(); print("trusted"); + break; + case opTrustedCerts: + print("anchor trusted"); + break; + default: + if (op & opGenericFalse) { + print(" false /* opcode %d */", op & ~opFlagMask); + break; + } else if (op & opGenericSkip) { + print(" /* opcode %d */", op & ~opFlagMask); + break; + } else { + print("OPCODE %d NOT UNDERSTOOD (ending print)", op); + return; + } + } +} + +void Dumper::certSlot() +{ + switch (uint32_t slot = get()) { + case Requirement::anchorCert: + print(" root"); + break; + case Requirement::leafCert: + print(" leaf"); + break; + default: + print(" %d", slot); + break; + } +} + +void Dumper::match() +{ + switch (MatchOperation op = MatchOperation(get())) { + case matchExists: + print(" /* exists */"); + break; + case matchEqual: + print(" = "); data(); + break; + case matchContains: + print(" ~ "); data(); + break; + default: + print("MATCH OPCODE %d NOT UNDERSTOOD", op); + break; + } +} + +void Dumper::hashData() +{ + print("H\""); + const unsigned char *data; size_t length; + getData(data, length); + printBytes(data, length); + print("\""); +} + +void Dumper::data(PrintMode bestMode /* = isSimple */, bool dotOkay /* = false */) +{ + const unsigned char *data; size_t length; + getData(data, length); + for (unsigned n = 0; n < length; n++) + if ((isalnum(data[n]) || (data[n] == '.' && dotOkay))) { // simple + if (n == 0 && isdigit(data[n])) // unquoted idents can't start with a digit + bestMode = isPrintable; + } else if (isgraph(data[n]) || isspace(data[n])) { + if (bestMode == isSimple) + bestMode = isPrintable; + } else { + bestMode = isBinary; + break; // pessimal + } + if (length == 0 && bestMode == isSimple) + bestMode = isPrintable; // force quotes for empty string + + switch (bestMode) { + case isSimple: + print("%.*s", length, data); + break; + case isPrintable: + print("\"%.*s\"", length, data); + break; + default: + print("0x"); + printBytes(data, length); + break; + } +} + +void Dumper::printBytes(const Byte *data, size_t length) +{ + for (unsigned n = 0; n < length; n++) + print("%02.2x", data[n]); +} + + +} // CodeSigning +} // Security diff --git a/lib/reqdumper.h b/lib/reqdumper.h new file mode 100644 index 0000000..70c881f --- /dev/null +++ b/lib/reqdumper.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// reqdumper - Requirement un-parsing (disassembly) +// +#ifndef _H_REQDUMPER +#define _H_REQDUMPER + +#include "reqreader.h" +#include + + +namespace Security { +namespace CodeSigning { + + +// +// A decompiler for (compiled) requirements programs. +// This is intended to produce compiler-ready source, and the +// (decompile . compile) cycle is meant to be loss-less. +// +// Note that a Dumper is a type of Interpreter, so it can use the program stream +// accessors of the Interpreter. However, the evaluaton Context is absent, so +// actual validation functions must not be called. +// +class Dumper : public Requirement::Reader { +public: + explicit Dumper(const Requirement *req, bool debug = false) + : Reader(req), mDebug(debug) { } + + enum SyntaxLevel { + slPrimary, // syntax primary + slAnd, // conjunctive + slOr, // disjunctive + slTop // where we start + }; + + void dump(); // decompile this (entire) requirement + void expr(SyntaxLevel level = slTop); // decompile one requirement expression + + std::string value() const { return mOutput; } + operator std::string () const { return value(); } + + typedef unsigned char Byte; + +public: + // all-in-one dumping + static string dump(const Requirements *reqs, bool debug = false); + static string dump(const Requirement *req, bool debug = false); + static string dump(const BlobCore *req, bool debug = false); // dumps either + +protected: + enum PrintMode { + isSimple, // printable and does not require quotes + isPrintable, // can be quoted safely + isBinary // contains binary bytes (use 0xnnn form) + }; + void data(PrintMode bestMode = isSimple, bool dotOkay = false); + void dotString() { data(isSimple, true); } + void quotedString() { data(isPrintable); } + void hashData(); // H"bytes" + void certSlot(); // symbolic certificate slot indicator (explicit) + void match(); // a match suffix (op + value) + + void print(const char *format, ...); + +private: + void printBytes(const Byte *data, size_t length); // just write hex bytes + +private: + std::string mOutput; // output accumulator + bool mDebug; // include debug output in mOutput +}; + + +} // CodeSigning +} // Security + +#endif //_H_REQDUMPER diff --git a/lib/reqinterp.cpp b/lib/reqinterp.cpp new file mode 100644 index 0000000..0c20659 --- /dev/null +++ b/lib/reqinterp.cpp @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// reqinterp - Requirement language (exprOp) interpreter +// +#include "reqinterp.h" +#include +#include +#include +#include // for hex encoding +#include "csutilities.h" + +namespace Security { +namespace CodeSigning { + + +// +// Construct an interpreter given a Requirement and an evaluation context. +// +Requirement::Interpreter::Interpreter(const Requirement *req, const Context *ctx) + : Reader(req), mContext(ctx) +{ +} + + +// +// Main interpreter function. +// +// ExprOp code is in Polish Notation (operator followed by operands), +// and this engine uses opportunistic evaluation. +// +bool Requirement::Interpreter::evaluate() +{ + ExprOp op = ExprOp(get()); + switch (op & ~opFlagMask) { + case opFalse: + return false; + case opTrue: + return true; + case opIdent: + return getString() == mContext->directory->identifier(); + case opAppleAnchor: + if (SecCertificateRef cert = mContext->cert(anchorCert)) + return verifyAnchor(cert, appleAnchorHash()) +#if defined(TEST_APPLE_ANCHOR) + || verifyAnchor(cert, testAppleAnchorHash()) +#endif + ; + else + return false; + case opAnchorHash: + { + SecCertificateRef cert = mContext->cert(get()); + return verifyAnchor(cert, getSHA1()); + } + case opInfoKeyValue: // [legacy; use opInfoKeyField] + { + string key = getString(); + return infoKeyValue(key, Match(CFTempString(getString()), matchEqual)); + } + case opAnd: + return evaluate() && evaluate(); + case opOr: + return evaluate() || evaluate(); + case opCDHash: + { + SHA1 hash; + hash(mContext->directory, mContext->directory->length()); + return hash.verify(getHash()); + } + case opNot: + return !evaluate(); + case opInfoKeyField: + { + string key = getString(); + Match match(*this); + return infoKeyValue(key, match); + } + case opCertField: + { + SecCertificateRef cert = mContext->cert(get()); + string key = getString(); + Match match(*this); + return certFieldValue(key, match, cert); + } + case opTrustedCert: + return trustedCert(get()); + case opTrustedCerts: + return trustedCerts(); + default: + // opcode not recognized - handle generically if possible, fail otherwise + if (op & (opGenericFalse | opGenericSkip)) { + // unknown opcode, but it has a size field and can be safely bypassed + skip(get()); + if (op & opGenericFalse) { + secdebug("csinterp", "opcode 0x%x interpreted as false", op); + return false; + } else { + secdebug("csinterp", "opcode 0x%x ignored; continuing", op); + return evaluate(); + } + } + // unrecognized opcode and no way to interpret it + secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op); + MacOSError::throwMe(errSecCSUnimplemented); + } +} + + +// +// Evaluate an Info.plist key condition +// +bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &match) +{ + if (mContext->info) // we have an Info.plist + if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key))) + return match(value); + return false; +} + + +bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert) +{ + // no cert, no chance + if (cert == NULL) + return false; + + // a table of recognized keys for the "certificate[foo]" syntax + static const struct CertField { + const char *name; + const CSSM_OID *oid; + } certFields[] = { + { "subject.C", &CSSMOID_CountryName }, + { "subject.CN", &CSSMOID_CommonName }, + { "subject.D", &CSSMOID_Description }, + { "subject.L", &CSSMOID_LocalityName }, + { "subject.O", &CSSMOID_OrganizationName }, + { "subject.OU", &CSSMOID_OrganizationalUnitName }, + { "subject.ST", &CSSMOID_StateProvinceName }, + { "subject.STREET", &CSSMOID_StreetAddress }, + { NULL, NULL } + }; + + // email multi-valued match (any of...) + if (key == "email") { + CFRef value; + if (IFDEBUG(OSStatus rc =) SecCertificateCopyEmailAddresses(cert, &value.aref())) { + secdebug("csinterp", "cert %p lookup for email failed rc=%ld", cert, rc); + return false; + } + return match(value); + } + + // DN-component single-value match + for (const CertField *cf = certFields; cf->name; cf++) + if (cf->name == key) { + CFRef value; + if (IFDEBUG(OSStatus rc =) SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref())) { + secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%ld", cert, key.c_str(), rc); + return false; + } + return match(value); + } + + // unrecognized key. Fail but do not abort to promote backward compatibility down the road + secdebug("csinterp", "cert field notation \"%s\" not understood", key.c_str()); + return false; +} + + +// +// Verify an anchor requirement against the context +// +bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsigned char *digest) +{ + // get certificate bytes + if (cert) { + CSSM_DATA certData; + MacOSError::check(SecCertificateGetData(cert, &certData)); + + // verify hash + //@@@ should get SHA1(cert(-1).data) precalculated during chain verification + SHA1 hasher; + hasher(certData.Data, certData.Length); + return hasher.verify(digest); + } + return false; +} + + +// +// Check one or all certificate(s) in the cert chain against the Trust Settings database. +// +bool Requirement::Interpreter::trustedCerts() +{ + int anchor = mContext->certCount() - 1; + for (int slot = 0; slot <= anchor; slot++) + if (SecCertificateRef cert = mContext->cert(slot)) + switch (trustSetting(cert, slot == anchor)) { + case kSecTrustSettingsResultTrustRoot: + case kSecTrustSettingsResultTrustAsRoot: + return true; + case kSecTrustSettingsResultDeny: + return false; + case kSecTrustSettingsResultUnspecified: + break; + default: + assert(false); + return false; + } + else + return false; + return false; +} + +bool Requirement::Interpreter::trustedCert(int slot) +{ + if (SecCertificateRef cert = mContext->cert(slot)) { + int anchorSlot = mContext->certCount() - 1; + switch (trustSetting(cert, slot == anchorCert || slot == anchorSlot)) { + case kSecTrustSettingsResultTrustRoot: + case kSecTrustSettingsResultTrustAsRoot: + return true; + case kSecTrustSettingsResultDeny: + case kSecTrustSettingsResultUnspecified: + return false; + default: + assert(false); + return false; + } + } else + return false; +} + + +// +// Explicitly check one certificate against the Trust Settings database and report +// the findings. This is a helper for the various Trust Settings evaluators. +// +SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor) +{ + // the SPI input is the uppercase hex form of the SHA-1 of the certificate... + assert(cert); + SHA1::Digest digest; + hashOfCertificate(cert, digest); + string Certhex = CssmData(digest, sizeof(digest)).toHex(); + for (string::iterator it = Certhex.begin(); it != Certhex.end(); ++it) + if (islower(*it)) + *it = toupper(*it); + + // call Trust Settings and see what it finds + SecTrustSettingsDomain domain; + SecTrustSettingsResult result; + CSSM_RETURN *errors = NULL; + uint32 errorCount = 0; + bool foundMatch, foundAny; + switch (OSStatus rc = SecTrustSettingsEvaluateCert( + CFTempString(Certhex), // settings index + &CSSMOID_APPLE_TP_CODE_SIGNING, // standard code signing policy + NULL, 0, // policy string (unused) + kSecTrustSettingsKeyUseAny, // no restriction on key usage @@@ + isAnchor, // consult system default anchor set + + &domain, // domain of found setting + &errors, &errorCount, // error set and maximum count + &result, // the actual setting + &foundMatch, &foundAny // optimization hints (not used) + )) { + case noErr: + ::free(errors); + if (foundMatch) + return result; + else + return kSecTrustSettingsResultUnspecified; + default: + ::free(errors); + MacOSError::throwMe(rc); + } +} + + +// +// Create a Match object from the interpreter stream +// +Requirement::Interpreter::Match::Match(Interpreter &interp) +{ + switch (mOp = interp.get()) { + case matchExists: + break; + case matchEqual: + case matchContains: + mValue = makeCFString(interp.getString()); + break; + default: + assert(false); + break; + } +} + + +// +// Execute a match against a candidate value +// +bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const +{ + // null candidates always fail + if (!candidate) + return false; + + // interpret an array as matching alternatives (any one succeeds) + if (CFGetTypeID(candidate) == CFArrayGetTypeID()) { + CFArrayRef array = CFArrayRef(candidate); + CFIndex count = CFArrayGetCount(array); + for (CFIndex n = 0; n < count; n++) + if ((*this)(CFArrayGetValueAtIndex(array, n))) // yes, it's recursive + return true; + } + + switch (mOp) { + case matchExists: // anything but NULL and boolean false "exists" + return !CFEqual(candidate, kCFBooleanFalse); + case matchEqual: // equality works for all CF types + return CFEqual(candidate, mValue); + case matchContains: + if (CFGetTypeID(candidate) == CFStringGetTypeID()) { + CFStringRef value = CFStringRef(candidate); + if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(value)), 0, NULL)) + return true; + } + return false; + default: + assert(false); + return false; + } +} + + +} // CodeSigning +} // Security diff --git a/lib/reqinterp.h b/lib/reqinterp.h new file mode 100644 index 0000000..222bd17 --- /dev/null +++ b/lib/reqinterp.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// reqinterp - Requirement language (exprOp) interpreter +// +#ifndef _H_REQINTERP +#define _H_REQINTERP + +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// An interpreter for exprForm-type requirements. +// This is a simple Polish Notation stack evaluator. +// +class Requirement::Interpreter : public Requirement::Reader { +public: + Interpreter(const Requirement *req, const Context *ctx); + + bool evaluate(); + +protected: + class Match { + public: + Match(Interpreter &interp); // reads match postfix from interp + Match(CFStringRef value, MatchOperation op) : mValue(value), mOp(op) { } // explicit + bool operator () (CFTypeRef candidate) const; // match to candidate + + private: + CFCopyRef mValue; // match value + MatchOperation mOp; // type of match + }; + +protected: + bool infoKeyValue(const std::string &key, const Match &match); + bool certFieldValue(const string &key, const Match &match, SecCertificateRef cert); + bool verifyAnchor(SecCertificateRef cert, const unsigned char *digest); + bool trustedCerts(); + bool trustedCert(int slot); + + SecTrustSettingsResult trustSetting(SecCertificateRef cert, bool isAnchor); + +private: + const Context * const mContext; +}; + + +} // CodeSigning +} // Security + +#endif //_H_REQINTERP diff --git a/lib/reqmaker.cpp b/lib/reqmaker.cpp new file mode 100644 index 0000000..e4cd628 --- /dev/null +++ b/lib/reqmaker.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// reqmaker - Requirement assembler +// +#include "reqmaker.h" + +namespace Security { +namespace CodeSigning { + + +// +// Requirement::Makers +// +Requirement::Maker::Maker(Kind k) + : mSize(1024) +{ + mBuffer = (Requirement *)malloc(mSize); + mBuffer->initialize(); + mBuffer->kind(k); + mPC = sizeof(Requirement); +} + +// need at least (size) bytes in the creation buffer +void Requirement::Maker::require(size_t size) +{ + if (mPC + size > mSize) { + mSize *= 2; + if (mPC + size > mSize) + mSize = mPC + size; + if (!(mBuffer = (Requirement *)realloc(mBuffer, mSize))) + UnixError::throwMe(ENOMEM); + } +} + +// allocate (size) bytes at end of buffer and return pointer to that +void *Requirement::Maker::alloc(size_t size) +{ + // round size up to preserve alignment + size_t usedSize = LowLevelMemoryUtilities::alignUp(size, baseAlignment); + require(usedSize); + void *data = mBuffer->at(mPC); + mPC += usedSize; + + // clear any padding (avoid random bytes in code image) + const uint32_t zero = 0; + memcpy(mBuffer->at(mPC - usedSize + size), &zero, usedSize - size); + + // all done + return data; +} + +// put contiguous data blob +void Requirement::Maker::putData(const void *data, size_t length) +{ + put(uint32_t(length)); + memcpy(alloc(length), data, length); +} + +// Specialized Maker put operations +void Requirement::Maker::anchor() +{ + put(opAppleAnchor); +} + +void Requirement::Maker::anchor(int slot, SHA1::Digest digest) +{ + put(opAnchorHash); + put(slot); + putData(digest, SHA1::digestLength); +} + +void Requirement::Maker::anchor(int slot, const void *cert, size_t length) +{ + SHA1 hasher; + hasher(cert, length); + SHA1::Digest digest; + hasher.finish(digest); + anchor(slot, digest); +} + +void Requirement::Maker::trustedAnchor() +{ + put(opTrustedCerts); +} + +void Requirement::Maker::trustedAnchor(int slot) +{ + put(opTrustedCert); + put(slot); +} + +void Requirement::Maker::infoKey(const string &key, const string &value) +{ + put(opInfoKeyValue); + put(key); + put(value); +} + +void Requirement::Maker::ident(const string &identifier) +{ + put(opIdent); + put(identifier); +} + +void Requirement::Maker::cdhash(SHA1::Digest digest) +{ + put(opCDHash); + putData(digest, SHA1::digestLength); +} + + +void *Requirement::Maker::insert(const Label &label, size_t length) +{ + require(length); + memmove(mBuffer->at(label.pos + length), + mBuffer->at(label.pos), mPC - label.pos); + mPC += length; + return mBuffer->at(label.pos); +} + + +Requirement *Requirement::Maker::make() +{ + mBuffer->length(mPC); + Requirement *result = mBuffer; + mBuffer = NULL; + return result; +} + + +} // CodeSigning +} // Security diff --git a/lib/reqmaker.h b/lib/reqmaker.h new file mode 100644 index 0000000..9a72e8b --- /dev/null +++ b/lib/reqmaker.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// reqmaker - Requirement assembler +// +#ifndef _H_REQMAKER +#define _H_REQMAKER + +#include + +namespace Security { +namespace CodeSigning { + + +// +// A Requirement::Maker is a tool for creating a Requirement blob. +// It's primarily an assember for the binary requirements (exprOp) language. +// Initialize it, call put() methods to generate the exprOp program, then +// call make() to get the assembled Requirement blob, malloc'ed for you. +// The Maker is not reusable. +// +class Requirement::Maker { +public: + Maker(Kind k = exprForm); + ~Maker() { free(mBuffer); } + + template + T *alloc(size_t size) { return reinterpret_cast(alloc(size)); } + + template + void put(const T &value) { *alloc >(sizeof(T)) = value; } + void put(ExprOp op) { put(uint32_t(op)); } + void put(MatchOperation op) { put(uint32_t(op)); } + void put(const std::string &s) { putData(s.data(), s.size()); } + void put(const char *s) { putData(s, strlen(s)); } + void putData(const void *data, size_t length); + + void anchor(int slot, SHA1::Digest digest); // given slot/digest + void anchor(int slot, const void *cert, size_t length); // given slot/cert + void anchor(); // canonical Apple anchor + + void trustedAnchor(); + void trustedAnchor(int slot); + + void infoKey(const std::string &key, const std::string &value); + void ident(const std::string &identHash); + void cdhash(SHA1::Digest digest); + + // + // Keep labels into exprOp code, and allow for "shifting in" + // prefix code as needed (exprOp is a prefix-code language). + // + struct Label { + const Offset pos; + Label(const Maker &maker) : pos(maker.length()) { } + }; + void *insert(const Label &label, size_t length = sizeof(uint32_t)); + + template + Endian &insert(const Label &label, size_t length = sizeof(T)) + { return *reinterpret_cast*>(insert(label, length)); } + + void kind(Kind k) { mBuffer->kind(k); } + size_t length() const { return mPC; } + Requirement *make(); + Requirement *operator () () { return make(); } + +protected: + void require(size_t size); + void *alloc(size_t size); + +private: + Requirement *mBuffer; + Offset mSize; + Offset mPC; +}; + + +} // CodeSigning +} // Security + +#endif //_H_REQMAKER diff --git a/lib/reqparser.cpp b/lib/reqparser.cpp new file mode 100644 index 0000000..e419f35 --- /dev/null +++ b/lib/reqparser.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// reqparser - interface to Requirement language parser/compiler +// +#include "reqparser.h" +#include "antlrplugin.h" +#include "cserror.h" +#include +#include + +namespace Security { +namespace CodeSigning { + + +struct PluginHost { + PluginHost(); + RefPointer plugin; + AntlrPlugin *antlr; +}; + +ModuleNexus plugin; + + +// +// The PluginHost constructor runs under the protection of ModuleNexus's constructor, +// so it doesn't have to worry about thread safety and such. +// +PluginHost::PluginHost() +{ + if (CFBundleRef securityFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"))) + if (CFURLRef plugins = CFBundleCopyBuiltInPlugInsURL(securityFramework)) + if (CFRef pluginURL = CFURLCreateWithFileSystemPathRelativeToBase(NULL, + CFSTR("csparser.bundle"), kCFURLPOSIXPathStyle, true, plugins)) { + secdebug("antlrplugin", "loading antlr parser plugin from %s", cfString(pluginURL).c_str()); + plugin = new LoadableBundle(cfString(pluginURL).c_str()); + plugin->load(); + antlr = reinterpret_cast(plugin->lookupSymbol(FINDANTLRPLUGIN))(); + return; + } + + // can't load plugin - fail + MacOSError::throwMe(errSecCSInternalError); +} + + +// +// Drive a parsing function through the plugin harness and translate any errors +// into a CFError exception. +// +template +const Result *parse(Source source, const Result *(*AntlrPlugin::*func)(Source, string &)) +{ + string errors; + if (const Result *result = (plugin().antlr->*func)(source, errors)) + return result; + else + CSError::throwMe(errSecCSReqInvalid, kSecCFErrorRequirementSyntax, CFTempString(errors)); +} + + +// +// Implement the template instances by passing them through the plugin's eye-of-the-needle. +// Any other combination of input and output types will cause linker errors. +// +template <> +const Requirement *RequirementParser::operator () (std::FILE *source) +{ + return parse(source, &AntlrPlugin::fileRequirement); +} + +template <> +const Requirement *RequirementParser::operator () (const std::string &source) +{ + return parse(source, &AntlrPlugin::stringRequirement); +} + +template <> +const Requirements *RequirementParser::operator () (std::FILE *source) +{ + return parse(source, &AntlrPlugin::fileRequirements); +} + +template <> +const Requirements *RequirementParser::operator () (const std::string &source) +{ + return parse(source, &AntlrPlugin::stringRequirements); +} + +template <> +const BlobCore *RequirementParser::operator () (std::FILE *source) +{ + return parse(source, &AntlrPlugin::fileGeneric); +} + +template <> +const BlobCore *RequirementParser::operator () (const std::string &source) +{ + return parse(source, &AntlrPlugin::stringGeneric); +} + + +} // CodeSigning +} // Security diff --git a/lib/reqparser.h b/lib/reqparser.h new file mode 100644 index 0000000..e4f85e8 --- /dev/null +++ b/lib/reqparser.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// reqparser - interface to Requirement language parser/compiler +// +#ifndef _H_REQPARSER +#define _H_REQPARSER + +#include "requirement.h" + +namespace Security { +namespace CodeSigning { + + +// +// Generic parser interface +// +template +class RequirementParser { +public: + const ReqType *operator () (std::FILE *file); + const ReqType *operator () (const std::string &text); +}; + + +// +// Specifics for easier readability +// +template +inline const Requirement *parseRequirement(const Input &source) +{ return RequirementParser()(source); } + +template +inline const Requirements *parseRequirements(const Input &source) +{ return RequirementParser()(source); } + +template +inline const Requirements *parseGeneric(const Input &source) +{ return RequirementParser()(source); } + + +} // CodeSigning +} // Security + +#endif //_H_REQPARSER diff --git a/lib/reqreader.cpp b/lib/reqreader.cpp new file mode 100644 index 0000000..066517f --- /dev/null +++ b/lib/reqreader.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// reqreader - Requirement language (exprOp) reader/scanner +// +#include "reqreader.h" +#include +#include +#include // for hex encoding +#include "csutilities.h" + +namespace Security { +namespace CodeSigning { + + +// +// Requirement::Reader +// +Requirement::Reader::Reader(const Requirement *req) + : mReq(req), mPC(sizeof(Requirement)) +{ +} + + +// +// Access helpers to retrieve various data types from the data stream +// +void Requirement::Reader::getData(const void *&data, size_t &length) +{ + length = get(); + checkSize(length); + data = (mReq->at(mPC)); + mPC += LowLevelMemoryUtilities::alignUp(length, baseAlignment); +} + +string Requirement::Reader::getString() +{ + const char *s; size_t length; + getData(s, length); + return string(s, length); +} + +const unsigned char *Requirement::Reader::getHash() +{ + const unsigned char *s; size_t length; + getData(s, length); + if (length != SHA1::digestLength) + MacOSError::throwMe(errSecCSReqInvalid); + return s; +} + +const unsigned char *Requirement::Reader::getSHA1() +{ + const unsigned char *digest; size_t length; + getData(digest, length); + if (length != CC_SHA1_DIGEST_LENGTH) + MacOSError::throwMe(errSecCSReqInvalid); + return digest; +} + +void Requirement::Reader::skip(size_t length) +{ + checkSize(length); + mPC += length; +} + + +} // CodeSigning +} // Security diff --git a/lib/reqreader.h b/lib/reqreader.h new file mode 100644 index 0000000..4a7a251 --- /dev/null +++ b/lib/reqreader.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// reqreader - Requirement language (exprOp) reader/scanner +// +#ifndef _H_REQREADER +#define _H_REQREADER + +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// The Reader class provides structured access to a opExpr-type code requirement. +// +class Requirement::Reader { +public: + Reader(const Requirement *req); + + const Requirement *requirement() const { return mReq; } + + template T get(); + void getData(const void *&data, size_t &length); + + std::string getString(); + const unsigned char *getHash(); + const unsigned char *getSHA1(); + + template void getData(T *&data, size_t &length) + { return getData(reinterpret_cast(data), length); } + +protected: + void checkSize(size_t length) + { + if (mPC + length > mReq->length()) + MacOSError::throwMe(errSecCSReqInvalid); + } + + void skip(size_t length); + + Offset pc() const { return mPC; } + bool atEnd() const { return mPC >= mReq->length(); } + +private: + const Requirement * const mReq; + Offset mPC; +}; + +template +T Requirement::Reader::get() +{ + checkSize(sizeof(T)); + const Endian *value = mReq->at >(mPC); + mPC += sizeof(value); + return *value; +} + + +} // CodeSigning +} // Security + +#endif //_H_REQREADER diff --git a/lib/requirement.cpp b/lib/requirement.cpp new file mode 100644 index 0000000..f4b294a --- /dev/null +++ b/lib/requirement.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// requirement - Code Requirement Blob description +// +#include "requirement.h" +#include "reqinterp.h" +#include +#include +#include +#include +#include + +#ifdef DEBUGDUMP +#include +#endif + +namespace Security { +namespace CodeSigning { + + +// +// The (SHA-1) hash of the canonical Apple certificate root anchor +// +static const SHA1::Digest gAppleAnchorHash = + { 0x61, 0x1e, 0x5b, 0x66, 0x2c, 0x59, 0x3a, 0x08, 0xff, 0x58, + 0xd1, 0x4a, 0xe2, 0x24, 0x52, 0xd1, 0x98, 0xdf, 0x6c, 0x60 }; + + +// +// Canonical names for requirement types +// +const char *const Requirement::typeNames[] = { + "invalid", + "host", + "guest", + "designated", + "library" +}; + + +// +// validate a requirement against a code context +// +void Requirement::validate(const Requirement::Context &ctx, OSStatus failure /* = errSecCSReqFailed */) const +{ + switch (kind()) { + case exprForm: + if (!Requirement::Interpreter(this, &ctx).evaluate()) + MacOSError::throwMe(failure); + return; + default: + secdebug("reqval", "unrecognized requirement kind %d", kind()); + MacOSError::throwMe(errSecCSReqUnsupported); + } +} + + +// +// Retrieve one certificate from the cert chain. +// Positive and negative indices can be used: +// [ leaf, intermed-1, ..., intermed-n, anchor ] +// 0 1 ... -2 -1 +// Returns NULL if unavailable for any reason. +// +SecCertificateRef Requirement::Context::cert(int ix) const +{ + if (certs) { + if (ix < 0) + ix += certCount(); + if (CFTypeRef element = CFArrayGetValueAtIndex(certs, ix)) + return SecCertificateRef(element); + } + return NULL; +} + +unsigned int Requirement::Context::certCount() const +{ + if (certs) + return CFArrayGetCount(certs); + else + return 0; +} + + +// +// Return the hash of the canonical Apple certificate root (anchor). +// In a special test mode, also return an alternate root hash for testing. +// +const SHA1::Digest &Requirement::appleAnchorHash() +{ + return gAppleAnchorHash; +} + +#if defined(TEST_APPLE_ANCHOR) + +const char Requirement::testAppleAnchorEnv[] = "TEST_APPLE_ANCHOR"; + +const SHA1::Digest &Requirement::testAppleAnchorHash() +{ + static bool tried = false; + static SHA1::Digest testHash; + if (!tried) { + // see if we have one configured + if (const char *path = getenv(testAppleAnchorEnv)) + try { + UnixPlusPlus::FileDesc fd(path); + char buffer[2048]; // arbitrary limit + size_t size = fd.read(buffer, sizeof(buffer)); + SHA1 hash; + hash(buffer, size); + hash.finish(testHash); + Syslog::alert("ACCEPTING TEST AUTHORITY %s FOR APPLE CODE IDENTITY", path); + } catch (...) { } + tried = true; + } + return testHash; // will be zeroes (no match) if not configured +} + +#endif //TEST_APPLE_ANCHOR + + + +// +// Debug dump support +// +#ifdef DEBUGDUMP + +void Requirement::dump() const +{ + Debug::dump("%s\n", Dumper::dump(this).c_str()); +} + +#endif //DEBUGDUMP + + +} // CodeSigning +} // Security diff --git a/lib/requirement.h b/lib/requirement.h new file mode 100644 index 0000000..41871e0 --- /dev/null +++ b/lib/requirement.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// requirement - Code Requirement Blob description +// +#ifndef _H_REQUIREMENT +#define _H_REQUIREMENT + +#include +#include +#include +#include +#include "codedirectory.h" +#include + +namespace Security { +namespace CodeSigning { + + +// +// Single requirement. +// This is a contiguous binary blob, starting with this header +// and followed by binary expr-code. All links within the blob +// are offset-relative to the start of the header. +// This is designed to be a binary stable format. Note that we restrict +// outselves to 4GB maximum size (4 byte size/offset), and we expect real +// Requirement blobs to be fairly small (a few kilobytes at most). +// +// The "kind" field allows for adding different kinds of Requirements altogether +// in the future. We expect to stay within the framework of "opExpr" requirements, +// but it never hurts to have a way out. +// +class Requirement: public Blob { +public: + class Maker; // makes Requirement blobs + class Context; // evaluation context + class Reader; // structured reader + class Interpreter; // evaluation engine + + // different forms of Requirements. Right now, we only support exprForm ("opExprs") + enum Kind { + exprForm = 1 // postfix expr form + }; + + void kind(Kind k) { mKind = k; } + Kind kind() const { return Kind(uint32_t(mKind)); } + + // validate this requirement against a code context + void validate(const Context &ctx, OSStatus failure = errSecCSReqFailed) const; + + // certificate positions (within a standard certificate chain) + static const int leafCert = 0; // index for leaf (first in chain) + static const int anchorCert = -1; // index for anchor (last in chain) + + // the SHA1 hash of the canonical "Apple Anchor", i.e. the X509 Anchor + // that is considered "Apple's anchor certificate", as defined by hashOfCertificate(). + static const SHA1::Digest &appleAnchorHash(); +#if defined(TEST_APPLE_ANCHOR) + static const char testAppleAnchorEnv[]; + static const SHA1::Digest &testAppleAnchorHash(); +#endif //TEST_APPLE_ANCHOR + + // common alignment rule for all requirement forms + static const size_t baseAlignment = sizeof(uint32_t); // (we might as well say "four") + + // canonical (source) names of Requirement types (matched to SecRequirementType in CSCommon.h) + static const char *const typeNames[]; + + IFDUMP(void dump() const); + +private: + Endian mKind; // expression kind +}; + + +// +// An interpretation context +// +struct Requirement::Context { + Context(CFArrayRef certChain, CFDictionaryRef infoDict, const CodeDirectory *dir) + : certs(certChain), info(infoDict), directory(dir) { } + + const CFArrayRef certs; + const CFDictionaryRef info; + const CodeDirectory * const directory; + + SecCertificateRef cert(int ix) const; // get a cert from the cert chain + unsigned int certCount() const; // length of cert chain +}; + + +// +// exprForm opcodes. +// +// Opcodes are broken into flags in the (HBO) high byte, and an opcode value +// in the remaining 24 bits. Note that opcodes will remain fairly small +// (almost certainly <60000), so we have the third byte to play around with +// in the future, if needed. For now, small opcodes effective reserve this byte +// as zero. +// The flag byte allows for limited understanding of unknown opcodes. It allows +// the interpreter to use the known opcode parts of the program while semi-creatively +// disregarding the parts it doesn't know about. An unrecognized opcode with zero +// flag byte causes evaluation to categorically fail, since the semantics of such +// an opcode cannot safely be predicted. +// +enum { + // semantic bits or'ed into the opcode + opFlagMask = 0xFF000000, // high bit flags + opGenericFalse = 0x80000000, // has size field; okay to default to false + opGenericSkip = 0x40000000, // has size field; skip and continue +}; + +enum ExprOp { + opFalse, // unconditionally false + opTrue, // unconditionally true + opIdent, // match canonical code [string] + opAppleAnchor, // match apple anchor + opAnchorHash, // match anchor [cert hash] + opInfoKeyValue, // *legacy* match Info.plist field [key; value] + opAnd, // binary prefix expr AND expr + opOr, // binary prefix expr OR expr + opCDHash, // match hash of CodeDirectory directly + opNot, // logical inverse + opInfoKeyField, // Info.plist key field [string; match suffix] + opCertField, // Certificate field [cert index; field name; match suffix] + opTrustedCert, // require trust settings to approve one particular cert [cert index] + opTrustedCerts, // require trust settings to approve the cert chain + exprOpCount // (total opcode count in use) +}; + +// match suffix opcodes +enum MatchOperation { + matchExists, // anything but explicit "false" - no value stored + matchEqual, // equal (CFEqual) + matchContains, // partial match (substring) +}; + + +// +// We keep Requirement groups in SuperBlobs, indexed by SecRequirementType +// +typedef SuperBlob<0xfade0c01> Requirements; + + +} // CodeSigning + + +// +// Flipper overloads must go directly into the Security namespace +// +inline CodeSigning::ExprOp h2n(CodeSigning::ExprOp op) { return CodeSigning::ExprOp(h2n(uint32_t(op))); } +inline CodeSigning::ExprOp n2h(CodeSigning::ExprOp op) { return CodeSigning::ExprOp(n2h(uint32_t(op))); } +inline CodeSigning::MatchOperation h2n(CodeSigning::MatchOperation op) { return CodeSigning::MatchOperation(h2n(uint32_t(op))); } +inline CodeSigning::MatchOperation n2h(CodeSigning::MatchOperation op) { return CodeSigning::MatchOperation(n2h(uint32_t(op))); } + + +} // Security + +#endif //_H_REQUIREMENT diff --git a/lib/resources.cpp b/lib/resources.cpp new file mode 100644 index 0000000..a98f924 --- /dev/null +++ b/lib/resources.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// resource directory construction and verification +// +#include "resources.h" +#include "renum.h" +#include + +namespace Security { +namespace CodeSigning { + + +// +// Construction and maintainance +// +ResourceBuilder::ResourceBuilder(const std::string &root, CFDictionaryRef rulesDict) + : ResourceEnumerator(root) +{ + CFDictionary rules(rulesDict, errSecCSResourceRulesInvalid); + rules.apply(this, &ResourceBuilder::addRule); + mRawRules = rules; +} + +ResourceBuilder::~ResourceBuilder() +{ + for (Rules::iterator it = mRules.begin(); it != mRules.end(); ++it) + delete *it; +} + + +// +// Parse and add one matching rule +// +void ResourceBuilder::addRule(CFTypeRef key, CFTypeRef value) +{ + string pattern = cfString(key, errSecCSResourceRulesInvalid); + unsigned weight = 1; + uint32_t flags = 0; + if (CFGetTypeID(value) == CFBooleanGetTypeID()) { + if (value == kCFBooleanFalse) + flags |= omitted; + } else { + CFDictionary rule(value, errSecCSResourceRulesInvalid); + if (CFNumberRef weightRef = rule.get("weight")) + weight = cfNumber(weightRef); + if (CFBooleanRef omitRef = rule.get("omit")) + if (omitRef == kCFBooleanTrue) + flags |= omitted; + if (CFBooleanRef optRef = rule.get("optional")) + if (optRef == kCFBooleanTrue) + flags |= optional; + } + addRule(new Rule(pattern, weight, flags)); +} + + +// +// Locate the next non-ignored file, look up its rule, and return it. +// Returns NULL when we're out of files. +// +FTSENT *ResourceBuilder::next(string &path, Rule * &rule) +{ + while (FTSENT *ent = ResourceEnumerator::next(path)) { + // find best matching rule + Rule *bestRule = NULL; + for (Rules::const_iterator it = mRules.begin(); it != mRules.end(); ++it) { + Rule *rule = *it; + if (rule->match(path.c_str())) + if (!bestRule || rule->weight > bestRule->weight) + bestRule = rule; + } + if (!bestRule || (bestRule->flags & omitted)) + continue; + rule = bestRule; + return ent; + } + return NULL; +} + + +// +// Build the ResourceDirectory given the currently established rule set. +// +CFDictionaryRef ResourceBuilder::build() +{ + secdebug("codesign", "start building resource directory"); + CFRef files = makeCFMutableDictionary(0); + + string path; + Rule *rule; + while (FTSENT *ent = next(path, rule)) { + assert(rule); + CFRef hash = hashFile(ent->fts_accpath); + if (rule->flags == 0) { // default case - plain hash + cfadd(files, "{%s=%O}", path.c_str(), hash.get()); + secdebug("csresource", "%s added simple (rule %p)", path.c_str(), rule); + } else { // more complicated - use a sub-dictionary + cfadd(files, "{%s={hash=%O,optional=%B}}", + path.c_str(), hash.get(), rule->flags & optional); + secdebug("csresource", "%s added complex (rule %p)", path.c_str(), rule); + } + } + secdebug("codesign", "finished code directory with %d entries", + int(CFDictionaryGetCount(files))); + + return makeCFDictionary(2, + CFSTR("rules"), mRawRules.get(), + CFSTR("files"), files.get() + ); +} + + +// +// Hash a file and return a CFDataRef with the hash +// +CFDataRef ResourceBuilder::hashFile(const char *path) +{ + CFRef data = cfLoadFile(path); + secdebug("rdirenum", " %s (%d bytes)", path, int(CFDataGetLength(data))); + SHA1 hasher; + hasher(CFDataGetBytePtr(data), CFDataGetLength(data)); + unsigned char digest[CC_SHA1_DIGEST_LENGTH]; + hasher.finish(digest); + return CFDataCreate(NULL, digest, sizeof(digest)); +} + + +// +// Regex matching objects +// +ResourceBuilder::Rule::Rule(const std::string &pattern, unsigned w, uint32_t f) + : weight(w), flags(f) +{ + if (::regcomp(this, pattern.c_str(), REG_EXTENDED | REG_NOSUB)) //@@@ REG_ICASE? + MacOSError::throwMe(errSecCSResourceRulesInvalid); + secdebug("csresource", "%p rule %s added (weight %d, flags 0x%x)", + this, pattern.c_str(), w, f); +} + +ResourceBuilder::Rule::~Rule() +{ + ::regfree(this); +} + +bool ResourceBuilder::Rule::match(const char *s) const +{ + switch (::regexec(this, s, 0, NULL, 0)) { + case 0: + return true; + case REG_NOMATCH: + return false; + default: + MacOSError::throwMe(errSecCSResourceRulesInvalid); + } +} + + +// +// Resource Seals +// +ResourceSeal::ResourceSeal(CFTypeRef it) +{ + if (it == NULL) + MacOSError::throwMe(errSecCSResourcesInvalid); + if (CFGetTypeID(it) == CFDataGetTypeID()) { + mHash = CFDataRef(it); + mOptional = false; + } else { + mOptional = false; + if (!cfscan(it, "{hash=%XO,?optional=%B}", &mHash, &mOptional) + || size_t(CFDataGetLength(mHash)) != SHA1::digestLength) + MacOSError::throwMe(errSecCSResourcesInvalid); + } +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/resources.h b/lib/resources.h new file mode 100644 index 0000000..35ee00a --- /dev/null +++ b/lib/resources.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// resource directory construction and verification +// +#ifndef _H_RSIGN +#define _H_RSIGN + +#include "CodeSigner.h" +#include "renum.h" +#include +#include "regex.h" +#include + +namespace Security { +namespace CodeSigning { + + +// +// The builder of ResourceDirectories. +// +// Note that this *is* a ResourceEnumerate, which can enumerate +// its source directory once (only). +// +class ResourceBuilder : public ResourceEnumerator { +public: + ResourceBuilder(const std::string &root, CFDictionaryRef rules); + ~ResourceBuilder(); + + CFDictionaryRef build(); + + enum Action { + optional = 0x01, // may be absent at runtime + omitted = 0x02, // do not seal even if present + }; + + typedef unsigned int Weight; + +public: + class Rule : private regex_t { + public: + Rule(const std::string &pattern, Weight weight, uint32_t flags); + ~Rule(); + + bool match(const char *s) const; + + const Weight weight; + const uint32_t flags; + }; + void addRule(Rule *rule) { mRules.push_back(rule); } + + FTSENT *next(std::string &path, Rule * &rule); // enumerate next file and match rule + +protected: + void addRule(CFTypeRef key, CFTypeRef value); + CFDataRef hashFile(const char *path); + +private: + CFCopyRef mRawRules; + typedef std::vector Rules; + Rules mRules; +}; + + +// +// The "seal" on a single resource. +// +class ResourceSeal { +public: + ResourceSeal(CFTypeRef ref); + +public: + operator bool () const { return mHash; } + bool operator ! () const { return mHash == NULL; } + + const SHA1::Byte *hash() const { return CFDataGetBytePtr(mHash); } + bool optional() const { return mOptional; } + +private: + CFDataRef mHash; + int mOptional; +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_RSIGN diff --git a/lib/security_codesigning.exp b/lib/security_codesigning.exp new file mode 100644 index 0000000..6109eb5 --- /dev/null +++ b/lib/security_codesigning.exp @@ -0,0 +1,93 @@ +# +# Copyright (c) 2006-2007 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@ +# +_SecCodeGetTypeID +_SecGetRootCode +_SecCodeCopySelf +_SecCodeCopyStaticCode +_SecCodeCopyHost +_SecCodeCopyGuestWithAttributes +_SecCodeCreateWithPID +_SecCodeCheckValidity +_SecCodeCheckValidityWithErrors +_SecCodeCopyPath +_SecCodeCopyDesignatedRequirement +_SecCodeCopySigningInformation +_SecCodeMapMemory +_SecCodeSetDetachedSignature +_SecStaticCodeGetTypeID +_SecStaticCodeCreateWithPath +_SecStaticCodeCheckValidity +_SecStaticCodeCheckValidityWithErrors +_SecRequirementGetTypeID +_SecRequirementCreateWithData +_SecRequirementCreateWithResource +_SecRequirementCreateWithString +_SecRequirementCreateWithStringAndErrors +_SecRequirementCreateGroup +_SecRequirementCopyData +_SecRequirementCopyString +_SecCodeSignerGetTypeID +_SecCodeSignerCreate +_SecCodeSignerAddSignature +_SecCodeSignerAddSignatureWithErrors +_SecHostCreateGuest +_SecHostRemoveGuest +_SecHostSetGuestStatus +_SecHostSelectGuest +_SecHostSelectedGuest +_SecHostSetHostingPort +_kSecCodeSignerApplicationData +_kSecCodeSignerDetached +_kSecCodeSignerDryRun +_kSecCodeSignerFlags +_kSecCodeSignerIdentifier +_kSecCodeSignerIdentifierPrefix +_kSecCodeSignerIdentity +_kSecCodeSignerPageSize +_kSecCodeSignerRequirements +_kSecCodeSignerResourceRules +_kSecCodeSignerSigningTime +_kSecCodeInfoCertificates +_kSecCodeInfoChangedFiles +_kSecCodeInfoCMS +_kSecCodeInfoTime +_kSecCodeInfoDesignatedRequirement +_kSecCodeInfoFormat +_kSecCodeInfoIdentifier +_kSecCodeInfoImplicitDesignatedRequirement +_kSecCodeInfoMainExecutable +_kSecCodeInfoPList +_kSecCodeInfoRequirements +_kSecCodeInfoStatus +_kSecCodeInfoTrust +_kSecGuestAttributePid +_kSecGuestAttributeCanonical +_kSecGuestAttributeMachPort +_kSecCFErrorPattern +_kSecCFErrorResourceSeal +_kSecCFErrorResourceAdded +_kSecCFErrorResourceAltered +_kSecCFErrorResourceMissing +_kSecCFErrorInfoPlist +_kSecCFErrorGuestAttributes +_kSecCFErrorRequirementSyntax diff --git a/lib/sigblob.cpp b/lib/sigblob.cpp new file mode 100644 index 0000000..bdf6a0b --- /dev/null +++ b/lib/sigblob.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// sigblob - signature (Super)Blob types +// +#include "sigblob.h" + + +namespace Security { +namespace CodeSigning { + + +CFDataRef EmbeddedSignatureBlob::component(CodeDirectory::SpecialSlot slot) const +{ + if (const BlobCore *blob = this->find(slot)) + if (CodeDirectory::slotAttributes(slot) & cdComponentIsBlob) + return makeCFData(*blob); // is a native Blob + else + return makeCFData(*BlobWrapper::specific(blob)); // unwrap payload + return NULL; +} + + +void EmbeddedSignatureBlob::Maker::component(CodeDirectory::SpecialSlot slot, CFDataRef data) +{ + if (CodeDirectory::slotAttributes(slot) & cdComponentIsBlob) + add(slot, reinterpret_cast(CFDataGetBytePtr(data))->clone()); + else + add(slot, BlobWrapper::alloc(CFDataGetBytePtr(data), CFDataGetLength(data))); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/sigblob.h b/lib/sigblob.h new file mode 100644 index 0000000..01f73d8 --- /dev/null +++ b/lib/sigblob.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// sigblob - signature (Super)Blob types +// +#ifndef _H_SIGBLOB +#define _H_SIGBLOB + +#include "codedirectory.h" +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// An EmbeddedSignatureBlob is a SuperBlob indexed by component slot number. +// This is what we embed in Mach-O images. It is also what we use for detached +// signatures for non-Mach-O binaries. +// +class EmbeddedSignatureBlob : public SuperBlobCore { + typedef SuperBlobCore _Core; +public: + CFDataRef component(CodeDirectory::SpecialSlot slot) const; + + class Maker : public _Core::Maker { + public: + void component(CodeDirectory::SpecialSlot type, CFDataRef data); + }; +}; + + +// +// A DetachedSignatureBlob collects multiple architectures' worth of +// EmbeddedSignatureBlobs into one, well, Super-SuperBlob. +// This is what we use for Mach-O detached signatures. +// +typedef SuperBlob<0xfade0cc1> DetachedSignatureBlob; // indexed by main architecture + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_SIGBLOB diff --git a/lib/signer.cpp b/lib/signer.cpp new file mode 100644 index 0000000..d3f964c --- /dev/null +++ b/lib/signer.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// signer - Signing operation supervisor and controller +// +#include "signer.h" +#include "resources.h" +#include "signerutils.h" +#include "SecCodeSigner.h" +#include +#include +#include +#include "renum.h" +#include +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// Sign some code. +// +void SecCodeSigner::Signer::sign(SecCSFlags flags) +{ + // set up access to the subject Code + rep = code->diskRep(); + + // get the Info.plist out of the rep for some creative defaulting + CFRef infoDict; + if (CFRef infoData = rep->component(cdInfoSlot)) + infoDict.take(makeCFDictionaryFrom(infoData)); + + // work out the canonical identifier + identifier = state.mIdentifier; + if (identifier.empty()) { + identifier = rep->recommendedIdentifier(); + if (identifier.find('.') == string::npos && !state.mIdentifierPrefix.empty()) + identifier = state.mIdentifierPrefix + identifier; + secdebug("signer", "using default identifier=%s", identifier.c_str()); + } else + secdebug("signer", "using explicit identifier=%s", identifier.c_str()); + + // work out the CodeDirectory flags word + if (state.mCdFlagsGiven) { + cdFlags = state.mCdFlags; + secdebug("signer", "using explicit cdFlags=0x%x", cdFlags); + } else { + cdFlags = 0; + if (infoDict) + if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) + if (CFGetTypeID(csflags) == CFNumberGetTypeID()) { + cdFlags = cfNumber(CFNumberRef(csflags)); + secdebug("signer", "using numeric cdFlags=0x%x from Info.dict", cdFlags); + } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) { + cdFlags = CodeDirectory::textFlags(cfString(CFStringRef(csflags))); + secdebug("signer", "using text cdFlags=0x%x from Info.dict", cdFlags); + } else + MacOSError::throwMe(errSecCSBadDictionaryFormat); + } + if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested... + cdFlags |= kSecCodeSignatureAdhoc; // ... so allow that + + // prepare the resource directory, if any + string rpath = rep->resourcesRootPath(); + if (!rpath.empty()) { + CFCopyRef resourceRules(state.mResourceRules); + if (!resourceRules) + resourceRules.take(rep->defaultResourceRules()); + ResourceBuilder resources(rpath, cfget(resourceRules, "rules")); + CFRef rdir = resources.build(); + resourceDirectory.take(CFPropertyListCreateXMLData(NULL, rdir)); + } + + // screen and set the signing time + CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); + if (state.mSigningTime == CFDateRef(kCFNull)) { + signingTime = 0; // no time at all + } else if (!state.mSigningTime) { + signingTime = now; // default + } else { + CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime); + if (time > now) // not allowed to post-date a signature + MacOSError::throwMe(errSecCSBadDictionaryFormat); + signingTime = time; + } + + pagesize = state.mPageSize ? cfNumber(state.mPageSize) : rep->pageSize(); + + if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) { + signMachO(fat); + } else { + signArchitectureAgnostic(); + } +} + + +// +// Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce. +// Note that this will deal just fine with non-fat Mach-O binaries, but it will +// treat them as architectural binaries containing (only) one architecture - that +// interpretation is courtesy of the Universal/MachO support classes. +// +void SecCodeSigner::Signer::signMachO(Universal *fat) +{ + // Mach-O executable at the core - perform multi-architecture signing + auto_ptr editor(state.mDetached + ? static_cast(new BlobEditor(*fat, *this)) + : new MachOEditor(rep->writer(), *fat, rep->mainExecutablePath())); + assert(editor->count() > 0); + if (!editor->attribute(writerNoGlobal)) // can store architecture-common components + populate(*editor); + + // pass 1: prepare signature blobs and calculate sizes + for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) { + MachOEditor::Arch &arch = *it->second; + arch.source.reset(fat->architecture(it->first)); + arch.ireqs(state.mRequirements, rep->defaultRequirements(&arch.architecture)); + if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch + populate(arch); + populate(arch.cdbuilder, arch, arch.ireqs, + arch.source->offset(), arch.source->signingExtent()); + // prepare SuperBlob size estimate + size_t cdSize = arch.cdbuilder.size(); + arch.blobSize = arch.size(cdSize, state.mCMSSize, 0); + } + + editor->allocate(); + + // pass 2: Finish and generate signatures, and write them + for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) { + MachOEditor::Arch &arch = *it->second; + editor->reset(arch); + + // finish CodeDirectory (off new binary) and sign it + CodeDirectory *cd = arch.cdbuilder.build(); + CFRef signature = signCodeDirectory(cd); + + // complete the SuperBlob + arch.add(cdCodeDirectorySlot, cd); // takes ownership + arch.add(cdSignatureSlot, BlobWrapper::alloc( + CFDataGetBytePtr(signature), CFDataGetLength(signature))); + if (!state.mDryRun) { + EmbeddedSignatureBlob *blob = arch.make(); + editor->write(arch, blob); // takes ownership of blob + } + } + + // done: write edit copy back over the original + if (!state.mDryRun) + editor->commit(); +} + + +// +// Sign a binary that has no notion of architecture. +// That currently means anything that isn't Mach-O format. +// +void SecCodeSigner::Signer::signArchitectureAgnostic() +{ + // non-Mach-O executable - single-instance signing + RefPointer writer = state.mDetached ? + (new DetachedBlobWriter(*this)) : rep->writer(); + CodeDirectory::Builder builder; + InternalRequirements ireqs; + ireqs(state.mRequirements, rep->defaultRequirements(NULL)); + populate(*writer); + populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit()); + CodeDirectory *cd = builder.build(); + CFRef signature = signCodeDirectory(cd); + if (!state.mDryRun) { + writer->codeDirectory(cd); + writer->signature(signature); + writer->flush(); + } + ::free(cd); +} + + +// +// Global populate - send components to destination buffers ONCE +// +void SecCodeSigner::Signer::populate(DiskRep::Writer &writer) +{ + if (resourceDirectory) + writer.component(cdResourceDirSlot, resourceDirectory); +} + + +// +// Per-architecture populate - send components to per-architecture buffers +// and populate the CodeDirectory for an architecture. In architecture-agnostic +// signing operations, the non-architectural binary is considered one (arbitrary) architecture +// for the purposes of this call. +// +void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer, + InternalRequirements &ireqs, size_t offset /* = 0 */, size_t length /* = 0 */) +{ + // fill the CodeDirectory + builder.executable(rep->mainExecutablePath(), pagesize, offset, length); + builder.flags(cdFlags); + builder.identifier(identifier); + + for (CodeDirectory::Slot slot = cdSlotMax; slot >= 1; --slot) + switch (slot) { + case cdRequirementsSlot: + if (ireqs) { + CFRef data = makeCFData(*ireqs); + writer.component(cdRequirementsSlot, data); + builder.special(slot, data); + } + break; + case cdResourceDirSlot: + if (resourceDirectory) + builder.special(slot, resourceDirectory); + break; + case cdApplicationSlot: +#if NOT_YET + if (state.mApplicationData) + builder.special(slot, state.mApplicationData); +#endif + break; + default: + if (CFDataRef data = rep->component(slot)) + builder.special(slot, data); + break; + } +} + + +// +// Generate the CMS signature for a (finished) CodeDirectory. +// +CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd) +{ + assert(state.mSigner); + + // a null signer generates a null signature blob + if (state.mSigner == SecIdentityRef(kCFNull)) + return CFDataCreate(NULL, NULL, 0); + + // generate CMS signature + CFRef cms; + MacOSError::check(CMSEncoderCreate(&cms.aref())); + MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot)); + CMSEncoderAddSigners(cms, state.mSigner); + MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true)); + + if (signingTime) { + MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime)); + MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime)); + } + + MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length())); + CFDataRef signature; + MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature)); + return signature; +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/signer.h b/lib/signer.h new file mode 100644 index 0000000..ba236d1 --- /dev/null +++ b/lib/signer.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +// +// signer - Signing operation supervisor and controller +// +#ifndef _H_SIGNER +#define _H_SIGNER + +#include "CodeSigner.h" +#include "cdbuilder.h" +#include "signerutils.h" +#include "StaticCode.h" +#include + +namespace Security { +namespace CodeSigning { + + +// +// The signer driver class. +// This is a workflow object, containing all the data needed for the various +// signing stages to cooperate. It is not meant to be API visible; that is +// SecCodeSigner's job. +// +class SecCodeSigner::Signer { +public: + Signer(SecCodeSigner &s, SecStaticCode *c) : state(s), code(c) { } + void sign(SecCSFlags flags); + + SecCodeSigner &state; + SecStaticCode * const code; + +protected: + void signMachO(Universal *fat); // sign a Mach-O binary + void signArchitectureAgnostic(); // sign anything else + + void populate(DiskRep::Writer &writer); // global + void populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer, + InternalRequirements &ireqs, size_t offset = 0, size_t length = 0); // per-architecture + CFDataRef signCodeDirectory(const CodeDirectory *cd); + +private: + RefPointer rep; // DiskRep of Code being signed + CFRef resourceDirectory; // resource directory + std::string identifier; // signing identifier + uint32_t cdFlags; // CodeDirectory flags + size_t pagesize; // size of main executable pages + CFAbsoluteTime signingTime; // signing time for CMS signature (0 => none) +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_CODESIGNER diff --git a/lib/signerutils.cpp b/lib/signerutils.cpp new file mode 100644 index 0000000..e005e82 --- /dev/null +++ b/lib/signerutils.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// signerutils - utilities for signature generation +// +#include "signerutils.h" +#include "signer.h" +#include "SecCodeSigner.h" +#include +#include +#include "renum.h" +#include +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// About the Mach-O allocation helper +// +static const char helperName[] = "codesign_allocate"; +static const char helperPath[] = "/usr/bin/codesign_allocate"; +static const size_t csAlign = 16; + + +// +// InternalRequirements +// +void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted) +{ + if (defaulted) { + this->add(defaulted); + ::free((void *)defaulted); // was malloc(3)ed by DiskRep + } + if (given) + this->add(given); + mReqs = make(); +} + + +// +// BlobWriters +// +void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data) +{ + return EmbeddedSignatureBlob::Maker::component(slot, data); +} + + +void DetachedBlobWriter::flush() +{ + EmbeddedSignatureBlob *blob = this->make(); + signer.code->detachedSignature(CFTempData(*blob)); + signer.state.returnDetachedSignature(blob); + ::free(blob); +} + + +// +// ArchEditor +// +ArchEditor::ArchEditor(Universal &code, uint32_t attrs /* = 0 */) + : DiskRep::Writer(attrs) +{ + Universal::Architectures archList; + code.architectures(archList); + for (Universal::Architectures::const_iterator it = archList.begin(); + it != archList.end(); ++it) + architecture[*it] = new Arch(*it); +} + + +ArchEditor::~ArchEditor() +{ + for (ArchMap::iterator it = begin(); it != end(); ++it) + delete it->second; +} + + +// +// BlobEditor +// +void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data) +{ + mGlobal.component(slot, data); +} + +void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob) +{ + mMaker.add(arch.architecture.cpuType(), blob); +} + + +void BlobEditor::commit() +{ + // create the architecture-global blob and store it into the superblob + mMaker.add(0, mGlobal.make()); // takes ownership of blob + + // finish up the superblob and deliver it + DetachedSignatureBlob *blob = mMaker.make(); + signer.state.returnDetachedSignature(blob); + ::free(blob); +} + + +// +// MachOEditor's allocate() method spawns the codesign_allocate helper tool to +// "drill up" the Mach-O binary for insertion of Code Signing signature data. +// After the tool succeeds, we open the new file and are ready to write it. +// +MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, std::string srcPath) + : ArchEditor(code, w->attributes()), writer(w), sourcePath(srcPath), tempPath(srcPath + ".cstemp"), + mNewCode(NULL), mTempMayExist(false) +{ +} + +MachOEditor::~MachOEditor() +{ + delete mNewCode; + if (mTempMayExist) + ::remove(tempPath.c_str()); // ignore error (can't do anything about it) +} + + +void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data) +{ + writer->component(slot, data); +} + + +void MachOEditor::allocate() +{ + // note that we may have a temporary file from now on (for cleanup in the error case) + mTempMayExist = true; + + // run codesign_allocate to make room in the executable file + fork(); + wait(); + if (!Child::succeeded()) + UnixError::throwMe(ENOEXEC); //@@@ how to signal "it din' work"? + + // open the new (temporary) Universal file + { + UidGuard guard(0); + mFd.open(tempPath, O_RDWR); + } + mNewCode = new Universal(mFd); +} + +void MachOEditor::childAction() +{ + vector arguments; + arguments.push_back(helperName); + arguments.push_back("-i"); + arguments.push_back(sourcePath.c_str()); + arguments.push_back("-o"); + arguments.push_back(tempPath.c_str()); + + for (Iterator it = architecture.begin(); it != architecture.end(); ++it) { + char *size; // we'll leak this (execv is coming soon) + asprintf(&size, "%d", LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign)); + secdebug("machoedit", "preparing %s size=%s", it->first.name(), size); + arguments.push_back("-a"); + arguments.push_back(it->first.name()); + arguments.push_back(size); + } + arguments.push_back(NULL); + ::seteuid(0); // activate privilege if caller has it; ignore error if not + execv(helperPath, (char * const *)&arguments[0]); +} + +void MachOEditor::reset(Arch &arch) +{ + arch.source.reset(mNewCode->architecture(arch.architecture)); + arch.cdbuilder.reopen(tempPath, + arch.source->offset(), arch.source->signingOffset()); +} + + +// +// MachOEditor's write() method actually writes the blob into the CODESIGNING section +// of the executable image file. +// +void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob) +{ + if (size_t offset = arch.source->signingOffset()) { + size_t signingLength = arch.source->signingLength(); + secdebug("codesign", "writing architecture %s at 0x%zx (%zd of %zd)", + arch.architecture.name(), offset, blob->length(), signingLength); + if (signingLength < blob->length()) { + secdebug("codesign", "trying to write %zd bytes into %zd area", + blob->length(), signingLength); + MacOSError::throwMe(errSecCSInternalError); + } + arch.source->seek(offset); + arch.source->writeAll(*blob); + ::free(blob); // done with it + } else { + secdebug("signer", "%p cannot find CODESIGNING section", this); + MacOSError::throwMe(errSecCSInternalError); + } +} + + +// +// Commit the edit. +// This moves the temporary editor copy over the source image file. +// Note that the Universal object returned by allocate() is still open +// and valid; the caller owns it. +// +void MachOEditor::commit() +{ + // if the file's owned by someone else *and* we can become root... + struct stat st; + UnixError::check(::stat(sourcePath.c_str(), &st)); + + // copy over all the *other* stuff + Copyfile copy; + int fd = mFd; + copy.set(COPYFILE_STATE_DST_FD, &fd); + { + // perform copy under root or file-owner privileges if available + UidGuard guard; + if (!guard.seteuid(0)) + guard.seteuid(st.st_uid); + copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA); + + // move the new file into place + UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str())); + mTempMayExist = false; // we renamed it away + } +} + + +// +// Copyfile +// +Copyfile::Copyfile() +{ + if (!(mState = copyfile_state_alloc())) + UnixError::throwMe(); +} + +void Copyfile::set(uint32_t flag, const void *value) +{ + check(::copyfile_state_set(mState, flag, value)); +} + +void Copyfile::get(uint32_t flag, void *value) +{ + check(::copyfile_state_set(mState, flag, value)); +} + +void Copyfile::operator () (const char *src, const char *dst, copyfile_flags_t flags) +{ + check(::copyfile(src, dst, mState, flags)); +} + +void Copyfile::check(int rc) +{ + if (rc < 0) + UnixError::throwMe(); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/signerutils.h b/lib/signerutils.h new file mode 100644 index 0000000..827c11e --- /dev/null +++ b/lib/signerutils.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// signerutils - utilities for signature generation +// +#ifndef _H_SIGNERUTILS +#define _H_SIGNERUTILS + +#include "CodeSigner.h" +#include "sigblob.h" +#include "cdbuilder.h" +#include +#include +#include +#include + +extern "C" { +#include +} + +namespace Security { +namespace CodeSigning { + + +// +// A helper to deal with the magic merger logic of internal requirements +// +class InternalRequirements : public Requirements::Maker { +public: + InternalRequirements() : mReqs(NULL) { } + ~InternalRequirements() { ::free((void *)mReqs); } + void operator () (const Requirements *given, const Requirements *defaulted); + operator const Requirements * () const { return mReqs; } + +private: + const Requirements *mReqs; +}; + + +// +// A DiskRep::Writer that assembles data in a SuperBlob (in memory) +// +class BlobWriter : public DiskRep::Writer, public EmbeddedSignatureBlob::Maker { +public: + void component(CodeDirectory::SpecialSlot slot, CFDataRef data); +}; + + +class DetachedBlobWriter : public BlobWriter { +public: + DetachedBlobWriter(SecCodeSigner::Signer &s) : signer(s) { } + + SecCodeSigner::Signer &signer; + + void flush(); +}; + + +// +// A multi-architecture editing assistant. +// ArchEditor collects (Mach-O) architectures in use, and maintains per-archtitecture +// data structures. It must be subclassed to express a particular way to handle the signing +// data. +// +class ArchEditor : public DiskRep::Writer { +public: + ArchEditor(Universal &fat, uint32_t attrs = 0); + virtual ~ArchEditor(); + +public: + // + // One architecture's signing construction element. + // This also implements DispRep::Writer so generic writing code + // can work with both Mach-O and other files. + // + struct Arch : public BlobWriter { + Architecture architecture; // our architecture + auto_ptr source; // Mach-O object to be signed + CodeDirectory::Builder cdbuilder; // builder for CodeDirectory + InternalRequirements ireqs; // consolidated internal requirements + size_t blobSize; // calculated SuperBlob size + + Arch(const Architecture &arch) : architecture(arch) { } + }; + + // + // Our callers access the architectural universe through a map + // from Architectures to Arch objects. + // + typedef std::map ArchMap; + typedef ArchMap::iterator Iterator; + ArchMap::iterator begin() { return architecture.begin(); } + ArchMap::iterator end() { return architecture.end(); } + unsigned count() const { return architecture.size(); } + + // methods needed for an actual implementation + virtual void allocate() = 0; // interpass allocations + virtual void reset(Arch &arch) = 0; // pass 2 prep + virtual void write(Arch &arch, EmbeddedSignatureBlob *blob) = 0; // takes ownership of blob + virtual void commit() = 0; // write/flush result + +protected: + ArchMap architecture; +}; + + +// +// An ArchEditor that collects all architectures into a single SuperBlob, +// usually for writing a detached multi-architecture signature. +// +class BlobEditor : public ArchEditor { +public: + BlobEditor(Universal &fat, SecCodeSigner::Signer &s) : ArchEditor(fat), signer(s) { } + ~BlobEditor() { } + + SecCodeSigner::Signer &signer; + + void component(CodeDirectory::SpecialSlot slot, CFDataRef data); + void allocate() { } + void reset(Arch &arch) { } + void write(Arch &arch, EmbeddedSignatureBlob *blob); + void commit(); + +private: + DetachedSignatureBlob::Maker mMaker; + EmbeddedSignatureBlob::Maker mGlobal; +}; + + +// +// An ArchEditor that writes its signatures into a (fat) binary file. +// +class MachOEditor : public ArchEditor, private UnixPlusPlus::Child { +public: + MachOEditor(DiskRep::Writer *w, Universal &code, std::string srcPath); + ~MachOEditor(); + + const RefPointer writer; + const std::string sourcePath; + const std::string tempPath; + + void component(CodeDirectory::SpecialSlot slot, CFDataRef data); + void allocate(); + void reset(Arch &arch); + void write(Arch &arch, EmbeddedSignatureBlob *blob); + void commit(); + +private: + void childAction(); + Universal *mNewCode; + UnixPlusPlus::AutoFileDesc mFd; + bool mTempMayExist; +}; + + +// +// Encapsulation of the copyfile(3) API. +// This is slated to go into utilities once stable. +// +class Copyfile { +public: + Copyfile(); + ~Copyfile() { copyfile_state_free(mState); } + + operator copyfile_state_t () const { return mState; } + + void set(uint32_t flag, const void *value); + void get(uint32_t flag, void *value); + + void operator () (const char *src, const char *dst, copyfile_flags_t flags); + +private: + void check(int rc); + +private: + copyfile_state_t mState; +}; + + +// +// A reliable uid set/reset bracket +// +class UidGuard { +public: + UidGuard() : mPrevious(-1) { } + UidGuard(uid_t uid) : mPrevious(-1) { seteuid(uid); } + ~UidGuard() + { + if (active()) + UnixError::check(::seteuid(mPrevious)); + } + + bool seteuid(uid_t uid) + { + if (uid == geteuid()) + return true; // no change, don't bother the kernel + if (!active()) + mPrevious = ::geteuid(); + return ::seteuid(uid) == 0; + } + + bool active() const { return mPrevious != uid_t(-1); } + operator bool () const { return active(); } + uid_t saved() const { assert(active()); return mPrevious; } + +private: + uid_t mPrevious; +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_SIGNERUTILS diff --git a/lib/singlediskrep.cpp b/lib/singlediskrep.cpp new file mode 100644 index 0000000..8331a5a --- /dev/null +++ b/lib/singlediskrep.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// singlediskrep - semi-abstract diskrep for a single file of some kind +// +#include "singlediskrep.h" + + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// Construct a SingleDiskRep +// +SingleDiskRep::SingleDiskRep(const char *path) + : mPath(path) +{ +} + + +// +// Both the canonical and main executable path of a SingleDiskRep is, well, its path. +// +CFURLRef SingleDiskRep::canonicalPath() +{ + return makeCFURL(mPath); +} + +string SingleDiskRep::mainExecutablePath() +{ + return mPath; +} + + +// +// The recommended identifier of a SingleDiskRep is, absent any better clue, +// the basename of its path. +// +string SingleDiskRep::recommendedIdentifier() +{ + string::size_type p = mPath.rfind('/'); + if (p == string::npos) + return mPath; + else + return mPath.substr(p+1); +} + + +// +// The default signing limit is the size of the file. +// This will do unless the signing data gets creatively stuck in there somewhere. +// +size_t SingleDiskRep::signingLimit() +{ + return fd().fileSize(); +} + + +// +// A lazily opened read-only file descriptor for the path. +// +FileDesc &SingleDiskRep::fd() +{ + if (!mFd) + mFd.open(mPath, O_RDONLY); + return mFd; +} + + +// +// Flush cached state +// +void SingleDiskRep::flush() +{ + mFd.close(); +} + + +// +// Prototype Writers +// +FileDesc &SingleDiskRep::Writer::fd() +{ + if (!mFd) + mFd.open(rep->path(), O_RDWR); + return mFd; +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/lib/singlediskrep.h b/lib/singlediskrep.h new file mode 100644 index 0000000..e66ec47 --- /dev/null +++ b/lib/singlediskrep.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2007 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@ + */ + +// +// singlediskrep - semi-abstract diskrep for a single file of some kind +// +#ifndef _H_SINGLEDISKREP +#define _H_SINGLEDISKREP + +#include "diskrep.h" +#include + +namespace Security { +namespace CodeSigning { + + +// +// A slight specialization of DiskRep that knows that it's working with a single +// file at a path that is both the canonical and main executable path. This is a common +// pattern. +// +// A SingleDiskRep is not a fully formed DiskRep in its own right. It must be further +// subclassed. +// +class SingleDiskRep : public DiskRep { +public: + SingleDiskRep(const char *path); + + std::string mainExecutablePath(); // base path + CFURLRef canonicalPath(); // base path + std::string recommendedIdentifier(); // basename(path) + size_t signingLimit(); // size of file + UnixPlusPlus::FileDesc &fd(); // readable fd for this file + void flush(); // close cached fd + +public: + class Writer; + +protected: + std::string path() const { return mPath; } + +private: + std::string mPath; + UnixPlusPlus::AutoFileDesc mFd; // open file (cached) +}; + + +// +// A Writer for a SingleDiskRep +// +class SingleDiskRep::Writer : public DiskRep::Writer { +public: + Writer(SingleDiskRep *r, uint32_t attrs = 0) : DiskRep::Writer(attrs), rep(r) { } + + UnixPlusPlus::FileDesc &fd(); + +private: + RefPointer rep; // underlying SingleDiskRep + UnixPlusPlus::AutoFileDesc mFd; // cached writable fd +}; + + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_SINGLEDISKREP diff --git a/libsecurity_codesigning.xcodeproj/project.pbxproj b/libsecurity_codesigning.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6de0c93 --- /dev/null +++ b/libsecurity_codesigning.xcodeproj/project.pbxproj @@ -0,0 +1,1407 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXAggregateTarget section */ + C2D383B80A23A8C4005C63A2 /* Requirements Language */ = { + isa = PBXAggregateTarget; + buildConfigurationList = C2D383C00A23A8E3005C63A2 /* Build configuration list for PBXAggregateTarget "Requirements Language" */; + buildPhases = ( + C2D383B70A23A8C4005C63A2 /* ShellScript */, + ); + dependencies = ( + ); + name = "Requirements Language"; + productName = "Requirements Language"; + }; + C2E2873F0B5D8F8F009336A0 /* Everything */ = { + isa = PBXAggregateTarget; + buildConfigurationList = C2E287470B5D8FD8009336A0 /* Build configuration list for PBXAggregateTarget "Everything" */; + buildPhases = ( + ); + dependencies = ( + C2E287410B5D8F97009336A0 /* PBXTargetDependency */, + C250F6C50B5EF4E40076098F /* PBXTargetDependency */, + C2E287430B5D8F9A009336A0 /* PBXTargetDependency */, + ); + name = Everything; + productName = Everything; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + C2093AA80BB0948000EB8599 /* reqreader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2093AA60BB0948000EB8599 /* reqreader.cpp */; }; + C2093AA90BB0948000EB8599 /* reqreader.h in Headers */ = {isa = PBXBuildFile; fileRef = C2093AA70BB0948000EB8599 /* reqreader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2093AC80BB0967D00EB8599 /* reqreader.h in Headers */ = {isa = PBXBuildFile; fileRef = C2093AA70BB0948000EB8599 /* reqreader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C21CFC5F0A250D1C006CD5B1 /* reqdumper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C21CFC5D0A250D1C006CD5B1 /* reqdumper.cpp */; }; + C21CFC620A250D1C006CD5B1 /* reqdumper.h in Headers */ = {isa = PBXBuildFile; fileRef = C21CFC5E0A250D1C006CD5B1 /* reqdumper.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C21EA3DD0AD2F81300E6E31C /* SecCodeSigner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C21EA3DB0AD2F81300E6E31C /* SecCodeSigner.cpp */; }; + C21EA3DE0AD2F81300E6E31C /* SecCodeSigner.h in Headers */ = {isa = PBXBuildFile; fileRef = C21EA3DC0AD2F81300E6E31C /* SecCodeSigner.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C21EA3E00AD2F81300E6E31C /* SecCodeSigner.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C21EA3DC0AD2F81300E6E31C /* SecCodeSigner.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C21EA3E30AD2FA0900E6E31C /* CodeSigner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C21EA3E10AD2FA0900E6E31C /* CodeSigner.cpp */; }; + C224635F0B8620F800626F1B /* RequirementParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383F00A23A9D3005C63A2 /* RequirementParser.cpp */; }; + C22463600B8620F800626F1B /* RequirementLexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383EE0A23A9D3005C63A2 /* RequirementLexer.cpp */; }; + C22463610B86210100626F1B /* antlrplugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2CC31130B85254F005FA59D /* antlrplugin.cpp */; }; + C236E3D70AD59446000F5140 /* signer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C236E3D50AD59446000F5140 /* signer.cpp */; }; + C236E3D80AD59446000F5140 /* signer.h in Headers */ = {isa = PBXBuildFile; fileRef = C236E3D60AD59446000F5140 /* signer.h */; }; + C236E3DB0AD595C2000F5140 /* signerutils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C236E3D90AD595C2000F5140 /* signerutils.cpp */; }; + C236E3DC0AD595C2000F5140 /* signerutils.h in Headers */ = {isa = PBXBuildFile; fileRef = C236E3DA0AD595C2000F5140 /* signerutils.h */; }; + C250F6C30B5EF1910076098F /* SecIntegrity.h in Headers */ = {isa = PBXBuildFile; fileRef = C250F6C20B5EF1910076098F /* SecIntegrity.h */; }; + C25942440BA7095000877E56 /* foreigndiskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C25942420BA7095000877E56 /* foreigndiskrep.cpp */; }; + C25942450BA7095000877E56 /* foreigndiskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C25942430BA7095000877E56 /* foreigndiskrep.h */; }; + C259DFD60AD6D9BA00C9ACC6 /* sigblob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C259DFD40AD6D9BA00C9ACC6 /* sigblob.cpp */; }; + C259DFD70AD6D9BA00C9ACC6 /* sigblob.h in Headers */ = {isa = PBXBuildFile; fileRef = C259DFD50AD6D9BA00C9ACC6 /* sigblob.h */; }; + C26B45C10B8A9C0A003C0ACA /* ucspc in Frameworks */ = {isa = PBXBuildFile; fileRef = C26B45C00B8A9C00003C0ACA /* ucspc */; }; + C297DF250B014BB300E94EE0 /* SecCodeSigner.h in Headers */ = {isa = PBXBuildFile; fileRef = C21EA3DC0AD2F81300E6E31C /* SecCodeSigner.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2A487530B7914F400849490 /* SecCodeHostLib.h in Headers */ = {isa = PBXBuildFile; fileRef = C2BC1F340B580DA7003EC9DC /* SecCodeHostLib.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2A487540B79150C00849490 /* SecIntegrity.h in Headers */ = {isa = PBXBuildFile; fileRef = C250F6C20B5EF1910076098F /* SecIntegrity.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2A487550B79152A00849490 /* SecIntegrity.h in Headers */ = {isa = PBXBuildFile; fileRef = C250F6C20B5EF1910076098F /* SecIntegrity.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2A487560B79152A00849490 /* SecCodeHostLib.h in Headers */ = {isa = PBXBuildFile; fileRef = C2BC1F340B580DA7003EC9DC /* SecCodeHostLib.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2A752B70B80EAFB004CF655 /* SecIntegrity.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C250F6C20B5EF1910076098F /* SecIntegrity.h */; }; + C2A752B80B80EAFB004CF655 /* SecCodeHostLib.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2BC1F340B580DA7003EC9DC /* SecCodeHostLib.h */; }; + C2A976AA0B8A2E36008B4EA0 /* csutilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2A976A80B8A2E36008B4EA0 /* csutilities.cpp */; }; + C2A976AB0B8A2E36008B4EA0 /* csutilities.h in Headers */ = {isa = PBXBuildFile; fileRef = C2A976A90B8A2E36008B4EA0 /* csutilities.h */; }; + C2BC1F350B580DA7003EC9DC /* SecCodeHostLib.h in Headers */ = {isa = PBXBuildFile; fileRef = C2BC1F340B580DA7003EC9DC /* SecCodeHostLib.h */; }; + C2BD519C0A9392FD000FE43D /* machorep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2BD519A0A9392FD000FE43D /* machorep.cpp */; }; + C2BD519F0A9392FD000FE43D /* machorep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2BD519B0A9392FD000FE43D /* machorep.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2BD60FA0AC863FC0057FD3D /* csgeneric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2BD60F80AC863FC0057FD3D /* csgeneric.cpp */; }; + C2BD61BD0AC9C77B0057FD3D /* csgeneric.h in Headers */ = {isa = PBXBuildFile; fileRef = C2BD60F90AC863FC0057FD3D /* csgeneric.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2C1DF140A2E3D7200D1B02B /* requirement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383350A237F47005C63A2 /* requirement.cpp */; }; + C2C1DFBB0A2F80EB00D1B02B /* reqinterp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2C1DFB90A2F80EB00D1B02B /* reqinterp.cpp */; }; + C2C1DFBC0A2F80EB00D1B02B /* reqinterp.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C1DFBA0A2F80EB00D1B02B /* reqinterp.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2C1DFBE0A2F80EB00D1B02B /* reqinterp.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C1DFBA0A2F80EB00D1B02B /* reqinterp.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2C1DFC30A2F820500D1B02B /* reqmaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2C1DFC10A2F820500D1B02B /* reqmaker.cpp */; }; + C2C1DFC40A2F820500D1B02B /* reqmaker.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C1DFC20A2F820500D1B02B /* reqmaker.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2C1DFC60A2F820500D1B02B /* reqmaker.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C1DFC20A2F820500D1B02B /* reqmaker.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2C3BC5F0BA1D6FE00E869D1 /* cfmdiskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2C3BC5D0BA1D6FE00E869D1 /* cfmdiskrep.cpp */; }; + C2C3BC600BA1D6FE00E869D1 /* cfmdiskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C3BC5E0BA1D6FE00E869D1 /* cfmdiskrep.h */; }; + C2C3BCD30BA1E47E00E869D1 /* singlediskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2C3BCD10BA1E47E00E869D1 /* singlediskrep.cpp */; }; + C2C3BCD40BA1E47E00E869D1 /* singlediskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C3BCD20BA1E47E00E869D1 /* singlediskrep.h */; }; + C2C931B40AB8BA1200F83950 /* SecCodeHost.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2C931B30AB8BA1200F83950 /* SecCodeHost.cpp */; }; + C2C931B50AB8BA8200F83950 /* SecCodeHost.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383190A237F47005C63A2 /* SecCodeHost.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2C931B60AB8BAC200F83950 /* SecCodeHost.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383190A237F47005C63A2 /* SecCodeHost.h */; }; + C2CC31050B8523AD005FA59D /* SecIntegrityLib.h in Headers */ = {isa = PBXBuildFile; fileRef = C2CC31040B8523AD005FA59D /* SecIntegrityLib.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2CC31060B8523F8005FA59D /* SecIntegrityLib.h in Headers */ = {isa = PBXBuildFile; fileRef = C2CC31040B8523AD005FA59D /* SecIntegrityLib.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2CC310F0B852424005FA59D /* SecIntegrityLib.c in Sources */ = {isa = PBXBuildFile; fileRef = C2CC310E0B852424005FA59D /* SecIntegrityLib.c */; }; + C2CC31100B852447005FA59D /* SecIntegrityLib.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2CC31040B8523AD005FA59D /* SecIntegrityLib.h */; }; + C2CCF0310A3F523D0085795A /* macho++.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2CCF02F0A3F523D0085795A /* macho++.cpp */; }; + C2CCF0340A3F523D0085795A /* macho++.h in Headers */ = {isa = PBXBuildFile; fileRef = C2CCF0300A3F523D0085795A /* macho++.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D3833C0A237F47005C63A2 /* bundlediskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383120A237F47005C63A2 /* bundlediskrep.cpp */; }; + C2D3833E0A237F47005C63A2 /* cdbuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383140A237F47005C63A2 /* cdbuilder.cpp */; }; + C2D383400A237F47005C63A2 /* codedirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383160A237F47005C63A2 /* codedirectory.cpp */; }; + C2D383440A237F47005C63A2 /* cs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3831A0A237F47005C63A2 /* cs.cpp */; }; + C2D383460A237F47005C63A2 /* CSCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3831C0A237F47005C63A2 /* CSCommon.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2D383470A237F47005C63A2 /* SecCode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3831D0A237F47005C63A2 /* SecCode.cpp */; }; + C2D383480A237F47005C63A2 /* SecCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3831E0A237F47005C63A2 /* SecCode.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2D383490A237F47005C63A2 /* cskernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3831F0A237F47005C63A2 /* cskernel.cpp */; }; + C2D3834B0A237F47005C63A2 /* SecStaticCode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383210A237F47005C63A2 /* SecStaticCode.cpp */; }; + C2D3834C0A237F47005C63A2 /* SecStaticCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383220A237F47005C63A2 /* SecStaticCode.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2D3834D0A237F47005C63A2 /* csprocess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383230A237F47005C63A2 /* csprocess.cpp */; }; + C2D3834F0A237F47005C63A2 /* SecRequirement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383250A237F47005C63A2 /* SecRequirement.cpp */; }; + C2D383500A237F47005C63A2 /* SecRequirement.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383260A237F47005C63A2 /* SecRequirement.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2D383510A237F47005C63A2 /* diskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383270A237F47005C63A2 /* diskrep.cpp */; }; + C2D383550A237F47005C63A2 /* filediskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3832B0A237F47005C63A2 /* filediskrep.cpp */; }; + C2D383570A237F47005C63A2 /* Code.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3832D0A237F47005C63A2 /* Code.cpp */; }; + C2D383590A237F47005C63A2 /* kerneldiskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3832F0A237F47005C63A2 /* kerneldiskrep.cpp */; }; + C2D3835B0A237F47005C63A2 /* StaticCode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383310A237F47005C63A2 /* StaticCode.cpp */; }; + C2D3835D0A237F47005C63A2 /* reqparser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383330A237F47005C63A2 /* reqparser.cpp */; }; + C2D383610A237F47005C63A2 /* Requirements.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383370A237F47005C63A2 /* Requirements.cpp */; }; + C2D383630A237F47005C63A2 /* security_codesigning.exp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383390A237F47005C63A2 /* security_codesigning.exp */; }; + C2D383670A237F47005C63A2 /* bundlediskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383130A237F47005C63A2 /* bundlediskrep.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D383690A237F47005C63A2 /* cdbuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383150A237F47005C63A2 /* cdbuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D3836B0A237F47005C63A2 /* codedirectory.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383170A237F47005C63A2 /* codedirectory.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D3836C0A237F47005C63A2 /* CodeSigning.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383180A237F47005C63A2 /* CodeSigning.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2D3836D0A237F47005C63A2 /* SecCodeHost.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383190A237F47005C63A2 /* SecCodeHost.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2D3836F0A237F47005C63A2 /* cs.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3831B0A237F47005C63A2 /* cs.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D383700A237F47005C63A2 /* CSCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3831C0A237F47005C63A2 /* CSCommon.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2D383720A237F47005C63A2 /* SecCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3831E0A237F47005C63A2 /* SecCode.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2D383740A237F47005C63A2 /* cskernel.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383200A237F47005C63A2 /* cskernel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D383760A237F47005C63A2 /* SecStaticCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383220A237F47005C63A2 /* SecStaticCode.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2D383780A237F47005C63A2 /* csprocess.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383240A237F47005C63A2 /* csprocess.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D3837A0A237F47005C63A2 /* SecRequirement.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383260A237F47005C63A2 /* SecRequirement.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2D3837C0A237F47005C63A2 /* diskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383280A237F47005C63A2 /* diskrep.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D383800A237F47005C63A2 /* filediskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3832C0A237F47005C63A2 /* filediskrep.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D383820A237F47005C63A2 /* Code.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3832E0A237F47005C63A2 /* Code.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D383840A237F47005C63A2 /* kerneldiskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383300A237F47005C63A2 /* kerneldiskrep.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D383860A237F47005C63A2 /* StaticCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383320A237F47005C63A2 /* StaticCode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D383880A237F47005C63A2 /* reqparser.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383340A237F47005C63A2 /* reqparser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D3838A0A237F47005C63A2 /* requirement.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383360A237F47005C63A2 /* requirement.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D3838C0A237F47005C63A2 /* Requirements.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383380A237F47005C63A2 /* Requirements.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2D3838D0A237F47005C63A2 /* security_codesigning.exp in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383390A237F47005C63A2 /* security_codesigning.exp */; }; + C2D383900A23801C005C63A2 /* security_codesigning.exp in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383390A237F47005C63A2 /* security_codesigning.exp */; }; + C2D383910A23803A005C63A2 /* CSCommon.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D3831C0A237F47005C63A2 /* CSCommon.h */; }; + C2D383920A23803A005C63A2 /* SecCode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D3831E0A237F47005C63A2 /* SecCode.h */; }; + C2D383930A23803A005C63A2 /* SecStaticCode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383220A237F47005C63A2 /* SecStaticCode.h */; }; + C2D383940A23803A005C63A2 /* SecRequirement.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383260A237F47005C63A2 /* SecRequirement.h */; }; + C2D3839A0A238132005C63A2 /* CodeSigning.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383180A237F47005C63A2 /* CodeSigning.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2D3839B0A238150005C63A2 /* CodeSigning.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383180A237F47005C63A2 /* CodeSigning.h */; }; + C2D8A07F0AE7F6E300F68F79 /* cfmunge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D8A07D0AE7F6E300F68F79 /* cfmunge.cpp */; }; + C2D8A0800AE7F6E300F68F79 /* cfmunge.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D8A07E0AE7F6E300F68F79 /* cfmunge.h */; }; + C2D8A0980AE7F74500F68F79 /* cfmunge.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D8A07E0AE7F6E300F68F79 /* cfmunge.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2E2873D0B5D8D80009336A0 /* SecCodeHostLib.c in Sources */ = {isa = PBXBuildFile; fileRef = C2E2873C0B5D8D80009336A0 /* SecCodeHostLib.c */; }; + C2E911E20ADEBE3200275CB2 /* resources.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2E911E00ADEBE3200275CB2 /* resources.cpp */; }; + C2E911E30ADEBE3200275CB2 /* resources.h in Headers */ = {isa = PBXBuildFile; fileRef = C2E911E10ADEBE3200275CB2 /* resources.h */; }; + C2EF10100A49BD89005A44BB /* renum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2EF100E0A49BD89005A44BB /* renum.cpp */; }; + C2EF10110A49BD89005A44BB /* renum.h in Headers */ = {isa = PBXBuildFile; fileRef = C2EF100F0A49BD89005A44BB /* renum.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2EF10130A49BD89005A44BB /* renum.h in Headers */ = {isa = PBXBuildFile; fileRef = C2EF100F0A49BD89005A44BB /* renum.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2F6566E0BCBFB250078779E /* cserror.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2F6566C0BCBFB250078779E /* cserror.cpp */; }; + C2F6566F0BCBFB250078779E /* cserror.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F6566D0BCBFB250078779E /* cserror.h */; }; + C2F656930BCBFFF40078779E /* cserror.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F6566D0BCBFB250078779E /* cserror.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 4C7502900540C69C00056564 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4CA1FEAB052A3C3800F22E42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4C56351D0540A55300DCF0C8; + remoteInfo = security_codesigning; + }; + C21E3F8A0A23AE10006558D6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4CA1FEAB052A3C3800F22E42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C2D383B80A23A8C4005C63A2; + remoteInfo = "Requirements Language"; + }; + C250F6C40B5EF4E40076098F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4CA1FEAB052A3C3800F22E42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C2BC1F250B580D3A003EC9DC; + remoteInfo = libintegrity; + }; + C2E287400B5D8F97009336A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4CA1FEAB052A3C3800F22E42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4CA1FEBD052A3C8100F22E42; + remoteInfo = libsecurity_codesigning; + }; + C2E287420B5D8F9A009336A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4CA1FEAB052A3C3800F22E42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C2BC1F2E0B580D4B003EC9DC; + remoteInfo = libcodehost; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 4CCB00500580097400981D43 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = SecurityPieces/Exports/Security; + dstSubfolderSpec = 16; + files = ( + C2D3838D0A237F47005C63A2 /* security_codesigning.exp in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CCB00510580097600981D43 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/SecurityPieces/Exports/Security; + dstSubfolderSpec = 0; + files = ( + C2D383900A23801C005C63A2 /* security_codesigning.exp in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 4CCB00520580097800981D43 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = SecurityPieces/Headers/Security; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C26C39D3068368EC00ED5782 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = SecurityPieces/PrivateHeaders/Security; + dstSubfolderSpec = 16; + files = ( + C2CC31100B852447005FA59D /* SecIntegrityLib.h in CopyFiles */, + C21EA3E00AD2F81300E6E31C /* SecCodeSigner.h in CopyFiles */, + C2D3839B0A238150005C63A2 /* CodeSigning.h in CopyFiles */, + C2D383910A23803A005C63A2 /* CSCommon.h in CopyFiles */, + C2D383920A23803A005C63A2 /* SecCode.h in CopyFiles */, + C2D383930A23803A005C63A2 /* SecStaticCode.h in CopyFiles */, + C2D383940A23803A005C63A2 /* SecRequirement.h in CopyFiles */, + C2C931B60AB8BAC200F83950 /* SecCodeHost.h in CopyFiles */, + C2A752B70B80EAFB004CF655 /* SecIntegrity.h in CopyFiles */, + C2A752B80B80EAFB004CF655 /* SecCodeHostLib.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 4C56351E0540A55300DCF0C8 /* security_codesigning.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = security_codesigning.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CA1FEBE052A3C8100F22E42 /* security_codesigning */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = security_codesigning; sourceTree = BUILT_PRODUCTS_DIR; }; + C2093AA60BB0948000EB8599 /* reqreader.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = reqreader.cpp; sourceTree = ""; }; + C2093AA70BB0948000EB8599 /* reqreader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqreader.h; sourceTree = ""; }; + C21CFC5D0A250D1C006CD5B1 /* reqdumper.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = reqdumper.cpp; sourceTree = ""; }; + C21CFC5E0A250D1C006CD5B1 /* reqdumper.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqdumper.h; sourceTree = ""; }; + C21EA3DB0AD2F81300E6E31C /* SecCodeSigner.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecCodeSigner.cpp; sourceTree = ""; }; + C21EA3DC0AD2F81300E6E31C /* SecCodeSigner.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecCodeSigner.h; sourceTree = ""; }; + C21EA3E10AD2FA0900E6E31C /* CodeSigner.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CodeSigner.cpp; sourceTree = ""; }; + C21EA3E20AD2FA0900E6E31C /* CodeSigner.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CodeSigner.h; sourceTree = ""; }; + C236E3D50AD59446000F5140 /* signer.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = signer.cpp; sourceTree = ""; }; + C236E3D60AD59446000F5140 /* signer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = signer.h; sourceTree = ""; }; + C236E3D90AD595C2000F5140 /* signerutils.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = signerutils.cpp; sourceTree = ""; }; + C236E3DA0AD595C2000F5140 /* signerutils.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = signerutils.h; sourceTree = ""; }; + C250F6C20B5EF1910076098F /* SecIntegrity.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecIntegrity.h; sourceTree = ""; }; + C250F6C60B5EF5B50076098F /* SecIntegrity.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecIntegrity.cpp; sourceTree = ""; }; + C25942420BA7095000877E56 /* foreigndiskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = foreigndiskrep.cpp; sourceTree = ""; }; + C25942430BA7095000877E56 /* foreigndiskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = foreigndiskrep.h; sourceTree = ""; }; + C259DFD40AD6D9BA00C9ACC6 /* sigblob.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = sigblob.cpp; sourceTree = ""; }; + C259DFD50AD6D9BA00C9ACC6 /* sigblob.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = sigblob.h; sourceTree = ""; }; + C26B45C00B8A9C00003C0ACA /* ucspc */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = ucspc; sourceTree = ""; }; + C2A976A80B8A2E36008B4EA0 /* csutilities.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = csutilities.cpp; sourceTree = ""; }; + C2A976A90B8A2E36008B4EA0 /* csutilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = csutilities.h; sourceTree = ""; }; + C2BC1F260B580D3A003EC9DC /* libintegrity.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libintegrity.a; sourceTree = BUILT_PRODUCTS_DIR; }; + C2BC1F2F0B580D4B003EC9DC /* libcodehost.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcodehost.a; sourceTree = BUILT_PRODUCTS_DIR; }; + C2BC1F340B580DA7003EC9DC /* SecCodeHostLib.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecCodeHostLib.h; sourceTree = ""; }; + C2BD519A0A9392FD000FE43D /* machorep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = machorep.cpp; sourceTree = ""; }; + C2BD519B0A9392FD000FE43D /* machorep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = machorep.h; sourceTree = ""; }; + C2BD60F80AC863FC0057FD3D /* csgeneric.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = csgeneric.cpp; sourceTree = ""; }; + C2BD60F90AC863FC0057FD3D /* csgeneric.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = csgeneric.h; sourceTree = ""; }; + C2C1DF8F0A2E4A2700D1B02B /* requirements.grammar */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = requirements.grammar; sourceTree = SOURCE_ROOT; }; + C2C1DFB90A2F80EB00D1B02B /* reqinterp.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = reqinterp.cpp; sourceTree = ""; }; + C2C1DFBA0A2F80EB00D1B02B /* reqinterp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqinterp.h; sourceTree = ""; }; + C2C1DFC10A2F820500D1B02B /* reqmaker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = reqmaker.cpp; sourceTree = ""; }; + C2C1DFC20A2F820500D1B02B /* reqmaker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqmaker.h; sourceTree = ""; }; + C2C3BC5D0BA1D6FE00E869D1 /* cfmdiskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cfmdiskrep.cpp; sourceTree = ""; }; + C2C3BC5E0BA1D6FE00E869D1 /* cfmdiskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cfmdiskrep.h; sourceTree = ""; }; + C2C3BCD10BA1E47E00E869D1 /* singlediskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = singlediskrep.cpp; sourceTree = ""; }; + C2C3BCD20BA1E47E00E869D1 /* singlediskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = singlediskrep.h; sourceTree = ""; }; + C2C931B30AB8BA1200F83950 /* SecCodeHost.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecCodeHost.cpp; sourceTree = ""; }; + C2CC30A00B8519CC005FA59D /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = CoreFoundation.framework; sourceTree = ""; }; + C2CC31040B8523AD005FA59D /* SecIntegrityLib.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecIntegrityLib.h; sourceTree = ""; }; + C2CC310E0B852424005FA59D /* SecIntegrityLib.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = SecIntegrityLib.c; sourceTree = ""; }; + C2CC31130B85254F005FA59D /* antlrplugin.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = antlrplugin.cpp; path = lib/antlrplugin.cpp; sourceTree = ""; }; + C2CC31140B85254F005FA59D /* antlrplugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = antlrplugin.h; path = lib/antlrplugin.h; sourceTree = ""; }; + C2CCF02F0A3F523D0085795A /* macho++.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = "macho++.cpp"; sourceTree = ""; }; + C2CCF0300A3F523D0085795A /* macho++.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "macho++.h"; sourceTree = ""; }; + C2D383120A237F47005C63A2 /* bundlediskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = bundlediskrep.cpp; sourceTree = ""; }; + C2D383130A237F47005C63A2 /* bundlediskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = bundlediskrep.h; sourceTree = ""; }; + C2D383140A237F47005C63A2 /* cdbuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cdbuilder.cpp; sourceTree = ""; }; + C2D383150A237F47005C63A2 /* cdbuilder.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cdbuilder.h; sourceTree = ""; }; + C2D383160A237F47005C63A2 /* codedirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = codedirectory.cpp; sourceTree = ""; }; + C2D383170A237F47005C63A2 /* codedirectory.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = codedirectory.h; sourceTree = ""; }; + C2D383180A237F47005C63A2 /* CodeSigning.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CodeSigning.h; sourceTree = ""; }; + C2D383190A237F47005C63A2 /* SecCodeHost.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecCodeHost.h; sourceTree = ""; }; + C2D3831A0A237F47005C63A2 /* cs.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cs.cpp; sourceTree = ""; }; + C2D3831B0A237F47005C63A2 /* cs.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cs.h; sourceTree = ""; }; + C2D3831C0A237F47005C63A2 /* CSCommon.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSCommon.h; sourceTree = ""; }; + C2D3831D0A237F47005C63A2 /* SecCode.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecCode.cpp; sourceTree = ""; }; + C2D3831E0A237F47005C63A2 /* SecCode.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecCode.h; sourceTree = ""; }; + C2D3831F0A237F47005C63A2 /* cskernel.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cskernel.cpp; sourceTree = ""; }; + C2D383200A237F47005C63A2 /* cskernel.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cskernel.h; sourceTree = ""; }; + C2D383210A237F47005C63A2 /* SecStaticCode.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecStaticCode.cpp; sourceTree = ""; }; + C2D383220A237F47005C63A2 /* SecStaticCode.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecStaticCode.h; sourceTree = ""; }; + C2D383230A237F47005C63A2 /* csprocess.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = csprocess.cpp; sourceTree = ""; }; + C2D383240A237F47005C63A2 /* csprocess.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = csprocess.h; sourceTree = ""; }; + C2D383250A237F47005C63A2 /* SecRequirement.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecRequirement.cpp; sourceTree = ""; }; + C2D383260A237F47005C63A2 /* SecRequirement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecRequirement.h; sourceTree = ""; }; + C2D383270A237F47005C63A2 /* diskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = diskrep.cpp; sourceTree = ""; }; + C2D383280A237F47005C63A2 /* diskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = diskrep.h; sourceTree = ""; }; + C2D3832B0A237F47005C63A2 /* filediskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = filediskrep.cpp; sourceTree = ""; }; + C2D3832C0A237F47005C63A2 /* filediskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = filediskrep.h; sourceTree = ""; }; + C2D3832D0A237F47005C63A2 /* Code.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = Code.cpp; sourceTree = ""; }; + C2D3832E0A237F47005C63A2 /* Code.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Code.h; sourceTree = ""; }; + C2D3832F0A237F47005C63A2 /* kerneldiskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = kerneldiskrep.cpp; sourceTree = ""; }; + C2D383300A237F47005C63A2 /* kerneldiskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = kerneldiskrep.h; sourceTree = ""; }; + C2D383310A237F47005C63A2 /* StaticCode.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = StaticCode.cpp; sourceTree = ""; }; + C2D383320A237F47005C63A2 /* StaticCode.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = StaticCode.h; sourceTree = ""; }; + C2D383330A237F47005C63A2 /* reqparser.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = reqparser.cpp; sourceTree = ""; }; + C2D383340A237F47005C63A2 /* reqparser.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqparser.h; sourceTree = ""; }; + C2D383350A237F47005C63A2 /* requirement.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = requirement.cpp; sourceTree = ""; }; + C2D383360A237F47005C63A2 /* requirement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = requirement.h; sourceTree = ""; }; + C2D383370A237F47005C63A2 /* Requirements.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = Requirements.cpp; sourceTree = ""; }; + C2D383380A237F47005C63A2 /* Requirements.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Requirements.h; sourceTree = ""; }; + C2D383390A237F47005C63A2 /* security_codesigning.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; path = security_codesigning.exp; sourceTree = ""; }; + C2D383EE0A23A9D3005C63A2 /* RequirementLexer.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = RequirementLexer.cpp; sourceTree = ""; }; + C2D383EF0A23A9D3005C63A2 /* RequirementLexer.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = RequirementLexer.hpp; sourceTree = ""; }; + C2D383F00A23A9D3005C63A2 /* RequirementParser.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = RequirementParser.cpp; sourceTree = ""; }; + C2D383F10A23A9D3005C63A2 /* RequirementParser.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = RequirementParser.hpp; sourceTree = ""; }; + C2D383F20A23A9D3005C63A2 /* RequirementParserTokenTypes.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = RequirementParserTokenTypes.hpp; sourceTree = ""; }; + C2D383F30A23A9D3005C63A2 /* RequirementParserTokenTypes.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = RequirementParserTokenTypes.txt; sourceTree = ""; }; + C2D8A07D0AE7F6E300F68F79 /* cfmunge.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cfmunge.cpp; sourceTree = ""; }; + C2D8A07E0AE7F6E300F68F79 /* cfmunge.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cfmunge.h; sourceTree = ""; }; + C2E2873C0B5D8D80009336A0 /* SecCodeHostLib.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = SecCodeHostLib.c; sourceTree = ""; }; + C2E911E00ADEBE3200275CB2 /* resources.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = resources.cpp; sourceTree = ""; }; + C2E911E10ADEBE3200275CB2 /* resources.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = resources.h; sourceTree = ""; }; + C2EF100E0A49BD89005A44BB /* renum.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = renum.cpp; sourceTree = ""; }; + C2EF100F0A49BD89005A44BB /* renum.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = renum.h; sourceTree = ""; }; + C2F6566C0BCBFB250078779E /* cserror.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cserror.cpp; sourceTree = ""; }; + C2F6566D0BCBFB250078779E /* cserror.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cserror.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworkTarget section */ + 4C56351D0540A55300DCF0C8 /* security_codesigning */ = { + isa = PBXFrameworkTarget; + buildConfigurationList = C263E67109A2971B000043F1 /* Build configuration list for PBXFrameworkTarget "security_codesigning" */; + buildPhases = ( + 4C5635180540A55300DCF0C8 /* Headers */, + 4CCB00500580097400981D43 /* CopyFiles */, + 4CCB00510580097600981D43 /* CopyFiles */, + 4CCB00520580097800981D43 /* CopyFiles */, + C26C39D3068368EC00ED5782 /* CopyFiles */, + ); + dependencies = ( + ); + name = security_codesigning; + productInstallPath = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + productName = security_codesigning; + productReference = 4C56351E0540A55300DCF0C8 /* security_codesigning.framework */; + productSettingsXML = " + + + + +"; + }; +/* End PBXFrameworkTarget section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4CA1FEBB052A3C8100F22E42 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2BC1F240B580D3A003EC9DC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2BC1F2D0B580D4B003EC9DC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C26B45C10B8A9C0A003C0ACA /* ucspc in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4C308388053237100028A8C6 /* lib */ = { + isa = PBXGroup; + children = ( + C2C1DF5F0A2E457E00D1B02B /* API */, + C2C1DF600A2E458D00D1B02B /* API Objects */, + C216C72D0AD59B22003B07D4 /* Signing Operations */, + C2C1DF630A2E45BF00D1B02B /* Code Directory */, + C2C1DF620A2E45B600D1B02B /* Requirements */, + C2C1DF640A2E45F500D1B02B /* Code Classes */, + C2C1DF610A2E459E00D1B02B /* Disk Representations */, + C2BC1F370B580DAE003EC9DC /* Static Support */, + C2CCF0360A3F524B0085795A /* Local Utilities */, + C2CC31160B852554005FA59D /* Security Plugins */, + ); + path = lib; + sourceTree = ""; + }; + 4CA1FEA7052A3C3800F22E42 = { + isa = PBXGroup; + children = ( + 4C308388053237100028A8C6 /* lib */, + C2D383F90A23A9D9005C63A2 /* cstemp */, + C2A4A43E0B7BABFD004AAC3F /* Pieces */, + C2CC30EF0B8519CF005FA59D /* Frameworks */, + 4CA1FEBF052A3C8100F22E42 /* Products */, + ); + sourceTree = ""; + }; + 4CA1FEBF052A3C8100F22E42 /* Products */ = { + isa = PBXGroup; + children = ( + 4CA1FEBE052A3C8100F22E42 /* security_codesigning */, + 4C56351E0540A55300DCF0C8 /* security_codesigning.framework */, + C2BC1F260B580D3A003EC9DC /* libintegrity.a */, + C2BC1F2F0B580D4B003EC9DC /* libcodehost.a */, + ); + name = Products; + sourceTree = ""; + }; + C216C72D0AD59B22003B07D4 /* Signing Operations */ = { + isa = PBXGroup; + children = ( + C236E3D60AD59446000F5140 /* signer.h */, + C236E3D50AD59446000F5140 /* signer.cpp */, + C236E3DA0AD595C2000F5140 /* signerutils.h */, + C236E3D90AD595C2000F5140 /* signerutils.cpp */, + ); + name = "Signing Operations"; + sourceTree = ""; + }; + C2A4A43E0B7BABFD004AAC3F /* Pieces */ = { + isa = PBXGroup; + children = ( + ); + name = Pieces; + sourceTree = ""; + }; + C2BC1F370B580DAE003EC9DC /* Static Support */ = { + isa = PBXGroup; + children = ( + C2CC31040B8523AD005FA59D /* SecIntegrityLib.h */, + C2CC310E0B852424005FA59D /* SecIntegrityLib.c */, + C2BC1F340B580DA7003EC9DC /* SecCodeHostLib.h */, + C2E2873C0B5D8D80009336A0 /* SecCodeHostLib.c */, + ); + name = "Static Support"; + sourceTree = ""; + }; + C2C1DF5F0A2E457E00D1B02B /* API */ = { + isa = PBXGroup; + children = ( + C2D383180A237F47005C63A2 /* CodeSigning.h */, + C2D3831C0A237F47005C63A2 /* CSCommon.h */, + C2D3831E0A237F47005C63A2 /* SecCode.h */, + C2D3831D0A237F47005C63A2 /* SecCode.cpp */, + C2D383220A237F47005C63A2 /* SecStaticCode.h */, + C2D383210A237F47005C63A2 /* SecStaticCode.cpp */, + C2D383260A237F47005C63A2 /* SecRequirement.h */, + C2D383250A237F47005C63A2 /* SecRequirement.cpp */, + C21EA3DC0AD2F81300E6E31C /* SecCodeSigner.h */, + C21EA3DB0AD2F81300E6E31C /* SecCodeSigner.cpp */, + C2D383190A237F47005C63A2 /* SecCodeHost.h */, + C2C931B30AB8BA1200F83950 /* SecCodeHost.cpp */, + C250F6C20B5EF1910076098F /* SecIntegrity.h */, + C250F6C60B5EF5B50076098F /* SecIntegrity.cpp */, + C2D383390A237F47005C63A2 /* security_codesigning.exp */, + ); + name = API; + sourceTree = ""; + }; + C2C1DF600A2E458D00D1B02B /* API Objects */ = { + isa = PBXGroup; + children = ( + C2D3831B0A237F47005C63A2 /* cs.h */, + C2D3831A0A237F47005C63A2 /* cs.cpp */, + C2D3832E0A237F47005C63A2 /* Code.h */, + C2D3832D0A237F47005C63A2 /* Code.cpp */, + C2D383320A237F47005C63A2 /* StaticCode.h */, + C2D383310A237F47005C63A2 /* StaticCode.cpp */, + C2D383380A237F47005C63A2 /* Requirements.h */, + C2D383370A237F47005C63A2 /* Requirements.cpp */, + C21EA3E20AD2FA0900E6E31C /* CodeSigner.h */, + C21EA3E10AD2FA0900E6E31C /* CodeSigner.cpp */, + ); + name = "API Objects"; + sourceTree = ""; + }; + C2C1DF610A2E459E00D1B02B /* Disk Representations */ = { + isa = PBXGroup; + children = ( + C2D383280A237F47005C63A2 /* diskrep.h */, + C2D383270A237F47005C63A2 /* diskrep.cpp */, + C2D3832C0A237F47005C63A2 /* filediskrep.h */, + C2D3832B0A237F47005C63A2 /* filediskrep.cpp */, + C2D383130A237F47005C63A2 /* bundlediskrep.h */, + C2D383120A237F47005C63A2 /* bundlediskrep.cpp */, + C2D383300A237F47005C63A2 /* kerneldiskrep.h */, + C2D3832F0A237F47005C63A2 /* kerneldiskrep.cpp */, + C2C3BC5E0BA1D6FE00E869D1 /* cfmdiskrep.h */, + C2C3BC5D0BA1D6FE00E869D1 /* cfmdiskrep.cpp */, + C25942430BA7095000877E56 /* foreigndiskrep.h */, + C25942420BA7095000877E56 /* foreigndiskrep.cpp */, + C2BD519B0A9392FD000FE43D /* machorep.h */, + C2BD519A0A9392FD000FE43D /* machorep.cpp */, + C2C3BCD20BA1E47E00E869D1 /* singlediskrep.h */, + C2C3BCD10BA1E47E00E869D1 /* singlediskrep.cpp */, + ); + name = "Disk Representations"; + sourceTree = ""; + }; + C2C1DF620A2E45B600D1B02B /* Requirements */ = { + isa = PBXGroup; + children = ( + C2C1DF8F0A2E4A2700D1B02B /* requirements.grammar */, + C2D383360A237F47005C63A2 /* requirement.h */, + C2D383350A237F47005C63A2 /* requirement.cpp */, + C2C1DFC20A2F820500D1B02B /* reqmaker.h */, + C2C1DFC10A2F820500D1B02B /* reqmaker.cpp */, + C2093AA70BB0948000EB8599 /* reqreader.h */, + C2093AA60BB0948000EB8599 /* reqreader.cpp */, + C2C1DFBA0A2F80EB00D1B02B /* reqinterp.h */, + C2C1DFB90A2F80EB00D1B02B /* reqinterp.cpp */, + C2D383340A237F47005C63A2 /* reqparser.h */, + C2D383330A237F47005C63A2 /* reqparser.cpp */, + C21CFC5E0A250D1C006CD5B1 /* reqdumper.h */, + C21CFC5D0A250D1C006CD5B1 /* reqdumper.cpp */, + ); + name = Requirements; + sourceTree = ""; + }; + C2C1DF630A2E45BF00D1B02B /* Code Directory */ = { + isa = PBXGroup; + children = ( + C2D383170A237F47005C63A2 /* codedirectory.h */, + C2D383160A237F47005C63A2 /* codedirectory.cpp */, + C2D383150A237F47005C63A2 /* cdbuilder.h */, + C2D383140A237F47005C63A2 /* cdbuilder.cpp */, + ); + name = "Code Directory"; + sourceTree = ""; + }; + C2C1DF640A2E45F500D1B02B /* Code Classes */ = { + isa = PBXGroup; + children = ( + C2D383200A237F47005C63A2 /* cskernel.h */, + C2D3831F0A237F47005C63A2 /* cskernel.cpp */, + C2D383240A237F47005C63A2 /* csprocess.h */, + C2D383230A237F47005C63A2 /* csprocess.cpp */, + C2BD60F90AC863FC0057FD3D /* csgeneric.h */, + C2BD60F80AC863FC0057FD3D /* csgeneric.cpp */, + ); + name = "Code Classes"; + sourceTree = ""; + }; + C2CC30EF0B8519CF005FA59D /* Frameworks */ = { + isa = PBXGroup; + children = ( + C2CC30A00B8519CC005FA59D /* CoreFoundation.framework */, + ); + name = Frameworks; + path = /System/Library/Frameworks; + sourceTree = ""; + }; + C2CC31160B852554005FA59D /* Security Plugins */ = { + isa = PBXGroup; + children = ( + C2CC31140B85254F005FA59D /* antlrplugin.h */, + C2CC31130B85254F005FA59D /* antlrplugin.cpp */, + ); + name = "Security Plugins"; + path = ..; + sourceTree = ""; + }; + C2CCF0360A3F524B0085795A /* Local Utilities */ = { + isa = PBXGroup; + children = ( + C2F6566D0BCBFB250078779E /* cserror.h */, + C2F6566C0BCBFB250078779E /* cserror.cpp */, + C2E911E10ADEBE3200275CB2 /* resources.h */, + C2E911E00ADEBE3200275CB2 /* resources.cpp */, + C259DFD50AD6D9BA00C9ACC6 /* sigblob.h */, + C259DFD40AD6D9BA00C9ACC6 /* sigblob.cpp */, + C2CCF0300A3F523D0085795A /* macho++.h */, + C2CCF02F0A3F523D0085795A /* macho++.cpp */, + C2D8A07E0AE7F6E300F68F79 /* cfmunge.h */, + C2D8A07D0AE7F6E300F68F79 /* cfmunge.cpp */, + C2EF100F0A49BD89005A44BB /* renum.h */, + C2EF100E0A49BD89005A44BB /* renum.cpp */, + C2A976A90B8A2E36008B4EA0 /* csutilities.h */, + C2A976A80B8A2E36008B4EA0 /* csutilities.cpp */, + ); + name = "Local Utilities"; + sourceTree = ""; + }; + C2D383F90A23A9D9005C63A2 /* cstemp */ = { + isa = PBXGroup; + children = ( + C2D383EF0A23A9D3005C63A2 /* RequirementLexer.hpp */, + C2D383EE0A23A9D3005C63A2 /* RequirementLexer.cpp */, + C2D383F10A23A9D3005C63A2 /* RequirementParser.hpp */, + C2D383F00A23A9D3005C63A2 /* RequirementParser.cpp */, + C2D383F20A23A9D3005C63A2 /* RequirementParserTokenTypes.hpp */, + C2D383F30A23A9D3005C63A2 /* RequirementParserTokenTypes.txt */, + C26B45C00B8A9C00003C0ACA /* ucspc */, + ); + path = cstemp; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 4C5635180540A55300DCF0C8 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + C2F656930BCBFFF40078779E /* cserror.h in Headers */, + C2A487540B79150C00849490 /* SecIntegrity.h in Headers */, + C2A487530B7914F400849490 /* SecCodeHostLib.h in Headers */, + C297DF250B014BB300E94EE0 /* SecCodeSigner.h in Headers */, + C2D8A0980AE7F74500F68F79 /* cfmunge.h in Headers */, + C2D3836F0A237F47005C63A2 /* cs.h in Headers */, + C2D383820A237F47005C63A2 /* Code.h in Headers */, + C2D383860A237F47005C63A2 /* StaticCode.h in Headers */, + C2D3838C0A237F47005C63A2 /* Requirements.h in Headers */, + C2BD61BD0AC9C77B0057FD3D /* csgeneric.h in Headers */, + C2D383670A237F47005C63A2 /* bundlediskrep.h in Headers */, + C2D383690A237F47005C63A2 /* cdbuilder.h in Headers */, + C2D3836B0A237F47005C63A2 /* codedirectory.h in Headers */, + C2D3836C0A237F47005C63A2 /* CodeSigning.h in Headers */, + C2D3836D0A237F47005C63A2 /* SecCodeHost.h in Headers */, + C2D383700A237F47005C63A2 /* CSCommon.h in Headers */, + C2D383720A237F47005C63A2 /* SecCode.h in Headers */, + C2D383740A237F47005C63A2 /* cskernel.h in Headers */, + C2D383760A237F47005C63A2 /* SecStaticCode.h in Headers */, + C2D383780A237F47005C63A2 /* csprocess.h in Headers */, + C2D3837A0A237F47005C63A2 /* SecRequirement.h in Headers */, + C2D3837C0A237F47005C63A2 /* diskrep.h in Headers */, + C2D383800A237F47005C63A2 /* filediskrep.h in Headers */, + C2D383840A237F47005C63A2 /* kerneldiskrep.h in Headers */, + C2D383880A237F47005C63A2 /* reqparser.h in Headers */, + C2D3838A0A237F47005C63A2 /* requirement.h in Headers */, + C21CFC620A250D1C006CD5B1 /* reqdumper.h in Headers */, + C2093AC80BB0967D00EB8599 /* reqreader.h in Headers */, + C2C1DFBE0A2F80EB00D1B02B /* reqinterp.h in Headers */, + C2C1DFC60A2F820500D1B02B /* reqmaker.h in Headers */, + C2CCF0340A3F523D0085795A /* macho++.h in Headers */, + C2EF10130A49BD89005A44BB /* renum.h in Headers */, + C2BD519F0A9392FD000FE43D /* machorep.h in Headers */, + C2CC31050B8523AD005FA59D /* SecIntegrityLib.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CA1FEB9052A3C8100F22E42 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + C2CC31060B8523F8005FA59D /* SecIntegrityLib.h in Headers */, + C2A487550B79152A00849490 /* SecIntegrity.h in Headers */, + C2A487560B79152A00849490 /* SecCodeHostLib.h in Headers */, + C2D3839A0A238132005C63A2 /* CodeSigning.h in Headers */, + C2D383460A237F47005C63A2 /* CSCommon.h in Headers */, + C2D383480A237F47005C63A2 /* SecCode.h in Headers */, + C2D3834C0A237F47005C63A2 /* SecStaticCode.h in Headers */, + C2D383500A237F47005C63A2 /* SecRequirement.h in Headers */, + C2C931B50AB8BA8200F83950 /* SecCodeHost.h in Headers */, + C2C1DFC40A2F820500D1B02B /* reqmaker.h in Headers */, + C2093AA90BB0948000EB8599 /* reqreader.h in Headers */, + C2C1DFBC0A2F80EB00D1B02B /* reqinterp.h in Headers */, + C2EF10110A49BD89005A44BB /* renum.h in Headers */, + C21EA3DE0AD2F81300E6E31C /* SecCodeSigner.h in Headers */, + C236E3D80AD59446000F5140 /* signer.h in Headers */, + C236E3DC0AD595C2000F5140 /* signerutils.h in Headers */, + C259DFD70AD6D9BA00C9ACC6 /* sigblob.h in Headers */, + C2E911E30ADEBE3200275CB2 /* resources.h in Headers */, + C2D8A0800AE7F6E300F68F79 /* cfmunge.h in Headers */, + C2A976AB0B8A2E36008B4EA0 /* csutilities.h in Headers */, + C2C3BC600BA1D6FE00E869D1 /* cfmdiskrep.h in Headers */, + C2C3BCD40BA1E47E00E869D1 /* singlediskrep.h in Headers */, + C25942450BA7095000877E56 /* foreigndiskrep.h in Headers */, + C2F6566F0BCBFB250078779E /* cserror.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2BC1F220B580D3A003EC9DC /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + C250F6C30B5EF1910076098F /* SecIntegrity.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2BC1F2B0B580D4B003EC9DC /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + C2BC1F350B580DA7003EC9DC /* SecCodeHostLib.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXLibraryTarget section */ + 4CA1FEBD052A3C8100F22E42 /* libsecurity_codesigning */ = { + isa = PBXLibraryTarget; + buildConfigurationList = C263E67509A2971B000043F1 /* Build configuration list for PBXLibraryTarget "libsecurity_codesigning" */; + buildPhases = ( + 4CA1FEB9052A3C8100F22E42 /* Headers */, + 4CA1FEBA052A3C8100F22E42 /* Sources */, + 4CA1FEBB052A3C8100F22E42 /* Frameworks */, + 4C789C16055AF56700B6FC95 /* ShellScript */, + 4CD0D468055B0D40001715CB /* ShellScript */, + ); + dependencies = ( + 4C7502910540C69C00056564 /* PBXTargetDependency */, + C21E3F8B0A23AE10006558D6 /* PBXTargetDependency */, + ); + name = libsecurity_codesigning; + productName = libsecurity_codesigning; + productReference = 4CA1FEBE052A3C8100F22E42 /* security_codesigning */; + }; +/* End PBXLibraryTarget section */ + +/* Begin PBXNativeTarget section */ + C2BC1F250B580D3A003EC9DC /* libintegrity */ = { + isa = PBXNativeTarget; + buildConfigurationList = C2BC1F270B580D3F003EC9DC /* Build configuration list for PBXNativeTarget "libintegrity" */; + buildPhases = ( + C2BC1F220B580D3A003EC9DC /* Headers */, + C2BC1F230B580D3A003EC9DC /* Sources */, + C2BC1F240B580D3A003EC9DC /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libintegrity; + productName = libintegrity; + productReference = C2BC1F260B580D3A003EC9DC /* libintegrity.a */; + productType = "com.apple.product-type.library.static"; + }; + C2BC1F2E0B580D4B003EC9DC /* libcodehost */ = { + isa = PBXNativeTarget; + buildConfigurationList = C2BC1F300B580D69003EC9DC /* Build configuration list for PBXNativeTarget "libcodehost" */; + buildPhases = ( + C26B45C30B8A9C1A003C0ACA /* Prepare ucspc */, + C2BC1F2B0B580D4B003EC9DC /* Headers */, + C2BC1F2C0B580D4B003EC9DC /* Sources */, + C2BC1F2D0B580D4B003EC9DC /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libcodehost; + productName = libcodehost; + productReference = C2BC1F2F0B580D4B003EC9DC /* libcodehost.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4CA1FEAB052A3C3800F22E42 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C263E67909A2971B000043F1 /* Build configuration list for PBXProject "libsecurity_codesigning" */; + compatibilityVersion = "Xcode 2.4"; + hasScannedForEncodings = 1; + mainGroup = 4CA1FEA7052A3C3800F22E42; + productRefGroup = 4CA1FEBF052A3C8100F22E42 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C2E2873F0B5D8F8F009336A0 /* Everything */, + 4CA1FEBD052A3C8100F22E42 /* libsecurity_codesigning */, + 4C56351D0540A55300DCF0C8 /* security_codesigning */, + C2D383B80A23A8C4005C63A2 /* Requirements Language */, + C2BC1F250B580D3A003EC9DC /* libintegrity */, + C2BC1F2E0B580D4B003EC9DC /* libcodehost */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 4C789C16055AF56700B6FC95 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "for variant in ${BUILD_VARIANTS}\ndo\n\tpostfix=`echo _${variant} | sed 's/_normal//'`\n\tln -fs \"../../../${PRODUCT_NAME}${postfix}\" ${SYMROOT}/${PRODUCT_NAME}.framework/Versions/A\n\tln -fs \"Versions/Current/${PRODUCT_NAME}${postfix}\" ${SYMROOT}/${PRODUCT_NAME}.framework\ndone"; + }; + 4CD0D468055B0D40001715CB /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "for variant in ${BUILD_VARIANTS}\ndo\n\tpostfix=`echo _${variant} | sed 's/_normal//'`\n\tcp -p \"${SYMROOT}/${PRODUCT_NAME}${postfix}\" \"${DSTROOT}/usr/local/SecurityPieces/Components/Security/${PRODUCT_NAME}.framework/Versions/A\"\n\tranlib \"${DSTROOT}/usr/local/SecurityPieces/Components/Security/${PRODUCT_NAME}.framework/Versions/A/${PRODUCT_NAME}${postfix}\"\n\tln -fs \"Versions/Current/${PRODUCT_NAME}${postfix}\" \"${DSTROOT}/usr/local/SecurityPieces/Components/Security/${PRODUCT_NAME}.framework\"\ndone"; + }; + C26B45C30B8A9C1A003C0ACA /* Prepare ucspc */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Prepare ucspc"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "cstemp=\"${BUILT_PRODUCTS_DIR}/cstemp\"\nif [ -r \"${BUILT_PRODUCTS_DIR}/ucspc\" ]; then\t# local build case\n\tcp \"${BUILT_PRODUCTS_DIR}/ucspc\" \"$cstemp\"\nelse\t\t\t\t\t\t\t\t\t\t# buildit build case\n\tcp \"/usr/local/SecurityPieces/Frameworks/ucspc.framework/Versions/A/ucspc\" \"$cstemp\"\nfi\n"; + }; + C2D383B70A23A8C4005C63A2 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/requirements.grammar", + ); + outputPaths = ( + "$(TEMPDIR)/RequirementLexer.hpp", + "$(TEMPDIR)/RequirementLexer.cpp", + "$(TEMPDIR)/RequirementParser.hpp", + "$(TEMPDIR)/RequirementParser.cpp", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "antlr=/usr/local/bin/antlr.jar\nDEBUG=\"\"\nmkdir -p $TEMPDIR\nrm -f $TEMPDIR/Requirement{Parser,Lexer}*\njava -cp \"$antlr\" antlr.Tool -o $TEMPDIR $DEBUG requirements.grammar\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4CA1FEBA052A3C8100F22E42 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C2D3833C0A237F47005C63A2 /* bundlediskrep.cpp in Sources */, + C2D3833E0A237F47005C63A2 /* cdbuilder.cpp in Sources */, + C2D383400A237F47005C63A2 /* codedirectory.cpp in Sources */, + C2D383440A237F47005C63A2 /* cs.cpp in Sources */, + C2D383470A237F47005C63A2 /* SecCode.cpp in Sources */, + C2D383490A237F47005C63A2 /* cskernel.cpp in Sources */, + C2D3834B0A237F47005C63A2 /* SecStaticCode.cpp in Sources */, + C2D3834D0A237F47005C63A2 /* csprocess.cpp in Sources */, + C2D3834F0A237F47005C63A2 /* SecRequirement.cpp in Sources */, + C2D383510A237F47005C63A2 /* diskrep.cpp in Sources */, + C2D383550A237F47005C63A2 /* filediskrep.cpp in Sources */, + C2D383570A237F47005C63A2 /* Code.cpp in Sources */, + C2D383590A237F47005C63A2 /* kerneldiskrep.cpp in Sources */, + C2D3835B0A237F47005C63A2 /* StaticCode.cpp in Sources */, + C2D3835D0A237F47005C63A2 /* reqparser.cpp in Sources */, + C2C1DF140A2E3D7200D1B02B /* requirement.cpp in Sources */, + C2D383610A237F47005C63A2 /* Requirements.cpp in Sources */, + C2D383630A237F47005C63A2 /* security_codesigning.exp in Sources */, + C21CFC5F0A250D1C006CD5B1 /* reqdumper.cpp in Sources */, + C2C1DFBB0A2F80EB00D1B02B /* reqinterp.cpp in Sources */, + C2C1DFC30A2F820500D1B02B /* reqmaker.cpp in Sources */, + C224635F0B8620F800626F1B /* RequirementParser.cpp in Sources */, + C22463600B8620F800626F1B /* RequirementLexer.cpp in Sources */, + C22463610B86210100626F1B /* antlrplugin.cpp in Sources */, + C2CCF0310A3F523D0085795A /* macho++.cpp in Sources */, + C2EF10100A49BD89005A44BB /* renum.cpp in Sources */, + C2BD519C0A9392FD000FE43D /* machorep.cpp in Sources */, + C2C931B40AB8BA1200F83950 /* SecCodeHost.cpp in Sources */, + C2BD60FA0AC863FC0057FD3D /* csgeneric.cpp in Sources */, + C21EA3DD0AD2F81300E6E31C /* SecCodeSigner.cpp in Sources */, + C21EA3E30AD2FA0900E6E31C /* CodeSigner.cpp in Sources */, + C236E3D70AD59446000F5140 /* signer.cpp in Sources */, + C236E3DB0AD595C2000F5140 /* signerutils.cpp in Sources */, + C259DFD60AD6D9BA00C9ACC6 /* sigblob.cpp in Sources */, + C2E911E20ADEBE3200275CB2 /* resources.cpp in Sources */, + C2D8A07F0AE7F6E300F68F79 /* cfmunge.cpp in Sources */, + C2A976AA0B8A2E36008B4EA0 /* csutilities.cpp in Sources */, + C2C3BC5F0BA1D6FE00E869D1 /* cfmdiskrep.cpp in Sources */, + C2C3BCD30BA1E47E00E869D1 /* singlediskrep.cpp in Sources */, + C25942440BA7095000877E56 /* foreigndiskrep.cpp in Sources */, + C2093AA80BB0948000EB8599 /* reqreader.cpp in Sources */, + C2F6566E0BCBFB250078779E /* cserror.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2BC1F230B580D3A003EC9DC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C2CC310F0B852424005FA59D /* SecIntegrityLib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2BC1F2C0B580D4B003EC9DC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C2E2873D0B5D8D80009336A0 /* SecCodeHostLib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 4C7502910540C69C00056564 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C56351D0540A55300DCF0C8 /* security_codesigning */; + targetProxy = 4C7502900540C69C00056564 /* PBXContainerItemProxy */; + }; + C21E3F8B0A23AE10006558D6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C2D383B80A23A8C4005C63A2 /* Requirements Language */; + targetProxy = C21E3F8A0A23AE10006558D6 /* PBXContainerItemProxy */; + }; + C250F6C50B5EF4E40076098F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C2BC1F250B580D3A003EC9DC /* libintegrity */; + targetProxy = C250F6C40B5EF4E40076098F /* PBXContainerItemProxy */; + }; + C2E287410B5D8F97009336A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CA1FEBD052A3C8100F22E42 /* libsecurity_codesigning */; + targetProxy = C2E287400B5D8F97009336A0 /* PBXContainerItemProxy */; + }; + C2E287430B5D8F9A009336A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C2BC1F2E0B580D4B003EC9DC /* libcodehost */; + targetProxy = C2E287420B5D8F9A009336A0 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + C263E67209A2971B000043F1 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_VARIANTS = debug; + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + INSTALL_PATH = /usr/local/SecurityPieces/Components/Security; + PRODUCT_NAME = security_codesigning; + WRAPPER_EXTENSION = framework; + ZERO_LINK = YES; + }; + name = Development; + }; + C263E67309A2971B000043F1 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_VERSION = A; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + INSTALL_PATH = /usr/local/SecurityPieces/Components/Security; + PRODUCT_NAME = security_codesigning; + WRAPPER_EXTENSION = framework; + ZERO_LINK = NO; + }; + name = Deployment; + }; + C263E67409A2971B000043F1 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_VERSION = A; + INSTALL_PATH = /usr/local/SecurityPieces/Components/Security; + PRODUCT_NAME = security_codesigning; + WRAPPER_EXTENSION = framework; + }; + name = Default; + }; + C263E67609A2971B000043F1 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_VARIANTS = debug; + CURRENT_PROJECT_VERSION = 32568; + FRAMEWORK_SEARCH_PATHS = ( + /usr/local/SecurityPieces/Frameworks, + /usr/local/SecurityPieces/Components/Security, + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + HEADER_SEARCH_PATHS = ( + "$(BUILT_PRODUCTS_DIR)/SecurityPieces/Headers", + "$(BUILT_PRODUCTS_DIR)/SecurityPieces/PrivateHeaders", + "$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders", + ); + LIBRARY_STYLE = "\U0001STATIC"; + OPT_CFLAGS = "-DNDEBUG -Os $(OPT_INLINEFLAGS)"; + OPT_CPPFLAGS = "$(OPT_CFLAGS)"; + OPT_INLINEFLAGS = "-finline-functions"; + OPT_LDFLAGS = ""; + OTHER_ASFLAGS_debug = "$(OTHER_CFLAGS)"; + OTHER_ASFLAGS_normal = "-DNDEBUG $(OTHER_CFLAGS)"; + OTHER_ASFLAGS_profile = "-DNDEBUG $(OTHER_CFLAGS) -pg"; + OTHER_CFLAGS_debug = "$(OTHER_CFLAGS) -O0 -fno-inline"; + OTHER_CFLAGS_normal = "$(OPT_CFLAGS) $(OTHER_CFLAGS)"; + OTHER_CFLAGS_profile = "$(OPT_CFLAGS) $(OTHER_CFLAGS) -pg"; + OTHER_CPLUSPLUSFLAGS_debug = "$(OTHER_CFLAGS) -O0 -fno-inline"; + OTHER_CPLUSPLUSFLAGS_normal = "$(OPT_CPPFLAGS) $(OTHER_CFLAGS)"; + OTHER_CPLUSPLUSFLAGS_profile = "$(OPT_CPPFLAGS) $(OTHER_CFLAGS) -pg"; + OTHER_LDFLAGS_debug = "$(OTHER_LDFLAGS)"; + OTHER_LDFLAGS_normal = "$(OPT_LDFLAGS) $(OTHER_LDFLAGS)"; + OTHER_LDFLAGS_profile = "$(OPT_LDFLAGS) $(OTHER_LDFLAGS) -pg"; + PRIVATE_HEADER_DIR = "$(DSTROOT)/usr/local/SecurityPieces/PrivateHeaders/Security"; + PRODUCT_NAME = security_codesigning; + PUBLIC_HEADER_DIR = "$(DSTROOT)/usr/local/SecurityPieces/Headers/Security"; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + ZERO_LINK = YES; + }; + name = Development; + }; + C263E67709A2971B000043F1 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_VARIANTS = ( + normal, + debug, + ); + CURRENT_PROJECT_VERSION = 32568; + FRAMEWORK_SEARCH_PATHS = ( + /usr/local/SecurityPieces/Frameworks, + /usr/local/SecurityPieces/Components/Security, + ); + GCC_ENABLE_FIX_AND_CONTINUE = NO; + HEADER_SEARCH_PATHS = ( + "$(BUILT_PRODUCTS_DIR)/SecurityPieces/Headers", + "$(BUILT_PRODUCTS_DIR)/SecurityPieces/PrivateHeaders", + "$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders", + ); + LIBRARY_STYLE = STATIC; + OPT_CFLAGS = "-DNDEBUG -Os $(OPT_INLINEFLAGS)"; + OPT_CPPFLAGS = "$(OPT_CFLAGS)"; + OPT_INLINEFLAGS = "-finline-functions"; + OPT_LDFLAGS = ""; + OTHER_ASFLAGS_debug = "$(OTHER_CFLAGS)"; + OTHER_ASFLAGS_normal = "-DNDEBUG $(OTHER_CFLAGS)"; + OTHER_ASFLAGS_profile = "-DNDEBUG $(OTHER_CFLAGS) -pg"; + OTHER_CFLAGS_debug = "$(OTHER_CFLAGS) -O0 -fno-inline"; + OTHER_CFLAGS_normal = "$(OPT_CFLAGS) $(OTHER_CFLAGS)"; + OTHER_CFLAGS_profile = "$(OPT_CFLAGS) $(OTHER_CFLAGS) -pg"; + OTHER_CPLUSPLUSFLAGS_debug = "$(OTHER_CFLAGS) -O0 -fno-inline"; + OTHER_CPLUSPLUSFLAGS_normal = "$(OPT_CPPFLAGS) $(OTHER_CFLAGS)"; + OTHER_CPLUSPLUSFLAGS_profile = "$(OPT_CPPFLAGS) $(OTHER_CFLAGS) -pg"; + OTHER_LDFLAGS_debug = "$(OTHER_LDFLAGS)"; + OTHER_LDFLAGS_normal = "$(OPT_LDFLAGS) $(OTHER_LDFLAGS)"; + OTHER_LDFLAGS_profile = "$(OPT_LDFLAGS) $(OTHER_LDFLAGS) -pg"; + PRIVATE_HEADER_DIR = "$(DSTROOT)/usr/local/SecurityPieces/PrivateHeaders/Security"; + PRODUCT_NAME = security_codesigning; + PUBLIC_HEADER_DIR = "$(DSTROOT)/usr/local/SecurityPieces/Headers/Security"; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + ZERO_LINK = NO; + }; + name = Deployment; + }; + C263E67809A2971B000043F1 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_VARIANTS = ( + normal, + debug, + ); + CURRENT_PROJECT_VERSION = 32568; + FRAMEWORK_SEARCH_PATHS = ( + /usr/local/SecurityPieces/Frameworks, + /usr/local/SecurityPieces/Components/Security, + ); + HEADER_SEARCH_PATHS = ( + "$(BUILT_PRODUCTS_DIR)/SecurityPieces/Headers", + "$(BUILT_PRODUCTS_DIR)/SecurityPieces/PrivateHeaders", + "$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders", + ); + LIBRARY_STYLE = STATIC; + OPT_CFLAGS = "-DNDEBUG -Os $(OPT_INLINEFLAGS)"; + OPT_CPPFLAGS = "$(OPT_CFLAGS)"; + OPT_INLINEFLAGS = "-finline-functions"; + OPT_LDFLAGS = ""; + OTHER_ASFLAGS_debug = "$(OTHER_CFLAGS)"; + OTHER_ASFLAGS_normal = "-DNDEBUG $(OTHER_CFLAGS)"; + OTHER_ASFLAGS_profile = "-DNDEBUG $(OTHER_CFLAGS) -pg"; + OTHER_CFLAGS_debug = "$(OTHER_CFLAGS) -O0 -fno-inline"; + OTHER_CFLAGS_normal = "$(OPT_CFLAGS) $(OTHER_CFLAGS)"; + OTHER_CFLAGS_profile = "$(OPT_CFLAGS) $(OTHER_CFLAGS) -pg"; + OTHER_CPLUSPLUSFLAGS_debug = "$(OTHER_CFLAGS) -O0 -fno-inline"; + OTHER_CPLUSPLUSFLAGS_normal = "$(OPT_CPPFLAGS) $(OTHER_CFLAGS)"; + OTHER_CPLUSPLUSFLAGS_profile = "$(OPT_CPPFLAGS) $(OTHER_CFLAGS) -pg"; + OTHER_LDFLAGS_debug = "$(OTHER_LDFLAGS)"; + OTHER_LDFLAGS_normal = "$(OPT_LDFLAGS) $(OTHER_LDFLAGS)"; + OTHER_LDFLAGS_profile = "$(OPT_LDFLAGS) $(OTHER_LDFLAGS) -pg"; + PRIVATE_HEADER_DIR = "$(DSTROOT)/usr/local/SecurityPieces/PrivateHeaders/Security"; + PRODUCT_NAME = security_codesigning; + PUBLIC_HEADER_DIR = "$(DSTROOT)/usr/local/SecurityPieces/Headers/Security"; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Default; + }; + C263E67A09A2971B000043F1 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)"; + CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)"; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + OTHER_CFLAGS = "$(SEC_DEFINES)"; + TEMPDIR = "$(BUILT_PRODUCTS_DIR)/cstemp"; + }; + name = Development; + }; + C263E67B09A2971B000043F1 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)"; + CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)"; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + OTHER_CFLAGS = "$(SEC_DEFINES)"; + TEMPDIR = "$(BUILT_PRODUCTS_DIR)/cstemp"; + }; + name = Deployment; + }; + C263E67C09A2971B000043F1 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)"; + CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)"; + HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders; + OTHER_CFLAGS = "$(SEC_DEFINES)"; + TEMPDIR = "$(BUILT_PRODUCTS_DIR)/cstemp"; + UNSTRIPPED_PRODUCT = YES; + }; + name = Default; + }; + C2BC1F280B580D3F003EC9DC /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = /usr/local/SecurityPieces/Frameworks; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/lib; + PREBINDING = NO; + PRODUCT_NAME = integrity; + ZERO_LINK = YES; + }; + name = Development; + }; + C2BC1F290B580D3F003EC9DC /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = /usr/local/SecurityPieces/Frameworks; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + PREBINDING = NO; + PRODUCT_NAME = integrity; + ZERO_LINK = NO; + }; + name = Deployment; + }; + C2BC1F2A0B580D3F003EC9DC /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = /usr/local/SecurityPieces/Frameworks; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + PREBINDING = NO; + PRODUCT_NAME = integrity; + ZERO_LINK = YES; + }; + name = Default; + }; + C2BC1F310B580D69003EC9DC /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = /usr/local/SecurityPieces/Frameworks; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/lib; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", + "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", + ); + PREBINDING = NO; + PRODUCT_NAME = codehost; + ZERO_LINK = YES; + }; + name = Development; + }; + C2BC1F320B580D69003EC9DC /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = /usr/local/SecurityPieces/Frameworks; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(LIBRARY_SEARCH_PATHS_QUOTED_1)", + "$(LIBRARY_SEARCH_PATHS_QUOTED_1)", + ); + PREBINDING = NO; + PRODUCT_NAME = codehost; + ZERO_LINK = NO; + }; + name = Deployment; + }; + C2BC1F330B580D69003EC9DC /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = /usr/local/SecurityPieces/Frameworks; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(LIBRARY_SEARCH_PATHS_QUOTED_1)", + "$(LIBRARY_SEARCH_PATHS_QUOTED_1)", + ); + PREBINDING = NO; + PRODUCT_NAME = codehost; + ZERO_LINK = YES; + }; + name = Default; + }; + C2D383C10A23A8E3005C63A2 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = "Requirements Language"; + }; + name = Development; + }; + C2D383C20A23A8E3005C63A2 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + PRODUCT_NAME = "Requirements Language"; + ZERO_LINK = NO; + }; + name = Deployment; + }; + C2D383C30A23A8E3005C63A2 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "Requirements Language"; + }; + name = Default; + }; + C2E287480B5D8FD8009336A0 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = Everything; + }; + name = Development; + }; + C2E287490B5D8FD8009336A0 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = Everything; + ZERO_LINK = NO; + }; + name = Deployment; + }; + C2E2874A0B5D8FD8009336A0 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = Everything; + }; + name = Default; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C263E67109A2971B000043F1 /* Build configuration list for PBXFrameworkTarget "security_codesigning" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C263E67209A2971B000043F1 /* Development */, + C263E67309A2971B000043F1 /* Deployment */, + C263E67409A2971B000043F1 /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; + C263E67509A2971B000043F1 /* Build configuration list for PBXLibraryTarget "libsecurity_codesigning" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C263E67609A2971B000043F1 /* Development */, + C263E67709A2971B000043F1 /* Deployment */, + C263E67809A2971B000043F1 /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; + C263E67909A2971B000043F1 /* Build configuration list for PBXProject "libsecurity_codesigning" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C263E67A09A2971B000043F1 /* Development */, + C263E67B09A2971B000043F1 /* Deployment */, + C263E67C09A2971B000043F1 /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; + C2BC1F270B580D3F003EC9DC /* Build configuration list for PBXNativeTarget "libintegrity" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2BC1F280B580D3F003EC9DC /* Development */, + C2BC1F290B580D3F003EC9DC /* Deployment */, + C2BC1F2A0B580D3F003EC9DC /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; + C2BC1F300B580D69003EC9DC /* Build configuration list for PBXNativeTarget "libcodehost" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2BC1F310B580D69003EC9DC /* Development */, + C2BC1F320B580D69003EC9DC /* Deployment */, + C2BC1F330B580D69003EC9DC /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; + C2D383C00A23A8E3005C63A2 /* Build configuration list for PBXAggregateTarget "Requirements Language" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2D383C10A23A8E3005C63A2 /* Development */, + C2D383C20A23A8E3005C63A2 /* Deployment */, + C2D383C30A23A8E3005C63A2 /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; + C2E287470B5D8FD8009336A0 /* Build configuration list for PBXAggregateTarget "Everything" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2E287480B5D8FD8009336A0 /* Development */, + C2E287490B5D8FD8009336A0 /* Deployment */, + C2E2874A0B5D8FD8009336A0 /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4CA1FEAB052A3C3800F22E42 /* Project object */; +} diff --git a/req/cfm.ireqs b/req/cfm.ireqs new file mode 100644 index 0000000..02d53dc --- /dev/null +++ b/req/cfm.ireqs @@ -0,0 +1,4 @@ +# +# Suggested internal requirements for CFM code +# +host => anchor apple and identifier com.apple.LaunchCFMApp // CFM runner tool diff --git a/req/ppc.ireqs b/req/ppc.ireqs new file mode 100644 index 0000000..618abaa --- /dev/null +++ b/req/ppc.ireqs @@ -0,0 +1,4 @@ +# +# Suggested internal requirements for ppc (only) Mach-O code +# +host => anchor apple and identifier com.apple.translate // Rosetta diff --git a/req/workaround.ireqs b/req/workaround.ireqs new file mode 100644 index 0000000..4252ada --- /dev/null +++ b/req/workaround.ireqs @@ -0,0 +1 @@ +anchor apple and (identifier com.apple.translate or identifier com.apple.LaunchCFMApp) diff --git a/requirements.grammar b/requirements.grammar new file mode 100644 index 0000000..296847f --- /dev/null +++ b/requirements.grammar @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// Requirements Language Grammar +// +// This file describes two distinct (related) grammars: +// Requirement => single requirement (Requirement *) +// RequirementSet => set of labeled requirements (Requirements *) +// The grammar can "autosense" - i.e. recognize which one it's fed and +// return appropriate semantic data. +// The semantic data compiled is a malloc'ed BlobCore * - a Requirement +// object or a SuperBlob containing multiple Requirements. +// +header "post_include_hpp" { +#include "requirement.h" +using namespace CodeSigning; +typedef Requirement::Maker Maker; +} + +header "post_include_cpp" { +#include "requirement.h" +#include "reqmaker.h" +#include "csutilities.h" +#include +#include +using namespace CodeSigning; +typedef Requirement::Maker Maker; +} + +options { + language="Cpp"; + namespace="Security_CodeSigning"; + namespaceStd="std"; + namespaceAntlr="antlr"; + genHashLines=false; +} + + +{ + // + // Collect error messages + // + void RequirementParser::reportError(const antlr::RecognitionException &ex) + { + errors += ex.toString() + "\n"; + } + + void RequirementParser::reportError(const std::string &s) + { + errors += s + "\n"; + } + + + // + // Parser helper functions + // + string RequirementParser::hexString(const string &s) + { + if (s.size() % 2) + throw antlr::SemanticException("odd number of digits"); + const char *p = s.data(); + string result; + for (unsigned n = 0; n < s.length(); n += 2) { + char c; + sscanf(p+n, "%2hhx", &c); + result.push_back(c); + } + return result; + } + + void RequirementParser::hashString(const string &s, SHA1::Digest hash) + { + if (s.size() != 2 * SHA1::digestLength) + throw antlr::SemanticException("invalid hash length"); + memcpy(hash, hexString(s).data(), SHA1::digestLength); + } +} + +class RequirementParser extends Parser; + +options { + k=2; +} + +{ +public: + std::string errors; + void reportError(const antlr::RecognitionException &ex); + void reportError(const std::string &s); + +private: + static string hexString(const string &s); + static void hashString(const string &s, SHA1::Digest hash); +} + + +// +// Compound target; compiles single requirements or requirement sets +// and returns them as a BlobCore. +// +autosense returns [BlobCore *result = NULL] + : result=requirement + | result=requirementSet + ; + + +// +// A Requirements Set. +// +requirementSet returns [Requirements *result = NULL] + { Requirements::Maker maker; } + : ( { uint32_t t; Requirement *req; } + t=requirementType ARROW req=requirementElement + { maker.add(t, req); } + )+ + { result = errors.empty() ? maker() : NULL; } + EOF + ; + +requirementType returns [uint32_t type = kSecInvalidRequirementType] + : "guest" + { type = kSecGuestRequirementType; } + | "host" + { type = kSecHostRequirementType; } + | "designated" + { type = kSecDesignatedRequirementType; } + | "library" + { type = kSecLibraryRequirementType; } + | stype:INTEGER + { type = atol(stype->getText().c_str()); } + ; + + +// +// A single Requirement (untyped) +// +requirement returns [Requirement *result = NULL] + : result = requirementElement + EOF + ; + +requirementElement returns [Requirement *result = NULL] + { Requirement::Maker maker; } + : expr[maker] + { result = maker(); } + ( fluff )* + ; + + +// +// Classic recursive expressions +// +expr[Maker &maker] + { Maker::Label label(maker); } + : term[maker] ( "and" { maker.insert(label) = opAnd; } term[maker] )* + ; + +term[Maker &maker] + { Maker::Label label(maker); } + : primary[maker] ( "or" { maker.insert(label) = opOr; } primary[maker] )* + ; + +primary[Maker &maker] + : LPAREN expr[maker] RPAREN + | NOT { maker.put(opNot); } primary[maker] + | ( "always" | "true" ) + { maker.put(opTrue); } + | ( "never" | "false" ) + { maker.put(opFalse); } + | certspec[maker] + | infospec[maker] + | "identifier" { string code; } eql code=identifierString + { maker.ident(code); } + | "cdhash" { SHA1::Digest digest; } hash[digest] + { maker.cdhash(digest); } + ; + + +// +// Certificate specifications restrict certificates in the signing chain +// +certspec[Maker &maker] + : "anchor" "apple" + { maker.put(opAppleAnchor); } + | ( "certificate" | "cert" | "anchor" ) "trusted" + { maker.trustedAnchor(); } + | ( "certificate" | "cert" ) { int slot; } slot=certSlot + ( certslotspec[maker, slot] | "trusted" { maker.trustedAnchor(slot); } ) + | "anchor" certslotspec[maker, Requirement::anchorCert] + ; + +certslotspec[Maker &maker, int slot] { string key; } + : eql { SHA1::Digest digest; } certificateDigest[digest] + { maker.anchor(slot, digest); } + | key=bracketKey + { maker.put(opCertField); maker.put(slot); maker.put(key); } + match_suffix[maker] + ; + + +// +// Info specifications place conditions on entries in the Info.plist +// +infospec[Maker &maker] { string key; } + : "info" key=bracketKey + { maker.put(opInfoKeyField); maker.put(key); } + match_suffix[maker] + ; + +match_suffix[Maker &maker] + : empty + { maker.put(matchExists); } + | EQL { string value; } value=datavalue + { maker.put(matchEqual); maker.put(value); } + | SUBS { string value; } value=datavalue + { maker.put(matchContains); maker.put(value); } + ; + +bracketKey returns [string key] + : LBRACK key=stringvalue RBRACK + ; + +// +// A certSlot identifiers one certificate from the certificate chain +// +certSlot returns [int slot] + : s:INTEGER // counting from the anchor up + { slot = atol(s->getText().c_str()); } + | NEG ss:INTEGER // counting from the leaf down + { slot = -atol(ss->getText().c_str()); } + | "leaf" // the leaf ( == -1) + { slot = Requirement::leafCert; } + | "root" // the root ( == 0) + { slot = Requirement::anchorCert; } + ; + +certSlotAnchor returns [int slot] + : slot=certSlot + | empty // defaults to anchor ( == 0) + { slot = Requirement::anchorCert; } + ; + +// an arbitrary digest value +hash[SHA1::Digest digest] + : hash:HASHCONSTANT + { hashString(hash->getText(), digest); } + ; + +// various forms to specify a certificate hash +certificateDigest[SHA1::Digest digest] + : hash[digest] + | { string path; } path=pathstring + { if (CFRef certData = cfLoadFile(path)) + hashOfCertificate(CFDataGetBytePtr(certData), CFDataGetLength(certData), digest); + else + throw antlr::SemanticException(path + ": not found"); + } + ; + +// generic data - can be simple string, quoted string, or 0x-style hex +datavalue returns [string result] + : result=stringvalue + | hex:HEXCONSTANT { result = hexString(hex->getText()); } + ; + +// strings can always be quoted, but DOTKEYs don't need to be +stringvalue returns [string result] + : dk:DOTKEY { result = dk->getText(); } + | s:STRING { result = s->getText(); } + ; + +// pathstrings are like strings, but PATHNAMEs don't need to be quoted either +pathstring returns [string result] + : dk:DOTKEY { result = dk->getText(); } + | s:STRING { result = s->getText(); } + | pn:PATHNAME { result = pn->getText(); } + ; + +// unique identifier value +identifierString returns [string result] + : dk:DOTKEY { result = dk->getText(); } + | s:STRING { result = s->getText(); } + ; + +// syntactic cavity generators +fluff + : SEMI + ; + +eql + : EQL + | empty + ; + +empty : ; + + +// +// The lexer for the Requirement language. +// Really straightforward and conventional. +// A subset of strings don't need to be quoted (DOTKEYs), though the disassembler +// will always quote them anyway. +// There's a syntax H"abcd" to denote hash values (in hex). +// +class RequirementLexer extends Lexer; + +options { + k=2; + testLiterals=false; +} + +protected +IDENT options { testLiterals=true; } + : ( 'A' .. 'Z' | 'a' .. 'z' ) ( 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' )* + ; + +DOTKEY options { testLiterals=true; } + : IDENT ( "." IDENT )* + ; + +PATHNAME + : "/" IDENT ( "/" IDENT )+ + ; + +HASHCONSTANT + : 'H'! '"'! ( HEX )+ '"'! + ; + +HEXCONSTANT + : '0'! 'x'! ( HEX )+ + ; + +STRING + : '"'! ( ( '\\'! '"' ) | ( ~ ( '"' | '\\' ) ) )* '"'! + ; + +INTEGER + : ( '0' .. '9' ) + + ; + +protected +HEX : '0' .. '9' | 'a' .. 'f' | 'A' .. 'F' ; + +// operator tokens +ARROW : "=>" ; +SEMI : ';' ; +LPAREN : '(' ; +RPAREN : ')' ; +LBRACK : '[' ; +RBRACK : ']' ; +COMMA : ',' ; +EQL : '=' ; +SUBS : '~' ; +NEG : '-' ; +NOT : '!' ; + + +// +// White spaces +// +WS : ( ' ' | '\n' { newline(); } | '\t' )+ + { $setType(antlr::Token::SKIP); } + ; + +SHELLCOMMENT + : '#' ( ~ '\n' )* + { $setType(antlr::Token::SKIP); } + ; + +C_COMMENT + : "/*" ( (~'*')|('*'(~'/')) )* "*/" + { $setType(antlr::Token::SKIP); } + ; + +CPP_COMMENT + : "//" ( ~ '\n' )* + { $setType(antlr::Token::SKIP); } + ; -- 2.45.2