]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2006,2011-2012,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
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 | ||
35 | using namespace CodeSigning; | |
36 | ||
37 | ||
38 | // | |
39 | // CF-standard type code function | |
40 | // | |
41 | CFTypeID 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 | // | |
52 | OSStatus 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 | // | |
67 | OSStatus 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 | // | |
84 | OSStatus SecRequirementCreateWithString(CFStringRef text, SecCSFlags flags, | |
85 | SecRequirementRef *requirementRef) | |
86 | { | |
87 | return SecRequirementCreateWithStringAndErrors(text, flags, NULL, requirementRef); | |
88 | } | |
89 | ||
90 | OSStatus 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 | // | |
106 | OSStatus 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) { | |
866f8763 | 116 | #if TARGET_OS_OSX |
b1ab9ed8 A |
117 | CSSM_DATA certData; |
118 | MacOSError::check(SecCertificateGetData(anchorRef, &certData)); | |
119 | maker.anchor(0, certData.Data, certData.Length); | |
866f8763 A |
120 | #else |
121 | maker.anchor(0, SecCertificateGetBytePtr(anchorRef), SecCertificateGetLength(anchorRef)); | |
122 | #endif | |
b1ab9ed8 A |
123 | } else { |
124 | maker.anchor(); // canonical Apple anchor | |
125 | } | |
126 | CodeSigning::Required(requirementRef) = (new SecRequirement(maker.make(), true))->handle(); | |
127 | ||
128 | END_CSAPI | |
129 | } | |
130 | ||
131 | ||
132 | // | |
133 | // Extract the stable binary from from a SecRequirementRef | |
134 | // | |
135 | OSStatus SecRequirementCopyData(SecRequirementRef requirementRef, SecCSFlags flags, | |
136 | CFDataRef *data) | |
137 | { | |
138 | BEGIN_CSAPI | |
139 | ||
140 | const Requirement *req = SecRequirement::required(requirementRef)->requirement(); | |
141 | checkFlags(flags); | |
142 | CodeSigning::Required(data); | |
143 | *data = makeCFData(*req); | |
144 | ||
145 | END_CSAPI | |
146 | } | |
147 | ||
148 | ||
149 | // | |
150 | // Generate source form for a SecRequirement (decompile/disassemble) | |
151 | // | |
152 | OSStatus SecRequirementCopyString(SecRequirementRef requirementRef, SecCSFlags flags, | |
153 | CFStringRef *text) | |
154 | { | |
155 | BEGIN_CSAPI | |
156 | ||
157 | const Requirement *req = SecRequirement::required(requirementRef)->requirement(); | |
158 | checkFlags(flags); | |
159 | CodeSigning::Required(text); | |
160 | *text = makeCFString(Dumper::dump(req)); | |
161 | ||
162 | END_CSAPI | |
163 | } | |
164 | ||
165 | ||
166 | // | |
167 | CFStringRef kSecRequirementKeyInfoPlist = CFSTR("requirement:eval:info"); | |
168 | CFStringRef kSecRequirementKeyEntitlements = CFSTR("requirement:eval:entitlements"); | |
169 | CFStringRef kSecRequirementKeyIdentifier = CFSTR("requirement:eval:identifier"); | |
170 | ||
171 | OSStatus SecRequirementEvaluate(SecRequirementRef requirementRef, | |
172 | CFArrayRef certificateChain, CFDictionaryRef context, | |
173 | SecCSFlags flags) | |
174 | { | |
175 | BEGIN_CSAPI | |
176 | ||
177 | const Requirement *req = SecRequirement::required(requirementRef)->requirement(); | |
178 | checkFlags(flags); | |
179 | CodeSigning::Required(certificateChain); | |
180 | ||
181 | Requirement::Context ctx(certificateChain, // mandatory | |
182 | context ? CFDictionaryRef(CFDictionaryGetValue(context, kSecRequirementKeyInfoPlist)) : NULL, | |
183 | context ? CFDictionaryRef(CFDictionaryGetValue(context, kSecRequirementKeyEntitlements)) : NULL, | |
184 | (context && CFDictionaryGetValue(context, kSecRequirementKeyIdentifier)) ? | |
185 | cfString(CFStringRef(CFDictionaryGetValue(context, kSecRequirementKeyIdentifier))) : "", | |
186 | NULL // can't specify a CodeDirectory here | |
187 | ); | |
188 | req->validate(ctx); | |
189 | ||
190 | END_CSAPI | |
191 | } | |
192 | ||
193 | ||
194 | // | |
195 | // Assemble a requirement set (as a CFData) from a dictionary of requirement objects. | |
196 | // An empty set is allowed. | |
197 | // | |
198 | OSStatus SecRequirementsCreateFromRequirements(CFDictionaryRef requirements, SecCSFlags flags, | |
199 | CFDataRef *requirementSet) | |
200 | { | |
201 | BEGIN_CSAPI | |
202 | ||
203 | checkFlags(flags); | |
204 | if (requirements == NULL) | |
205 | return errSecCSObjectRequired; | |
206 | CFIndex count = CFDictionaryGetCount(requirements); | |
207 | CFNumberRef keys[count]; | |
208 | SecRequirementRef reqs[count]; | |
209 | CFDictionaryGetKeysAndValues(requirements, (const void **)keys, (const void **)reqs); | |
210 | Requirements::Maker maker; | |
211 | for (CFIndex n = 0; n < count; n++) { | |
212 | const Requirement *req = SecRequirement::required(reqs[n])->requirement(); | |
213 | maker.add(cfNumber<Requirements::Type>(keys[n]), req->clone()); | |
214 | } | |
215 | Requirements *reqset = maker.make(); // malloc'ed | |
216 | CodeSigning::Required(requirementSet) = makeCFDataMalloc(*reqset); // takes ownership of reqs | |
217 | ||
218 | END_CSAPI | |
219 | } | |
220 | ||
221 | ||
222 | // | |
223 | // Break a requirement set (given as a CFData) into its constituent requirements | |
224 | // and return it as a CFDictionary. | |
225 | // | |
226 | OSStatus SecRequirementsCopyRequirements(CFDataRef requirementSet, SecCSFlags flags, | |
227 | CFDictionaryRef *requirements) | |
228 | { | |
229 | BEGIN_CSAPI | |
230 | ||
231 | checkFlags(flags); | |
232 | if (requirementSet == NULL) | |
233 | return errSecCSObjectRequired; | |
234 | const Requirements *reqs = (const Requirements *)CFDataGetBytePtr(requirementSet); | |
427c49bc A |
235 | if (!reqs->validateBlob()) |
236 | MacOSError::throwMe(errSecCSReqInvalid); | |
b1ab9ed8 A |
237 | CFRef<CFMutableDictionaryRef> dict = makeCFMutableDictionary(); |
238 | unsigned count = reqs->count(); | |
239 | for (unsigned n = 0; n < count; n++) { | |
240 | CFRef<SecRequirementRef> req = (new SecRequirement(reqs->blob<Requirement>(n)))->handle(); | |
241 | CFDictionaryAddValue(dict, CFTempNumber(reqs->type(n)), req); | |
242 | } | |
243 | CodeSigning::Required(requirements) = dict.yield(); | |
244 | ||
245 | END_CSAPI | |
246 | } | |
247 | ||
248 | ||
249 | // | |
250 | // Generically parse a string as some kind of requirement-related source form. | |
251 | // If properly recognized, return the result as a CF object: | |
252 | // SecRequirementRef for a single requirement | |
253 | // CFDataRef for a requirement set | |
254 | // | |
255 | OSStatus SecRequirementsCreateWithString(CFStringRef text, SecCSFlags flags, | |
256 | CFTypeRef *result, CFErrorRef *errors) | |
257 | { | |
258 | BEGIN_CSAPI | |
259 | ||
260 | checkFlags(flags, kSecCSParseRequirement | kSecCSParseRequirementSet); | |
261 | if (text == NULL || result == NULL) | |
262 | return errSecCSObjectRequired; | |
263 | std::string s = cfString(text); | |
264 | switch (flags & (kSecCSParseRequirement | kSecCSParseRequirementSet)) { | |
265 | case kSecCSParseRequirement: // single only | |
266 | *result = (new SecRequirement(parseRequirement(s), true))->handle(); | |
267 | break; | |
268 | case kSecCSParseRequirementSet: // single only | |
269 | { | |
270 | const Requirements *reqs = parseRequirements(s); | |
271 | *result = makeCFDataMalloc(*reqs); | |
272 | break; | |
273 | } | |
274 | case 0: | |
275 | case kSecCSParseRequirement | kSecCSParseRequirementSet: | |
276 | { | |
277 | const BlobCore *any = parseGeneric(s); | |
278 | if (any->is<Requirement>()) | |
279 | *result = (new SecRequirement(Requirement::specific(any), true))->handle(); | |
280 | else | |
281 | *result = makeCFDataMalloc(*any); | |
282 | break; | |
283 | } | |
284 | } | |
285 | ||
286 | END_CSAPI_ERRORS | |
287 | } | |
288 | ||
289 | ||
290 | // | |
291 | // Convert a SecRequirementRef or a CFDataRef containing a requirement set to text. | |
292 | // Requirement sets will be formatted as multiple lines (one per requirement). They can be empty. | |
293 | // A single requirement will return a single line that is NOT newline-terminated. | |
294 | // | |
295 | OSStatus SecRequirementsCopyString(CFTypeRef input, SecCSFlags flags, CFStringRef *text) | |
296 | { | |
297 | BEGIN_CSAPI | |
298 | ||
299 | checkFlags(flags); | |
300 | if (input == NULL) | |
301 | return errSecCSObjectRequired; | |
302 | if (CFGetTypeID(input) == SecRequirementGetTypeID()) { | |
303 | return SecRequirementCopyString(SecRequirementRef(input), flags, text); | |
304 | } else if (CFGetTypeID(input) == CFDataGetTypeID()) { | |
305 | const Requirements *reqs = (const Requirements *)CFDataGetBytePtr(CFDataRef(input)); | |
306 | if (!reqs->validateBlob(CFDataGetLength(CFDataRef(input)))) | |
307 | return errSecCSReqInvalid; | |
308 | CodeSigning::Required(text) = makeCFString(Dumper::dump(reqs, false)); | |
309 | } else | |
310 | return errSecCSInvalidObjectRef; | |
311 | ||
312 | END_CSAPI | |
313 | } |