1/*
2 * Copyright (c) 2006,2011-2012,2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24//
25// SecRequirement - API frame for SecRequirement objects
26//
27#include "cs.h"
28#include "Requirements.h"
29#include "reqparser.h"
30#include "reqmaker.h"
31#include "reqdumper.h"
32#include <Security/SecCertificate.h>
33#include <security_utilities/cfutilities.h>
34
35using namespace CodeSigning;
36
37
38//
39// CF-standard type code function
40//
41CFTypeID SecRequirementGetTypeID(void)
42{
43	BEGIN_CSAPI
44	return gCFObjects().Requirement.typeID;
45    END_CSAPI1(_kCFRuntimeNotATypeID)
46}
47
48
49//
50// Create a Requirement from data
51//
52OSStatus SecRequirementCreateWithData(CFDataRef data, SecCSFlags flags,
53	SecRequirementRef *requirementRef)
54{
55	BEGIN_CSAPI
56
57	checkFlags(flags);
58	CodeSigning::Required(requirementRef) = (new SecRequirement(CFDataGetBytePtr(data), CFDataGetLength(data)))->handle();
59
60	END_CSAPI
61}
62
63
64//
65// Create a Requirement from data in a file
66//
67OSStatus SecRequirementCreateWithResource(CFURLRef resource, SecCSFlags flags,
68	SecRequirementRef *requirementRef)
69{
70	BEGIN_CSAPI
71
72	checkFlags(flags);
73	CFRef<CFDataRef> data = cfLoadFile(resource);
74	CodeSigning::Required(requirementRef) =
75		(new SecRequirement(CFDataGetBytePtr(data), CFDataGetLength(data)))->handle();
76
77	END_CSAPI
78}
79
80
81//
82// Create a Requirement from source text (compiling it)
83//
84OSStatus SecRequirementCreateWithString(CFStringRef text, SecCSFlags flags,
85	SecRequirementRef *requirementRef)
86{
87	return SecRequirementCreateWithStringAndErrors(text, flags, NULL, requirementRef);
88}
89
90OSStatus SecRequirementCreateWithStringAndErrors(CFStringRef text, SecCSFlags flags,
91	CFErrorRef *errors, SecRequirementRef *requirementRef)
92{
93	BEGIN_CSAPI
94
95	checkFlags(flags);
96	CodeSigning::Required(requirementRef) = (new SecRequirement(parseRequirement(cfString(text)), true))->handle();
97
98	END_CSAPI_ERRORS
99}
100
101
102//
103// Create a Requirement group.
104// This is the canonical point where "application group" is defined.
105//
106OSStatus SecRequirementCreateGroup(CFStringRef groupName, SecCertificateRef anchorRef,
107	SecCSFlags flags, SecRequirementRef *requirementRef)
108{
109	BEGIN_CSAPI
110
111	checkFlags(flags);
112	Requirement::Maker maker;
113	maker.put(opAnd);		// both of...
114	maker.infoKey("Application-Group", cfString(groupName));
115	if (anchorRef) {
116		CSSM_DATA certData;
117		MacOSError::check(SecCertificateGetData(anchorRef, &certData));
118		maker.anchor(0, certData.Data, certData.Length);
119	} else {
120		maker.anchor();			// canonical Apple anchor
121	}
122	CodeSigning::Required(requirementRef) = (new SecRequirement(maker.make(), true))->handle();
123
124	END_CSAPI
125}
126
127
128//
129// Extract the stable binary from from a SecRequirementRef
130//
131OSStatus SecRequirementCopyData(SecRequirementRef requirementRef, SecCSFlags flags,
132	CFDataRef *data)
133{
134	BEGIN_CSAPI
135
136	const Requirement *req = SecRequirement::required(requirementRef)->requirement();
137	checkFlags(flags);
138	CodeSigning::Required(data);
139	*data = makeCFData(*req);
140
141	END_CSAPI
142}
143
144
145//
146// Generate source form for a SecRequirement (decompile/disassemble)
147//
148OSStatus SecRequirementCopyString(SecRequirementRef requirementRef, SecCSFlags flags,
149	CFStringRef *text)
150{
151	BEGIN_CSAPI
152
153	const Requirement *req = SecRequirement::required(requirementRef)->requirement();
154	checkFlags(flags);
155	CodeSigning::Required(text);
156	*text = makeCFString(Dumper::dump(req));
157
158	END_CSAPI
159}
160
161
162//
163CFStringRef kSecRequirementKeyInfoPlist = CFSTR("requirement:eval:info");
164CFStringRef kSecRequirementKeyEntitlements = CFSTR("requirement:eval:entitlements");
165CFStringRef kSecRequirementKeyIdentifier = CFSTR("requirement:eval:identifier");
166
167OSStatus SecRequirementEvaluate(SecRequirementRef requirementRef,
168	CFArrayRef certificateChain, CFDictionaryRef context,
169	SecCSFlags flags)
170{
171	BEGIN_CSAPI
172
173	const Requirement *req = SecRequirement::required(requirementRef)->requirement();
174	checkFlags(flags);
175	CodeSigning::Required(certificateChain);
176
177	Requirement::Context ctx(certificateChain,		// mandatory
178		context ? CFDictionaryRef(CFDictionaryGetValue(context, kSecRequirementKeyInfoPlist)) : NULL,
179		context ? CFDictionaryRef(CFDictionaryGetValue(context, kSecRequirementKeyEntitlements)) : NULL,
180		(context && CFDictionaryGetValue(context, kSecRequirementKeyIdentifier)) ?
181			cfString(CFStringRef(CFDictionaryGetValue(context, kSecRequirementKeyIdentifier))) : "",
182		NULL	// can't specify a CodeDirectory here
183	);
184	req->validate(ctx);
185
186	END_CSAPI
187}
188
189
190//
191// Assemble a requirement set (as a CFData) from a dictionary of requirement objects.
192// An empty set is allowed.
193//
194OSStatus SecRequirementsCreateFromRequirements(CFDictionaryRef requirements, SecCSFlags flags,
195	CFDataRef *requirementSet)
196{
197	BEGIN_CSAPI
198
199	checkFlags(flags);
200	if (requirements == NULL)
201		return errSecCSObjectRequired;
202	CFIndex count = CFDictionaryGetCount(requirements);
203	CFNumberRef keys[count];
204	SecRequirementRef reqs[count];
205	CFDictionaryGetKeysAndValues(requirements, (const void **)keys, (const void **)reqs);
206	Requirements::Maker maker;
207	for (CFIndex n = 0; n < count; n++) {
208		const Requirement *req = SecRequirement::required(reqs[n])->requirement();
209		maker.add(cfNumber<Requirements::Type>(keys[n]), req->clone());
210	}
211	Requirements *reqset = maker.make();					// malloc'ed
212	CodeSigning::Required(requirementSet) = makeCFDataMalloc(*reqset);	// takes ownership of reqs
213
214	END_CSAPI
215}
216
217
218//
219// Break a requirement set (given as a CFData) into its constituent requirements
220// and return it as a CFDictionary.
221//
222OSStatus SecRequirementsCopyRequirements(CFDataRef requirementSet, SecCSFlags flags,
223	CFDictionaryRef *requirements)
224{
225	BEGIN_CSAPI
226
227	checkFlags(flags);
228	if (requirementSet == NULL)
229		return errSecCSObjectRequired;
230	const Requirements *reqs = (const Requirements *)CFDataGetBytePtr(requirementSet);
231	if (!reqs->validateBlob())
232		MacOSError::throwMe(errSecCSReqInvalid);
233	CFRef<CFMutableDictionaryRef> dict = makeCFMutableDictionary();
234	unsigned count = reqs->count();
235	for (unsigned n = 0; n < count; n++) {
236		CFRef<SecRequirementRef> req = (new SecRequirement(reqs->blob<Requirement>(n)))->handle();
237		CFDictionaryAddValue(dict, CFTempNumber(reqs->type(n)), req);
238	}
239	CodeSigning::Required(requirements) = dict.yield();
240
241	END_CSAPI
242}
243
244
245//
246// Generically parse a string as some kind of requirement-related source form.
247// If properly recognized, return the result as a CF object:
248//	SecRequirementRef for a single requirement
249//	CFDataRef for a requirement set
250//
251OSStatus SecRequirementsCreateWithString(CFStringRef text, SecCSFlags flags,
252	CFTypeRef *result, CFErrorRef *errors)
253{
254	BEGIN_CSAPI
255
256	checkFlags(flags, kSecCSParseRequirement | kSecCSParseRequirementSet);
257	if (text == NULL || result == NULL)
258		return errSecCSObjectRequired;
259	std::string s = cfString(text);
260	switch (flags & (kSecCSParseRequirement | kSecCSParseRequirementSet)) {
261	case kSecCSParseRequirement:		// single only
262		*result = (new SecRequirement(parseRequirement(s), true))->handle();
263		break;
264	case kSecCSParseRequirementSet:		// single only
265		{
266			const Requirements *reqs = parseRequirements(s);
267			*result = makeCFDataMalloc(*reqs);
268			break;
269		}
270	case 0:
271	case kSecCSParseRequirement | kSecCSParseRequirementSet:
272		{
273			const BlobCore *any = parseGeneric(s);
274			if (any->is<Requirement>())
275				*result = (new SecRequirement(Requirement::specific(any), true))->handle();
276			else
277				*result = makeCFDataMalloc(*any);
278			break;
279		}
280	}
281
282	END_CSAPI_ERRORS
283}
284
285
286//
287// Convert a SecRequirementRef or a CFDataRef containing a requirement set to text.
288// Requirement sets will be formatted as multiple lines (one per requirement). They can be empty.
289// A single requirement will return a single line that is NOT newline-terminated.
290//
291OSStatus SecRequirementsCopyString(CFTypeRef input, SecCSFlags flags, CFStringRef *text)
292{
293	BEGIN_CSAPI
294
295	checkFlags(flags);
296	if (input == NULL)
297		return errSecCSObjectRequired;
298	if (CFGetTypeID(input) == SecRequirementGetTypeID()) {
299		return SecRequirementCopyString(SecRequirementRef(input), flags, text);
300	} else if (CFGetTypeID(input) == CFDataGetTypeID()) {
301		const Requirements *reqs = (const Requirements *)CFDataGetBytePtr(CFDataRef(input));
302		if (!reqs->validateBlob(CFDataGetLength(CFDataRef(input))))
303			return errSecCSReqInvalid;
304		CodeSigning::Required(text) = makeCFString(Dumper::dump(reqs, false));
305	} else
306		return errSecCSInvalidObjectRef;
307
308	END_CSAPI
309}
310