]>
Commit | Line | Data |
---|---|---|
bac41a7b A |
1 | /* |
2 | * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * The contents of this file constitute Original Code as defined in and are | |
5 | * subject to the Apple Public Source License Version 1.2 (the 'License'). | |
6 | * You may not use this file except in compliance with the License. Please obtain | |
7 | * a copy of the License at http://www.apple.com/publicsource and read it before | |
8 | * using this file. | |
9 | * | |
10 | * This Original Code and all software distributed under the License are | |
11 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS | |
12 | * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT | |
13 | * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | |
14 | * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the | |
15 | * specific language governing rights and limitations under the License. | |
16 | */ | |
17 | ||
18 | ||
19 | /* | |
20 | policies.cpp - TP module policy implementation | |
21 | ||
22 | Created 10/9/2000 by Doug Mitchell. | |
23 | */ | |
24 | ||
25 | #include <Security/cssmtype.h> | |
26 | #include <Security/cssmapi.h> | |
27 | #include "tpPolicies.h" | |
28 | #include <Security/oidsattr.h> | |
29 | #include <Security/cssmerr.h> | |
30 | #include "tpdebugging.h" | |
31 | #include "rootCerts.h" | |
32 | #include "certGroupUtils.h" | |
33 | #include <Security/x509defs.h> | |
34 | #include <Security/oidscert.h> | |
35 | #include <Security/certextensions.h> | |
36 | #include <Security/cssmapple.h> | |
37 | #include <string.h> | |
29654253 A |
38 | #include <ctype.h> |
39 | #include <assert.h> | |
bac41a7b A |
40 | |
41 | /* | |
42 | * Our private per-extension info. One of these per (understood) extension per | |
43 | * cert. | |
44 | */ | |
45 | typedef struct { | |
46 | CSSM_BOOL present; | |
47 | CSSM_BOOL critical; | |
48 | CE_Data *extnData; // mallocd by CL | |
49 | CSSM_DATA *valToFree; // the data we pass to freeField() | |
50 | } iSignExtenInfo; | |
51 | ||
52 | /* | |
53 | * Struct to keep track of info pertinent to one cert. | |
54 | */ | |
55 | typedef struct { | |
56 | ||
57 | /* extensions pertinent to iSign */ | |
58 | iSignExtenInfo authorityId; | |
59 | iSignExtenInfo subjectId; | |
60 | iSignExtenInfo keyUsage; | |
61 | iSignExtenInfo extendKeyUsage; | |
62 | iSignExtenInfo basicConstraints; | |
63 | iSignExtenInfo netscapeCertType; | |
64 | ||
65 | /* flag indicating presence of a critical extension we don't understand */ | |
66 | CSSM_BOOL foundUnknownCritical; | |
67 | ||
68 | } iSignCertInfo; | |
69 | ||
70 | ||
71 | /* | |
72 | * Setup a single iSignExtenInfo. Called once per known extension | |
73 | * per cert. | |
74 | */ | |
75 | static CSSM_RETURN tpSetupExtension( | |
76 | CssmAllocator &alloc, | |
77 | CSSM_DATA *extnData, | |
78 | iSignExtenInfo *extnInfo) // which component of certInfo | |
79 | { | |
80 | if(extnData->Length != sizeof(CSSM_X509_EXTENSION)) { | |
81 | errorLog0("tpSetupExtension: malformed CSSM_FIELD\n"); | |
82 | return CSSMERR_TP_UNKNOWN_FORMAT; | |
83 | } | |
84 | CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)extnData->Data; | |
85 | extnInfo->present = CSSM_TRUE; | |
86 | extnInfo->critical = cssmExt->critical; | |
87 | extnInfo->extnData = (CE_Data *)cssmExt->value.parsedValue; | |
88 | extnInfo->valToFree = extnData; | |
89 | return CSSM_OK; | |
90 | } | |
91 | ||
92 | /* | |
93 | * Fetch a known extension, set up associated iSignExtenInfo if present. | |
94 | */ | |
95 | static CSSM_RETURN iSignFetchExtension( | |
96 | CssmAllocator &alloc, | |
97 | TPCertInfo *tpCert, | |
98 | const CSSM_OID *fieldOid, // which extension to fetch | |
99 | iSignExtenInfo *extnInfo) // where the info goes | |
100 | { | |
101 | CSSM_DATA_PTR fieldValue; // mallocd by CL | |
102 | CSSM_RETURN crtn; | |
103 | ||
104 | crtn = tpCert->fetchField(fieldOid, &fieldValue); | |
105 | switch(crtn) { | |
106 | case CSSM_OK: | |
107 | break; | |
108 | case CSSMERR_CL_NO_FIELD_VALUES: | |
109 | /* field not present, OK */ | |
110 | return CSSM_OK; | |
111 | default: | |
112 | return crtn; | |
113 | } | |
114 | return tpSetupExtension(alloc, | |
115 | fieldValue, | |
116 | extnInfo); | |
117 | } | |
118 | ||
119 | /* | |
120 | * Search for al unknown extensions. If we find one which is flagged critical, | |
121 | * flag certInfo->foundUnknownCritical. Only returns error on gross errors. | |
122 | */ | |
123 | static CSSM_RETURN iSignSearchUnknownExtensions( | |
124 | TPCertInfo *tpCert, | |
125 | iSignCertInfo *certInfo) | |
126 | { | |
127 | CSSM_RETURN crtn; | |
128 | CSSM_DATA_PTR fieldValue = NULL; | |
129 | CSSM_HANDLE searchHand = CSSM_INVALID_HANDLE; | |
130 | uint32 numFields = 0; | |
131 | ||
132 | crtn = CSSM_CL_CertGetFirstCachedFieldValue(tpCert->clHand(), | |
133 | tpCert->cacheHand(), | |
134 | &CSSMOID_X509V3CertificateExtensionCStruct, | |
135 | &searchHand, | |
136 | &numFields, | |
137 | &fieldValue); | |
138 | switch(crtn) { | |
139 | case CSSM_OK: | |
140 | /* found one, proceed */ | |
141 | break; | |
142 | case CSSMERR_CL_NO_FIELD_VALUES: | |
143 | /* no unknown extensions present, OK */ | |
144 | return CSSM_OK; | |
145 | default: | |
146 | return crtn; | |
147 | } | |
148 | ||
149 | if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) { | |
150 | errorLog0("iSignSearchUnknownExtensions: malformed CSSM_FIELD\n"); | |
151 | return CSSMERR_TP_UNKNOWN_FORMAT; | |
152 | } | |
153 | CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data; | |
154 | if(cssmExt->critical) { | |
155 | /* BRRZAPP! Found an unknown extension marked critical */ | |
156 | certInfo->foundUnknownCritical = CSSM_TRUE; | |
157 | goto fini; | |
158 | } | |
159 | CSSM_CL_FreeFieldValue(tpCert->clHand(), | |
160 | &CSSMOID_X509V3CertificateExtensionCStruct, | |
161 | fieldValue); | |
162 | fieldValue = NULL; | |
163 | ||
164 | /* process remaining unknown extensions */ | |
165 | for(unsigned i=1; i<numFields; i++) { | |
166 | crtn = CSSM_CL_CertGetNextCachedFieldValue(tpCert->clHand(), | |
167 | searchHand, | |
168 | &fieldValue); | |
169 | if(crtn) { | |
170 | /* should never happen */ | |
171 | errorLog0("searchUnknownExtensions: GetNextCachedFieldValue error\n"); | |
172 | break; | |
173 | } | |
174 | if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) { | |
175 | errorLog0("iSignSearchUnknownExtensions: malformed CSSM_FIELD\n"); | |
176 | crtn = CSSMERR_TP_UNKNOWN_FORMAT; | |
177 | break; | |
178 | } | |
179 | CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data; | |
180 | if(cssmExt->critical) { | |
181 | /* BRRZAPP! Found an unknown extension marked critical */ | |
182 | certInfo->foundUnknownCritical = CSSM_TRUE; | |
183 | break; | |
184 | } | |
185 | CSSM_CL_FreeFieldValue(tpCert->clHand(), | |
186 | &CSSMOID_X509V3CertificateExtensionCStruct, | |
187 | fieldValue); | |
188 | fieldValue = NULL; | |
189 | } /* for additional fields */ | |
190 | ||
191 | fini: | |
192 | if(fieldValue) { | |
193 | CSSM_CL_FreeFieldValue(tpCert->clHand(), | |
194 | &CSSMOID_X509V3CertificateExtensionCStruct, | |
195 | fieldValue); | |
196 | } | |
197 | if(searchHand != CSSM_INVALID_HANDLE) { | |
198 | CSSM_CL_CertAbortQuery(tpCert->clHand(), searchHand); | |
199 | } | |
200 | return crtn; | |
201 | } | |
202 | /* | |
203 | * Given a TPCertInfo, fetch the associated iSignCertInfo fields. | |
204 | * Returns CSSM_FAIL on error. | |
205 | */ | |
206 | static CSSM_RETURN iSignGetCertInfo( | |
207 | CssmAllocator &alloc, | |
208 | TPCertInfo *tpCert, | |
209 | iSignCertInfo *certInfo) | |
210 | { | |
211 | CSSM_RETURN crtn; | |
212 | ||
213 | /* first grind thru the extensions we're interested in */ | |
214 | crtn = iSignFetchExtension(alloc, | |
215 | tpCert, | |
216 | &CSSMOID_AuthorityKeyIdentifier, | |
217 | &certInfo->authorityId); | |
218 | if(crtn) { | |
219 | return crtn; | |
220 | } | |
221 | crtn = iSignFetchExtension(alloc, | |
222 | tpCert, | |
223 | &CSSMOID_SubjectKeyIdentifier, | |
224 | &certInfo->subjectId); | |
225 | if(crtn) { | |
226 | return crtn; | |
227 | } | |
228 | crtn = iSignFetchExtension(alloc, | |
229 | tpCert, | |
230 | &CSSMOID_KeyUsage, | |
231 | &certInfo->keyUsage); | |
232 | if(crtn) { | |
233 | return crtn; | |
234 | } | |
235 | crtn = iSignFetchExtension(alloc, | |
236 | tpCert, | |
237 | &CSSMOID_ExtendedKeyUsage, | |
238 | &certInfo->extendKeyUsage); | |
239 | if(crtn) { | |
240 | return crtn; | |
241 | } | |
242 | crtn = iSignFetchExtension(alloc, | |
243 | tpCert, | |
244 | &CSSMOID_BasicConstraints, | |
245 | &certInfo->basicConstraints); | |
246 | if(crtn) { | |
247 | return crtn; | |
248 | } | |
249 | crtn = iSignFetchExtension(alloc, | |
250 | tpCert, | |
251 | &CSSMOID_NetscapeCertType, | |
252 | &certInfo->netscapeCertType); | |
253 | if(crtn) { | |
254 | return crtn; | |
255 | } | |
256 | ||
257 | /* now look for extensions we don't understand - the only thing we're interested | |
258 | * in is the critical flag. */ | |
259 | return iSignSearchUnknownExtensions(tpCert, certInfo); | |
260 | } | |
261 | ||
262 | /* | |
263 | * Free (via CL) the fields allocated in iSignGetCertInfo(). | |
264 | */ | |
265 | static void iSignFreeCertInfo( | |
266 | CSSM_CL_HANDLE clHand, | |
267 | iSignCertInfo *certInfo) | |
268 | { | |
269 | if(certInfo->authorityId.present) { | |
270 | CSSM_CL_FreeFieldValue(clHand, &CSSMOID_AuthorityKeyIdentifier, | |
271 | certInfo->authorityId.valToFree); | |
272 | } | |
273 | if(certInfo->subjectId.present) { | |
274 | CSSM_CL_FreeFieldValue(clHand, &CSSMOID_SubjectKeyIdentifier, | |
275 | certInfo->subjectId.valToFree); | |
276 | } | |
277 | if(certInfo->keyUsage.present) { | |
278 | CSSM_CL_FreeFieldValue(clHand, &CSSMOID_KeyUsage, | |
279 | certInfo->keyUsage.valToFree); | |
280 | } | |
281 | if(certInfo->extendKeyUsage.present) { | |
282 | CSSM_CL_FreeFieldValue(clHand, &CSSMOID_ExtendedKeyUsage, | |
283 | certInfo->extendKeyUsage.valToFree); | |
284 | } | |
285 | if(certInfo->basicConstraints.present) { | |
286 | CSSM_CL_FreeFieldValue(clHand, &CSSMOID_BasicConstraints, | |
287 | certInfo->basicConstraints.valToFree); | |
288 | } | |
289 | if(certInfo->netscapeCertType.present) { | |
290 | CSSM_CL_FreeFieldValue(clHand, &CSSMOID_NetscapeCertType, | |
291 | certInfo->netscapeCertType.valToFree); | |
292 | } | |
293 | } | |
294 | ||
29654253 | 295 | #if TP_ROOT_CERT_ENABLE |
bac41a7b A |
296 | /* |
297 | * Common code for comparing a root to a list of known embedded roots. | |
298 | */ | |
299 | static CSSM_BOOL tp_isKnownRootCert( | |
300 | TPCertInfo *rootCert, // raw cert to compare | |
301 | const tpRootCert *knownRoots, | |
302 | unsigned numKnownRoots) | |
303 | { | |
304 | const CSSM_DATA *subjectName = NULL; | |
305 | CSSM_DATA_PTR publicKey = NULL; | |
306 | unsigned dex; | |
307 | CSSM_BOOL brtn = CSSM_FALSE; | |
308 | CSSM_DATA_PTR valToFree = NULL; | |
309 | ||
310 | subjectName = rootCert->subjectName(); | |
311 | publicKey = tp_CertGetPublicKey(rootCert, &valToFree); | |
312 | if(publicKey == NULL) { | |
313 | errorLog0("tp_isKnownRootCert: error retrieving public key info!\n"); | |
314 | goto errOut; | |
315 | } | |
316 | ||
317 | /* | |
318 | * Grind thru the list of known certs, demanding perfect match of | |
319 | * both fields | |
320 | */ | |
321 | for(dex=0; dex<numKnownRoots; dex++) { | |
322 | if(!tpCompareCssmData(subjectName, | |
29654253 | 323 | &knownRoots[dex].subjectName)) { |
bac41a7b A |
324 | continue; |
325 | } | |
326 | if(!tpCompareCssmData(publicKey, | |
29654253 | 327 | &knownRoots[dex].publicKey)) { |
bac41a7b A |
328 | continue; |
329 | } | |
330 | #if ENABLE_APPLE_DEBUG_ROOT | |
331 | if( dex == (knownRoots - 1) ){ | |
332 | brtn = CSSM_FALSE; | |
333 | //tpSetError(CSSM_TP_DEBUG_CERT); | |
334 | break; | |
335 | } | |
336 | #endif | |
337 | brtn = CSSM_TRUE; | |
338 | break; | |
339 | } | |
340 | errOut: | |
341 | tp_CertFreePublicKey(rootCert->clHand(), valToFree); | |
342 | return brtn; | |
343 | } | |
344 | ||
345 | /* | |
346 | * See if specified root cert is a known (embedded) iSign root cert. | |
347 | * Returns CSSM_TRUE if the cert is a known root cert. | |
29654253 A |
348 | * |
349 | * Note as of 6/12/02, we do not distinguish between internally | |
350 | * cached iSign roots and SSL roots. Maybe someday we will do so again, | |
351 | * so let's leave these two functions separate. | |
bac41a7b A |
352 | */ |
353 | static CSSM_BOOL tp_isIsignRootCert( | |
354 | CSSM_CL_HANDLE clHand, | |
355 | TPCertInfo *rootCert) // raw cert from cert group | |
356 | { | |
29654253 A |
357 | const tpRootCert *roots; |
358 | unsigned numRoots; | |
359 | roots = TPRootStore::tpGlobalRoots().rootCerts(clHand, numRoots); | |
360 | return tp_isKnownRootCert(rootCert, roots, numRoots); | |
bac41a7b A |
361 | } |
362 | ||
363 | /* | |
364 | * See if specified root cert is a known (embedded) SSL root cert. | |
365 | * Returns CSSM_TRUE if the cert is a known root cert. | |
366 | */ | |
367 | static CSSM_BOOL tp_isSslRootCert( | |
368 | CSSM_CL_HANDLE clHand, | |
369 | TPCertInfo *rootCert) // raw cert from cert group | |
370 | { | |
29654253 A |
371 | const tpRootCert *roots; |
372 | unsigned numRoots; | |
373 | roots = TPRootStore::tpGlobalRoots().rootCerts(clHand, numRoots); | |
374 | return tp_isKnownRootCert(rootCert, roots, numRoots); | |
bac41a7b A |
375 | } |
376 | ||
377 | /* | |
378 | * Attempt to verify specified cert (from the end of a chain) with one of | |
379 | * our known SSL roots. | |
380 | */ | |
381 | CSSM_BOOL tp_verifyWithSslRoots( | |
382 | CSSM_CL_HANDLE clHand, | |
383 | CSSM_CSP_HANDLE cspHand, | |
384 | TPCertInfo *certToVfy) // last in chain, not root | |
385 | { | |
386 | CSSM_KEY rootKey; // pub key manufactured from tpRootCert info | |
387 | CSSM_CC_HANDLE ccHand; // signature context | |
388 | CSSM_RETURN crtn; | |
389 | unsigned dex; | |
390 | const tpRootCert *rootInfo; | |
391 | CSSM_BOOL brtn = CSSM_FALSE; | |
392 | CSSM_KEYHEADER *hdr = &rootKey.KeyHeader; | |
393 | CSSM_X509_ALGORITHM_IDENTIFIER_PTR algId; | |
394 | CSSM_DATA_PTR valToFree = NULL; | |
395 | CSSM_ALGORITHMS sigAlg; | |
29654253 A |
396 | const tpRootCert *rootCerts = NULL; |
397 | unsigned numRootCerts = 0; | |
398 | ||
bac41a7b A |
399 | memset(&rootKey, 0, sizeof(CSSM_KEY)); |
400 | ||
401 | /* | |
402 | * Get signature algorithm from subject key | |
403 | */ | |
404 | algId = tp_CertGetAlgId(certToVfy, &valToFree); | |
405 | if(algId == NULL) { | |
406 | /* bad cert */ | |
407 | return CSSM_FALSE; | |
408 | } | |
409 | /* subsequest errors to errOut: */ | |
410 | ||
411 | /* map to key and signature algorithm */ | |
412 | sigAlg = tpOidToAldId(&algId->algorithm, &hdr->AlgorithmId); | |
413 | if(sigAlg == CSSM_ALGID_NONE) { | |
414 | errorLog0("tp_verifyWithSslRoots: unknown sig alg\n"); | |
415 | goto errOut; | |
416 | } | |
417 | ||
418 | /* Set up other constant key fields */ | |
419 | hdr->BlobType = CSSM_KEYBLOB_RAW; | |
420 | switch(hdr->AlgorithmId) { | |
421 | case CSSM_ALGID_RSA: | |
422 | hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; | |
423 | break; | |
424 | case CSSM_ALGID_DSA: | |
425 | hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_FIPS186; | |
426 | break; | |
427 | case CSSM_ALGID_FEE: | |
428 | hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING; | |
429 | break; | |
430 | default: | |
431 | /* punt */ | |
432 | hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_NONE; | |
433 | } | |
434 | hdr->KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; | |
435 | hdr->KeyAttr = CSSM_KEYATTR_MODIFIABLE | CSSM_KEYATTR_EXTRACTABLE; | |
436 | hdr->KeyUsage = CSSM_KEYUSE_VERIFY; | |
29654253 A |
437 | |
438 | rootCerts = TPRootStore::tpGlobalRoots().rootCerts(clHand, numRootCerts); | |
439 | for(dex=0; dex<numRootCerts; dex++) { | |
440 | rootInfo = &rootCerts[dex]; | |
441 | if(!tpIsSameName(&rootInfo->subjectName, certToVfy->issuerName())) { | |
bac41a7b A |
442 | /* not this root */ |
443 | continue; | |
444 | } | |
29654253 A |
445 | |
446 | /* only variation in key in the loop - raw key bits and size */ | |
447 | rootKey.KeyData = rootInfo->publicKey; | |
bac41a7b A |
448 | hdr->LogicalKeySizeInBits = rootInfo->keySize; |
449 | crtn = CSSM_CSP_CreateSignatureContext(cspHand, | |
450 | sigAlg, | |
451 | NULL, // AcccedCred | |
452 | &rootKey, | |
453 | &ccHand); | |
454 | if(crtn) { | |
455 | errorLog0("tp_verifyWithSslRoots: CSSM_CSP_CreateSignatureContext err\n"); | |
456 | CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR); | |
457 | } | |
458 | crtn = CSSM_CL_CertVerify(clHand, | |
459 | ccHand, | |
460 | certToVfy->certData(), | |
461 | NULL, // no signer cert | |
462 | NULL, // VerifyScope | |
463 | 0); // ScopeSize | |
464 | CSSM_DeleteContext(ccHand); | |
465 | if(crtn == CSSM_OK) { | |
466 | /* success! */ | |
467 | brtn = CSSM_TRUE; | |
468 | break; | |
469 | } | |
470 | } | |
471 | errOut: | |
472 | if(valToFree != NULL) { | |
473 | tp_CertFreeAlgId(clHand, valToFree); | |
474 | } | |
475 | return brtn; | |
476 | } | |
29654253 A |
477 | #endif /* TP_ROOT_CERT_ENABLE */ |
478 | ||
5a719ac8 A |
479 | /* |
480 | * See if cert's Subject.commonName matches caller-specified hostname. | |
481 | * Returns CSSM_TRUE if match, else returns CSSM_FALSE. | |
29654253 | 482 | */ |
5a719ac8 A |
483 | static CSSM_BOOL tpCompareCommonName( |
484 | TPCertInfo &cert, | |
485 | const char *hostName, | |
486 | uint32 hostNameLen) | |
29654253 | 487 | { |
5a719ac8 A |
488 | char *commonName = NULL; // from cert's subject name |
489 | uint32 commonNameLen = 0; | |
490 | CSSM_DATA_PTR subjNameData = NULL; | |
491 | CSSM_RETURN crtn; | |
492 | CSSM_BOOL ourRtn = CSSM_FALSE; | |
29654253 | 493 | |
5a719ac8 | 494 | crtn = cert.fetchField(&CSSMOID_X509V1SubjectNameCStruct, &subjNameData); |
29654253 A |
495 | if(crtn) { |
496 | /* should never happen, we shouldn't be here if there is no subject */ | |
5a719ac8 A |
497 | errorLog0("tp_verifySslOpts: error retrieving subject name"); |
498 | return CSSM_FALSE; | |
29654253 A |
499 | } |
500 | CSSM_X509_NAME_PTR x509name = (CSSM_X509_NAME_PTR)subjNameData->Data; | |
501 | if((x509name == NULL) || (subjNameData->Length != sizeof(CSSM_X509_NAME))) { | |
5a719ac8 A |
502 | errorLog0("tp_verifySslOpts: malformed CSSM_X509_NAME"); |
503 | cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData); | |
504 | return CSSM_FALSE; | |
29654253 A |
505 | } |
506 | ||
507 | /* Now grunge thru the X509 name looking for a common name */ | |
508 | CSSM_X509_TYPE_VALUE_PAIR *ptvp; | |
509 | CSSM_X509_RDN_PTR rdnp; | |
510 | unsigned rdnDex; | |
511 | unsigned pairDex; | |
512 | ||
513 | for(rdnDex=0; rdnDex<x509name->numberOfRDNs; rdnDex++) { | |
514 | rdnp = &x509name->RelativeDistinguishedName[rdnDex]; | |
515 | for(pairDex=0; pairDex<rdnp->numberOfPairs; pairDex++) { | |
516 | ptvp = &rdnp->AttributeTypeAndValue[pairDex]; | |
517 | if(tpCompareOids(&ptvp->type, &CSSMOID_CommonName)) { | |
518 | commonName = (char *)ptvp->value.Data; | |
519 | commonNameLen = ptvp->value.Length; | |
5a719ac8 A |
520 | ourRtn = tpCompareHostNames(hostName, hostNameLen, |
521 | commonName, commonNameLen); | |
522 | if(ourRtn) { | |
523 | /* success */ | |
524 | break; | |
525 | } | |
526 | /* else keep going, maybe there's another common name */ | |
29654253 A |
527 | } |
528 | } | |
5a719ac8 A |
529 | if(ourRtn) { |
530 | break; | |
531 | } | |
29654253 | 532 | } |
5a719ac8 A |
533 | cert.freeField(&CSSMOID_X509V1SubjectNameCStruct, subjNameData); |
534 | return ourRtn; | |
535 | } | |
536 | ||
537 | /* | |
538 | * Compare ASCII form of an IP address to a CSSM_DATA containing | |
539 | * the IP address's numeric components. Returns true on match. | |
540 | */ | |
541 | static CSSM_BOOL tpCompIpAddrStr( | |
542 | const char *str, | |
543 | unsigned strLen, | |
544 | const CSSM_DATA *numeric) | |
545 | { | |
546 | const char *cp = str; | |
547 | const char *nextDot; | |
548 | char buf[100]; | |
29654253 | 549 | |
5a719ac8 A |
550 | if((numeric == NULL) || (numeric->Length == 0) || (str == NULL)) { |
551 | return CSSM_FALSE; | |
29654253 | 552 | } |
5a719ac8 A |
553 | if(cp[strLen - 1] == '\0') { |
554 | /* ignore NULL terminator */ | |
555 | strLen--; | |
556 | } | |
557 | for(unsigned dex=0; dex<numeric->Length; dex++) { | |
558 | /* cp points to start of current string digit */ | |
559 | /* find next dot */ | |
560 | const char *lastChar = cp + strLen; | |
561 | nextDot = cp + 1; | |
562 | for( ; nextDot<lastChar; nextDot++) { | |
563 | if(*nextDot == '.') { | |
564 | break; | |
565 | } | |
566 | } | |
567 | if(nextDot == lastChar) { | |
568 | /* legal and required on last digit */ | |
569 | if(dex != (numeric->Length - 1)) { | |
570 | return CSSM_FALSE; | |
571 | } | |
572 | } | |
573 | else if(dex == (numeric->Length - 1)) { | |
574 | return CSSM_FALSE; | |
575 | } | |
576 | unsigned digLen = nextDot - cp; | |
577 | if(digLen >= sizeof(buf)) { | |
578 | /* preposterous */ | |
579 | return CSSM_FALSE; | |
580 | } | |
581 | memmove(buf, cp, digLen); | |
582 | buf[digLen] = '\0'; | |
583 | /* incr digLen to include the next dot */ | |
584 | digLen++; | |
585 | cp += digLen; | |
586 | strLen -= digLen; | |
587 | int digVal = atoi(buf); | |
588 | if(digVal != numeric->Data[dex]) { | |
589 | return CSSM_FALSE; | |
590 | } | |
29654253 | 591 | } |
5a719ac8 A |
592 | return CSSM_TRUE; |
593 | } | |
594 | ||
595 | /* | |
596 | * See if cert's subjectAltName matches caller-specified hostname, either | |
597 | * as a dnsName or an iPAddress. | |
598 | * | |
599 | * Returns CSSM_TRUE if match, else returns CSSM_FALSE. Also indicates | |
600 | * whether or not a dnsName was found (in which case the subject's | |
601 | * common name should NOT be a candidate for verification). | |
602 | */ | |
603 | static CSSM_BOOL tpCompareSubjectAltName( | |
604 | TPCertInfo &cert, | |
605 | const char *hostName, | |
606 | uint32 hostNameLen, | |
607 | bool &dnsNameFound) // RETURNED | |
608 | { | |
609 | CSSM_DATA_PTR subjAltNameData = NULL; | |
610 | CSSM_RETURN crtn; | |
611 | CSSM_BOOL ourRtn = CSSM_FALSE; | |
29654253 | 612 | |
5a719ac8 A |
613 | dnsNameFound = false; |
614 | crtn = cert.fetchField(&CSSMOID_SubjectAltName, &subjAltNameData); | |
615 | if(crtn) { | |
616 | /* common failure, no subjectAltName found */ | |
617 | return CSSM_FALSE; | |
618 | } | |
619 | CSSM_X509_EXTENSION_PTR exten = | |
620 | (CSSM_X509_EXTENSION_PTR)subjAltNameData->Data; | |
621 | /* Paranoid check of extension integrity */ | |
622 | if((exten == NULL) || | |
623 | (subjAltNameData->Length != sizeof(CSSM_X509_EXTENSION)) || | |
624 | (exten->format != CSSM_X509_DATAFORMAT_PARSED) || | |
625 | (exten->value.parsedValue == NULL)) { | |
626 | errorLog0("tpCompareSubjectAltName: malformed CSSM_X509_EXTENSION"); | |
627 | cert.freeField(&CSSMOID_SubjectAltName, subjAltNameData); | |
628 | return CSSM_FALSE; | |
29654253 | 629 | } |
5a719ac8 A |
630 | |
631 | CE_GeneralNames *names = (CE_GeneralNames *)exten->value.parsedValue; | |
632 | char *serverName; | |
633 | unsigned serverNameLen; | |
29654253 | 634 | |
5a719ac8 A |
635 | /* Search thru the CE_GeneralNames looking for a DNSName or IP Address */ |
636 | for(unsigned dex=0; dex<names->numNames; dex++) { | |
637 | CE_GeneralName *name = &names->generalName[dex]; | |
638 | switch(name->nameType) { | |
639 | case GNT_IPAddress: | |
640 | ourRtn = tpCompIpAddrStr(hostName, hostNameLen, &name->name); | |
641 | break; | |
642 | ||
643 | case GNT_DNSName: | |
644 | if(name->berEncoded) { | |
645 | errorLog0("tpCompareSubjectAltName: malformed " | |
646 | "CE_GeneralName (1)\n"); | |
647 | break; | |
648 | } | |
649 | serverName = (char *)name->name.Data; | |
650 | if(serverName == NULL) { | |
651 | errorLog0("tpCompareSubjectAltName: malformed " | |
652 | "CE_GeneralName (2)\n"); | |
653 | break; | |
654 | } | |
655 | serverNameLen = name->name.Length; | |
656 | ourRtn = tpCompareHostNames(hostName, hostNameLen, | |
657 | serverName, serverNameLen); | |
658 | dnsNameFound = true; | |
659 | break; | |
660 | ||
661 | default: | |
662 | /* not interested, proceed to next name */ | |
663 | break; | |
29654253 | 664 | } |
5a719ac8 A |
665 | if(ourRtn) { |
666 | /* success */ | |
667 | break; | |
29654253 | 668 | } |
5a719ac8 A |
669 | } |
670 | cert.freeField(&CSSMOID_SubjectAltName, subjAltNameData); | |
671 | return ourRtn; | |
672 | } | |
673 | ||
674 | /* is host name in the form of a.b.c.d, where a,b,c, and d are digits? */ | |
675 | static CSSM_BOOL tpIsNumeric( | |
676 | const char *hostName, | |
677 | unsigned hostNameLen) | |
678 | { | |
679 | if(hostName[hostNameLen - 1] == '\0') { | |
680 | /* ignore NULL terminator */ | |
681 | hostNameLen--; | |
682 | } | |
683 | for(unsigned i=0; i<hostNameLen; i++) { | |
684 | char c = *hostName++; | |
685 | if(isdigit(c)) { | |
686 | continue; | |
687 | } | |
688 | if(c != '.') { | |
689 | return CSSM_FALSE; | |
29654253 A |
690 | } |
691 | } | |
5a719ac8 A |
692 | return CSSM_TRUE; |
693 | } | |
694 | ||
695 | /* | |
696 | * Verify SSL options. Currently this just consists of matching the | |
697 | * leaf cert's subject common name against the caller's (optional) | |
698 | * server name. | |
699 | */ | |
700 | static CSSM_RETURN tp_verifySslOpts( | |
701 | TPCertGroup &certGroup, | |
702 | const CSSM_APPLE_TP_SSL_OPTIONS *sslOpts) | |
703 | { | |
704 | if(sslOpts == NULL) { | |
705 | /* optional */ | |
706 | return CSSM_OK; | |
29654253 | 707 | } |
5a719ac8 A |
708 | |
709 | unsigned hostNameLen = sslOpts->ServerNameLen; | |
710 | ||
711 | if(hostNameLen == 0) { | |
712 | /* optional */ | |
713 | return CSSM_OK; | |
29654253 | 714 | } |
5a719ac8 A |
715 | if(sslOpts->ServerName == NULL) { |
716 | return CSSMERR_TP_INVALID_POINTER; | |
29654253 | 717 | } |
5a719ac8 A |
718 | |
719 | /* convert caller's hostname string to lower case */ | |
720 | char *hostName = (char *)certGroup.alloc().malloc(hostNameLen); | |
721 | memmove(hostName, sslOpts->ServerName, hostNameLen); | |
722 | tpToLower(hostName, hostNameLen); | |
723 | ||
724 | TPCertInfo *leaf = certGroup.certAtIndex(0); | |
725 | assert(leaf != NULL); | |
726 | ||
727 | CSSM_BOOL match = CSSM_FALSE; | |
728 | ||
729 | /* First check subjectAltName... */ | |
730 | bool dnsNameFound = false; | |
731 | match = tpCompareSubjectAltName(*leaf, hostName, hostNameLen, | |
732 | dnsNameFound); | |
733 | /* | |
734 | * Then common name, if | |
735 | * -- no match from subjectAltName, AND | |
736 | * -- dnsName was NOT found, AND | |
737 | * -- hostName is not strictly numeric form (1.2.3.4) | |
738 | */ | |
739 | if(!match && !dnsNameFound && !tpIsNumeric(hostName, hostNameLen)) { | |
740 | match = tpCompareCommonName(*leaf, hostName, hostNameLen); | |
741 | } | |
742 | certGroup.alloc().free(hostName); | |
743 | if(match) { | |
744 | return CSSM_OK; | |
745 | } | |
746 | else { | |
29654253 | 747 | leaf->addStatusCode(CSSMERR_APPLETP_HOSTNAME_MISMATCH); |
5a719ac8 | 748 | return CSSMERR_TP_VERIFY_ACTION_FAILED; |
29654253 | 749 | } |
29654253 | 750 | } |
bac41a7b A |
751 | |
752 | /* | |
753 | * RFC2459 says basicConstraints must be flagged critical for | |
754 | * CA certs, but Verisign doesn't work that way. | |
755 | */ | |
756 | #define BASIC_CONSTRAINTS_MUST_BE_CRITICAL 0 | |
757 | ||
758 | /* | |
759 | * TP iSign spec says Extended Key Usage required for leaf certs, | |
760 | * but Verisign doesn't work that way. | |
761 | */ | |
762 | #define EXTENDED_KEY_USAGE_REQUIRED_FOR_LEAF 0 | |
763 | ||
764 | /* | |
765 | * TP iSign spec says Subject Alternate Name required for leaf certs, | |
766 | * but Verisign doesn't work that way. | |
767 | */ | |
768 | #define SUBJECT_ALT_NAME_REQUIRED_FOR_LEAF 0 | |
769 | ||
770 | /* | |
771 | * TP iSign spec originally required KeyUsage for all certs, but | |
772 | * Verisign doesn't have that in their roots. | |
773 | */ | |
774 | #define KEY_USAGE_REQUIRED_FOR_ROOT 0 | |
775 | ||
776 | /* | |
777 | * Public routine to perform TP verification on a constructed | |
778 | * cert group. | |
779 | * Returns CSSM_TRUE on success. | |
780 | * Asumes the chain has passed basic subject/issuer verification. First cert of | |
781 | * incoming certGroup is end-entity (leaf). | |
782 | * | |
783 | * Per-policy details: | |
784 | * iSign: Assumes that last cert in incoming certGroup is a root cert. | |
785 | * Also assumes a cert group of more than one cert. | |
786 | * kTPx509Basic: CertGroup of length one allowed. | |
787 | */ | |
788 | CSSM_RETURN tp_policyVerify( | |
29654253 A |
789 | TPPolicy policy, |
790 | CssmAllocator &alloc, | |
791 | CSSM_CL_HANDLE clHand, | |
792 | CSSM_CSP_HANDLE cspHand, | |
793 | TPCertGroup *certGroup, | |
794 | CSSM_BOOL verifiedToRoot, // last cert is good root | |
795 | const CSSM_APPLE_TP_ACTION_DATA *actionData, | |
796 | const CSSM_APPLE_TP_SSL_OPTIONS *sslOpts, | |
797 | void *policyOpts) // future options | |
bac41a7b A |
798 | { |
799 | iSignCertInfo *certInfo = NULL; | |
800 | uint32 numCerts; | |
801 | iSignCertInfo *thisCertInfo; | |
802 | uint16 expUsage; | |
803 | uint16 actUsage; | |
804 | unsigned certDex; | |
29654253 A |
805 | CSSM_BOOL cA = CSSM_FALSE; // init for compiler warning |
806 | CSSM_BOOL isLeaf; // end entity | |
807 | CSSM_BOOL isRoot; // root cert | |
bac41a7b A |
808 | CE_ExtendedKeyUsage *extendUsage; |
809 | CE_AuthorityKeyID *authorityId; | |
29654253 A |
810 | CSSM_RETURN outErr = CSSM_OK; // for gross, non-policy errors |
811 | CSSM_BOOL policyFail = CSSM_FALSE; | |
bac41a7b A |
812 | |
813 | /* First, kTPDefault is a nop here */ | |
814 | if(policy == kTPDefault) { | |
815 | return CSSM_OK; | |
816 | } | |
817 | ||
818 | if(certGroup == NULL) { | |
819 | return CSSMERR_TP_INVALID_CERTGROUP; | |
820 | } | |
821 | numCerts = certGroup->numCerts(); | |
822 | if(numCerts == 0) { | |
823 | return CSSMERR_TP_INVALID_CERTGROUP; | |
824 | } | |
825 | if(policy == kTPiSign) { | |
826 | if(!verifiedToRoot) { | |
827 | /* no way, this requires a root cert */ | |
828 | return CSSMERR_TP_INVALID_CERTGROUP; | |
829 | } | |
830 | if(numCerts <= 1) { | |
831 | /* nope, not for iSign */ | |
832 | return CSSMERR_TP_INVALID_CERTGROUP; | |
833 | } | |
834 | } | |
835 | ||
836 | /* cook up an iSignCertInfo array */ | |
837 | certInfo = (iSignCertInfo *)tpCalloc(alloc, numCerts, sizeof(iSignCertInfo)); | |
838 | /* subsequent errors to errOut: */ | |
839 | ||
840 | /* fill it with interesting info from parsed certs */ | |
841 | for(certDex=0; certDex<numCerts; certDex++) { | |
842 | if(iSignGetCertInfo(alloc, | |
843 | certGroup->certAtIndex(certDex), | |
844 | &certInfo[certDex])) { | |
29654253 A |
845 | (certGroup->certAtIndex(certDex))->addStatusCode( |
846 | CSSMERR_TP_INVALID_CERTIFICATE); | |
847 | /* this one is fatal */ | |
bac41a7b A |
848 | outErr = CSSMERR_TP_INVALID_CERTIFICATE; |
849 | goto errOut; | |
850 | } | |
851 | } | |
852 | ||
853 | /* | |
854 | * OK, the heart of TP enforcement. | |
855 | * First check for presence of required extensions and | |
856 | * critical extensions we don't understand. | |
857 | */ | |
858 | for(certDex=0; certDex<numCerts; certDex++) { | |
859 | thisCertInfo = &certInfo[certDex]; | |
29654253 | 860 | TPCertInfo *thisTpCertInfo = certGroup->certAtIndex(certDex); |
bac41a7b A |
861 | |
862 | if(thisCertInfo->foundUnknownCritical) { | |
863 | /* illegal for all policies */ | |
864 | errorLog0("tp_policyVerify: critical flag in unknown extension\n"); | |
29654253 A |
865 | thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN); |
866 | policyFail = CSSM_TRUE; | |
bac41a7b A |
867 | } |
868 | ||
869 | /* | |
870 | * Note it's possible for both of these to be true, for a | |
871 | * of length one (kTPx509Basic only!) | |
872 | */ | |
873 | isLeaf = (certDex == 0) ? CSSM_TRUE : CSSM_FALSE; | |
874 | isRoot = (certDex == (numCerts - 1)) ? CSSM_TRUE : CSSM_FALSE; | |
875 | ||
876 | /* | |
877 | * BasicConstraints.cA | |
878 | * iSign: required in all but leaf and root, | |
879 | * for which it is optional (with default values of false | |
880 | * for leaf and true for root). | |
881 | * kTPx509Basic, | |
882 | * kTP_SSL: always optional, default of false for leaf and | |
883 | * true for others | |
884 | * All: cA must be false for leaf, true for others | |
885 | */ | |
886 | if(!thisCertInfo->basicConstraints.present) { | |
887 | if(isLeaf) { | |
888 | /* cool, use default; note that kTPx509Basic with | |
889 | * certGroup length of one may take this case */ | |
890 | cA = CSSM_FALSE; | |
891 | } | |
892 | else if(isRoot) { | |
893 | /* cool, use default */ | |
894 | cA = CSSM_TRUE; | |
895 | } | |
896 | else { | |
897 | switch(policy) { | |
898 | case kTPx509Basic: | |
899 | case kTP_SSL: | |
900 | /* | |
901 | * not present, not leaf, not root, kTPx509Basic | |
67c7378d | 902 | * ....RFC2459 says this can not be a CA |
bac41a7b | 903 | */ |
67c7378d | 904 | cA = CSSM_FALSE; |
bac41a7b A |
905 | break; |
906 | case kTPiSign: | |
907 | /* required for iSign in this position */ | |
908 | errorLog0("tp_policyVerify: no basicConstraints\n"); | |
29654253 A |
909 | policyFail = CSSM_TRUE; |
910 | thisTpCertInfo->addStatusCode( | |
911 | CSSMERR_APPLETP_NO_BASIC_CONSTRAINTS); | |
912 | break; | |
bac41a7b A |
913 | default: |
914 | /* not reached */ | |
915 | break; | |
916 | } | |
917 | } | |
918 | } | |
919 | else { | |
920 | /* basicConstraints present */ | |
921 | #if BASIC_CONSTRAINTS_MUST_BE_CRITICAL | |
922 | /* disabled for verisign compatibility */ | |
923 | if(!thisCertInfo->basicConstraints.critical) { | |
924 | /* per RFC 2459 */ | |
925 | errorLog0("tp_policyVerify: basicConstraints marked not critical\n"); | |
29654253 A |
926 | policyFail = CSSM_TRUE; |
927 | thisTpCertInfo->addStatusCode(CSSMERR_TP_VERIFY_ACTION_FAILED); | |
bac41a7b A |
928 | } |
929 | #endif /* BASIC_CONSTRAINTS_MUST_BE_CRITICAL */ | |
67c7378d A |
930 | |
931 | const CE_BasicConstraints *bcp = | |
932 | &thisCertInfo->basicConstraints.extnData->basicConstraints; | |
933 | ||
934 | cA = bcp->cA; | |
935 | ||
936 | /* Verify pathLenConstraint if present */ | |
937 | if(!isLeaf && // leaf, certDex=0, don't care | |
938 | cA && // p.l.c. only valid for CAs | |
939 | bcp->pathLenConstraintPresent) { // present? | |
940 | /* | |
941 | * pathLenConstraint=0 legal for certDex 1 only | |
942 | * pathLenConstraint=1 legal for certDex {1,2} | |
943 | * etc. | |
944 | */ | |
945 | if(certDex > (bcp->pathLenConstraint + 1)) { | |
946 | errorLog0("tp_policyVerify: pathLenConstraint exceeded\n"); | |
947 | policyFail = CSSM_TRUE; | |
948 | thisTpCertInfo->addStatusCode( | |
949 | CSSMERR_APPLETP_PATH_LEN_CONSTRAINT); | |
950 | } | |
951 | } | |
bac41a7b A |
952 | } |
953 | ||
954 | if(isLeaf) { | |
955 | /* special case to allow a chain of length 1, leaf and root | |
956 | * both true (kTPx509Basic, kTP_SSL only) */ | |
957 | if(cA && !isRoot) { | |
958 | errorLog0("tp_policyVerify: cA true for leaf\n"); | |
29654253 A |
959 | policyFail = CSSM_TRUE; |
960 | thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA); | |
bac41a7b A |
961 | } |
962 | } else if(!cA) { | |
963 | errorLog0("tp_policyVerify: cA false for non-leaf\n"); | |
29654253 A |
964 | policyFail = CSSM_TRUE; |
965 | thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_CA); | |
bac41a7b A |
966 | } |
967 | ||
968 | /* | |
969 | * Authority Key Identifier optional | |
970 | * iSign : only allowed in !root. | |
971 | * If present, must not be critical. | |
972 | * kTPx509Basic : | |
973 | * kTP_SSL : ignored (though used later for chain verification) | |
974 | */ | |
975 | if((policy == kTPiSign) && thisCertInfo->authorityId.present) { | |
976 | if(isRoot) { | |
977 | errorLog0("tp_policyVerify: authorityId in root\n"); | |
29654253 A |
978 | policyFail = CSSM_TRUE; |
979 | thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID); | |
bac41a7b A |
980 | } |
981 | if(thisCertInfo->authorityId.critical) { | |
982 | /* illegal per RFC 2459 */ | |
983 | errorLog0("tp_policyVerify: authorityId marked critical\n"); | |
29654253 A |
984 | policyFail = CSSM_TRUE; |
985 | thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_AUTHORITY_ID); | |
bac41a7b A |
986 | } |
987 | } | |
988 | ||
989 | /* | |
990 | * Subject Key Identifier optional | |
991 | * iSign : can't be critical. | |
992 | * kTPx509Basic, | |
993 | * kTP_SSL : ignored (though used later for chain verification) | |
994 | */ | |
995 | if(thisCertInfo->subjectId.present) { | |
996 | if((policy == kTPiSign) && thisCertInfo->subjectId.critical) { | |
997 | errorLog0("tp_policyVerify: subjectId marked critical\n"); | |
29654253 A |
998 | policyFail = CSSM_TRUE; |
999 | thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_SUBJECT_ID); | |
bac41a7b A |
1000 | } |
1001 | } | |
1002 | ||
1003 | /* | |
1004 | * Key Usage optional except as noted required | |
1005 | * iSign : required for non-root/non-leaf | |
1006 | * Leaf cert : if present, usage = digitalSignature | |
1007 | * Exception : if leaf, and keyUsage not present, | |
1008 | * netscape-cert-type must be present, with | |
1009 | * Object Signing bit set | |
1010 | * kTPx509Basic : non-leaf : usage = keyCertSign | |
1011 | * Leaf: don't care | |
1012 | */ | |
1013 | if(thisCertInfo->keyUsage.present) { | |
1014 | /* | |
1015 | * Leaf cert: usage = digitalSignature | |
1016 | * Others: usage = keyCertSign | |
1017 | * We only require that one bit to be set, we ignore others. | |
1018 | */ | |
1019 | if(isLeaf) { | |
1020 | if(policy == kTPiSign) { | |
1021 | expUsage = CE_KU_DigitalSignature; | |
1022 | } | |
1023 | else { | |
1024 | /* hack to accept whatever's there */ | |
1025 | expUsage = thisCertInfo->keyUsage.extnData->keyUsage; | |
1026 | } | |
1027 | } | |
1028 | else { | |
1029 | /* this is true for all policies */ | |
1030 | expUsage = CE_KU_KeyCertSign; | |
1031 | } | |
1032 | actUsage = thisCertInfo->keyUsage.extnData->keyUsage; | |
1033 | if(!(actUsage & expUsage)) { | |
1034 | errorLog2("tp_policyVerify: bad keyUsage (leaf %s; usage 0x%x)\n", | |
1035 | (certDex == 0) ? "TRUE" : "FALSE", actUsage); | |
29654253 A |
1036 | policyFail = CSSM_TRUE; |
1037 | thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE); | |
bac41a7b A |
1038 | } |
1039 | } | |
1040 | else if(policy == kTPiSign) { | |
1041 | /* | |
1042 | * iSign requires keyUsage present for non root OR | |
1043 | * netscape-cert-type/ObjectSigning for leaf | |
1044 | */ | |
1045 | if(isLeaf && thisCertInfo->netscapeCertType.present) { | |
1046 | CE_NetscapeCertType ct = | |
1047 | thisCertInfo->netscapeCertType.extnData->netscapeCertType; | |
1048 | ||
1049 | if(!(ct & CE_NCT_ObjSign)) { | |
1050 | errorLog0("tp_policyVerify: netscape-cert-type, !ObjectSign\n"); | |
29654253 A |
1051 | policyFail = CSSM_TRUE; |
1052 | thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE); | |
bac41a7b A |
1053 | } |
1054 | } | |
1055 | else if(!isRoot) { | |
1056 | errorLog0("tp_policyVerify: !isRoot, no keyUsage, !(leaf and netscapeCertType)\n"); | |
29654253 A |
1057 | policyFail = CSSM_TRUE; |
1058 | thisTpCertInfo->addStatusCode(CSSMERR_APPLETP_INVALID_KEY_USAGE); | |
bac41a7b A |
1059 | } |
1060 | } | |
1061 | } /* for certDex, checking presence of extensions */ | |
1062 | ||
1063 | /* | |
1064 | * Special case checking for leaf (end entity) cert | |
1065 | * | |
1066 | * iSign only: Extended key usage, optional for leaf, | |
1067 | * value CSSMOID_ExtendedUseCodeSigning | |
1068 | */ | |
1069 | if((policy == kTPiSign) && certInfo[0].extendKeyUsage.present) { | |
1070 | extendUsage = &certInfo[0].extendKeyUsage.extnData->extendedKeyUsage; | |
1071 | if(extendUsage->numPurposes != 1) { | |
1072 | errorLog1("tp_policyVerify: bad extendUsage->numPurposes (%d)\n", | |
1073 | (int)extendUsage->numPurposes); | |
29654253 A |
1074 | policyFail = CSSM_TRUE; |
1075 | (certGroup->certAtIndex(0))->addStatusCode( | |
1076 | CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE); | |
bac41a7b A |
1077 | } |
1078 | if(!tpCompareOids(extendUsage->purposes, | |
1079 | &CSSMOID_ExtendedUseCodeSigning)) { | |
1080 | errorLog0("tp_policyVerify: bad extendKeyUsage\n"); | |
29654253 A |
1081 | policyFail = CSSM_TRUE; |
1082 | (certGroup->certAtIndex(0))->addStatusCode( | |
1083 | CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE); | |
bac41a7b A |
1084 | } |
1085 | } | |
1086 | ||
1087 | /* | |
1088 | * Verify authorityId-->subjectId linkage. | |
1089 | * All optional - skip if needed fields not present. | |
1090 | * Also, always skip last (root) cert. | |
1091 | */ | |
1092 | for(certDex=0; certDex<(numCerts-1); certDex++) { | |
1093 | if(!certInfo[certDex].authorityId.present || | |
1094 | !certInfo[certDex+1].subjectId.present) { | |
1095 | continue; | |
1096 | } | |
1097 | authorityId = &certInfo[certDex].authorityId.extnData->authorityKeyID; | |
1098 | if(!authorityId->keyIdentifierPresent) { | |
1099 | /* we only know how to compare keyIdentifier */ | |
1100 | continue; | |
1101 | } | |
1102 | if(!tpCompareCssmData(&authorityId->keyIdentifier, | |
1103 | &certInfo[certDex+1].subjectId.extnData->subjectKeyID)) { | |
1104 | errorLog0("tp_policyVerify: bad key ID linkage\n"); | |
29654253 A |
1105 | policyFail = CSSM_TRUE; |
1106 | (certGroup->certAtIndex(certDex))->addStatusCode( | |
1107 | CSSMERR_APPLETP_INVALID_ID_LINKAGE); | |
bac41a7b A |
1108 | } |
1109 | } | |
1110 | ||
29654253 A |
1111 | /* |
1112 | * SSL: optionally verify common name. | |
1113 | * FIXME - should this be before or after the root cert test? How can | |
1114 | * we return both errors? | |
1115 | */ | |
1116 | if(policy == kTP_SSL) { | |
1117 | CSSM_RETURN cerr = tp_verifySslOpts(*certGroup, sslOpts); | |
1118 | if(cerr) { | |
1119 | policyFail = CSSM_TRUE; | |
bac41a7b A |
1120 | } |
1121 | } | |
29654253 A |
1122 | |
1123 | /* iSign, SSL: compare root against known root certs */ | |
1124 | /* FIXME - this goes away soon */ | |
1125 | #if TP_ROOT_CERT_ENABLE | |
1126 | if((outErr == CSSM_OK) && // skip if we have a gross error (other than policy failure) | |
1127 | (actionData != NULL) && | |
1128 | (actionData->ActionFlags & 0x80000000)) { // The secret "enable root cert check" flag | |
1129 | TPCertInfo *lastCert = certGroup->lastCert(); | |
1130 | if(policy == kTPiSign) { | |
1131 | bool brtn = tp_isIsignRootCert(clHand, lastCert); | |
1132 | if(!brtn) { | |
1133 | policyFail = CSSM_TRUE; | |
1134 | } | |
1135 | } | |
1136 | else if(verifiedToRoot && (policy == kTP_SSL)) { | |
1137 | /* note SSL doesn't require root here */ | |
1138 | bool brtn = tp_isSslRootCert(clHand, lastCert); | |
1139 | if(!brtn) { | |
1140 | outErr = CSSMERR_TP_INVALID_ANCHOR_CERT; | |
1141 | } | |
bac41a7b A |
1142 | } |
1143 | } | |
29654253 A |
1144 | #endif /* TP_ROOT_CERT_ENABLE */ |
1145 | if(policyFail && (outErr == CSSM_OK)) { | |
1146 | /* only error in this function was policy failure */ | |
1147 | outErr = CSSMERR_TP_VERIFY_ACTION_FAILED; | |
bac41a7b A |
1148 | } |
1149 | errOut: | |
1150 | /* free resources */ | |
1151 | for(certDex=0; certDex<numCerts; certDex++) { | |
1152 | thisCertInfo = &certInfo[certDex]; | |
1153 | iSignFreeCertInfo(clHand, thisCertInfo); | |
1154 | } | |
1155 | tpFree(alloc, certInfo); | |
1156 | return outErr; | |
1157 | } |