]> git.saurik.com Git - apple/libsecurity_codesigning.git/commitdiff
libsecurity_codesigning-32568.tar.gz mac-os-x-105 mac-os-x-1051 v32568
authorApple <opensource@apple.com>
Wed, 29 Aug 2007 22:27:19 +0000 (22:27 +0000)
committerApple <opensource@apple.com>
Wed, 29 Aug 2007 22:27:19 +0000 (22:27 +0000)
94 files changed:
lib/CSCommon.h [new file with mode: 0644]
lib/Code.cpp [new file with mode: 0644]
lib/Code.h [new file with mode: 0644]
lib/CodeSigner.cpp [new file with mode: 0644]
lib/CodeSigner.h [new file with mode: 0644]
lib/CodeSigning.h [new file with mode: 0644]
lib/Requirements.cpp [new file with mode: 0644]
lib/Requirements.h [new file with mode: 0644]
lib/SecCode.cpp [new file with mode: 0644]
lib/SecCode.h [new file with mode: 0644]
lib/SecCodeHost.cpp [new file with mode: 0644]
lib/SecCodeHost.h [new file with mode: 0644]
lib/SecCodeHostLib.c [new file with mode: 0644]
lib/SecCodeHostLib.h [new file with mode: 0644]
lib/SecCodeSigner.cpp [new file with mode: 0644]
lib/SecCodeSigner.h [new file with mode: 0644]
lib/SecIntegrity.cpp [new file with mode: 0644]
lib/SecIntegrity.h [new file with mode: 0644]
lib/SecIntegrityLib.c [new file with mode: 0644]
lib/SecIntegrityLib.h [new file with mode: 0644]
lib/SecRequirement.cpp [new file with mode: 0644]
lib/SecRequirement.h [new file with mode: 0644]
lib/SecStaticCode.cpp [new file with mode: 0644]
lib/SecStaticCode.h [new file with mode: 0644]
lib/StaticCode.cpp [new file with mode: 0644]
lib/StaticCode.h [new file with mode: 0644]
lib/antlrplugin.cpp [new file with mode: 0644]
lib/antlrplugin.h [new file with mode: 0644]
lib/bundlediskrep.cpp [new file with mode: 0644]
lib/bundlediskrep.h [new file with mode: 0644]
lib/cdbuilder.cpp [new file with mode: 0644]
lib/cdbuilder.h [new file with mode: 0644]
lib/cfmdiskrep.cpp [new file with mode: 0644]
lib/cfmdiskrep.h [new file with mode: 0644]
lib/cfmunge.cpp [new file with mode: 0644]
lib/cfmunge.h [new file with mode: 0644]
lib/codedirectory.cpp [new file with mode: 0644]
lib/codedirectory.h [new file with mode: 0644]
lib/cs.cpp [new file with mode: 0644]
lib/cs.h [new file with mode: 0644]
lib/cscdefs.c [new file with mode: 0644]
lib/cscdefs.h [new file with mode: 0644]
lib/cserror.cpp [new file with mode: 0644]
lib/cserror.h [new file with mode: 0644]
lib/csgeneric.cpp [new file with mode: 0644]
lib/csgeneric.h [new file with mode: 0644]
lib/cskernel.cpp [new file with mode: 0644]
lib/cskernel.h [new file with mode: 0644]
lib/csprocess.cpp [new file with mode: 0644]
lib/csprocess.h [new file with mode: 0644]
lib/csutilities.cpp [new file with mode: 0644]
lib/csutilities.h [new file with mode: 0644]
lib/diskrep.cpp [new file with mode: 0644]
lib/diskrep.h [new file with mode: 0644]
lib/filediskrep.cpp [new file with mode: 0644]
lib/filediskrep.h [new file with mode: 0644]
lib/foreigndiskrep.cpp [new file with mode: 0644]
lib/foreigndiskrep.h [new file with mode: 0644]
lib/kerneldiskrep.cpp [new file with mode: 0644]
lib/kerneldiskrep.h [new file with mode: 0644]
lib/macho++.cpp [new file with mode: 0644]
lib/macho++.h [new file with mode: 0644]
lib/machorep.cpp [new file with mode: 0644]
lib/machorep.h [new file with mode: 0644]
lib/renum.cpp [new file with mode: 0644]
lib/renum.h [new file with mode: 0644]
lib/reqdumper.cpp [new file with mode: 0644]
lib/reqdumper.h [new file with mode: 0644]
lib/reqinterp.cpp [new file with mode: 0644]
lib/reqinterp.h [new file with mode: 0644]
lib/reqmaker.cpp [new file with mode: 0644]
lib/reqmaker.h [new file with mode: 0644]
lib/reqparser.cpp [new file with mode: 0644]
lib/reqparser.h [new file with mode: 0644]
lib/reqreader.cpp [new file with mode: 0644]
lib/reqreader.h [new file with mode: 0644]
lib/requirement.cpp [new file with mode: 0644]
lib/requirement.h [new file with mode: 0644]
lib/resources.cpp [new file with mode: 0644]
lib/resources.h [new file with mode: 0644]
lib/security_codesigning.exp [new file with mode: 0644]
lib/sigblob.cpp [new file with mode: 0644]
lib/sigblob.h [new file with mode: 0644]
lib/signer.cpp [new file with mode: 0644]
lib/signer.h [new file with mode: 0644]
lib/signerutils.cpp [new file with mode: 0644]
lib/signerutils.h [new file with mode: 0644]
lib/singlediskrep.cpp [new file with mode: 0644]
lib/singlediskrep.h [new file with mode: 0644]
libsecurity_codesigning.xcodeproj/project.pbxproj [new file with mode: 0644]
req/cfm.ireqs [new file with mode: 0644]
req/ppc.ireqs [new file with mode: 0644]
req/workaround.ireqs [new file with mode: 0644]
requirements.grammar [new file with mode: 0644]

