X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_codesigning/lib/SecRequirement.cpp diff --git a/Security/libsecurity_codesigning/lib/SecRequirement.cpp b/Security/libsecurity_codesigning/lib/SecRequirement.cpp new file mode 100644 index 00000000..eab15a41 --- /dev/null +++ b/Security/libsecurity_codesigning/lib/SecRequirement.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2006,2011-2012,2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// SecRequirement - API frame for SecRequirement objects +// +#include "cs.h" +#include "Requirements.h" +#include "reqparser.h" +#include "reqmaker.h" +#include "reqdumper.h" +#include +#include + +using namespace CodeSigning; + + +// +// CF-standard type code function +// +CFTypeID SecRequirementGetTypeID(void) +{ + BEGIN_CSAPI + return gCFObjects().Requirement.typeID; + END_CSAPI1(_kCFRuntimeNotATypeID) +} + + +// +// Create a Requirement from data +// +OSStatus SecRequirementCreateWithData(CFDataRef data, SecCSFlags flags, + SecRequirementRef *requirementRef) +{ + BEGIN_CSAPI + + checkFlags(flags); + 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 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(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); + if (!reqs->validateBlob()) + MacOSError::throwMe(errSecCSReqInvalid); + CFRef dict = makeCFMutableDictionary(); + unsigned count = reqs->count(); + for (unsigned n = 0; n < count; n++) { + CFRef req = (new SecRequirement(reqs->blob(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()) + *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 +}