]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/reqinterp.cpp
0c20659268b5f29a08ec63f2076d0450d9848558
[apple/libsecurity_codesigning.git] / lib / reqinterp.cpp
1 /*
2 * Copyright (c) 2006 Apple Computer, 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 // reqinterp - Requirement language (exprOp) interpreter
26 //
27 #include "reqinterp.h"
28 #include <Security/SecTrustSettingsPriv.h>
29 #include <Security/SecCertificatePriv.h>
30 #include <security_utilities/memutils.h>
31 #include <security_cdsa_utilities/cssmdata.h> // for hex encoding
32 #include "csutilities.h"
33
34 namespace Security {
35 namespace CodeSigning {
36
37
38 //
39 // Construct an interpreter given a Requirement and an evaluation context.
40 //
41 Requirement::Interpreter::Interpreter(const Requirement *req, const Context *ctx)
42 : Reader(req), mContext(ctx)
43 {
44 }
45
46
47 //
48 // Main interpreter function.
49 //
50 // ExprOp code is in Polish Notation (operator followed by operands),
51 // and this engine uses opportunistic evaluation.
52 //
53 bool Requirement::Interpreter::evaluate()
54 {
55 ExprOp op = ExprOp(get<uint32_t>());
56 switch (op & ~opFlagMask) {
57 case opFalse:
58 return false;
59 case opTrue:
60 return true;
61 case opIdent:
62 return getString() == mContext->directory->identifier();
63 case opAppleAnchor:
64 if (SecCertificateRef cert = mContext->cert(anchorCert))
65 return verifyAnchor(cert, appleAnchorHash())
66 #if defined(TEST_APPLE_ANCHOR)
67 || verifyAnchor(cert, testAppleAnchorHash())
68 #endif
69 ;
70 else
71 return false;
72 case opAnchorHash:
73 {
74 SecCertificateRef cert = mContext->cert(get<int32_t>());
75 return verifyAnchor(cert, getSHA1());
76 }
77 case opInfoKeyValue: // [legacy; use opInfoKeyField]
78 {
79 string key = getString();
80 return infoKeyValue(key, Match(CFTempString(getString()), matchEqual));
81 }
82 case opAnd:
83 return evaluate() && evaluate();
84 case opOr:
85 return evaluate() || evaluate();
86 case opCDHash:
87 {
88 SHA1 hash;
89 hash(mContext->directory, mContext->directory->length());
90 return hash.verify(getHash());
91 }
92 case opNot:
93 return !evaluate();
94 case opInfoKeyField:
95 {
96 string key = getString();
97 Match match(*this);
98 return infoKeyValue(key, match);
99 }
100 case opCertField:
101 {
102 SecCertificateRef cert = mContext->cert(get<int32_t>());
103 string key = getString();
104 Match match(*this);
105 return certFieldValue(key, match, cert);
106 }
107 case opTrustedCert:
108 return trustedCert(get<int32_t>());
109 case opTrustedCerts:
110 return trustedCerts();
111 default:
112 // opcode not recognized - handle generically if possible, fail otherwise
113 if (op & (opGenericFalse | opGenericSkip)) {
114 // unknown opcode, but it has a size field and can be safely bypassed
115 skip(get<uint32_t>());
116 if (op & opGenericFalse) {
117 secdebug("csinterp", "opcode 0x%x interpreted as false", op);
118 return false;
119 } else {
120 secdebug("csinterp", "opcode 0x%x ignored; continuing", op);
121 return evaluate();
122 }
123 }
124 // unrecognized opcode and no way to interpret it
125 secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op);
126 MacOSError::throwMe(errSecCSUnimplemented);
127 }
128 }
129
130
131 //
132 // Evaluate an Info.plist key condition
133 //
134 bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &match)
135 {
136 if (mContext->info) // we have an Info.plist
137 if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key)))
138 return match(value);
139 return false;
140 }
141
142
143 bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert)
144 {
145 // no cert, no chance
146 if (cert == NULL)
147 return false;
148
149 // a table of recognized keys for the "certificate[foo]" syntax
150 static const struct CertField {
151 const char *name;
152 const CSSM_OID *oid;
153 } certFields[] = {
154 { "subject.C", &CSSMOID_CountryName },
155 { "subject.CN", &CSSMOID_CommonName },
156 { "subject.D", &CSSMOID_Description },
157 { "subject.L", &CSSMOID_LocalityName },
158 { "subject.O", &CSSMOID_OrganizationName },
159 { "subject.OU", &CSSMOID_OrganizationalUnitName },
160 { "subject.ST", &CSSMOID_StateProvinceName },
161 { "subject.STREET", &CSSMOID_StreetAddress },
162 { NULL, NULL }
163 };
164
165 // email multi-valued match (any of...)
166 if (key == "email") {
167 CFRef<CFArrayRef> value;
168 if (IFDEBUG(OSStatus rc =) SecCertificateCopyEmailAddresses(cert, &value.aref())) {
169 secdebug("csinterp", "cert %p lookup for email failed rc=%ld", cert, rc);
170 return false;
171 }
172 return match(value);
173 }
174
175 // DN-component single-value match
176 for (const CertField *cf = certFields; cf->name; cf++)
177 if (cf->name == key) {
178 CFRef<CFStringRef> value;
179 if (IFDEBUG(OSStatus rc =) SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref())) {
180 secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%ld", cert, key.c_str(), rc);
181 return false;
182 }
183 return match(value);
184 }
185
186 // unrecognized key. Fail but do not abort to promote backward compatibility down the road
187 secdebug("csinterp", "cert field notation \"%s\" not understood", key.c_str());
188 return false;
189 }
190
191
192 //
193 // Verify an anchor requirement against the context
194 //
195 bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsigned char *digest)
196 {
197 // get certificate bytes
198 if (cert) {
199 CSSM_DATA certData;
200 MacOSError::check(SecCertificateGetData(cert, &certData));
201
202 // verify hash
203 //@@@ should get SHA1(cert(-1).data) precalculated during chain verification
204 SHA1 hasher;
205 hasher(certData.Data, certData.Length);
206 return hasher.verify(digest);
207 }
208 return false;
209 }
210
211
212 //
213 // Check one or all certificate(s) in the cert chain against the Trust Settings database.
214 //
215 bool Requirement::Interpreter::trustedCerts()
216 {
217 int anchor = mContext->certCount() - 1;
218 for (int slot = 0; slot <= anchor; slot++)
219 if (SecCertificateRef cert = mContext->cert(slot))
220 switch (trustSetting(cert, slot == anchor)) {
221 case kSecTrustSettingsResultTrustRoot:
222 case kSecTrustSettingsResultTrustAsRoot:
223 return true;
224 case kSecTrustSettingsResultDeny:
225 return false;
226 case kSecTrustSettingsResultUnspecified:
227 break;
228 default:
229 assert(false);
230 return false;
231 }
232 else
233 return false;
234 return false;
235 }
236
237 bool Requirement::Interpreter::trustedCert(int slot)
238 {
239 if (SecCertificateRef cert = mContext->cert(slot)) {
240 int anchorSlot = mContext->certCount() - 1;
241 switch (trustSetting(cert, slot == anchorCert || slot == anchorSlot)) {
242 case kSecTrustSettingsResultTrustRoot:
243 case kSecTrustSettingsResultTrustAsRoot:
244 return true;
245 case kSecTrustSettingsResultDeny:
246 case kSecTrustSettingsResultUnspecified:
247 return false;
248 default:
249 assert(false);
250 return false;
251 }
252 } else
253 return false;
254 }
255
256
257 //
258 // Explicitly check one certificate against the Trust Settings database and report
259 // the findings. This is a helper for the various Trust Settings evaluators.
260 //
261 SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor)
262 {
263 // the SPI input is the uppercase hex form of the SHA-1 of the certificate...
264 assert(cert);
265 SHA1::Digest digest;
266 hashOfCertificate(cert, digest);
267 string Certhex = CssmData(digest, sizeof(digest)).toHex();
268 for (string::iterator it = Certhex.begin(); it != Certhex.end(); ++it)
269 if (islower(*it))
270 *it = toupper(*it);
271
272 // call Trust Settings and see what it finds
273 SecTrustSettingsDomain domain;
274 SecTrustSettingsResult result;
275 CSSM_RETURN *errors = NULL;
276 uint32 errorCount = 0;
277 bool foundMatch, foundAny;
278 switch (OSStatus rc = SecTrustSettingsEvaluateCert(
279 CFTempString(Certhex), // settings index
280 &CSSMOID_APPLE_TP_CODE_SIGNING, // standard code signing policy
281 NULL, 0, // policy string (unused)
282 kSecTrustSettingsKeyUseAny, // no restriction on key usage @@@
283 isAnchor, // consult system default anchor set
284
285 &domain, // domain of found setting
286 &errors, &errorCount, // error set and maximum count
287 &result, // the actual setting
288 &foundMatch, &foundAny // optimization hints (not used)
289 )) {
290 case noErr:
291 ::free(errors);
292 if (foundMatch)
293 return result;
294 else
295 return kSecTrustSettingsResultUnspecified;
296 default:
297 ::free(errors);
298 MacOSError::throwMe(rc);
299 }
300 }
301
302
303 //
304 // Create a Match object from the interpreter stream
305 //
306 Requirement::Interpreter::Match::Match(Interpreter &interp)
307 {
308 switch (mOp = interp.get<MatchOperation>()) {
309 case matchExists:
310 break;
311 case matchEqual:
312 case matchContains:
313 mValue = makeCFString(interp.getString());
314 break;
315 default:
316 assert(false);
317 break;
318 }
319 }
320
321
322 //
323 // Execute a match against a candidate value
324 //
325 bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
326 {
327 // null candidates always fail
328 if (!candidate)
329 return false;
330
331 // interpret an array as matching alternatives (any one succeeds)
332 if (CFGetTypeID(candidate) == CFArrayGetTypeID()) {
333 CFArrayRef array = CFArrayRef(candidate);
334 CFIndex count = CFArrayGetCount(array);
335 for (CFIndex n = 0; n < count; n++)
336 if ((*this)(CFArrayGetValueAtIndex(array, n))) // yes, it's recursive
337 return true;
338 }
339
340 switch (mOp) {
341 case matchExists: // anything but NULL and boolean false "exists"
342 return !CFEqual(candidate, kCFBooleanFalse);
343 case matchEqual: // equality works for all CF types
344 return CFEqual(candidate, mValue);
345 case matchContains:
346 if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
347 CFStringRef value = CFStringRef(candidate);
348 if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(value)), 0, NULL))
349 return true;
350 }
351 return false;
352 default:
353 assert(false);
354 return false;
355 }
356 }
357
358
359 } // CodeSigning
360 } // Security