--- /dev/null
+/*
+ * 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 <stdint.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+
+/*
+ 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
--- /dev/null
+/*
+ * 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 <Security/SecCodeHost.h>
+#include "cskernel.h"
+#include "cfmunge.h"
+#include <security_utilities/debugging.h>
+#include <sys/codesign.h>
+
+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<CFDictionaryRef>("{%O=%d}", kSecGuestAttributePid, pid))) {
+ SecPointer<SecCode> 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<CFMutableDictionaryRef> 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
--- /dev/null
+/*
+ * 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 <security_utilities/utilities.h>
+
+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<SecCode> mHost;
+ SecPointer<SecStaticCode> mStaticCode;
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_CODE
--- /dev/null
+/*
+ * 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 <security_utilities/unix++.h>
+#include <security_utilities/unixchild.h>
+#include <vector>
+
+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<CFBooleanRef>(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<CFTypeRef>(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<CFNumberRef>(kSecCodeSignerFlags)) {
+ state.mCdFlagsGiven = true;
+ state.mCdFlags = cfNumber<uint32_t>(flags);
+ } else
+ state.mCdFlagsGiven = false;
+
+ if (CFNumberRef cmsSize = get<CFNumberRef>(CFSTR("cmssize")))
+ state.mCMSSize = cfNumber<size_t>(cmsSize);
+ else
+ state.mCMSSize = 5000; // likely big enough
+
+ // signing time can be a CFDateRef or null
+ if (CFTypeRef time = get<CFTypeRef>(kSecCodeSignerSigningTime))
+ if (CFGetTypeID(time) == CFDateGetTypeID() || time == kCFNull)
+ state.mSigningTime = CFDateRef(time);
+ else
+ MacOSError::throwMe(errSecCSInvalidObjectRef);
+
+ if (CFStringRef ident = get<CFStringRef>(kSecCodeSignerIdentifier))
+ state.mIdentifier = cfString(ident);
+
+ if (CFStringRef prefix = get<CFStringRef>(kSecCodeSignerIdentifierPrefix))
+ state.mIdentifierPrefix = cfString(prefix);
+
+ // requirements can be binary or string (to be compiled)
+ if (CFTypeRef reqs = get<CFTypeRef>(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<CFNumberRef>(kSecCodeSignerPageSize);
+
+ // detached can be (destination) file URL or (mutable) Data to be appended-to
+ if (state.mDetached = get<CFTypeRef>(kSecCodeSignerDetached)) {
+ if (CFGetTypeID(state.mDetached) != CFURLGetTypeID()
+ && CFGetTypeID(state.mDetached) != CFDataGetTypeID())
+ MacOSError::throwMe(errSecCSInvalidObjectRef);
+ }
+
+ state.mDryRun = getBool(kSecCodeSignerDryRun);
+
+ state.mResourceRules = get<CFDictionaryRef>(kSecCodeSignerResourceRules);
+
+ state.mApplicationData = get<CFDataRef>(kSecCodeSignerApplicationData);
+}
+
+
+} // end namespace CodeSigning
+} // end namespace Security
--- /dev/null
+/*
+ * 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 <Security/SecIdentity.h>
+#include <security_utilities/utilities.h>
+
+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<SecIdentityRef> mSigner; // signing identity
+ CFRef<CFTypeRef> mDetached; // detached-signing information (NULL => attached)
+ CFRef<CFDictionaryRef> mResourceRules; // explicit resource collection rules (override)
+ CFRef<CFDateRef> mSigningTime; // signing time desired (kCFNull for none)
+ CFRef<CFDataRef> 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<CFNumberRef> mPageSize; // main executable page size
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_CODESIGNER
--- /dev/null
+/*
+ * 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 <Security/SecStaticCode.h>
+#include <Security/SecCode.h>
+#include <Security/SecRequirement.h>
+
+#endif //_H_CODESIGNING
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 <security_codesigning/cfmunge.h>
+#include <sys/codesign.h>
+
+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<CFMutableDictionaryRef> 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<SecStaticCode> 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<SecCode> 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<CFDictionaryRef>("{%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<SecCode> 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<SecStaticCode> code = SecStaticCode::requiredStatic(codeRef);
+ CFRef<CFDictionaryRef> info = code->signingInformation(flags);
+
+ if (flags & kSecCSDynamicInformation)
+ if (SecPointer<SecCode> dcode = SecStaticCode::optionalDynamic(codeRef)) {
+ uint32_t status;
+ if (SecPointer<SecCode> host = dcode->host())
+ status = host->getGuestStatus(dcode);
+ else
+ status = CS_VALID; // root of trust, presumed valid
+ info = cfmake<CFDictionaryRef>("{+%O,%O=%u}", info.get(),
+ kSecCodeInfoStatus, status);
+ }
+
+ Required(infoRef) = info.yield();
+
+ END_CSAPI
+}
+
--- /dev/null
+/*
+ * 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 <Security/CSCommon.h>
+
+
+/*!
+ @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
--- /dev/null
+/*
+ * 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 <security_utilities/cfutilities.h>
+#include <security_utilities/globalizer.h>
+#include <securityd_client/ssclient.h>
+
+using namespace CodeSigning;
+
+
+//
+// Munge a CFDictionary into a CssmData representing its plist
+//
+class DictData : public CFRef<CFDataRef> {
+public:
+ DictData(CFDictionaryRef dict) : CFRef<CFDataRef>(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
+}
--- /dev/null
+/*
+ * 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 <Security/CSCommon.h>
+#include <System/sys/codesign.h>
+
+#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 <sys/codesign.h>. 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
--- /dev/null
+/*
+ * 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 <Security/SecCodeHostLib.h>
+#include "SecCodeHostLib.h"
+#include <Security/Security.h>
+#include <Security/AuthSession.h>
+#include <securityd_client/ucsp.h>
+#include <servers/bootstrap.h>
+
+
+//
+// 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));
+}
--- /dev/null
+/*
+ * 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 <Security/SecCodeHost.h>.
+
+ This file is documented as a delta to <Security/SecCodeHost.h>, which
+ you should consult as a baseline.
+*/
+#ifndef _H_SECCODEHOSTLIB
+#define _H_SECCODEHOSTLIB
+
+//#include <Security/SecCodeHost.h>
+#include "SecCodeHost.h"
+#include <System/sys/codesign.h>
+
+#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
--- /dev/null
+/*
+ * 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<SecCodeSigner> 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
+}
--- /dev/null
+/*
+ * 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 <Security/CSCommon.h>
+
+/*!
+ @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
--- /dev/null
+/*
+ * 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 <Security/CSCommon.h>
+#include "SecIntegrity.h"
+
+
--- /dev/null
+/*
+ * 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 <Security/CSCommon.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_H_SECINTEGRITY
--- /dev/null
+/*
+ * 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 <Security/SecCodeHostLib.h>
+#include "SecIntegrityLib.h"
--- /dev/null
+/*
+ * 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 <Security/SecIntegrity.h>.
+
+ This file is documented as a delta to <Security/SecIntegrity.h>, 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
--- /dev/null
+/*
+ * 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 <Security/SecCertificate.h>
+#include <security_utilities/cfutilities.h>
+
+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<CFDataRef> 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
+}
+
--- /dev/null
+/*
+ * 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 <Security/CSCommon.h>
+#include <Security/SecCertificate.h>
+
+
+/*!
+ @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
--- /dev/null
+/*
+ * 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 <fcntl.h>
+
+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<SecStaticCode> 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<SecStaticCode> 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<SecStaticCode> 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<SecStaticCode> 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
+}
--- /dev/null
+/*
+ * 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 <Security/CSCommon.h>
+
+
+/*!
+ @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
--- /dev/null
+/*
+ * 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 <CoreFoundation/CFURLAccess.h>
+#include <Security/SecPolicyPriv.h>
+#include <Security/SecTrustPriv.h>
+#include <Security/CMSPrivate.h>
+#include <Security/SecCmsContentInfo.h>
+#include <Security/SecCmsSignerInfo.h>
+#include <Security/SecCmsSignedData.h>
+#include <security_utilities/unix++.h>
+#include <security_codesigning/cfmunge.h>
+#include <Security/CMSDecoder.h>
+
+
+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<DiskRep> 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<CFDataRef> 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<Requirement *>(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<SecStaticCode *>(object))
+ return scode;
+ else if (SecCode *code = dynamic_cast<SecCode *>(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<SecStaticCode *>(object))
+ return NULL;
+ else if (SecCode *code = dynamic_cast<SecCode *>(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<CFDataRef> &cache = mCache[slot];
+ if (!cache) {
+ if (CFRef<CFDataRef> 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<const CodeDirectory *>(CFDataGetBytePtr(mDir));
+ dir->checkVersion();
+ }
+ }
+ if (mDir)
+ return reinterpret_cast<const CodeDirectory *>(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<CMSDecoderRef> 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<CFDictionaryRef>(sealedResources, "rules");
+ CFDictionaryRef files = cfget<CFDictionaryRef>(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<CFMutableDictionaryRef> 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<CollectingContext *>(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<CFDictionaryRef> 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<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
+ if (CFRef<CFDataRef> 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<Requirement>(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<CFMutableDictionaryRef> 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<CFDateRef> 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<SecRequirementRef> 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<SecRequirementRef>((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<CFArrayRef> 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<const BlobCore *>(CFDataGetBytePtr(sig));
+ if (sigBlob->is<EmbeddedSignatureBlob>()) { // architecture-less
+ mArch = EmbeddedSignatureBlob::specific(sigBlob);
+ mGlobal = NULL;
+ return;
+ } else if (sigBlob->is<DetachedSignatureBlob>()) { // 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
--- /dev/null
+/*
+ * 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 <Security/SecTrust.h>
+#include <CoreFoundation/CFData.h>
+
+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<CFMutableDictionaryRef> 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<DiskRep> 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<CFDataRef> mDir; // code directory data
+ CFRef<CFDataRef> mSignature; // CMS signature data
+ CFAbsoluteTime mSigningTime; // (signed) signing time
+ CFRef<CFDataRef> mCache[cdSlotCount]; // NULL => not tried, kCFNull => absent, other => present
+
+ // alternative cache forms (storage may depend on cached contents above)
+ CFRef<CFDictionaryRef> mInfoDict; // derived from mCache slot
+ CFRef<CFDictionaryRef> mResourceDict; // derived from mCache slot
+ const Requirement *mDesignatedReq; // cached designated req if we made one up
+
+ bool mGotResourceBase; // asked mRep for resourceBasePath
+ CFRef<CFURLRef> mResourceBase; // URL form of resource base directory
+
+ // signature verification outcome (mTrust == NULL => not done yet)
+ CFRef<SecTrustRef> mTrust; // outcome of crypto validation (valid or not)
+ CFRef<CFArrayRef> mCertChain;
+ CSSM_TP_APPLE_EVIDENCE_INFO *mEvalDetails;
+
+ // cached verification policy
+ CFRef<SecPolicyRef> mPolicy;
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_STATICCODE
--- /dev/null
+/*
+ * 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 <antlr/TokenStreamException.hpp>
+
+
+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 <class Input, class Source, class Result>
+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<StdioInputStream>(source, &Parser::RequirementParser::requirement, errors); }
+
+const Requirement *stringRequirement(string source, string &errors)
+{ return parse<StringInputStream>(source, &Parser::RequirementParser::requirement, errors); }
+
+const Requirements *fileRequirements(FILE *source, string &errors)
+{ return parse<StdioInputStream>(source, &Parser::RequirementParser::requirementSet, errors); }
+
+const Requirements *stringRequirements(string source, string &errors)
+{ return parse<StringInputStream>(source, &Parser::RequirementParser::requirementSet, errors); }
+
+const BlobCore *fileGeneric(FILE *source, string &errors)
+{ return parse<StdioInputStream>(source, &Parser::RequirementParser::autosense, errors); }
+
+const BlobCore *stringGeneric(string source, string &errors)
+{ return parse<StringInputStream>(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
--- /dev/null
+/*
+ * 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 <Security/CodeSigning.h>
+#include "requirement.h"
+#include <cstdio>
+#include <string>
+
+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
--- /dev/null
+/*
+ * 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 <CoreFoundation/CFURLAccess.h>
+#include <CoreFoundation/CFBundlePriv.h>
+#include <security_codesigning/cfmunge.h>
+
+
+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<CFURLRef> 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<CFDictionaryRef>("{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
--- /dev/null
+/*
+ * 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<CFBundleRef> mBundle;
+ std::string mResourcePath;
+ RefPointer<DiskRep> 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<BundleDiskRep> rep;
+ RefPointer<DiskRep::Writer> execWriter;
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_BUNDLEDISKREP
--- /dev/null
+/*
+ * 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 <security_utilities/memutils.h>
+#include <cmath>
+
+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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 <cstring>
+
+
+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
--- /dev/null
+/*
+ * 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 <security_utilities/unix++.h>
+#include <security_utilities/cfutilities.h>
+
+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<uint32_t> magic; // EmbeddedSignatureBlob::magic()
+ Endian<uint32_t> 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<CFMDiskRep> rep;
+ EmbeddedSignatureBlob *mSigningData;
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_CFMDISKREP
--- /dev/null
+/*
+ * 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 <security_utilities/cfutilities.h>
+#include <security_utilities/errors.h>
+
+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 <class Value>
+ bool CFScan::scannumber(CFTypeRef obj);
+
+ template <class Type>
+ 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<CFTypeRef>(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 <class Type>
+void CFScan::store(Type value)
+{
+ if (!suppress)
+ *va_arg(args, Type *) = value;
+}
+
+
+//
+// Convert a CFNumber to an external numeric form
+//
+template <class Value>
+bool CFScan::scannumber(CFTypeRef obj)
+{
+ ++format; // consume format code
+ if (!obj)
+ return true; // suppressed, okay
+ if (CFGetTypeID(obj) != CFNumberGetTypeID())
+ return false;
+ store<Value>(cfNumber<Value>(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<CFTypeRef>(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<int>(obj == kCFBooleanTrue);
+ return true;
+ }
+ case F_DICTIONARY:
+ return typescan(obj, CFDictionaryGetTypeID()) == done;
+ case 'd': // %d - int
+ return scannumber<int>(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<std::string>(cfString(CFStringRef(obj)));
+ return true;
+ case 'u':
+ return scannumber<unsigned int>(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
--- /dev/null
+/*
+ * 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 <security_utilities/cfutilities.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <cstdarg>
+
+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 <class CFType>
+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 <class CFType>
+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<CFType>::check(result) ? result : NULL;
+}
+
+template <class CFType>
+class CFTemp : public CFRef<CFType> {
+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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 <security_utilities/unix++.h>
+#include <security_utilities/blob.h>
+#include <security_utilities/cfutilities.h>
+#include <security_utilities/hashing.h>
+
+
+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<CodeDirectory, 0xfade0c02> {
+ typedef SHA1 Hash;
+public:
+ Endian<uint32_t> version; // compatibility version
+ Endian<uint32_t> flags; // setup and mode flags
+ Endian<uint32_t> hashOffset; // offset of hash slot element at index zero
+ Endian<uint32_t> identOffset; // offset of identifier string
+ Endian<uint32_t> nSpecialSlots; // number of special hash slots
+ Endian<uint32_t> nCodeSlots; // number of ordinary (code) hash slots
+ Endian<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
+ Endian<uint32_t> 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<const char>(identOffset); }
+ char *identifier() { return at<char>(identOffset); }
+
+ unsigned char *operator [] (Slot slot)
+ {
+ assert(slot >= int(-nSpecialSlots) && slot < int(nCodeSlots));
+ return at<unsigned char>(hashOffset) + hashSize * slot;
+ }
+
+ const unsigned char *operator [] (Slot slot) const
+ {
+ assert(slot >= int(-nSpecialSlots) && slot < int(nCodeSlots));
+ return at<unsigned char>(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
--- /dev/null
+/*
+ * 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<CFObjects> gCFObjects;
+
+CFObjects::CFObjects()
+ : Code("SecCode", false),
+ StaticCode("SecStaticCode", false),
+ Requirement("SecRequirements", false),
+ CodeSigner("SecCodeSigner", false)
+{
+}
+
+
+} // CodeSigning
+} // Security
--- /dev/null
+/*
+ * 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 <Security/CodeSigning.h>
+#include <Security/SecCodeSigner.h>
+#include <Security/SecBasePriv.h>
+#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+#include <security_utilities/globalizer.h>
+#include <security_utilities/seccfobject.h>
+#include <security_utilities/cfclass.h>
+#include <security_utilities/errors.h>
+#include <security_utilities/cfutilities.h>
+
+
+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> perThread;
+
+ SecCSFlags &flags() { return perThread().flags; }
+};
+
+extern ModuleNexus<CFObjects> 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 <class T>
+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
--- /dev/null
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include "cscdefs.h"
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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<CFDictionaryRef>("{%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
--- /dev/null
+/*
+ * 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 <security_utilities/cfutilities.h>
+#include <security_utilities/debugging.h>
+
+
+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<CFDictionaryRef> mInfoDict;
+};
+
+
+} // CodeSigning
+} // Security
+
+#endif //_H_CSERRORS
--- /dev/null
+/*
+ * 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 <securityd_client/cshosting.h>
+#include <sys/param.h>
+
+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<CFDataRef> 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<SecCode> 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<GenericCode *>(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<GenericCode *>(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<GenericCode *>(host()))
+ return genericHost->getHostingPort();
+ else
+ MacOSError::throwMe(errSecCSNotAHost);
+}
+
+
+} // CodeSigning
+} // Security
--- /dev/null
+/*
+ * 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 <Security/SecCodeHost.h>
+#include <security_utilities/utilities.h>
+#include <security_utilities/mach++.h>
+
+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
--- /dev/null
+/*
+ * 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 <libproc.h>
+#include <sys/codesign.h>
+#include <sys/param.h> // MAXPATHLEN
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// The running-kernel singletons
+//
+ModuleNexus<KernelCode::Globals> 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<pid_t>(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<ProcessCode *>(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<ProcessCode *>(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
--- /dev/null
+/*
+ * 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 <security_utilities/utilities.h>
+
+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<KernelCode> code;
+ SecPointer<KernelStaticCode> staticCode;
+ };
+ static ModuleNexus<Globals> globals;
+
+protected:
+ SecStaticCode *getStaticCode();
+
+private:
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_CSKERNEL
--- /dev/null
+/*
+ * 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 <securityd_client/ssclient.h>
+
+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
--- /dev/null
+/*
+ * 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 <security_utilities/utilities.h>
+
+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
--- /dev/null
+/*
+ * 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 <security_codesigning/requirement.h>
+#include <security_utilities/debugging.h>
+#include <security_utilities/errors.h>
+
+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
--- /dev/null
+/*
+ * 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 <Security/Security.h>
+#include <security_utilities/hashing.h>
+
+
+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
--- /dev/null
+/*
+ * 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 <sys/stat.h>
+#include <CoreFoundation/CFBundlePriv.h>
+
+// 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<CFURLRef> pathURL = makeCFURL(path))
+ if (CFRef<CFBundleRef> 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<CFURLRef> 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
--- /dev/null
+/*
+ * 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 <security_utilities/refcount.h>
+#include <security_utilities/superblob.h>
+#include <CoreFoundation/CFData.h>
+
+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
--- /dev/null
+/*
+ * 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 <cstring>
+
+
+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<DiskRep> rep = DiskRep::bestFileGuess(path))
+ if (SecPointer<SecStaticCode> 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
--- /dev/null
+/*
+ * 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 <security_utilities/cfutilities.h>
+
+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<FileDiskRep> rep;
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_FILEDISKREP
--- /dev/null
+/*
+ * 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 <cstring>
+
+
+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
--- /dev/null
+/*
+ * 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 <security_utilities/unix++.h>
+#include <security_utilities/cfutilities.h>
+
+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<ForeignDiskRep> rep;
+ EmbeddedSignatureBlob *mSigningData;
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_FOREIGNDISKREP
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 <security_utilities/memutils.h>
+
+namespace Security {
+
+
+//
+// Architecture values
+//
+Architecture::Architecture(const fat_arch &arch)
+ : pair<cpu_type_t, cpu_subtype_t>(ntohl(arch.cputype), ntohl(arch.cpusubtype))
+{
+}
+
+
+//
+// The local architecture (on demand; cached)
+//
+struct LocalArch {
+ const NXArchInfo *arch;
+ LocalArch() { arch = NXGetLocalArchInfo(); }
+};
+static ModuleNexus<LocalArch> 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<load_command>(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<const load_command>(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<const segment_command *>(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<const segment_command_64 *>(seg);
+ const section_64 *sect = increment<const section_64>(seg64 + 1, 0);
+ for (unsigned n = flip(seg64->nsects); n > 0; n--, sect++) {
+ if (!strcmp(sect->sectname, sectname))
+ return reinterpret_cast<const section *>(sect);
+ }
+ } else {
+ const section *sect = increment<const section>(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<const linkedit_data_command *>(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<mach_header &>(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<mach_header &>(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> 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<fat_arch>(&header, sizeof(fat_header));
+ if (fd.read(&header, sizeof(header), ntohl(arch1->offset)) != sizeof(header))
+ return 0;
+ continue;
+ }
+ default:
+ return 0;
+ }
+ }
+}
+
+
+} // Security
--- /dev/null
+/*
+ * 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 <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <mach-o/arch.h>
+#include <security_utilities/globalizer.h>
+#include <security_utilities/endian.h>
+#include <security_utilities/unix++.h>
+#include <security_utilities/cfutilities.h>
+
+namespace Security {
+
+
+//
+// An architecture specification.
+// Simply a pair or (cpu type, cpu subtype), really.
+//
+class Architecture : public std::pair<cpu_type_t, cpu_subtype_t> {
+public:
+ Architecture() { }
+ explicit Architecture(cpu_type_t type, cpu_subtype_t sub = 0)
+ : std::pair<cpu_type_t, cpu_subtype_t>(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 <class T>
+ 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<Architecture> 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
--- /dev/null
+/*
+ * 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> 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<CFDataRef> info;
+ try {
+ auto_ptr<MachO> macho(mainExecutableImage()->architecture());
+ if (const section *sect = macho->findSection("__TEXT", "__info_plist")) {
+ if (macho->is64()) {
+ const section_64 *sect64 = reinterpret_cast<const section_64 *>(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
--- /dev/null
+/*
+ * 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 <security_utilities/unix++.h>
+#include <security_codesigning/macho++.h>
+
+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
--- /dev/null
+/*
+ * 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 <security_utilities/unix++.h>
+#include <security_utilities/debugging.h>
+
+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
--- /dev/null
+/*
+ * 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 <fts.h>
+#include <string>
+
+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
--- /dev/null
+/*
+ * 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 <cstdarg>
+
+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<Requirement>(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<const Requirement *>(req), debug);
+ break;
+ case Requirements::typeMagic:
+ return dump(static_cast<const Requirements *>(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<uint32_t>());
+ 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<uint32_t>()) {
+ 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<uint32_t>())) {
+ 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
--- /dev/null
+/*
+ * 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 <ctype.h>
+
+
+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
--- /dev/null
+/*
+ * 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 <Security/SecTrustSettingsPriv.h>
+#include <Security/SecCertificatePriv.h>
+#include <security_utilities/memutils.h>
+#include <security_cdsa_utilities/cssmdata.h> // 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<uint32_t>());
+ 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<int32_t>());
+ 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<int32_t>());
+ string key = getString();
+ Match match(*this);
+ return certFieldValue(key, match, cert);
+ }
+ case opTrustedCert:
+ return trustedCert(get<int32_t>());
+ 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<uint32_t>());
+ 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<CFArrayRef> 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<CFStringRef> 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<MatchOperation>()) {
+ 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
--- /dev/null
+/*
+ * 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 <security_codesigning/reqreader.h>
+#include <Security/SecTrustSettings.h>
+
+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<CFStringRef> 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
--- /dev/null
+/*
+ * 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<void>(mPC);
+ mPC += usedSize;
+
+ // clear any padding (avoid random bytes in code image)
+ const uint32_t zero = 0;
+ memcpy(mBuffer->at<void>(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<void>(label.pos + length),
+ mBuffer->at<void>(label.pos), mPC - label.pos);
+ mPC += length;
+ return mBuffer->at<void>(label.pos);
+}
+
+
+Requirement *Requirement::Maker::make()
+{
+ mBuffer->length(mPC);
+ Requirement *result = mBuffer;
+ mBuffer = NULL;
+ return result;
+}
+
+
+} // CodeSigning
+} // Security
--- /dev/null
+/*
+ * 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 <security_codesigning/requirement.h>
+
+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 <class T>
+ T *alloc(size_t size) { return reinterpret_cast<T *>(alloc(size)); }
+
+ template <class T>
+ void put(const T &value) { *alloc<Endian<T> >(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 <class T>
+ Endian<T> &insert(const Label &label, size_t length = sizeof(T))
+ { return *reinterpret_cast<Endian<T>*>(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
--- /dev/null
+/*
+ * 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 <CoreFoundation/CoreFoundation.h>
+#include <security_utilities/osxcode.h>
+
+namespace Security {
+namespace CodeSigning {
+
+
+struct PluginHost {
+ PluginHost();
+ RefPointer<LoadableBundle> plugin;
+ AntlrPlugin *antlr;
+};
+
+ModuleNexus<PluginHost> 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<CFURLRef> 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<FindAntlrPlugin *>(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 <class Result, class Source>
+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<Requirement>::operator () (std::FILE *source)
+{
+ return parse(source, &AntlrPlugin::fileRequirement);
+}
+
+template <>
+const Requirement *RequirementParser<Requirement>::operator () (const std::string &source)
+{
+ return parse(source, &AntlrPlugin::stringRequirement);
+}
+
+template <>
+const Requirements *RequirementParser<Requirements>::operator () (std::FILE *source)
+{
+ return parse(source, &AntlrPlugin::fileRequirements);
+}
+
+template <>
+const Requirements *RequirementParser<Requirements>::operator () (const std::string &source)
+{
+ return parse(source, &AntlrPlugin::stringRequirements);
+}
+
+template <>
+const BlobCore *RequirementParser<BlobCore>::operator () (std::FILE *source)
+{
+ return parse(source, &AntlrPlugin::fileGeneric);
+}
+
+template <>
+const BlobCore *RequirementParser<BlobCore>::operator () (const std::string &source)
+{
+ return parse(source, &AntlrPlugin::stringGeneric);
+}
+
+
+} // CodeSigning
+} // Security
--- /dev/null
+/*
+ * 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 ReqType>
+class RequirementParser {
+public:
+ const ReqType *operator () (std::FILE *file);
+ const ReqType *operator () (const std::string &text);
+};
+
+
+//
+// Specifics for easier readability
+//
+template <class Input>
+inline const Requirement *parseRequirement(const Input &source)
+{ return RequirementParser<Requirement>()(source); }
+
+template <class Input>
+inline const Requirements *parseRequirements(const Input &source)
+{ return RequirementParser<Requirements>()(source); }
+
+template <class Input>
+inline const Requirements *parseGeneric(const Input &source)
+{ return RequirementParser<BlobCore>()(source); }
+
+
+} // CodeSigning
+} // Security
+
+#endif //_H_REQPARSER
--- /dev/null
+/*
+ * 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 <Security/SecTrustSettingsPriv.h>
+#include <security_utilities/memutils.h>
+#include <security_cdsa_utilities/cssmdata.h> // 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<uint32_t>();
+ checkSize(length);
+ data = (mReq->at<void>(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
--- /dev/null
+/*
+ * 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 <security_codesigning/requirement.h>
+#include <Security/SecCertificate.h>
+
+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 <class T> T get();
+ void getData(const void *&data, size_t &length);
+
+ std::string getString();
+ const unsigned char *getHash();
+ const unsigned char *getSHA1();
+
+ template <class T> void getData(T *&data, size_t &length)
+ { return getData(reinterpret_cast<const void *&>(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 <class T>
+T Requirement::Reader::get()
+{
+ checkSize(sizeof(T));
+ const Endian<const T> *value = mReq->at<Endian<const T> >(mPC);
+ mPC += sizeof(value);
+ return *value;
+}
+
+
+} // CodeSigning
+} // Security
+
+#endif //_H_REQREADER
--- /dev/null
+/*
+ * 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 <security_utilities/errors.h>
+#include <security_utilities/unix++.h>
+#include <security_utilities/logging.h>
+#include <security_utilities/cfutilities.h>
+#include <security_utilities/hashing.h>
+
+#ifdef DEBUGDUMP
+#include <security_codesigning/reqdumper.h>
+#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
--- /dev/null
+/*
+ * 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 <security_utilities/blob.h>
+#include <security_utilities/superblob.h>
+#include <security_utilities/hashing.h>
+#include <Security/CodeSigning.h>
+#include "codedirectory.h"
+#include <map>
+
+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<Requirement, 0xfade0c00> {
+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<uint32_t> 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
--- /dev/null
+/*
+ * 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 <security_codesigning/cfmunge.h>
+
+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<CFNumberRef>("weight"))
+ weight = cfNumber<unsigned int>(weightRef);
+ if (CFBooleanRef omitRef = rule.get<CFBooleanRef>("omit"))
+ if (omitRef == kCFBooleanTrue)
+ flags |= omitted;
+ if (CFBooleanRef optRef = rule.get<CFBooleanRef>("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<CFMutableDictionaryRef> files = makeCFMutableDictionary(0);
+
+ string path;
+ Rule *rule;
+ while (FTSENT *ent = next(path, rule)) {
+ assert(rule);
+ CFRef<CFDataRef> 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<CFDataRef> 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
--- /dev/null
+/*
+ * 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 <security_utilities/utilities.h>
+#include "regex.h"
+#include <vector>
+
+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<CFDictionaryRef> mRawRules;
+ typedef std::vector<Rule *> 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
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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<const BlobCore *>(CFDataGetBytePtr(data))->clone());
+ else
+ add(slot, BlobWrapper::alloc(CFDataGetBytePtr(data), CFDataGetLength(data)));
+}
+
+
+} // end namespace CodeSigning
+} // end namespace Security
--- /dev/null
+/*
+ * 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 <security_utilities/superblob.h>
+#include <CoreFoundation/CFData.h>
+
+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<EmbeddedSignatureBlob, 0xfade0cc0, uint32_t> {
+ typedef SuperBlobCore<EmbeddedSignatureBlob, 0xfade0cc0, uint32_t> _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
--- /dev/null
+/*
+ * 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 <Security/SecIdentity.h>
+#include <Security/CMSEncoder.h>
+#include <Security/CMSPrivate.h>
+#include "renum.h"
+#include <security_utilities/unix++.h>
+#include <security_utilities/unixchild.h>
+#include <security_codesigning/cfmunge.h>
+
+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<CFDictionaryRef> infoDict;
+ if (CFRef<CFDataRef> 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<uint32_t>(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<CFDictionaryRef> resourceRules(state.mResourceRules);
+ if (!resourceRules)
+ resourceRules.take(rep->defaultResourceRules());
+ ResourceBuilder resources(rpath, cfget<CFDictionaryRef>(resourceRules, "rules"));
+ CFRef<CFDictionaryRef> 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<size_t>(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<ArchEditor> editor(state.mDetached
+ ? static_cast<ArchEditor *>(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<CFDataRef> 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<DiskRep::Writer> 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<CFDataRef> 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<CFDataRef> 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<CMSEncoderRef> 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
--- /dev/null
+/*
+ * 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 <security_utilities/utilities.h>
+
+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<DiskRep> rep; // DiskRep of Code being signed
+ CFRef<CFDataRef> 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
--- /dev/null
+/*
+ * 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 <Security/SecIdentity.h>
+#include <Security/CMSEncoder.h>
+#include "renum.h"
+#include <security_utilities/unix++.h>
+#include <security_utilities/unixchild.h>
+#include <vector>
+
+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<const char *> 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
--- /dev/null
+/*
+ * 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 <security_utilities/utilities.h>
+#include <security_utilities/blob.h>
+#include <security_utilities/unix++.h>
+#include <security_utilities/unixchild.h>
+
+extern "C" {
+#include <copyfile.h>
+}
+
+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<MachO> 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<Architecture, Arch *> 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<DiskRep::Writer> 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 <security_utilities/unix++.h>
+
+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<SingleDiskRep> rep; // underlying SingleDiskRep
+ UnixPlusPlus::AutoFileDesc mFd; // cached writable fd
+};
+
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_SINGLEDISKREP
--- /dev/null
+// !$*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 = "<group>"; };
+ C2093AA70BB0948000EB8599 /* reqreader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqreader.h; sourceTree = "<group>"; };
+ C21CFC5D0A250D1C006CD5B1 /* reqdumper.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = reqdumper.cpp; sourceTree = "<group>"; };
+ C21CFC5E0A250D1C006CD5B1 /* reqdumper.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqdumper.h; sourceTree = "<group>"; };
+ C21EA3DB0AD2F81300E6E31C /* SecCodeSigner.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecCodeSigner.cpp; sourceTree = "<group>"; };
+ C21EA3DC0AD2F81300E6E31C /* SecCodeSigner.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecCodeSigner.h; sourceTree = "<group>"; };
+ C21EA3E10AD2FA0900E6E31C /* CodeSigner.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CodeSigner.cpp; sourceTree = "<group>"; };
+ C21EA3E20AD2FA0900E6E31C /* CodeSigner.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CodeSigner.h; sourceTree = "<group>"; };
+ C236E3D50AD59446000F5140 /* signer.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = signer.cpp; sourceTree = "<group>"; };
+ C236E3D60AD59446000F5140 /* signer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = signer.h; sourceTree = "<group>"; };
+ C236E3D90AD595C2000F5140 /* signerutils.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = signerutils.cpp; sourceTree = "<group>"; };
+ C236E3DA0AD595C2000F5140 /* signerutils.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = signerutils.h; sourceTree = "<group>"; };
+ C250F6C20B5EF1910076098F /* SecIntegrity.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecIntegrity.h; sourceTree = "<group>"; };
+ C250F6C60B5EF5B50076098F /* SecIntegrity.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecIntegrity.cpp; sourceTree = "<group>"; };
+ C25942420BA7095000877E56 /* foreigndiskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = foreigndiskrep.cpp; sourceTree = "<group>"; };
+ C25942430BA7095000877E56 /* foreigndiskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = foreigndiskrep.h; sourceTree = "<group>"; };
+ C259DFD40AD6D9BA00C9ACC6 /* sigblob.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = sigblob.cpp; sourceTree = "<group>"; };
+ C259DFD50AD6D9BA00C9ACC6 /* sigblob.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = sigblob.h; sourceTree = "<group>"; };
+ C26B45C00B8A9C00003C0ACA /* ucspc */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = ucspc; sourceTree = "<group>"; };
+ C2A976A80B8A2E36008B4EA0 /* csutilities.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = csutilities.cpp; sourceTree = "<group>"; };
+ C2A976A90B8A2E36008B4EA0 /* csutilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = csutilities.h; sourceTree = "<group>"; };
+ 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 = "<group>"; };
+ C2BD519A0A9392FD000FE43D /* machorep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = machorep.cpp; sourceTree = "<group>"; };
+ C2BD519B0A9392FD000FE43D /* machorep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = machorep.h; sourceTree = "<group>"; };
+ C2BD60F80AC863FC0057FD3D /* csgeneric.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = csgeneric.cpp; sourceTree = "<group>"; };
+ C2BD60F90AC863FC0057FD3D /* csgeneric.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = csgeneric.h; sourceTree = "<group>"; };
+ 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 = "<group>"; };
+ C2C1DFBA0A2F80EB00D1B02B /* reqinterp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqinterp.h; sourceTree = "<group>"; };
+ C2C1DFC10A2F820500D1B02B /* reqmaker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = reqmaker.cpp; sourceTree = "<group>"; };
+ C2C1DFC20A2F820500D1B02B /* reqmaker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqmaker.h; sourceTree = "<group>"; };
+ C2C3BC5D0BA1D6FE00E869D1 /* cfmdiskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cfmdiskrep.cpp; sourceTree = "<group>"; };
+ C2C3BC5E0BA1D6FE00E869D1 /* cfmdiskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cfmdiskrep.h; sourceTree = "<group>"; };
+ C2C3BCD10BA1E47E00E869D1 /* singlediskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = singlediskrep.cpp; sourceTree = "<group>"; };
+ C2C3BCD20BA1E47E00E869D1 /* singlediskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = singlediskrep.h; sourceTree = "<group>"; };
+ C2C931B30AB8BA1200F83950 /* SecCodeHost.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecCodeHost.cpp; sourceTree = "<group>"; };
+ C2CC30A00B8519CC005FA59D /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = CoreFoundation.framework; sourceTree = "<group>"; };
+ C2CC31040B8523AD005FA59D /* SecIntegrityLib.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecIntegrityLib.h; sourceTree = "<group>"; };
+ C2CC310E0B852424005FA59D /* SecIntegrityLib.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = SecIntegrityLib.c; sourceTree = "<group>"; };
+ C2CC31130B85254F005FA59D /* antlrplugin.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = antlrplugin.cpp; path = lib/antlrplugin.cpp; sourceTree = "<group>"; };
+ C2CC31140B85254F005FA59D /* antlrplugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = antlrplugin.h; path = lib/antlrplugin.h; sourceTree = "<group>"; };
+ C2CCF02F0A3F523D0085795A /* macho++.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = "macho++.cpp"; sourceTree = "<group>"; };
+ C2CCF0300A3F523D0085795A /* macho++.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "macho++.h"; sourceTree = "<group>"; };
+ C2D383120A237F47005C63A2 /* bundlediskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = bundlediskrep.cpp; sourceTree = "<group>"; };
+ C2D383130A237F47005C63A2 /* bundlediskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = bundlediskrep.h; sourceTree = "<group>"; };
+ C2D383140A237F47005C63A2 /* cdbuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cdbuilder.cpp; sourceTree = "<group>"; };
+ C2D383150A237F47005C63A2 /* cdbuilder.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cdbuilder.h; sourceTree = "<group>"; };
+ C2D383160A237F47005C63A2 /* codedirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = codedirectory.cpp; sourceTree = "<group>"; };
+ C2D383170A237F47005C63A2 /* codedirectory.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = codedirectory.h; sourceTree = "<group>"; };
+ C2D383180A237F47005C63A2 /* CodeSigning.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CodeSigning.h; sourceTree = "<group>"; };
+ C2D383190A237F47005C63A2 /* SecCodeHost.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecCodeHost.h; sourceTree = "<group>"; };
+ C2D3831A0A237F47005C63A2 /* cs.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cs.cpp; sourceTree = "<group>"; };
+ C2D3831B0A237F47005C63A2 /* cs.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cs.h; sourceTree = "<group>"; };
+ C2D3831C0A237F47005C63A2 /* CSCommon.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSCommon.h; sourceTree = "<group>"; };
+ C2D3831D0A237F47005C63A2 /* SecCode.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecCode.cpp; sourceTree = "<group>"; };
+ C2D3831E0A237F47005C63A2 /* SecCode.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecCode.h; sourceTree = "<group>"; };
+ C2D3831F0A237F47005C63A2 /* cskernel.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cskernel.cpp; sourceTree = "<group>"; };
+ C2D383200A237F47005C63A2 /* cskernel.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cskernel.h; sourceTree = "<group>"; };
+ C2D383210A237F47005C63A2 /* SecStaticCode.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecStaticCode.cpp; sourceTree = "<group>"; };
+ C2D383220A237F47005C63A2 /* SecStaticCode.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecStaticCode.h; sourceTree = "<group>"; };
+ C2D383230A237F47005C63A2 /* csprocess.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = csprocess.cpp; sourceTree = "<group>"; };
+ C2D383240A237F47005C63A2 /* csprocess.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = csprocess.h; sourceTree = "<group>"; };
+ C2D383250A237F47005C63A2 /* SecRequirement.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SecRequirement.cpp; sourceTree = "<group>"; };
+ C2D383260A237F47005C63A2 /* SecRequirement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SecRequirement.h; sourceTree = "<group>"; };
+ C2D383270A237F47005C63A2 /* diskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = diskrep.cpp; sourceTree = "<group>"; };
+ C2D383280A237F47005C63A2 /* diskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = diskrep.h; sourceTree = "<group>"; };
+ C2D3832B0A237F47005C63A2 /* filediskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = filediskrep.cpp; sourceTree = "<group>"; };
+ C2D3832C0A237F47005C63A2 /* filediskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = filediskrep.h; sourceTree = "<group>"; };
+ C2D3832D0A237F47005C63A2 /* Code.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = Code.cpp; sourceTree = "<group>"; };
+ C2D3832E0A237F47005C63A2 /* Code.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Code.h; sourceTree = "<group>"; };
+ C2D3832F0A237F47005C63A2 /* kerneldiskrep.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = kerneldiskrep.cpp; sourceTree = "<group>"; };
+ C2D383300A237F47005C63A2 /* kerneldiskrep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = kerneldiskrep.h; sourceTree = "<group>"; };
+ C2D383310A237F47005C63A2 /* StaticCode.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = StaticCode.cpp; sourceTree = "<group>"; };
+ C2D383320A237F47005C63A2 /* StaticCode.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = StaticCode.h; sourceTree = "<group>"; };
+ C2D383330A237F47005C63A2 /* reqparser.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = reqparser.cpp; sourceTree = "<group>"; };
+ C2D383340A237F47005C63A2 /* reqparser.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqparser.h; sourceTree = "<group>"; };
+ C2D383350A237F47005C63A2 /* requirement.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = requirement.cpp; sourceTree = "<group>"; };
+ C2D383360A237F47005C63A2 /* requirement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = requirement.h; sourceTree = "<group>"; };
+ C2D383370A237F47005C63A2 /* Requirements.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = Requirements.cpp; sourceTree = "<group>"; };
+ C2D383380A237F47005C63A2 /* Requirements.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Requirements.h; sourceTree = "<group>"; };
+ C2D383390A237F47005C63A2 /* security_codesigning.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; path = security_codesigning.exp; sourceTree = "<group>"; };
+ C2D383EE0A23A9D3005C63A2 /* RequirementLexer.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = RequirementLexer.cpp; sourceTree = "<group>"; };
+ C2D383EF0A23A9D3005C63A2 /* RequirementLexer.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = RequirementLexer.hpp; sourceTree = "<group>"; };
+ C2D383F00A23A9D3005C63A2 /* RequirementParser.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = RequirementParser.cpp; sourceTree = "<group>"; };
+ C2D383F10A23A9D3005C63A2 /* RequirementParser.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = RequirementParser.hpp; sourceTree = "<group>"; };
+ C2D383F20A23A9D3005C63A2 /* RequirementParserTokenTypes.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = RequirementParserTokenTypes.hpp; sourceTree = "<group>"; };
+ C2D383F30A23A9D3005C63A2 /* RequirementParserTokenTypes.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = RequirementParserTokenTypes.txt; sourceTree = "<group>"; };
+ C2D8A07D0AE7F6E300F68F79 /* cfmunge.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cfmunge.cpp; sourceTree = "<group>"; };
+ C2D8A07E0AE7F6E300F68F79 /* cfmunge.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cfmunge.h; sourceTree = "<group>"; };
+ C2E2873C0B5D8D80009336A0 /* SecCodeHostLib.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = SecCodeHostLib.c; sourceTree = "<group>"; };
+ C2E911E00ADEBE3200275CB2 /* resources.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = resources.cpp; sourceTree = "<group>"; };
+ C2E911E10ADEBE3200275CB2 /* resources.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = resources.h; sourceTree = "<group>"; };
+ C2EF100E0A49BD89005A44BB /* renum.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = renum.cpp; sourceTree = "<group>"; };
+ C2EF100F0A49BD89005A44BB /* renum.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = renum.h; sourceTree = "<group>"; };
+ C2F6566C0BCBFB250078779E /* cserror.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = cserror.cpp; sourceTree = "<group>"; };
+ C2F6566D0BCBFB250078779E /* cserror.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cserror.h; sourceTree = "<group>"; };
+/* 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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
+<plist version=\"1.0\">
+<dict/>
+</plist>
+";
+ };
+/* 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 = "<group>";
+ };
+ 4CA1FEA7052A3C3800F22E42 = {
+ isa = PBXGroup;
+ children = (
+ 4C308388053237100028A8C6 /* lib */,
+ C2D383F90A23A9D9005C63A2 /* cstemp */,
+ C2A4A43E0B7BABFD004AAC3F /* Pieces */,
+ C2CC30EF0B8519CF005FA59D /* Frameworks */,
+ 4CA1FEBF052A3C8100F22E42 /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 4CA1FEBF052A3C8100F22E42 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 4CA1FEBE052A3C8100F22E42 /* security_codesigning */,
+ 4C56351E0540A55300DCF0C8 /* security_codesigning.framework */,
+ C2BC1F260B580D3A003EC9DC /* libintegrity.a */,
+ C2BC1F2F0B580D4B003EC9DC /* libcodehost.a */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ C216C72D0AD59B22003B07D4 /* Signing Operations */ = {
+ isa = PBXGroup;
+ children = (
+ C236E3D60AD59446000F5140 /* signer.h */,
+ C236E3D50AD59446000F5140 /* signer.cpp */,
+ C236E3DA0AD595C2000F5140 /* signerutils.h */,
+ C236E3D90AD595C2000F5140 /* signerutils.cpp */,
+ );
+ name = "Signing Operations";
+ sourceTree = "<group>";
+ };
+ C2A4A43E0B7BABFD004AAC3F /* Pieces */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Pieces;
+ sourceTree = "<group>";
+ };
+ C2BC1F370B580DAE003EC9DC /* Static Support */ = {
+ isa = PBXGroup;
+ children = (
+ C2CC31040B8523AD005FA59D /* SecIntegrityLib.h */,
+ C2CC310E0B852424005FA59D /* SecIntegrityLib.c */,
+ C2BC1F340B580DA7003EC9DC /* SecCodeHostLib.h */,
+ C2E2873C0B5D8D80009336A0 /* SecCodeHostLib.c */,
+ );
+ name = "Static Support";
+ sourceTree = "<group>";
+ };
+ 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 = "<group>";
+ };
+ 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 = "<group>";
+ };
+ 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 = "<group>";
+ };
+ 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 = "<group>";
+ };
+ C2C1DF630A2E45BF00D1B02B /* Code Directory */ = {
+ isa = PBXGroup;
+ children = (
+ C2D383170A237F47005C63A2 /* codedirectory.h */,
+ C2D383160A237F47005C63A2 /* codedirectory.cpp */,
+ C2D383150A237F47005C63A2 /* cdbuilder.h */,
+ C2D383140A237F47005C63A2 /* cdbuilder.cpp */,
+ );
+ name = "Code Directory";
+ sourceTree = "<group>";
+ };
+ 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 = "<group>";
+ };
+ C2CC30EF0B8519CF005FA59D /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ C2CC30A00B8519CC005FA59D /* CoreFoundation.framework */,
+ );
+ name = Frameworks;
+ path = /System/Library/Frameworks;
+ sourceTree = "<absolute>";
+ };
+ C2CC31160B852554005FA59D /* Security Plugins */ = {
+ isa = PBXGroup;
+ children = (
+ C2CC31140B85254F005FA59D /* antlrplugin.h */,
+ C2CC31130B85254F005FA59D /* antlrplugin.cpp */,
+ );
+ name = "Security Plugins";
+ path = ..;
+ sourceTree = "<group>";
+ };
+ 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 = "<group>";
+ };
+ 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 */;
+}
--- /dev/null
+#
+# Suggested internal requirements for CFM code
+#
+host => anchor apple and identifier com.apple.LaunchCFMApp // CFM runner tool
--- /dev/null
+#
+# Suggested internal requirements for ppc (only) Mach-O code
+#
+host => anchor apple and identifier com.apple.translate // Rosetta
--- /dev/null
+anchor apple and (identifier com.apple.translate or identifier com.apple.LaunchCFMApp)
--- /dev/null
+/*
+ * 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 <security_utilities/cfutilities.h>
+#include <security_utilities/hashing.h>
+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<ExprOp>(label) = opAnd; } term[maker] )*
+ ;
+
+term[Maker &maker]
+ { Maker::Label label(maker); }
+ : primary[maker] ( "or" { maker.insert<ExprOp>(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<CFDataRef> 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); }
+ ;