diff --git a/lib/CSCommon.h b/lib/CSCommon.h
new file mode 100644 (file)
index 0000000..b1fd186
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*!
+       @header CSCommon
+       CSCommon is the common header of all Code Signing API headers.
+       It defines types, constants, and error codes.
+*/
+#ifndef _H_CSCOMMON
+#define _H_CSCOMMON
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <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
diff --git a/lib/Code.cpp b/lib/Code.cpp
new file mode 100644 (file)
index 0000000..d1ad41e
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// Code - SecCode API objects
+//
+#include "Code.h"
+#include "StaticCode.h"
+#include <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
diff --git a/lib/Code.h b/lib/Code.h
new file mode 100644 (file)
index 0000000..fca2dfd
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// Code - SecCode API objects
+//
+#ifndef _H_CODE
+#define _H_CODE
+
+#include "cs.h"
+#include "Requirements.h"
+#include <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
diff --git a/lib/CodeSigner.cpp b/lib/CodeSigner.cpp
new file mode 100644 (file)
index 0000000..6514c24
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// CodeSigner - SecCodeSigner API objects
+//
+#include "CodeSigner.h"
+#include "signer.h"
+#include "reqparser.h"
+#include "renum.h"
+#include <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
diff --git a/lib/CodeSigner.h b/lib/CodeSigner.h
new file mode 100644 (file)
index 0000000..150d528
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// CodeSigner - SecCodeSigner API objects
+//
+#ifndef _H_CODESIGNER
+#define _H_CODESIGNER
+
+#include "cs.h"
+#include "StaticCode.h"
+#include "cdbuilder.h"
+#include <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
diff --git a/lib/CodeSigning.h b/lib/CodeSigning.h
new file mode 100644 (file)
index 0000000..a33e9e7
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#ifndef _H_CODESIGNING
+#define _H_CODESIGNING
+
+
+/*!
+       @header CodeSigning
+       This header file includes all the headers that are needed to use
+       the client interface to Code Signing.
+       It does not include headers for the other Code Signing related interfaces.
+ */
+#include <Security/SecStaticCode.h>
+#include <Security/SecCode.h>
+#include <Security/SecRequirement.h>
+
+#endif //_H_CODESIGNING
diff --git a/lib/Requirements.cpp b/lib/Requirements.cpp
new file mode 100644 (file)
index 0000000..e6b05a2
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// Requirements - SecRequirement API objects
+//
+#include "Requirements.h"
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// Create from a Requirement blob in memory
+//
+SecRequirement::SecRequirement(const void *data, size_t length)
+       : mReq(NULL)
+{
+       const Requirement *req = (const Requirement *)data;
+       if (!req->validateBlob(length))
+               MacOSError::throwMe(errSecCSReqInvalid);
+       mReq = req->clone();
+}
+
+
+//
+// Create from a genuine Requirement object
+//
+SecRequirement::SecRequirement(const Requirement *req, bool transferOwnership)
+       : mReq(NULL)
+{
+       if (!req->validateBlob())
+               MacOSError::throwMe(errSecCSReqInvalid);
+       
+       if (transferOwnership)
+               mReq = req;
+       else
+               mReq = req->clone();
+}
+
+//
+// Clean up a SecRequirement object
+//
+SecRequirement::~SecRequirement() throw()
+{
+       ::free((void *)mReq);
+}
+
+
+} // end namespace CodeSigning
+} // end namespace Security
diff --git a/lib/Requirements.h b/lib/Requirements.h
new file mode 100644 (file)
index 0000000..dd3996d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// Requirements - SecRequirement API objects
+//
+#ifndef _H_REQUIREMENTS
+#define _H_REQUIREMENTS
+
+#include "cs.h"
+#include "requirement.h"
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// A SecRequirement object acts as the API representation for a code
+// requirement. All its semantics are within the Requirement object it holds.
+// The SecRequirement just manages the API appearances.
+//
+class SecRequirement : public SecCFObject {
+       NOCOPY(SecRequirement)
+public:
+       SECCFFUNCTIONS(SecRequirement, SecRequirementRef, errSecCSInvalidObjectRef, gCFObjects().Requirement)
+
+       SecRequirement(const void *data, size_t length);
+       SecRequirement(const Requirement *req, bool transferOwnership = false);
+    virtual ~SecRequirement() throw();
+       
+       const Requirement *requirement() const { return mReq; }
+
+private:
+       const Requirement *mReq;
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_REQUIREMENTS
diff --git a/lib/SecCode.cpp b/lib/SecCode.cpp
new file mode 100644 (file)
index 0000000..9eaa69f
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// SecCode - API frame for SecCode objects.
+//
+// Note that some SecCode* functions take SecStaticCodeRef arguments in order to
+// accept either static or dynamic code references, operating on the respective
+// StaticCode. Those functions are in SecStaticCode.cpp, not here, despite their name.
+//
+#include "cs.h"
+#include "Code.h"
+#include "cskernel.h"
+#include <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
+}
+
diff --git a/lib/SecCode.h b/lib/SecCode.h
new file mode 100644 (file)
index 0000000..471de30
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*!
+       @header SecCode
+       SecCode represents separately indentified running code in the system.
+       In addition to UNIX processes, this can also include (with suitable support)
+       scripts, applets, widgets, etc.
+*/
+#ifndef _H_SECCODE
+#define _H_SECCODE
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <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
diff --git a/lib/SecCodeHost.cpp b/lib/SecCodeHost.cpp
new file mode 100644 (file)
index 0000000..fce2403
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// SecCodeHost - Host Code API
+//
+#include "cs.h"
+#include "SecCodeHost.h"
+#include <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
+}
diff --git a/lib/SecCodeHost.h b/lib/SecCodeHost.h
new file mode 100644 (file)
index 0000000..fc9c83d
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*!
+       @header SecCodeHost
+       This header provides the hosting API for Code Signing. These are calls
+       that are (only) made by code that is hosting guests.
+       In the context of Code Signing, a Host is code that creates and manages other
+       codes from which it defends its own integrity. As part of that duty, it maintains
+       state for each of its children, and answers questions about them.
+
+       A Host is externally represented by a SecCodeRef (it is a SecCode object).
+       So is a Guest. There is no specific API object to represent Hosts or Guests.
+       Within the Hosting API, guests are identified by simple numeric handles that
+       are unique and valid only in the context of their specific host.
+
+       The functions in this API always apply to the Host making the API calls.
+       They cannot be used to (directly) interrogate another host.
+*/
+#ifndef _H_SECCODEHOST
+#define _H_SECCODEHOST
+
+#include <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
diff --git a/lib/SecCodeHostLib.c b/lib/SecCodeHostLib.c
new file mode 100644 (file)
index 0000000..a79c85d
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+//#include <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));
+}
diff --git a/lib/SecCodeHostLib.h b/lib/SecCodeHostLib.h
new file mode 100644 (file)
index 0000000..65fa720
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*!
+       @header SecCodeHostLib
+       This header provides a subset of the hosting API for Code Signing.
+       This subset functionality is implemented as a static library written
+       entirely in C, and depends on nothing except the system library and the
+       C runtime. It is thus suitable to be used by low-level libraries and
+       other such system facilities. On the other hand, it does not provide the
+       full functionality of <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
diff --git a/lib/SecCodeSigner.cpp b/lib/SecCodeSigner.cpp
new file mode 100644 (file)
index 0000000..b6d3b45
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// SecCode - API frame for SecCode objects.
+//
+// Note that some SecCode* functions take SecStaticCodeRef arguments in order to
+// accept either static or dynamic code references, operating on the respective
+// StaticCode. Those functions are in SecStaticCode.cpp, not here, despite their name.
+//
+#include "cs.h"
+#include "CodeSigner.h"
+#include "cskernel.h"
+
+using namespace CodeSigning;
+
+
+//
+// Parameter keys
+//
+const CFStringRef kSecCodeSignerApplicationData = CFSTR("application-specific");
+const CFStringRef kSecCodeSignerDetached =             CFSTR("detached");
+const CFStringRef kSecCodeSignerDryRun =               CFSTR("dryrun");
+const CFStringRef kSecCodeSignerFlags =                        CFSTR("flags");
+const CFStringRef kSecCodeSignerIdentifier =   CFSTR("identifier");
+const CFStringRef kSecCodeSignerIdentifierPrefix = CFSTR("identifier-prefix");
+const CFStringRef kSecCodeSignerIdentity =             CFSTR("signer");
+const CFStringRef kSecCodeSignerPageSize =             CFSTR("pagesize");
+const CFStringRef kSecCodeSignerRequirements = CFSTR("requirements");
+const CFStringRef kSecCodeSignerResourceRules =        CFSTR("resource-rules");
+const CFStringRef kSecCodeSignerSigningTime =  CFSTR("signing-time");
+
+
+//
+// CF-standard type code functions
+//
+CFTypeID SecCodeSignerGetTypeID(void)
+{
+       BEGIN_CSAPI
+       return gCFObjects().CodeSigner.typeID;
+    END_CSAPI1(_kCFRuntimeNotATypeID)
+}
+
+
+//
+// Create a signer object
+//
+OSStatus SecCodeSignerCreate(CFDictionaryRef parameters, SecCSFlags flags,
+       SecCodeSignerRef *signerRef)
+{
+       BEGIN_CSAPI
+       SecPointer<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
+}
diff --git a/lib/SecCodeSigner.h b/lib/SecCodeSigner.h
new file mode 100644 (file)
index 0000000..5d72ce4
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*!
+       @header SecCodeSigner
+       SecCodeSigner represents an object that signs code.
+*/
+#ifndef _H_SECCODESIGNER
+#define _H_SECCODESIGNER
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <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
diff --git a/lib/SecIntegrity.cpp b/lib/SecIntegrity.cpp
new file mode 100644 (file)
index 0000000..5bbb603
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <Security/CSCommon.h>
+#include "SecIntegrity.h"
+
+
diff --git a/lib/SecIntegrity.h b/lib/SecIntegrity.h
new file mode 100644 (file)
index 0000000..c8fb069
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*!
+       @header SecIntegrity
+       This header provides functionality that allows a program to use its own
+       code signature to extend trust to parts of its bundle that it covers.
+       
+       This file does not help you verify code signatures themselves; use SecCode.h
+       for that. It is reasonable to use SecCode.h and SecIntegrity.h together.
+*/
+#ifndef _H_SECINTEGRITY
+#define _H_SECINTEGRITY
+
+#include <Security/CSCommon.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_H_SECINTEGRITY
diff --git a/lib/SecIntegrityLib.c b/lib/SecIntegrityLib.c
new file mode 100644 (file)
index 0000000..bab8ccc
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+//#include <Security/SecCodeHostLib.h>
+#include "SecIntegrityLib.h"
diff --git a/lib/SecIntegrityLib.h b/lib/SecIntegrityLib.h
new file mode 100644 (file)
index 0000000..b0960c6
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*!
+       @header SecIntegrityLib
+       This header provides a subset of the code-integrity API for Code Signing.
+       This subset functionality is implemented as a static library written
+       entirely in C, and depends on nothing except the system library and the
+       C runtime. It is thus suitable to be used by low-level libraries and
+       other such system facilities. On the other hand, it does not provide the
+       full functionality of <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
diff --git a/lib/SecRequirement.cpp b/lib/SecRequirement.cpp
new file mode 100644 (file)
index 0000000..0b6616d
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// SecRequirement - API frame for SecRequirement objects
+//
+#include "cs.h"
+#include "Requirements.h"
+#include "reqparser.h"
+#include "reqmaker.h"
+#include "reqdumper.h"
+#include <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
+}
+
diff --git a/lib/SecRequirement.h b/lib/SecRequirement.h
new file mode 100644 (file)
index 0000000..358b1b2
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*!
+       @header SecRequirement
+       SecRequirement represents a condition or constraint (a "Code Requirement")
+       that code must satisfy to be considered valid for some purpose.
+       SecRequirement itself does not understand or care WHY such a constraint
+       is appropriate or useful; it is purely a tool for formulating, recording,
+       and evaluating it.
+       
+       Code Requirements are usually stored and retrieved in the form of a variable-length
+       binary Blob that can be encapsulated as a CFDataRef and safely stored in various
+       data structures. They can be formulated in a text form that can be compiled
+       into binary form and decompiled back into text form without loss of functionality
+       (though comments and formatting are not preserved).
+*/
+#ifndef _H_SECREQUIREMENT
+#define _H_SECREQUIREMENT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <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
diff --git a/lib/SecStaticCode.cpp b/lib/SecStaticCode.cpp
new file mode 100644 (file)
index 0000000..51dd2a4
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// SecStaticCode - API frame for SecStaticCode objects
+//
+#include "cs.h"
+#include "StaticCode.h"
+#include <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
+}
diff --git a/lib/SecStaticCode.h b/lib/SecStaticCode.h
new file mode 100644 (file)
index 0000000..8e3b04b
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*!
+       @header SecStaticCode
+       SecStaticCode represents the Code Signing identity of code in the file system.
+       This includes applications, tools, frameworks, plugins, scripts, and so on.
+       
+       Normally, each SecCode has a specific SecStaticCode that holds its static signing
+       data. Informally, that is the SecStaticCode the SecCode "was made from". There is
+       however no viable link in the other direction - given a SecStaticCode, it is not
+       possible to find, enumerate, or control any SecCode that originated from it.
+*/
+#ifndef _H_SECSTATICCODE
+#define _H_SECSTATICCODE
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <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
diff --git a/lib/StaticCode.cpp b/lib/StaticCode.cpp
new file mode 100644 (file)
index 0000000..78d7208
--- /dev/null
@@ -0,0 +1,973 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// StaticCode - SecStaticCode API objects
+//
+#include "StaticCode.h"
+#include "Code.h"
+#include "reqmaker.h"
+#include "reqdumper.h"
+#include "sigblob.h"
+#include "resources.h"
+#include "renum.h"
+#include "csutilities.h"
+#include <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
diff --git a/lib/StaticCode.h b/lib/StaticCode.h
new file mode 100644 (file)
index 0000000..49d7d51
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// StaticCode - SecStaticCode API objects
+//
+#ifndef _H_STATICCODE
+#define _H_STATICCODE
+
+#include "cs.h"
+#include "Requirements.h"
+#include "requirement.h"
+#include "diskrep.h"
+#include "codedirectory.h"
+#include <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
diff --git a/lib/antlrplugin.cpp b/lib/antlrplugin.cpp
new file mode 100644 (file)
index 0000000..fc38e53
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "antlrplugin.h"
+#include "cserror.h"
+#include "RequirementLexer.hpp"
+#include "RequirementParser.hpp"
+#include <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
diff --git a/lib/antlrplugin.h b/lib/antlrplugin.h
new file mode 100644 (file)
index 0000000..2d11139
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// Plugin interface for internal Security plug-ins
+//
+#ifndef _H_ANTLRPLUGIN
+#define _H_ANTLRPLUGIN
+
+#include <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
diff --git a/lib/bundlediskrep.cpp b/lib/bundlediskrep.cpp
new file mode 100644 (file)
index 0000000..ad4d819
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include "bundlediskrep.h"
+#include <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
diff --git a/lib/bundlediskrep.h b/lib/bundlediskrep.h
new file mode 100644 (file)
index 0000000..4aae953
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// bundlediskrep - bundle directory disk representation
+//
+#ifndef _H_BUNDLEDISKREP
+#define _H_BUNDLEDISKREP
+
+#include "diskrep.h"
+#include "machorep.h"
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// A BundleDiskRep represents a standard Mac OS X bundle on disk.
+// The bundle is expected to have an Info.plist, and a "main executable file"
+// of some sort (as indicated therein).
+// The BundleDiskRep stores the necessary components in the main executable
+// if it is in Mach-O format, or in Contents files if not.
+// This DiskRep supports resource sealing.
+//
+class BundleDiskRep : public DiskRep {
+public:
+       BundleDiskRep(const char *path);
+       BundleDiskRep(CFBundleRef ref);
+       
+       CFDataRef component(CodeDirectory::SpecialSlot slot);
+       std::string mainExecutablePath();
+       CFURLRef canonicalPath();
+       std::string recommendedIdentifier();
+       std::string resourcesRootPath();
+       CFDictionaryRef defaultResourceRules();
+       const Requirements *defaultRequirements(const Architecture *arch);
+       Universal *mainExecutableImage();
+       size_t pageSize();
+       size_t signingBase();
+       size_t signingLimit();
+       std::string format();
+       CFArrayRef modifiedFiles();
+       UnixPlusPlus::FileDesc &fd();
+       void flush();
+
+       CFBundleRef bundle() const { return mBundle; }
+       
+public:
+       Writer *writer();
+       class Writer;
+       friend class Writer;
+       
+protected:
+       CFDataRef resourceData(CFURLRef url);
+       CFDataRef resourceData(const char *name) { return resourceData(CFTempURL(resourcePath(name))); }
+       
+       std::string resourcePath(const char *name);
+       
+private:
+       void checkModifiedFile(CFMutableArrayRef files, CodeDirectory::SpecialSlot slot);
+
+private:
+       CFRef<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
diff --git a/lib/cdbuilder.cpp b/lib/cdbuilder.cpp
new file mode 100644 (file)
index 0000000..eac0a42
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// cdbuilder - constructor for CodeDirectories
+//
+#include "cdbuilder.h"
+#include <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
diff --git a/lib/cdbuilder.h b/lib/cdbuilder.h
new file mode 100644 (file)
index 0000000..01c0a67
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// cdbuilder - constructor for CodeDirectories
+//
+#ifndef _H_CDBUILDER
+#define _H_CDBUILDER
+
+#include "codedirectory.h"
+
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// Builder can construct CodeDirectories from pieces:
+//     Builder builder;
+//     builder.variousSetters(withSuitableData);
+//  CodeDirectory *result = builder.build();
+// Builder is not reusable.
+//
+class CodeDirectory::Builder {
+public:
+       Builder();
+       
+       void executable(string path, size_t pagesize, size_t offset, size_t length);
+       void reopen(string path, size_t offset, size_t length);
+
+       void special(size_t slot, CFDataRef data);
+       void identifier(const std::string &code) { mIdentifier = code; }
+       void flags(uint32_t f) { mFlags = f; }
+       
+       size_t size();                                                          // calculate size
+       CodeDirectory *build();                                         // build CodeDirectory and return it
+       
+private:
+       Hash::SDigest mSpecial[cdSlotCount];            // special slot hashes
+       UnixPlusPlus::AutoFileDesc mExec;                       // main executable file
+       size_t mExecOffset;                                                     // starting offset in mExec
+       size_t mExecLength;                                                     // total bytes of file to sign
+       size_t mPageSize;                                                       // page size of executable (bytes)
+       uint32_t mFlags;                                                        // CodeDirectory flags
+       std::string mIdentifier;                                        // canonical identifier
+       
+       size_t mSpecialSlots;                                           // highest special slot set
+       size_t mCodeSlots;                                                      // number of code pages (slots)
+       
+       CodeDirectory *mDir;                                            // what we're building
+};
+
+
+}      // CodeSigning
+}      // Security
+
+
+#endif //_H_CDBUILDER
diff --git a/lib/cfmdiskrep.cpp b/lib/cfmdiskrep.cpp
new file mode 100644 (file)
index 0000000..a74c0e0
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+//
+// cfmdiskrep - single-file CFM (PEF) executable disk representation
+//
+#include "cfmdiskrep.h"
+#include <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
diff --git a/lib/cfmdiskrep.h b/lib/cfmdiskrep.h
new file mode 100644 (file)
index 0000000..8eeaa2b
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// cfmdiskrep - single-file CFM (PEF) executable disk representation
+//
+#ifndef _H_CFMDISKREP
+#define _H_CFMDISKREP
+
+#include "singlediskrep.h"
+#include "sigblob.h"
+#include "signerutils.h"
+#include <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
diff --git a/lib/cfmunge.cpp b/lib/cfmunge.cpp
new file mode 100644 (file)
index 0000000..214cac5
--- /dev/null
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+//
+// CoreFoundation building and parsing functions.
+//
+// These classes provide a printf/scanf-like interface to nested data structures
+// of the Property List Subset of CoreFoundation.
+//
+#include "cfmunge.h"
+#include <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
diff --git a/lib/cfmunge.h b/lib/cfmunge.h
new file mode 100644 (file)
index 0000000..2af591b
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+//
+// CoreFoundation building and parsing functions
+//
+#ifndef _H_CFMUNGE
+#define _H_CFMUNGE
+
+#include <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
diff --git a/lib/codedirectory.cpp b/lib/codedirectory.cpp
new file mode 100644 (file)
index 0000000..f9ad55d
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// codedirectory - format and operations for code signing "code directory" structures
+//
+#include "codedirectory.h"
+#include "CSCommon.h"
+
+using namespace UnixPlusPlus;
+
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// Canonical filesystem names for select slot numbers.
+// These are variously used for filenames, extended attribute names, etc.
+// to get some consistency in naming. These are for storing signing-related
+// data; they have no bearing on the actual hash slots in the CodeDirectory.
+//
+const char *CodeDirectory::canonicalSlotName(SpecialSlot slot)
+{
+       switch (slot) {
+       case cdRequirementsSlot:
+               return kSecCS_REQUIREMENTSFILE;
+       case cdResourceDirSlot:
+               return kSecCS_RESOURCEDIRFILE;
+       case cdCodeDirectorySlot:
+               return kSecCS_CODEDIRECTORYFILE;
+       case cdSignatureSlot:
+               return kSecCS_SIGNATUREFILE;
+       case cdApplicationSlot:
+               return kSecCS_APPLICATIONFILE;
+       default:
+               return NULL;
+       }
+}
+
+
+//
+// Canonical attributes of SpecialSlots.
+//
+unsigned CodeDirectory::slotAttributes(SpecialSlot slot)
+{
+       switch (slot) {
+       case cdRequirementsSlot:
+               return cdComponentIsBlob; // global
+       case cdCodeDirectorySlot:
+               return cdComponentPerArchitecture | cdComponentIsBlob;
+       case cdSignatureSlot:
+               return cdComponentPerArchitecture; // raw
+       default:
+               return 0; // global, raw
+       }
+}
+
+
+//
+// Symbolic names for code directory special slots.
+// These are only used for debug output. They are not API-official.
+// Needs to be coordinated with the cd*Slot enumeration in codedirectory.h.
+//
+#if !defined(NDEBUG)
+const char * const CodeDirectory::debugSlotName[] = {
+       "codedirectory",
+       "info",
+       "requirements",
+       "resources",
+       "application"
+};
+#endif //NDEBUG
+
+
+//
+// Check the version of this CodeDirectory for basic sanity.
+// Throws if the directory is corrupted or out of versioning bounds.
+// Returns if the version is usable (perhaps with degraded features due to
+// compatibility hacks).
+//
+void CodeDirectory::checkVersion() const
+{
+       if (!this->validateBlob())
+               MacOSError::throwMe(errSecCSSignatureInvalid);  // busted
+       if (version > compatibilityLimit)
+               MacOSError::throwMe(errSecCSSignatureUnsupported);      // too new - no clue
+       if (version > currentVersion)
+               secdebug("codedir", "%p version 0x%x newer than current 0x%x",
+                       this, uint32_t(version), currentVersion);
+}
+
+
+//
+// Validate a slot against data in memory.
+//
+bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot) const
+{
+       secdebug("codedir", "%p validating slot %d", this, int(slot));
+       Hash::Byte digest[Hash::digestLength];
+       hash(data, length, digest);
+       return memcmp(digest, (*this)[slot], Hash::digestLength) == 0;
+}
+
+
+//
+// Validate a slot against the contents of an open file. At most 'length' bytes
+// will be read from the file.
+//
+bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot) const
+{
+       Hash::Digest digest;
+       hash(fd, digest, length);
+       return memcmp(digest, (*this)[slot], Hash::digestLength) == 0;
+}
+
+
+//
+// Check whether a particular slot is present.
+// Absense is indicated by either a zero hash, or by lying outside
+// the slot range.
+//
+bool CodeDirectory::slotIsPresent(Slot slot) const
+{
+       if (slot >= -Slot(nSpecialSlots) && slot < Slot(nCodeSlots)) {
+               const Hash::Byte *digest = (*this)[slot];
+               for (unsigned n = 0; n < Hash::digestLength; n++)
+                       if (digest[n])
+                               return true;    // non-zero digest => present
+       }
+       return false;   // absent
+}
+
+
+//
+// Hash the next limit bytes of a file and return the digest.
+// If the file is shorter, hash as much as you can.
+// Limit==0 means unlimited (to end of file).
+// Return how many bytes were actually hashed.
+// Throw on any errors.
+//
+size_t CodeDirectory::hash(FileDesc fd, Hash::Byte *digest, size_t limit)
+{
+       IFDEBUG(size_t hpos = fd.position());
+       IFDEBUG(size_t hlimit = limit);
+       unsigned char buffer[4096];
+       Hash hash;
+       size_t total = 0;
+       for (;;) {
+               size_t size = sizeof(buffer);
+               if (limit && limit < size)
+                       size = limit;
+               size_t got = fd.read(buffer, size);
+               total += got;
+               if (fd.atEnd())
+                       break;
+               hash(buffer, got);
+               if (limit && (limit -= got) == 0)
+                       break;
+       }
+       hash.finish(digest);
+       secdebug("cdhash", "fd %d %zd@0x%zx => %2x.%2x.%2x...",
+               fd.fd(), hpos, hlimit, digest[0], digest[1], digest[2]);
+       return total;
+}
+
+
+//
+// Ditto, but hash a memory buffer instead.
+//
+size_t CodeDirectory::hash(const void *data, size_t length, Hash::Byte *digest)
+{
+       Hash hash;
+       hash(data, length);
+       hash.finish(digest);
+       return length;
+}
+
+
+//
+// Canonical text form for user-settable code directory flags
+//
+const CodeDirectory::FlagItem CodeDirectory::flagItems[] = {
+       { "host",               kSecCodeSignatureHost,                  true },
+       { "adhoc",              kSecCodeSignatureAdhoc,                 false },
+       { "hard",               kSecCodeSignatureForceHard,             true },
+       { "kill",               kSecCodeSignatureForceKill,             true },
+       { "expires",    kSecCodeSignatureForceExpiration, true },
+       { NULL }
+};
+
+
+//
+// Parse a canonical text description of code flags, in the form
+//     flag,...,flag
+// where each flag can be a prefix of a known flag name.
+// Internally set flags are not accepted.
+//
+uint32_t CodeDirectory::textFlags(std::string text)
+{
+       uint32_t flags = 0;
+       for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) {
+               string word = (comma == string::npos) ? text : text.substr(0, comma);
+               const CodeDirectory::FlagItem *item;
+               for (item = CodeDirectory::flagItems; item->name; item++)
+                       if (item->external && !strncmp(word.c_str(), item->name, word.size())) {
+                               flags |= item->value;
+                               break;
+                       }
+               if (!item)      // not found
+                       MacOSError::throwMe(errSecCSInvalidFlags);
+               if (comma == string::npos)      // last word
+                       break;
+       }
+       return flags;
+}
+
+
+}      // CodeSigning
+}      // Security
diff --git a/lib/codedirectory.h b/lib/codedirectory.h
new file mode 100644 (file)
index 0000000..38cedc2
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// codedirectory - format and operations for code signing "code directory" structures
+//
+// A CodeDirectory is a contiguous binary blob containing a two-tiered
+// hash of (much of) the contents of a real StaticCode object.
+// It consists of a header followed by an array of hash vector elements.
+//
+// This structure is meant to be self-describing, binary stable, and endian independent.
+//
+#ifndef _H_CODEDIRECTORY
+#define _H_CODEDIRECTORY
+
+#include <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
diff --git a/lib/cs.cpp b/lib/cs.cpp
new file mode 100644 (file)
index 0000000..f55a48e
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// cs.h - code signing core header
+//
+#include "cs.h"
+#include "cfmunge.h"
+
+namespace Security {
+namespace CodeSigning {
+
+
+ModuleNexus<CFObjects> gCFObjects;
+
+CFObjects::CFObjects()
+       : Code("SecCode", false),
+         StaticCode("SecStaticCode", false),
+         Requirement("SecRequirements", false),
+         CodeSigner("SecCodeSigner", false)
+{
+}
+
+
+}      // CodeSigning
+}      // Security
diff --git a/lib/cs.h b/lib/cs.h
new file mode 100644 (file)
index 0000000..d7957fc
--- /dev/null
+++ b/lib/cs.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// cs.h - code signing core header
+//
+#ifndef _H_CS
+#define _H_CS
+
+#include "cserror.h"
+#include <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
diff --git a/lib/cscdefs.c b/lib/cscdefs.c
new file mode 100644 (file)
index 0000000..401c1a6
--- /dev/null
@@ -0,0 +1,4 @@
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include "cscdefs.h"
diff --git a/lib/cscdefs.h b/lib/cscdefs.h
new file mode 100644 (file)
index 0000000..4c3b998
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Magic numbers used by Code Signing
+ */
+enum {
+       CSMAGIC_REQUIREMENT     = 0xfade0c00,           /* single Requirement blob */
+       CSMAGIC_REQUIREMENTS = 0xfade0c01,              /* Requirements vector (internal requirements) */
+       CSMAGIC_CODEDIRECTORY = 0xfade0c02,             /* CodeDirectory blob */
+       CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */
+       CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */
+       
+       CSSLOT_CODEDIRECTORY = 0,                               /* slot index for CodeDirectory */
+};
+
+
+/*
+ * Structure of an embedded-signature SuperBlob
+ */
+typedef struct __BlobIndex {
+       uint32_t type;                                  /* type of entry */
+       uint32_t offset;                                /* offset of entry */
+} CS_BlobIndex;
+
+typedef struct __SuperBlob {
+       uint32_t magic;                                 /* magic number */
+       uint32_t length;                                /* total length of SuperBlob */
+       uint32_t count;                                 /* number of index entries following */
+       CS_BlobIndex index[];                   /* (count) entries */
+       /* followed by Blobs in no particular order as indicated by offsets in index */
+} CS_SuperBlob;
+
+
+/*
+ * C form of a CodeDirectory.
+ */
+typedef struct __CodeDirectory {
+       uint32_t magic;                                 /* magic number (CSMAGIC_CODEDIRECTORY) */
+       uint32_t length;                                /* total length of CodeDirectory blob */
+       uint32_t version;                               /* compatibility version */
+       uint32_t flags;                                 /* setup and mode flags */
+       uint32_t hashOffset;                    /* offset of hash slot element at index zero */
+       uint32_t identOffset;                   /* offset of identifier string */
+       uint32_t nSpecialSlots;                 /* number of special hash slots */
+       uint32_t nCodeSlots;                    /* number of ordinary (code) hash slots */
+       uint32_t codeLimit;                             /* limit to main image signature range */
+       uint8_t hashSize;                               /* size of each hash in bytes */
+       uint8_t hashType;                               /* type of hash (cdHashType* constants) */
+       uint8_t spare1;                                 /* unused (must be zero) */
+       uint8_t pageSize;                               /* log2(page size in bytes); 0 => infinite */
+       uint32_t spare2;                                /* unused (must be zero) */
+       /* followed by dynamic content as located by offset fields above */
+} CS_CodeDirectory;
+
+
+/*
+ * Sample code to locate the CodeDirectory from an embedded signature blob
+ */
+static inline const CS_CodeDirectory *findCodeDirectory(const CS_SuperBlob *embedded)
+{
+       if (embedded && ntohl(embedded->magic) == CSMAGIC_EMBEDDED_SIGNATURE) {
+               const CS_BlobIndex *limit = &embedded->index[ntohl(embedded->count)];
+               const CS_BlobIndex *p;
+               for (p = embedded->index; p < limit; ++p)
+                       if (ntohl(p->type) == CSSLOT_CODEDIRECTORY) {
+                               const unsigned char *base = (const unsigned char *)embedded;
+                               const CS_CodeDirectory *cd = (const CS_CodeDirectory *)(base + ntohl(p->offset));
+                               if (cd->magic == CSMAGIC_CODEDIRECTORY)
+                                       return cd;
+                               else
+                                       break;
+                       }
+       }
+       // not found
+       return NULL;
+}
+
+
+/*
+ * Locating a page hash
+ */
+static inline const unsigned char *hashes(const CS_CodeDirectory *cd, unsigned page)
+{
+       const unsigned char *base = (const unsigned char *)cd;
+       assert(page < ntohl(cd->nCodeSlots));
+       
+       // "20" below is the size of a SHA-1 hash. There's got to be a constant for that
+       // where you get your SHA-1 functions from. This can also be had as cd->hashSize
+       // (but the constant is marginally faster, I suppose)
+       return base + ntohl(cd->hashOffset) + page * 20;
+}
diff --git a/lib/cserror.cpp b/lib/cserror.cpp
new file mode 100644 (file)
index 0000000..8f3dba0
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// cserror.h - extended-diagnostics Code Signing errors
+//
+#include "cs.h"
+#include "cfmunge.h"
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// We need a nothrow destructor
+//
+CSError::~CSError() throw ()
+{ }
+
+
+//
+// Create and throw various forms of CSError
+//
+void CSError::throwMe(OSStatus rc)
+{
+       throw CSError(rc);
+}
+
+void CSError::throwMe(OSStatus rc, CFDictionaryRef dict)
+{
+       throw CSError(rc, dict);
+}
+
+void CSError::throwMe(OSStatus rc, CFStringRef key, CFTypeRef value)
+{
+       throw CSError(rc, cfmake<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
diff --git a/lib/cserror.h b/lib/cserror.h
new file mode 100644 (file)
index 0000000..e872b50
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// cserror.h - extended-diagnostics Code Signing errors
+//
+#ifndef _H_CSERRORS
+#define _H_CSERRORS
+
+#include <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
diff --git a/lib/csgeneric.cpp b/lib/csgeneric.cpp
new file mode 100644 (file)
index 0000000..0ea453e
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// csgeneric - generic Code representative
+//
+#include "csgeneric.h"
+#include "cs.h"
+#include "StaticCode.h"
+#include <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
diff --git a/lib/csgeneric.h b/lib/csgeneric.h
new file mode 100644 (file)
index 0000000..c848639
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// csgeneric - generic Code representative
+//
+#ifndef _H_CSGENERIC
+#define _H_CSGENERIC
+
+#include "Code.h"
+#include <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
diff --git a/lib/cskernel.cpp b/lib/cskernel.cpp
new file mode 100644 (file)
index 0000000..bd39355
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// cskernel - Kernel implementation of the Code Signing Host Interface
+//
+#include "cskernel.h"
+#include "csprocess.h"
+#include "kerneldiskrep.h"
+#include <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
diff --git a/lib/cskernel.h b/lib/cskernel.h
new file mode 100644 (file)
index 0000000..b5e272e
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// cskernel - Kernel implementation of the Code Signing Host Interface
+//
+#ifndef _H_CSKERNEL
+#define _H_CSKERNEL
+
+#include "Code.h"
+#include "StaticCode.h"
+#include <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
diff --git a/lib/csprocess.cpp b/lib/csprocess.cpp
new file mode 100644 (file)
index 0000000..0d14392
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// csprocess - UNIX process implementation of the Code Signing Host Interface
+//
+#include "csprocess.h"
+#include "cskernel.h"
+#include <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
diff --git a/lib/csprocess.h b/lib/csprocess.h
new file mode 100644 (file)
index 0000000..7fd6efc
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// csprocess - UNIX process implementation of the Code Signing Host Interface
+//
+#ifndef _H_CSPROCESS
+#define _H_CSPROCESS
+
+#include "csgeneric.h"
+#include <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
diff --git a/lib/csutilities.cpp b/lib/csutilities.cpp
new file mode 100644 (file)
index 0000000..f818b06
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// csutilities - miscellaneous utilities for the code signing implementation
+//
+#include "csutilities.h"
+#include <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
diff --git a/lib/csutilities.h b/lib/csutilities.h
new file mode 100644 (file)
index 0000000..ec3b7d9
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// csutilities - miscellaneous utilities for the code signing implementation
+//
+// This is a collection of odds and ends that wouldn't fit anywhere else.
+// The common theme is that the contents are otherwise naturally homeless.
+//
+#ifndef _H_CSUTILITIES
+#define _H_CSUTILITIES
+
+#include <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
diff --git a/lib/diskrep.cpp b/lib/diskrep.cpp
new file mode 100644 (file)
index 0000000..0a28490
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// diskrep - disk representations of code
+//
+#include "diskrep.h"
+#include <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
diff --git a/lib/diskrep.h b/lib/diskrep.h
new file mode 100644 (file)
index 0000000..001d1a5
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// diskrep - disk representations of code
+//
+#ifndef _H_DISKREP
+#define _H_DISKREP
+
+#include "cs.h"
+#include "codedirectory.h"
+#include "requirement.h"
+#include "macho++.h"           // for class Architecture
+#include <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
diff --git a/lib/filediskrep.cpp b/lib/filediskrep.cpp
new file mode 100644 (file)
index 0000000..6906c74
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include "filediskrep.h"
+#include "StaticCode.h"
+#include "macho++.h"   // may perhaps move into security_utilities...
+#include <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
diff --git a/lib/filediskrep.h b/lib/filediskrep.h
new file mode 100644 (file)
index 0000000..e84b455
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// filediskrep - single-file executable disk representation
+//
+#ifndef _H_FILEDISKREP
+#define _H_FILEDISKREP
+
+#include "singlediskrep.h"
+#include "machorep.h"
+#include <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
diff --git a/lib/foreigndiskrep.cpp b/lib/foreigndiskrep.cpp
new file mode 100644 (file)
index 0000000..89ac70f
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+//
+// foreigndiskrep - foreign executable disk representation
+//
+#include "foreigndiskrep.h"
+#include <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
diff --git a/lib/foreigndiskrep.h b/lib/foreigndiskrep.h
new file mode 100644 (file)
index 0000000..9a8dd17
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// foreigndiskrep - foreign executable disk representation
+//
+#ifndef _H_FOREIGNDISKREP
+#define _H_FOREIGNDISKREP
+
+#include "singlediskrep.h"
+#include "sigblob.h"
+#include "signerutils.h"
+#include <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
diff --git a/lib/kerneldiskrep.cpp b/lib/kerneldiskrep.cpp
new file mode 100644 (file)
index 0000000..35b5ad7
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include "kerneldiskrep.h"
+
+namespace Security {
+namespace CodeSigning {
+
+using namespace UnixPlusPlus;
+
+
+//
+// Everything about the kernel is pretty much fixed, so there's
+// no state to maintain.
+//
+KernelDiskRep::KernelDiskRep()
+{
+}
+
+
+//
+// We can't pull any resources from the kernel.
+// And we know where it all is.
+//
+CFDataRef KernelDiskRep::component(CodeDirectory::SpecialSlot slot)
+{
+       return NULL;
+}
+
+CFURLRef KernelDiskRep::canonicalPath()
+{
+       return makeCFURL("/mach_kernel");
+}
+
+string KernelDiskRep::recommendedIdentifier()
+{
+       return "@@kernel@@";    //@@@ use $(uname -whatever)?
+}
+
+size_t KernelDiskRep::signingLimit()
+{
+       return 0;                               // don't bother
+}
+
+string KernelDiskRep::format()
+{
+       return "system kernel";
+}
+
+UnixPlusPlus::FileDesc &KernelDiskRep::fd()
+{
+       UnixError::throwMe(EINVAL);             // don't have one
+}
+
+string KernelDiskRep::mainExecutablePath()
+{
+       return "/mach_kernel";
+}
+
+
+} // end namespace CodeSigning
+} // end namespace Security
diff --git a/lib/kerneldiskrep.h b/lib/kerneldiskrep.h
new file mode 100644 (file)
index 0000000..973971c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// kerneldiskrep - the kernel's own disk representation.
+//
+// This is a very special case.
+// It's here primarily so we don't have to add special cases for the kernel
+// all over the higher layers.
+//
+#ifndef _H_KERNELDISKREP
+#define _H_KERNELDISKREP
+
+#include "diskrep.h"
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// A KernelDiskRep represents a (the) kernel on disk.
+// It has no write support, so we can't sign the kernel,
+// which is fine since we unconditionally trust it anyway.
+//
+class KernelDiskRep : public DiskRep {
+public:
+       KernelDiskRep();
+       
+       CFDataRef component(CodeDirectory::SpecialSlot slot);
+       std::string mainExecutablePath();
+       CFURLRef canonicalPath();
+       std::string recommendedIdentifier();
+       size_t signingLimit();
+       std::string format();
+       UnixPlusPlus::FileDesc &fd();
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_KERNELDISKREP
diff --git a/lib/macho++.cpp b/lib/macho++.cpp
new file mode 100644 (file)
index 0000000..9c5572b
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// macho++ - Mach-O object file helpers
+//
+#include "macho++.h"
+#include <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
diff --git a/lib/macho++.h b/lib/macho++.h
new file mode 100644 (file)
index 0000000..3c02c5f
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// macho++ - Mach-O object file helpers
+//
+#ifndef _H_MACHOPLUSPLUS
+#define _H_MACHOPLUSPLUS
+
+#include <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
diff --git a/lib/machorep.cpp b/lib/machorep.cpp
new file mode 100644 (file)
index 0000000..dae7c25
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// machorep - DiskRep mix-in for handling Mach-O main executables
+//
+#include "machorep.h"
+
+
+namespace Security {
+namespace CodeSigning {
+
+using namespace UnixPlusPlus;
+
+
+//
+// Object management.
+// We open the main executable lazily, so nothing much happens on construction.
+//
+MachORep::MachORep(const char *path)
+       : SingleDiskRep(path), mSigningData(NULL)
+{
+       mExecutable = new Universal(fd());
+}
+
+MachORep::~MachORep()
+{
+       delete mExecutable;
+       ::free(mSigningData);
+}
+
+
+//
+// Sniffer function for "plausible Mach-O binary"
+//
+bool MachORep::candidiate(FileDesc &fd)
+{
+       switch (Universal::typeOf(fd)) {
+       case MH_EXECUTE:
+       case MH_DYLIB:
+       case MH_DYLINKER:
+       case MH_BUNDLE:
+       case MH_PRELOAD:
+               return true;            // dynamic image; supported
+       case MH_OBJECT:
+               return false;           // maybe later...
+       default:
+               return false;           // not Mach-O (or too exotic)
+       }
+}
+
+
+//
+// For Mach-O binaries that are of PowerPC architecture, we recommend
+// allowing the Rosetta translator as a host. Otherwise, no suggestions.
+//
+static const uint8_t ppc_ireqs[] = {   // host => anchor apple and identifier com.apple.translate
+       0xfa, 0xde, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+       0x00, 0x00, 0x00, 0x14, 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01,
+       0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13,
+       0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c,
+       0x61, 0x74, 0x65, 0x00,
+};
+
+const Requirements *MachORep::defaultRequirements(const Architecture *arch)
+{
+       assert(arch);           // enforced by signing infrastructure
+       if (arch->cpuType() == CPU_TYPE_POWERPC)
+               return ((const Requirements *)ppc_ireqs)->clone();      // need to pass ownership
+       else
+               return NULL;
+}
+
+
+//
+// Obtain, cache, and return a Universal reference to the main executable,
+// IF the main executable is a Mach-O binary (or fat version thereof).
+// Returns NULL if the main executable can't be opened as such.
+//
+Universal *MachORep::mainExecutableImage()
+{
+       if (!mExecutable)
+               mExecutable = new Universal(fd());
+       return mExecutable;
+}
+
+
+//
+// Default to system page size for segmented (paged) signatures
+//
+size_t MachORep::pageSize()
+{
+       return segmentedPageSize;
+}
+
+
+//
+// Signing base is the start of the Mach-O architecture we're using
+//
+size_t MachORep::signingBase()
+{
+       return mainExecutableImage()->archOffset();
+}
+
+
+//
+// Retrieve a component from the executable.
+// This reads the entire signing SuperBlob when first called for an executable,
+// and then caches it for further use.
+// Note that we could read individual components directly off disk and only cache
+// the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
+// to cache the pieces anyway.
+//
+CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot)
+{
+       switch (slot) {
+       case cdInfoSlot:
+               return infoPlist();
+       default:
+               return embeddedComponent(slot);
+       }
+}
+
+
+// Retrieve a component from the embedded signature SuperBlob (if present).
+// This reads the entire signing SuperBlob when first called for an executable,
+// and then caches it for further use.
+// Note that we could read individual components directly off disk and only cache
+// the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
+// to cache the pieces anyway. But it's not clear that the resulting multiple I/O
+// calls wouldn't be slower in the end.
+//
+CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot)
+{
+       if (!mSigningData)              // fetch and cache
+               try {
+                       auto_ptr<MachO> 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
diff --git a/lib/machorep.h b/lib/machorep.h
new file mode 100644 (file)
index 0000000..4b05cf8
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// machorep - DiskRep mix-in for handling Mach-O main executables
+//
+#ifndef _H_MACHOREP
+#define _H_MACHOREP
+
+#include "singlediskrep.h"
+#include "sigblob.h"
+#include <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
diff --git a/lib/renum.cpp b/lib/renum.cpp
new file mode 100644 (file)
index 0000000..8ab499e
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// renum - enumerator for code (usually bundle) resources
+//
+#include "renum.h"
+#include <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
diff --git a/lib/renum.h b/lib/renum.h
new file mode 100644 (file)
index 0000000..1d40983
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// renum - enumerator for code (usually bundle) resources
+//
+#ifndef _H_RENUM
+#define _H_RENUM
+
+#include <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
diff --git a/lib/reqdumper.cpp b/lib/reqdumper.cpp
new file mode 100644 (file)
index 0000000..33867de
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// reqdumper - Requirement un-parsing (disassembly)
+//
+#include "reqdumper.h"
+#include <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
diff --git a/lib/reqdumper.h b/lib/reqdumper.h
new file mode 100644 (file)
index 0000000..70c881f
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// reqdumper - Requirement un-parsing (disassembly)
+//
+#ifndef _H_REQDUMPER
+#define _H_REQDUMPER
+
+#include "reqreader.h"
+#include <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
diff --git a/lib/reqinterp.cpp b/lib/reqinterp.cpp
new file mode 100644 (file)
index 0000000..0c20659
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// reqinterp - Requirement language (exprOp) interpreter
+//
+#include "reqinterp.h"
+#include <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
diff --git a/lib/reqinterp.h b/lib/reqinterp.h
new file mode 100644 (file)
index 0000000..222bd17
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// reqinterp - Requirement language (exprOp) interpreter
+//
+#ifndef _H_REQINTERP
+#define _H_REQINTERP
+
+#include <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
diff --git a/lib/reqmaker.cpp b/lib/reqmaker.cpp
new file mode 100644 (file)
index 0000000..e4cd628
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// reqmaker - Requirement assembler
+//
+#include "reqmaker.h"
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// Requirement::Makers
+//
+Requirement::Maker::Maker(Kind k)
+       : mSize(1024)
+{
+       mBuffer = (Requirement *)malloc(mSize);
+       mBuffer->initialize();
+       mBuffer->kind(k);
+       mPC = sizeof(Requirement);
+}
+
+// need at least (size) bytes in the creation buffer
+void Requirement::Maker::require(size_t size)
+{
+       if (mPC + size > mSize) {
+               mSize *= 2;
+               if (mPC + size > mSize)
+                       mSize = mPC + size;
+               if (!(mBuffer = (Requirement *)realloc(mBuffer, mSize)))
+                       UnixError::throwMe(ENOMEM);
+       }
+}
+
+// allocate (size) bytes at end of buffer and return pointer to that
+void *Requirement::Maker::alloc(size_t size)
+{
+       // round size up to preserve alignment
+       size_t usedSize = LowLevelMemoryUtilities::alignUp(size, baseAlignment);
+       require(usedSize);
+       void *data = mBuffer->at<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
diff --git a/lib/reqmaker.h b/lib/reqmaker.h
new file mode 100644 (file)
index 0000000..9a72e8b
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// reqmaker - Requirement assembler
+//
+#ifndef _H_REQMAKER
+#define _H_REQMAKER
+
+#include <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
diff --git a/lib/reqparser.cpp b/lib/reqparser.cpp
new file mode 100644 (file)
index 0000000..e419f35
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// reqparser - interface to Requirement language parser/compiler
+//
+#include "reqparser.h"
+#include "antlrplugin.h"
+#include "cserror.h"
+#include <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
diff --git a/lib/reqparser.h b/lib/reqparser.h
new file mode 100644 (file)
index 0000000..e4f85e8
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// reqparser - interface to Requirement language parser/compiler
+//
+#ifndef _H_REQPARSER
+#define _H_REQPARSER
+
+#include "requirement.h"
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// Generic parser interface
+//
+template <class 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
diff --git a/lib/reqreader.cpp b/lib/reqreader.cpp
new file mode 100644 (file)
index 0000000..066517f
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// reqreader - Requirement language (exprOp) reader/scanner
+//
+#include "reqreader.h"
+#include <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
diff --git a/lib/reqreader.h b/lib/reqreader.h
new file mode 100644 (file)
index 0000000..4a7a251
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// reqreader - Requirement language (exprOp) reader/scanner
+//
+#ifndef _H_REQREADER
+#define _H_REQREADER
+
+#include <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
diff --git a/lib/requirement.cpp b/lib/requirement.cpp
new file mode 100644 (file)
index 0000000..f4b294a
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// requirement - Code Requirement Blob description
+//
+#include "requirement.h"
+#include "reqinterp.h"
+#include <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
diff --git a/lib/requirement.h b/lib/requirement.h
new file mode 100644 (file)
index 0000000..41871e0
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// requirement - Code Requirement Blob description
+//
+#ifndef _H_REQUIREMENT
+#define _H_REQUIREMENT
+
+#include <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
diff --git a/lib/resources.cpp b/lib/resources.cpp
new file mode 100644 (file)
index 0000000..a98f924
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// resource directory construction and verification
+//
+#include "resources.h"
+#include "renum.h"
+#include <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
diff --git a/lib/resources.h b/lib/resources.h
new file mode 100644 (file)
index 0000000..35ee00a
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// resource directory construction and verification
+//
+#ifndef _H_RSIGN
+#define _H_RSIGN
+
+#include "CodeSigner.h"
+#include "renum.h"
+#include <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
diff --git a/lib/security_codesigning.exp b/lib/security_codesigning.exp
new file mode 100644 (file)
index 0000000..6109eb5
--- /dev/null
@@ -0,0 +1,93 @@
+#
+# Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+#
+_SecCodeGetTypeID
+_SecGetRootCode
+_SecCodeCopySelf
+_SecCodeCopyStaticCode
+_SecCodeCopyHost
+_SecCodeCopyGuestWithAttributes
+_SecCodeCreateWithPID
+_SecCodeCheckValidity
+_SecCodeCheckValidityWithErrors
+_SecCodeCopyPath
+_SecCodeCopyDesignatedRequirement
+_SecCodeCopySigningInformation
+_SecCodeMapMemory
+_SecCodeSetDetachedSignature
+_SecStaticCodeGetTypeID
+_SecStaticCodeCreateWithPath
+_SecStaticCodeCheckValidity
+_SecStaticCodeCheckValidityWithErrors
+_SecRequirementGetTypeID
+_SecRequirementCreateWithData
+_SecRequirementCreateWithResource
+_SecRequirementCreateWithString
+_SecRequirementCreateWithStringAndErrors
+_SecRequirementCreateGroup
+_SecRequirementCopyData
+_SecRequirementCopyString
+_SecCodeSignerGetTypeID
+_SecCodeSignerCreate
+_SecCodeSignerAddSignature
+_SecCodeSignerAddSignatureWithErrors
+_SecHostCreateGuest
+_SecHostRemoveGuest
+_SecHostSetGuestStatus
+_SecHostSelectGuest
+_SecHostSelectedGuest
+_SecHostSetHostingPort
+_kSecCodeSignerApplicationData
+_kSecCodeSignerDetached
+_kSecCodeSignerDryRun
+_kSecCodeSignerFlags
+_kSecCodeSignerIdentifier
+_kSecCodeSignerIdentifierPrefix
+_kSecCodeSignerIdentity
+_kSecCodeSignerPageSize
+_kSecCodeSignerRequirements
+_kSecCodeSignerResourceRules
+_kSecCodeSignerSigningTime
+_kSecCodeInfoCertificates
+_kSecCodeInfoChangedFiles
+_kSecCodeInfoCMS
+_kSecCodeInfoTime
+_kSecCodeInfoDesignatedRequirement
+_kSecCodeInfoFormat
+_kSecCodeInfoIdentifier
+_kSecCodeInfoImplicitDesignatedRequirement
+_kSecCodeInfoMainExecutable
+_kSecCodeInfoPList
+_kSecCodeInfoRequirements
+_kSecCodeInfoStatus
+_kSecCodeInfoTrust
+_kSecGuestAttributePid
+_kSecGuestAttributeCanonical
+_kSecGuestAttributeMachPort
+_kSecCFErrorPattern
+_kSecCFErrorResourceSeal
+_kSecCFErrorResourceAdded
+_kSecCFErrorResourceAltered
+_kSecCFErrorResourceMissing
+_kSecCFErrorInfoPlist
+_kSecCFErrorGuestAttributes
+_kSecCFErrorRequirementSyntax
diff --git a/lib/sigblob.cpp b/lib/sigblob.cpp
new file mode 100644 (file)
index 0000000..bdf6a0b
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// sigblob - signature (Super)Blob types
+//
+#include "sigblob.h"
+
+
+namespace Security {
+namespace CodeSigning {
+
+
+CFDataRef EmbeddedSignatureBlob::component(CodeDirectory::SpecialSlot slot) const
+{
+       if (const BlobCore *blob = this->find(slot))
+               if (CodeDirectory::slotAttributes(slot) & cdComponentIsBlob)
+                       return makeCFData(*blob);       // is a native Blob
+               else
+                       return makeCFData(*BlobWrapper::specific(blob)); // unwrap payload
+       return NULL;
+}
+
+
+void EmbeddedSignatureBlob::Maker::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
+{
+       if (CodeDirectory::slotAttributes(slot) & cdComponentIsBlob)
+               add(slot, reinterpret_cast<const BlobCore *>(CFDataGetBytePtr(data))->clone());
+       else
+               add(slot, BlobWrapper::alloc(CFDataGetBytePtr(data), CFDataGetLength(data)));
+}
+
+
+} // end namespace CodeSigning
+} // end namespace Security
diff --git a/lib/sigblob.h b/lib/sigblob.h
new file mode 100644 (file)
index 0000000..01f73d8
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// sigblob - signature (Super)Blob types
+//
+#ifndef _H_SIGBLOB
+#define _H_SIGBLOB
+
+#include "codedirectory.h"
+#include <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
diff --git a/lib/signer.cpp b/lib/signer.cpp
new file mode 100644 (file)
index 0000000..d3f964c
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// signer - Signing operation supervisor and controller
+//
+#include "signer.h"
+#include "resources.h"
+#include "signerutils.h"
+#include "SecCodeSigner.h"
+#include <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
diff --git a/lib/signer.h b/lib/signer.h
new file mode 100644 (file)
index 0000000..ba236d1
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// signer - Signing operation supervisor and controller
+//
+#ifndef _H_SIGNER
+#define _H_SIGNER
+
+#include "CodeSigner.h"
+#include "cdbuilder.h"
+#include "signerutils.h"
+#include "StaticCode.h"
+#include <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
diff --git a/lib/signerutils.cpp b/lib/signerutils.cpp
new file mode 100644 (file)
index 0000000..e005e82
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// signerutils - utilities for signature generation
+//
+#include "signerutils.h"
+#include "signer.h"
+#include "SecCodeSigner.h"
+#include <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
diff --git a/lib/signerutils.h b/lib/signerutils.h
new file mode 100644 (file)
index 0000000..827c11e
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// signerutils - utilities for signature generation
+//
+#ifndef _H_SIGNERUTILS
+#define _H_SIGNERUTILS
+
+#include "CodeSigner.h"
+#include "sigblob.h"
+#include "cdbuilder.h"
+#include <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
diff --git a/lib/singlediskrep.cpp b/lib/singlediskrep.cpp
new file mode 100644 (file)
index 0000000..8331a5a
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// singlediskrep - semi-abstract diskrep for a single file of some kind
+//
+#include "singlediskrep.h"
+
+
+namespace Security {
+namespace CodeSigning {
+
+using namespace UnixPlusPlus;
+
+
+//
+// Construct a SingleDiskRep
+//
+SingleDiskRep::SingleDiskRep(const char *path)
+       : mPath(path)
+{
+}
+
+
+//
+// Both the canonical and main executable path of a SingleDiskRep is, well, its path.
+//
+CFURLRef SingleDiskRep::canonicalPath()
+{
+       return makeCFURL(mPath);
+}
+
+string SingleDiskRep::mainExecutablePath()
+{
+       return mPath;
+}
+
+
+//
+// The recommended identifier of a SingleDiskRep is, absent any better clue,
+// the basename of its path.
+//
+string SingleDiskRep::recommendedIdentifier()
+{      
+       string::size_type p = mPath.rfind('/');
+       if (p == string::npos)
+               return mPath;
+       else
+               return mPath.substr(p+1);
+}
+
+
+//
+// The default signing limit is the size of the file.
+// This will do unless the signing data gets creatively stuck in there somewhere.
+//
+size_t SingleDiskRep::signingLimit()
+{
+       return fd().fileSize();
+}
+
+
+//
+// A lazily opened read-only file descriptor for the path.
+//
+FileDesc &SingleDiskRep::fd()
+{
+       if (!mFd)
+               mFd.open(mPath, O_RDONLY);
+       return mFd;
+}
+
+
+//
+// Flush cached state
+//
+void SingleDiskRep::flush()
+{
+       mFd.close();
+}
+
+
+//
+// Prototype Writers
+//
+FileDesc &SingleDiskRep::Writer::fd()
+{
+       if (!mFd)
+               mFd.open(rep->path(), O_RDWR);
+       return mFd;
+}
+
+
+} // end namespace CodeSigning
+} // end namespace Security
diff --git a/lib/singlediskrep.h b/lib/singlediskrep.h
new file mode 100644 (file)
index 0000000..e66ec47
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// singlediskrep - semi-abstract diskrep for a single file of some kind
+//
+#ifndef _H_SINGLEDISKREP
+#define _H_SINGLEDISKREP
+
+#include "diskrep.h"
+#include <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
diff --git a/libsecurity_codesigning.xcodeproj/project.pbxproj b/libsecurity_codesigning.xcodeproj/project.pbxproj
new file mode 100644 (file)
index 0000000..6de0c93
--- /dev/null
@@ -0,0 +1,1407 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 42;
+       objects = {
+
+/* Begin PBXAggregateTarget section */
+               C2D383B80A23A8C4005C63A2 /* Requirements Language */ = {
+                       isa = PBXAggregateTarget;
+                       buildConfigurationList = C2D383C00A23A8E3005C63A2 /* Build configuration list for PBXAggregateTarget "Requirements Language" */;
+                       buildPhases = (
+                               C2D383B70A23A8C4005C63A2 /* ShellScript */,
+                       );
+                       dependencies = (
+                       );
+                       name = "Requirements Language";
+                       productName = "Requirements Language";
+               };
+               C2E2873F0B5D8F8F009336A0 /* Everything */ = {
+                       isa = PBXAggregateTarget;
+                       buildConfigurationList = C2E287470B5D8FD8009336A0 /* Build configuration list for PBXAggregateTarget "Everything" */;
+                       buildPhases = (
+                       );
+                       dependencies = (
+                               C2E287410B5D8F97009336A0 /* PBXTargetDependency */,
+                               C250F6C50B5EF4E40076098F /* PBXTargetDependency */,
+                               C2E287430B5D8F9A009336A0 /* PBXTargetDependency */,
+                       );
+                       name = Everything;
+                       productName = Everything;
+               };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+               C2093AA80BB0948000EB8599 /* reqreader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2093AA60BB0948000EB8599 /* reqreader.cpp */; };
+               C2093AA90BB0948000EB8599 /* reqreader.h in Headers */ = {isa = PBXBuildFile; fileRef = C2093AA70BB0948000EB8599 /* reqreader.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2093AC80BB0967D00EB8599 /* reqreader.h in Headers */ = {isa = PBXBuildFile; fileRef = C2093AA70BB0948000EB8599 /* reqreader.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C21CFC5F0A250D1C006CD5B1 /* reqdumper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C21CFC5D0A250D1C006CD5B1 /* reqdumper.cpp */; };
+               C21CFC620A250D1C006CD5B1 /* reqdumper.h in Headers */ = {isa = PBXBuildFile; fileRef = C21CFC5E0A250D1C006CD5B1 /* reqdumper.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C21EA3DD0AD2F81300E6E31C /* SecCodeSigner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C21EA3DB0AD2F81300E6E31C /* SecCodeSigner.cpp */; };
+               C21EA3DE0AD2F81300E6E31C /* SecCodeSigner.h in Headers */ = {isa = PBXBuildFile; fileRef = C21EA3DC0AD2F81300E6E31C /* SecCodeSigner.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C21EA3E00AD2F81300E6E31C /* SecCodeSigner.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C21EA3DC0AD2F81300E6E31C /* SecCodeSigner.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C21EA3E30AD2FA0900E6E31C /* CodeSigner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C21EA3E10AD2FA0900E6E31C /* CodeSigner.cpp */; };
+               C224635F0B8620F800626F1B /* RequirementParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383F00A23A9D3005C63A2 /* RequirementParser.cpp */; };
+               C22463600B8620F800626F1B /* RequirementLexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383EE0A23A9D3005C63A2 /* RequirementLexer.cpp */; };
+               C22463610B86210100626F1B /* antlrplugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2CC31130B85254F005FA59D /* antlrplugin.cpp */; };
+               C236E3D70AD59446000F5140 /* signer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C236E3D50AD59446000F5140 /* signer.cpp */; };
+               C236E3D80AD59446000F5140 /* signer.h in Headers */ = {isa = PBXBuildFile; fileRef = C236E3D60AD59446000F5140 /* signer.h */; };
+               C236E3DB0AD595C2000F5140 /* signerutils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C236E3D90AD595C2000F5140 /* signerutils.cpp */; };
+               C236E3DC0AD595C2000F5140 /* signerutils.h in Headers */ = {isa = PBXBuildFile; fileRef = C236E3DA0AD595C2000F5140 /* signerutils.h */; };
+               C250F6C30B5EF1910076098F /* SecIntegrity.h in Headers */ = {isa = PBXBuildFile; fileRef = C250F6C20B5EF1910076098F /* SecIntegrity.h */; };
+               C25942440BA7095000877E56 /* foreigndiskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C25942420BA7095000877E56 /* foreigndiskrep.cpp */; };
+               C25942450BA7095000877E56 /* foreigndiskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C25942430BA7095000877E56 /* foreigndiskrep.h */; };
+               C259DFD60AD6D9BA00C9ACC6 /* sigblob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C259DFD40AD6D9BA00C9ACC6 /* sigblob.cpp */; };
+               C259DFD70AD6D9BA00C9ACC6 /* sigblob.h in Headers */ = {isa = PBXBuildFile; fileRef = C259DFD50AD6D9BA00C9ACC6 /* sigblob.h */; };
+               C26B45C10B8A9C0A003C0ACA /* ucspc in Frameworks */ = {isa = PBXBuildFile; fileRef = C26B45C00B8A9C00003C0ACA /* ucspc */; };
+               C297DF250B014BB300E94EE0 /* SecCodeSigner.h in Headers */ = {isa = PBXBuildFile; fileRef = C21EA3DC0AD2F81300E6E31C /* SecCodeSigner.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2A487530B7914F400849490 /* SecCodeHostLib.h in Headers */ = {isa = PBXBuildFile; fileRef = C2BC1F340B580DA7003EC9DC /* SecCodeHostLib.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2A487540B79150C00849490 /* SecIntegrity.h in Headers */ = {isa = PBXBuildFile; fileRef = C250F6C20B5EF1910076098F /* SecIntegrity.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2A487550B79152A00849490 /* SecIntegrity.h in Headers */ = {isa = PBXBuildFile; fileRef = C250F6C20B5EF1910076098F /* SecIntegrity.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2A487560B79152A00849490 /* SecCodeHostLib.h in Headers */ = {isa = PBXBuildFile; fileRef = C2BC1F340B580DA7003EC9DC /* SecCodeHostLib.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2A752B70B80EAFB004CF655 /* SecIntegrity.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C250F6C20B5EF1910076098F /* SecIntegrity.h */; };
+               C2A752B80B80EAFB004CF655 /* SecCodeHostLib.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2BC1F340B580DA7003EC9DC /* SecCodeHostLib.h */; };
+               C2A976AA0B8A2E36008B4EA0 /* csutilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2A976A80B8A2E36008B4EA0 /* csutilities.cpp */; };
+               C2A976AB0B8A2E36008B4EA0 /* csutilities.h in Headers */ = {isa = PBXBuildFile; fileRef = C2A976A90B8A2E36008B4EA0 /* csutilities.h */; };
+               C2BC1F350B580DA7003EC9DC /* SecCodeHostLib.h in Headers */ = {isa = PBXBuildFile; fileRef = C2BC1F340B580DA7003EC9DC /* SecCodeHostLib.h */; };
+               C2BD519C0A9392FD000FE43D /* machorep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2BD519A0A9392FD000FE43D /* machorep.cpp */; };
+               C2BD519F0A9392FD000FE43D /* machorep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2BD519B0A9392FD000FE43D /* machorep.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2BD60FA0AC863FC0057FD3D /* csgeneric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2BD60F80AC863FC0057FD3D /* csgeneric.cpp */; };
+               C2BD61BD0AC9C77B0057FD3D /* csgeneric.h in Headers */ = {isa = PBXBuildFile; fileRef = C2BD60F90AC863FC0057FD3D /* csgeneric.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2C1DF140A2E3D7200D1B02B /* requirement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383350A237F47005C63A2 /* requirement.cpp */; };
+               C2C1DFBB0A2F80EB00D1B02B /* reqinterp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2C1DFB90A2F80EB00D1B02B /* reqinterp.cpp */; };
+               C2C1DFBC0A2F80EB00D1B02B /* reqinterp.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C1DFBA0A2F80EB00D1B02B /* reqinterp.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2C1DFBE0A2F80EB00D1B02B /* reqinterp.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C1DFBA0A2F80EB00D1B02B /* reqinterp.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2C1DFC30A2F820500D1B02B /* reqmaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2C1DFC10A2F820500D1B02B /* reqmaker.cpp */; };
+               C2C1DFC40A2F820500D1B02B /* reqmaker.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C1DFC20A2F820500D1B02B /* reqmaker.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2C1DFC60A2F820500D1B02B /* reqmaker.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C1DFC20A2F820500D1B02B /* reqmaker.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2C3BC5F0BA1D6FE00E869D1 /* cfmdiskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2C3BC5D0BA1D6FE00E869D1 /* cfmdiskrep.cpp */; };
+               C2C3BC600BA1D6FE00E869D1 /* cfmdiskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C3BC5E0BA1D6FE00E869D1 /* cfmdiskrep.h */; };
+               C2C3BCD30BA1E47E00E869D1 /* singlediskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2C3BCD10BA1E47E00E869D1 /* singlediskrep.cpp */; };
+               C2C3BCD40BA1E47E00E869D1 /* singlediskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C3BCD20BA1E47E00E869D1 /* singlediskrep.h */; };
+               C2C931B40AB8BA1200F83950 /* SecCodeHost.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2C931B30AB8BA1200F83950 /* SecCodeHost.cpp */; };
+               C2C931B50AB8BA8200F83950 /* SecCodeHost.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383190A237F47005C63A2 /* SecCodeHost.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2C931B60AB8BAC200F83950 /* SecCodeHost.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383190A237F47005C63A2 /* SecCodeHost.h */; };
+               C2CC31050B8523AD005FA59D /* SecIntegrityLib.h in Headers */ = {isa = PBXBuildFile; fileRef = C2CC31040B8523AD005FA59D /* SecIntegrityLib.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2CC31060B8523F8005FA59D /* SecIntegrityLib.h in Headers */ = {isa = PBXBuildFile; fileRef = C2CC31040B8523AD005FA59D /* SecIntegrityLib.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2CC310F0B852424005FA59D /* SecIntegrityLib.c in Sources */ = {isa = PBXBuildFile; fileRef = C2CC310E0B852424005FA59D /* SecIntegrityLib.c */; };
+               C2CC31100B852447005FA59D /* SecIntegrityLib.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2CC31040B8523AD005FA59D /* SecIntegrityLib.h */; };
+               C2CCF0310A3F523D0085795A /* macho++.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2CCF02F0A3F523D0085795A /* macho++.cpp */; };
+               C2CCF0340A3F523D0085795A /* macho++.h in Headers */ = {isa = PBXBuildFile; fileRef = C2CCF0300A3F523D0085795A /* macho++.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D3833C0A237F47005C63A2 /* bundlediskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383120A237F47005C63A2 /* bundlediskrep.cpp */; };
+               C2D3833E0A237F47005C63A2 /* cdbuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383140A237F47005C63A2 /* cdbuilder.cpp */; };
+               C2D383400A237F47005C63A2 /* codedirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383160A237F47005C63A2 /* codedirectory.cpp */; };
+               C2D383440A237F47005C63A2 /* cs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3831A0A237F47005C63A2 /* cs.cpp */; };
+               C2D383460A237F47005C63A2 /* CSCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3831C0A237F47005C63A2 /* CSCommon.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2D383470A237F47005C63A2 /* SecCode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3831D0A237F47005C63A2 /* SecCode.cpp */; };
+               C2D383480A237F47005C63A2 /* SecCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3831E0A237F47005C63A2 /* SecCode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2D383490A237F47005C63A2 /* cskernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3831F0A237F47005C63A2 /* cskernel.cpp */; };
+               C2D3834B0A237F47005C63A2 /* SecStaticCode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383210A237F47005C63A2 /* SecStaticCode.cpp */; };
+               C2D3834C0A237F47005C63A2 /* SecStaticCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383220A237F47005C63A2 /* SecStaticCode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2D3834D0A237F47005C63A2 /* csprocess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383230A237F47005C63A2 /* csprocess.cpp */; };
+               C2D3834F0A237F47005C63A2 /* SecRequirement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383250A237F47005C63A2 /* SecRequirement.cpp */; };
+               C2D383500A237F47005C63A2 /* SecRequirement.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383260A237F47005C63A2 /* SecRequirement.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2D383510A237F47005C63A2 /* diskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383270A237F47005C63A2 /* diskrep.cpp */; };
+               C2D383550A237F47005C63A2 /* filediskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3832B0A237F47005C63A2 /* filediskrep.cpp */; };
+               C2D383570A237F47005C63A2 /* Code.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3832D0A237F47005C63A2 /* Code.cpp */; };
+               C2D383590A237F47005C63A2 /* kerneldiskrep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D3832F0A237F47005C63A2 /* kerneldiskrep.cpp */; };
+               C2D3835B0A237F47005C63A2 /* StaticCode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383310A237F47005C63A2 /* StaticCode.cpp */; };
+               C2D3835D0A237F47005C63A2 /* reqparser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383330A237F47005C63A2 /* reqparser.cpp */; };
+               C2D383610A237F47005C63A2 /* Requirements.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383370A237F47005C63A2 /* Requirements.cpp */; };
+               C2D383630A237F47005C63A2 /* security_codesigning.exp in Sources */ = {isa = PBXBuildFile; fileRef = C2D383390A237F47005C63A2 /* security_codesigning.exp */; };
+               C2D383670A237F47005C63A2 /* bundlediskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383130A237F47005C63A2 /* bundlediskrep.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D383690A237F47005C63A2 /* cdbuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383150A237F47005C63A2 /* cdbuilder.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D3836B0A237F47005C63A2 /* codedirectory.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383170A237F47005C63A2 /* codedirectory.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D3836C0A237F47005C63A2 /* CodeSigning.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383180A237F47005C63A2 /* CodeSigning.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2D3836D0A237F47005C63A2 /* SecCodeHost.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383190A237F47005C63A2 /* SecCodeHost.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2D3836F0A237F47005C63A2 /* cs.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3831B0A237F47005C63A2 /* cs.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D383700A237F47005C63A2 /* CSCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3831C0A237F47005C63A2 /* CSCommon.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2D383720A237F47005C63A2 /* SecCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3831E0A237F47005C63A2 /* SecCode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2D383740A237F47005C63A2 /* cskernel.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383200A237F47005C63A2 /* cskernel.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D383760A237F47005C63A2 /* SecStaticCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383220A237F47005C63A2 /* SecStaticCode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2D383780A237F47005C63A2 /* csprocess.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383240A237F47005C63A2 /* csprocess.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D3837A0A237F47005C63A2 /* SecRequirement.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383260A237F47005C63A2 /* SecRequirement.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2D3837C0A237F47005C63A2 /* diskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383280A237F47005C63A2 /* diskrep.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D383800A237F47005C63A2 /* filediskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3832C0A237F47005C63A2 /* filediskrep.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D383820A237F47005C63A2 /* Code.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D3832E0A237F47005C63A2 /* Code.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D383840A237F47005C63A2 /* kerneldiskrep.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383300A237F47005C63A2 /* kerneldiskrep.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D383860A237F47005C63A2 /* StaticCode.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383320A237F47005C63A2 /* StaticCode.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D383880A237F47005C63A2 /* reqparser.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383340A237F47005C63A2 /* reqparser.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D3838A0A237F47005C63A2 /* requirement.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383360A237F47005C63A2 /* requirement.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D3838C0A237F47005C63A2 /* Requirements.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383380A237F47005C63A2 /* Requirements.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2D3838D0A237F47005C63A2 /* security_codesigning.exp in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383390A237F47005C63A2 /* security_codesigning.exp */; };
+               C2D383900A23801C005C63A2 /* security_codesigning.exp in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383390A237F47005C63A2 /* security_codesigning.exp */; };
+               C2D383910A23803A005C63A2 /* CSCommon.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D3831C0A237F47005C63A2 /* CSCommon.h */; };
+               C2D383920A23803A005C63A2 /* SecCode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D3831E0A237F47005C63A2 /* SecCode.h */; };
+               C2D383930A23803A005C63A2 /* SecStaticCode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383220A237F47005C63A2 /* SecStaticCode.h */; };
+               C2D383940A23803A005C63A2 /* SecRequirement.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383260A237F47005C63A2 /* SecRequirement.h */; };
+               C2D3839A0A238132005C63A2 /* CodeSigning.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383180A237F47005C63A2 /* CodeSigning.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C2D3839B0A238150005C63A2 /* CodeSigning.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2D383180A237F47005C63A2 /* CodeSigning.h */; };
+               C2D8A07F0AE7F6E300F68F79 /* cfmunge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D8A07D0AE7F6E300F68F79 /* cfmunge.cpp */; };
+               C2D8A0800AE7F6E300F68F79 /* cfmunge.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D8A07E0AE7F6E300F68F79 /* cfmunge.h */; };
+               C2D8A0980AE7F74500F68F79 /* cfmunge.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D8A07E0AE7F6E300F68F79 /* cfmunge.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2E2873D0B5D8D80009336A0 /* SecCodeHostLib.c in Sources */ = {isa = PBXBuildFile; fileRef = C2E2873C0B5D8D80009336A0 /* SecCodeHostLib.c */; };
+               C2E911E20ADEBE3200275CB2 /* resources.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2E911E00ADEBE3200275CB2 /* resources.cpp */; };
+               C2E911E30ADEBE3200275CB2 /* resources.h in Headers */ = {isa = PBXBuildFile; fileRef = C2E911E10ADEBE3200275CB2 /* resources.h */; };
+               C2EF10100A49BD89005A44BB /* renum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2EF100E0A49BD89005A44BB /* renum.cpp */; };
+               C2EF10110A49BD89005A44BB /* renum.h in Headers */ = {isa = PBXBuildFile; fileRef = C2EF100F0A49BD89005A44BB /* renum.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2EF10130A49BD89005A44BB /* renum.h in Headers */ = {isa = PBXBuildFile; fileRef = C2EF100F0A49BD89005A44BB /* renum.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C2F6566E0BCBFB250078779E /* cserror.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2F6566C0BCBFB250078779E /* cserror.cpp */; };
+               C2F6566F0BCBFB250078779E /* cserror.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F6566D0BCBFB250078779E /* cserror.h */; };
+               C2F656930BCBFFF40078779E /* cserror.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F6566D0BCBFB250078779E /* cserror.h */; settings = {ATTRIBUTES = (Public, ); }; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+               4C7502900540C69C00056564 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 4CA1FEAB052A3C3800F22E42 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 4C56351D0540A55300DCF0C8;
+                       remoteInfo = security_codesigning;
+               };
+               C21E3F8A0A23AE10006558D6 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 4CA1FEAB052A3C3800F22E42 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = C2D383B80A23A8C4005C63A2;
+                       remoteInfo = "Requirements Language";
+               };
+               C250F6C40B5EF4E40076098F /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 4CA1FEAB052A3C3800F22E42 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = C2BC1F250B580D3A003EC9DC;
+                       remoteInfo = libintegrity;
+               };
+               C2E287400B5D8F97009336A0 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 4CA1FEAB052A3C3800F22E42 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 4CA1FEBD052A3C8100F22E42;
+                       remoteInfo = libsecurity_codesigning;
+               };
+               C2E287420B5D8F9A009336A0 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 4CA1FEAB052A3C3800F22E42 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = C2BC1F2E0B580D4B003EC9DC;
+                       remoteInfo = libcodehost;
+               };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+               4CCB00500580097400981D43 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = SecurityPieces/Exports/Security;
+                       dstSubfolderSpec = 16;
+                       files = (
+                               C2D3838D0A237F47005C63A2 /* security_codesigning.exp in CopyFiles */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               4CCB00510580097600981D43 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 8;
+                       dstPath = /usr/local/SecurityPieces/Exports/Security;
+                       dstSubfolderSpec = 0;
+                       files = (
+                               C2D383900A23801C005C63A2 /* security_codesigning.exp in CopyFiles */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
+               4CCB00520580097800981D43 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = SecurityPieces/Headers/Security;
+                       dstSubfolderSpec = 16;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               C26C39D3068368EC00ED5782 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = SecurityPieces/PrivateHeaders/Security;
+                       dstSubfolderSpec = 16;
+                       files = (
+                               C2CC31100B852447005FA59D /* SecIntegrityLib.h in CopyFiles */,
+                               C21EA3E00AD2F81300E6E31C /* SecCodeSigner.h in CopyFiles */,
+                               C2D3839B0A238150005C63A2 /* CodeSigning.h in CopyFiles */,
+                               C2D383910A23803A005C63A2 /* CSCommon.h in CopyFiles */,
+                               C2D383920A23803A005C63A2 /* SecCode.h in CopyFiles */,
+                               C2D383930A23803A005C63A2 /* SecStaticCode.h in CopyFiles */,
+                               C2D383940A23803A005C63A2 /* SecRequirement.h in CopyFiles */,
+                               C2C931B60AB8BAC200F83950 /* SecCodeHost.h in CopyFiles */,
+                               C2A752B70B80EAFB004CF655 /* SecIntegrity.h in CopyFiles */,
+                               C2A752B80B80EAFB004CF655 /* SecCodeHostLib.h in CopyFiles */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+               4C56351E0540A55300DCF0C8 /* security_codesigning.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = security_codesigning.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               4CA1FEBE052A3C8100F22E42 /* security_codesigning */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = security_codesigning; sourceTree = BUILT_PRODUCTS_DIR; };
+               C2093AA60BB0948000EB8599 /* reqreader.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = reqreader.cpp; sourceTree = "<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 */;
+}
diff --git a/req/cfm.ireqs b/req/cfm.ireqs
new file mode 100644 (file)
index 0000000..02d53dc
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# Suggested internal requirements for CFM code
+#
+host => anchor apple and identifier com.apple.LaunchCFMApp             // CFM runner tool
diff --git a/req/ppc.ireqs b/req/ppc.ireqs
new file mode 100644 (file)
index 0000000..618abaa
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# Suggested internal requirements for ppc (only) Mach-O code
+#
+host => anchor apple and identifier com.apple.translate                // Rosetta
diff --git a/req/workaround.ireqs b/req/workaround.ireqs
new file mode 100644 (file)
index 0000000..4252ada
--- /dev/null
@@ -0,0 +1 @@
+anchor apple and (identifier com.apple.translate or identifier com.apple.LaunchCFMApp)
diff --git a/requirements.grammar b/requirements.grammar
new file mode 100644 (file)
index 0000000..296847f
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// Requirements Language Grammar
+//
+// This file describes two distinct (related) grammars:
+//     Requirement => single requirement (Requirement *)
+//     RequirementSet => set of labeled requirements (Requirements *)
+// The grammar can "autosense" - i.e. recognize which one it's fed and
+// return appropriate semantic data.
+// The semantic data compiled is a malloc'ed BlobCore * - a Requirement
+// object or a SuperBlob containing multiple Requirements.
+//
+header "post_include_hpp" {
+#include "requirement.h"
+using namespace CodeSigning;
+typedef Requirement::Maker Maker;
+}
+
+header "post_include_cpp" {
+#include "requirement.h"
+#include "reqmaker.h"
+#include "csutilities.h"
+#include <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); }
+       ;