]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_codesigning/lib/SecRequirement.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_codesigning / lib / SecRequirement.cpp
diff --git a/libsecurity_codesigning/lib/SecRequirement.cpp b/libsecurity_codesigning/lib/SecRequirement.cpp
new file mode 100644 (file)
index 0000000..68d95e4
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * 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);
+       CodeSigning::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);
+       CodeSigning::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);
+       CodeSigning::Required(requirementRef) = (new SecRequirement(parseRequirement(cfString(text)), true))->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
+       }
+       CodeSigning::Required(requirementRef) = (new SecRequirement(maker.make(), true))->handle();
+
+       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);
+       CodeSigning::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);
+       CodeSigning::Required(text);
+       *text = makeCFString(Dumper::dump(req));
+
+       END_CSAPI
+}
+
+
+//
+CFStringRef kSecRequirementKeyInfoPlist = CFSTR("requirement:eval:info");
+CFStringRef kSecRequirementKeyEntitlements = CFSTR("requirement:eval:entitlements");
+CFStringRef kSecRequirementKeyIdentifier = CFSTR("requirement:eval:identifier");
+
+OSStatus SecRequirementEvaluate(SecRequirementRef requirementRef,
+       CFArrayRef certificateChain, CFDictionaryRef context,
+       SecCSFlags flags)
+{
+       BEGIN_CSAPI
+
+       const Requirement *req = SecRequirement::required(requirementRef)->requirement();
+       checkFlags(flags);
+       CodeSigning::Required(certificateChain);
+       
+       Requirement::Context ctx(certificateChain,              // mandatory
+               context ? CFDictionaryRef(CFDictionaryGetValue(context, kSecRequirementKeyInfoPlist)) : NULL,
+               context ? CFDictionaryRef(CFDictionaryGetValue(context, kSecRequirementKeyEntitlements)) : NULL,
+               (context && CFDictionaryGetValue(context, kSecRequirementKeyIdentifier)) ?
+                       cfString(CFStringRef(CFDictionaryGetValue(context, kSecRequirementKeyIdentifier))) : "",
+               NULL    // can't specify a CodeDirectory here
+       );
+       req->validate(ctx);
+       
+       END_CSAPI
+}
+
+
+//
+// Assemble a requirement set (as a CFData) from a dictionary of requirement objects.
+// An empty set is allowed.
+//
+OSStatus SecRequirementsCreateFromRequirements(CFDictionaryRef requirements, SecCSFlags flags,
+       CFDataRef *requirementSet)
+{
+       BEGIN_CSAPI
+       
+       checkFlags(flags);
+       if (requirements == NULL)
+               return errSecCSObjectRequired;
+       CFIndex count = CFDictionaryGetCount(requirements);
+       CFNumberRef keys[count];
+       SecRequirementRef reqs[count];
+       CFDictionaryGetKeysAndValues(requirements, (const void **)keys, (const void **)reqs);
+       Requirements::Maker maker;
+       for (CFIndex n = 0; n < count; n++) {
+               const Requirement *req = SecRequirement::required(reqs[n])->requirement();
+               maker.add(cfNumber<Requirements::Type>(keys[n]), req->clone());
+       }
+       Requirements *reqset = maker.make();                                    // malloc'ed
+       CodeSigning::Required(requirementSet) = makeCFDataMalloc(*reqset);      // takes ownership of reqs
+
+       END_CSAPI
+}
+
+
+//
+// Break a requirement set (given as a CFData) into its constituent requirements
+// and return it as a CFDictionary.
+//
+OSStatus SecRequirementsCopyRequirements(CFDataRef requirementSet, SecCSFlags flags,
+       CFDictionaryRef *requirements)
+{
+       BEGIN_CSAPI
+       
+       checkFlags(flags);
+       if (requirementSet == NULL)
+               return errSecCSObjectRequired;
+       const Requirements *reqs = (const Requirements *)CFDataGetBytePtr(requirementSet);
+       CFRef<CFMutableDictionaryRef> dict = makeCFMutableDictionary();
+       unsigned count = reqs->count();
+       for (unsigned n = 0; n < count; n++) {
+               CFRef<SecRequirementRef> req = (new SecRequirement(reqs->blob<Requirement>(n)))->handle();
+               CFDictionaryAddValue(dict, CFTempNumber(reqs->type(n)), req);
+       }
+       CodeSigning::Required(requirements) = dict.yield();
+
+       END_CSAPI
+}
+
+       
+//
+// Generically parse a string as some kind of requirement-related source form.
+// If properly recognized, return the result as a CF object:
+//     SecRequirementRef for a single requirement
+//     CFDataRef for a requirement set
+//
+OSStatus SecRequirementsCreateWithString(CFStringRef text, SecCSFlags flags,
+       CFTypeRef *result, CFErrorRef *errors)
+{
+       BEGIN_CSAPI
+       
+       checkFlags(flags, kSecCSParseRequirement | kSecCSParseRequirementSet);
+       if (text == NULL || result == NULL)
+               return errSecCSObjectRequired;
+       std::string s = cfString(text);
+       switch (flags & (kSecCSParseRequirement | kSecCSParseRequirementSet)) {
+       case kSecCSParseRequirement:            // single only
+               *result = (new SecRequirement(parseRequirement(s), true))->handle();
+               break;
+       case kSecCSParseRequirementSet:         // single only
+               {
+                       const Requirements *reqs = parseRequirements(s);
+                       *result = makeCFDataMalloc(*reqs);
+                       break;
+               }
+       case 0:
+       case kSecCSParseRequirement | kSecCSParseRequirementSet:
+               {
+                       const BlobCore *any = parseGeneric(s);
+                       if (any->is<Requirement>())
+                               *result = (new SecRequirement(Requirement::specific(any), true))->handle();
+                       else
+                               *result = makeCFDataMalloc(*any);
+                       break;
+               }
+       }
+
+       END_CSAPI_ERRORS
+}
+
+       
+//
+// Convert a SecRequirementRef or a CFDataRef containing a requirement set to text.
+// Requirement sets will be formatted as multiple lines (one per requirement). They can be empty.
+// A single requirement will return a single line that is NOT newline-terminated.
+//
+OSStatus SecRequirementsCopyString(CFTypeRef input, SecCSFlags flags, CFStringRef *text)
+{
+       BEGIN_CSAPI
+       
+       checkFlags(flags);
+       if (input == NULL)
+               return errSecCSObjectRequired;
+       if (CFGetTypeID(input) == SecRequirementGetTypeID()) {
+               return SecRequirementCopyString(SecRequirementRef(input), flags, text);
+       } else if (CFGetTypeID(input) == CFDataGetTypeID()) {
+               const Requirements *reqs = (const Requirements *)CFDataGetBytePtr(CFDataRef(input));
+               if (!reqs->validateBlob(CFDataGetLength(CFDataRef(input))))
+                       return errSecCSReqInvalid;
+               CodeSigning::Required(text) = makeCFString(Dumper::dump(reqs, false));
+       } else
+               return errSecCSInvalidObjectRef;
+
+       END_CSAPI
+}