]> git.saurik.com Git - apple/security.git/blob - libsecurity_apple_x509_tp/lib/TPDatabase.cpp
Security-55471.14.tar.gz
[apple/security.git] / libsecurity_apple_x509_tp / lib / TPDatabase.cpp
1 /*
2 * Copyright (c) 2002-2009 Apple 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 * TPDatabase.cpp - TP's DL/DB access functions.
21 *
22 * Created 10/9/2002 by Doug Mitchell.
23 */
24
25 #include <Security/cssmtype.h>
26 #include <Security/cssmapi.h>
27 #include <security_cdsa_utilities/Schema.h> /* private API */
28 #include <security_keychain/TrustKeychains.h> /* private SecTrustKeychainsGetMutex() */
29 #include <Security/SecCertificatePriv.h> /* private SecInferLabelFromX509Name() */
30 #include <Security/oidscert.h>
31 #include "TPDatabase.h"
32 #include "tpdebugging.h"
33 #include "certGroupUtils.h"
34 #include "TPCertInfo.h"
35 #include "TPCrlInfo.h"
36 #include "tpCrlVerify.h"
37 #include "tpTime.h"
38
39
40 /*
41 * Given a DL/DB, look up cert by subject name. Subsequent
42 * certs can be found using the returned result handle.
43 */
44 static CSSM_DB_UNIQUE_RECORD_PTR tpCertLookup(
45 CSSM_DL_DB_HANDLE dlDb,
46 const CSSM_DATA *subjectName, // DER-encoded
47 CSSM_HANDLE_PTR resultHand, // RETURNED
48 CSSM_DATA_PTR cert) // RETURNED
49 {
50 CSSM_QUERY query;
51 CSSM_SELECTION_PREDICATE predicate;
52 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
53
54 cert->Data = NULL;
55 cert->Length = 0;
56
57 /* SWAG until cert schema nailed down */
58 predicate.DbOperator = CSSM_DB_EQUAL;
59 predicate.Attribute.Info.AttributeNameFormat =
60 CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
61 predicate.Attribute.Info.Label.AttributeName = (char*) "Subject";
62 predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
63 predicate.Attribute.Value = const_cast<CSSM_DATA_PTR>(subjectName);
64 predicate.Attribute.NumberOfValues = 1;
65
66 query.RecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
67 query.Conjunctive = CSSM_DB_NONE;
68 query.NumSelectionPredicates = 1;
69 query.SelectionPredicate = &predicate;
70 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful?
71 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful?
72 query.QueryFlags = 0; // FIXME - used?
73
74 CSSM_DL_DataGetFirst(dlDb,
75 &query,
76 resultHand,
77 NULL, // don't fetch attributes
78 cert,
79 &record);
80
81 return record;
82 }
83
84 /*
85 * Search a list of DBs for a cert which verifies specified subject item.
86 * Just a boolean return - we found it, or not. If we did, we return
87 * TPCertInfo associated with the raw cert.
88 * A true partialIssuerKey on return indicates that caller must deal
89 * with partial public key processing later.
90 * If verifyCurrent is true, we will not return a cert which is not
91 * temporally valid; else we may well do so.
92 */
93 TPCertInfo *tpDbFindIssuerCert(
94 Allocator &alloc,
95 CSSM_CL_HANDLE clHand,
96 CSSM_CSP_HANDLE cspHand,
97 const TPClItemInfo *subjectItem,
98 const CSSM_DL_DB_LIST *dbList,
99 const char *verifyTime, // may be NULL
100 bool &partialIssuerKey) // RETURNED
101 {
102 StLock<Mutex> _(SecTrustKeychainsGetMutex());
103
104 uint32 dbDex;
105 CSSM_HANDLE resultHand;
106 CSSM_DATA cert;
107 CSSM_DL_DB_HANDLE dlDb;
108 CSSM_DB_UNIQUE_RECORD_PTR record;
109 TPCertInfo *issuerCert = NULL;
110 bool foundIt;
111 TPCertInfo *expiredIssuer = NULL;
112
113 partialIssuerKey = false;
114 if(dbList == NULL) {
115 return NULL;
116 }
117 for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) {
118 dlDb = dbList->DLDBHandle[dbDex];
119 cert.Data = NULL;
120 cert.Length = 0;
121 resultHand = 0;
122 record = tpCertLookup(dlDb,
123 subjectItem->issuerName(),
124 &resultHand,
125 &cert);
126 /* remember we have to:
127 * -- abort this query regardless, and
128 * -- free the CSSM_DATA cert regardless, and
129 * -- free the unique record if we don't use it
130 * (by placing it in issuerCert)...
131 */
132 if(record != NULL) {
133 /* Found one */
134 assert(cert.Data != NULL);
135 tpDbDebug("tpDbFindIssuerCert: found cert record %p", record);
136 issuerCert = NULL;
137 CSSM_RETURN crtn = CSSM_OK;
138 try {
139 issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData, verifyTime);
140 }
141 catch(...) {
142 crtn = CSSMERR_TP_INVALID_CERTIFICATE;
143 }
144
145 /* we're done with raw cert data */
146 tpFreePluginMemory(dlDb.DLHandle, cert.Data);
147 cert.Data = NULL;
148 cert.Length = 0;
149
150 /* Does it verify the subject cert? */
151 if(crtn == CSSM_OK) {
152 crtn = subjectItem->verifyWithIssuer(issuerCert);
153 }
154
155 /*
156 * Handle temporal invalidity - if so and this is the first one
157 * we've seen, hold on to it while we search for better one.
158 */
159 if((crtn == CSSM_OK) && (expiredIssuer == NULL)) {
160 if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
161 /*
162 * Exact value not important here, this just uniquely identifies
163 * this situation in the switch below.
164 */
165 tpDbDebug("tpDbFindIssuerCert: holding expired cert (1)");
166 crtn = CSSM_CERT_STATUS_EXPIRED;
167 expiredIssuer = issuerCert;
168 expiredIssuer->dlDbHandle(dlDb);
169 expiredIssuer->uniqueRecord(record);
170 }
171 }
172 switch(crtn) {
173 case CSSM_OK:
174 break;
175 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
176 partialIssuerKey = true;
177 break;
178 default:
179 issuerCert = NULL;
180 if(crtn != CSSM_CERT_STATUS_EXPIRED) {
181 delete issuerCert;
182 CSSM_DL_FreeUniqueRecord(dlDb, record);
183 }
184
185 /*
186 * Continue searching this DB. Break on finding the holy
187 * grail or no more records found.
188 */
189 for(;;) {
190 cert.Data = NULL;
191 cert.Length = 0;
192 record = NULL;
193 CSSM_RETURN crtn = CSSM_DL_DataGetNext(dlDb,
194 resultHand,
195 NULL, // no attrs
196 &cert,
197 &record);
198 if(crtn) {
199 /* no more, done with this DB */
200 assert(cert.Data == NULL);
201 break;
202 }
203 assert(cert.Data != NULL);
204 tpDbDebug("tpDbFindIssuerCert: found cert record %p", record);
205
206 /* found one - does it verify subject? */
207 try {
208 issuerCert = new TPCertInfo(clHand, cspHand, &cert, TIC_CopyData,
209 verifyTime);
210 }
211 catch(...) {
212 crtn = CSSMERR_TP_INVALID_CERTIFICATE;
213 }
214 /* we're done with raw cert data */
215 tpFreePluginMemory(dlDb.DLHandle, cert.Data);
216 cert.Data = NULL;
217 cert.Length = 0;
218
219 if(crtn == CSSM_OK) {
220 crtn = subjectItem->verifyWithIssuer(issuerCert);
221 }
222
223 /* temporal validity check, again */
224 if((crtn == CSSM_OK) && (expiredIssuer == NULL)) {
225 if(issuerCert->isExpired() || issuerCert->isNotValidYet()) {
226 tpDbDebug("tpDbFindIssuerCert: holding expired cert (2)");
227 crtn = CSSM_CERT_STATUS_EXPIRED;
228 expiredIssuer = issuerCert;
229 expiredIssuer->dlDbHandle(dlDb);
230 expiredIssuer->uniqueRecord(record);
231 }
232 }
233
234 foundIt = false;
235 switch(crtn) {
236 case CSSM_OK:
237 foundIt = true;
238 break;
239 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
240 partialIssuerKey = true;
241 foundIt = true;
242 break;
243 default:
244 break;
245 }
246 if(foundIt) {
247 /* yes! */
248 break;
249 }
250 if(crtn != CSSM_CERT_STATUS_EXPIRED) {
251 delete issuerCert;
252 CSSM_DL_FreeUniqueRecord(dlDb, record);
253 }
254 issuerCert = NULL;
255 } /* searching subsequent records */
256 } /* switch verify */
257
258 if(record != NULL) {
259 /* NULL record --> end of search --> DB auto-aborted */
260 crtn = CSSM_DL_DataAbortQuery(dlDb, resultHand);
261 assert(crtn == CSSM_OK);
262 }
263 if(issuerCert != NULL) {
264 /* successful return */
265 tpDbDebug("tpDbFindIssuer: returning record %p", record);
266 issuerCert->dlDbHandle(dlDb);
267 issuerCert->uniqueRecord(record);
268 if(expiredIssuer != NULL) {
269 /* We found a replacement */
270 tpDbDebug("tpDbFindIssuer: discarding expired cert");
271 expiredIssuer->freeUniqueRecord();
272 delete expiredIssuer;
273 }
274 return issuerCert;
275 }
276 } /* tpCertLookup, i.e., CSSM_DL_DataGetFirst, succeeded */
277 else {
278 assert(cert.Data == NULL);
279 assert(resultHand == 0);
280 }
281 } /* main loop searching dbList */
282
283 if(expiredIssuer != NULL) {
284 /* OK, we'll take this one */
285 tpDbDebug("tpDbFindIssuer: taking expired cert after all, record %p",
286 expiredIssuer->uniqueRecord());
287 return expiredIssuer;
288 }
289 /* issuer not found */
290 return NULL;
291 }
292
293 /*
294 * Given a DL/DB, look up CRL by issuer name and validity time.
295 * Subsequent CRLs can be found using the returned result handle.
296 */
297 #define SEARCH_BY_DATE 1
298
299 static CSSM_DB_UNIQUE_RECORD_PTR tpCrlLookup(
300 CSSM_DL_DB_HANDLE dlDb,
301 const CSSM_DATA *issuerName, // DER-encoded
302 CSSM_TIMESTRING verifyTime, // may be NULL, implies "now"
303 CSSM_HANDLE_PTR resultHand, // RETURNED
304 CSSM_DATA_PTR crl) // RETURNED
305 {
306 CSSM_QUERY query;
307 CSSM_SELECTION_PREDICATE pred[3];
308 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
309 char timeStr[CSSM_TIME_STRLEN + 1];
310
311 crl->Data = NULL;
312 crl->Length = 0;
313
314 /* Three predicates...first, the issuer name */
315 pred[0].DbOperator = CSSM_DB_EQUAL;
316 pred[0].Attribute.Info.AttributeNameFormat =
317 CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
318 pred[0].Attribute.Info.Label.AttributeName = (char*) "Issuer";
319 pred[0].Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
320 pred[0].Attribute.Value = const_cast<CSSM_DATA_PTR>(issuerName);
321 pred[0].Attribute.NumberOfValues = 1;
322
323 /* now before/after. Cook up an appropriate time string. */
324 if(verifyTime != NULL) {
325 /* Caller spec'd tolerate any format */
326 int rtn = tpTimeToCssmTimestring(verifyTime, (unsigned)strlen(verifyTime), timeStr);
327 if(rtn) {
328 tpErrorLog("tpCrlLookup: Invalid VerifyTime string\n");
329 return NULL;
330 }
331 }
332 else {
333 /* right now */
334 StLock<Mutex> _(tpTimeLock());
335 timeAtNowPlus(0, TIME_CSSM, timeStr);
336 }
337 CSSM_DATA timeData;
338 timeData.Data = (uint8 *)timeStr;
339 timeData.Length = CSSM_TIME_STRLEN;
340
341 #if SEARCH_BY_DATE
342 pred[1].DbOperator = CSSM_DB_LESS_THAN;
343 pred[1].Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
344 pred[1].Attribute.Info.Label.AttributeName = (char*) "NextUpdate";
345 pred[1].Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
346 pred[1].Attribute.Value = &timeData;
347 pred[1].Attribute.NumberOfValues = 1;
348
349 pred[2].DbOperator = CSSM_DB_GREATER_THAN;
350 pred[2].Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
351 pred[2].Attribute.Info.Label.AttributeName = (char*) "ThisUpdate";
352 pred[2].Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
353 pred[2].Attribute.Value = &timeData;
354 pred[2].Attribute.NumberOfValues = 1;
355 #endif
356
357 query.RecordType = CSSM_DL_DB_RECORD_X509_CRL;
358 query.Conjunctive = CSSM_DB_AND;
359 #if SEARCH_BY_DATE
360 query.NumSelectionPredicates = 3;
361 #else
362 query.NumSelectionPredicates = 1;
363 #endif
364 query.SelectionPredicate = pred;
365 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful?
366 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful?
367 query.QueryFlags = 0; // FIXME - used?
368
369 CSSM_DL_DataGetFirst(dlDb,
370 &query,
371 resultHand,
372 NULL, // don't fetch attributes
373 crl,
374 &record);
375 return record;
376 }
377
378 /*
379 * Search a list of DBs for a CRL from the specified issuer and (optional)
380 * TPVerifyContext.verifyTime.
381 * Just a boolean return - we found it, or not. If we did, we return a
382 * TPCrlInfo which has been verified with the specified TPVerifyContext.
383 */
384 TPCrlInfo *tpDbFindIssuerCrl(
385 TPVerifyContext &vfyCtx,
386 const CSSM_DATA &issuer,
387 TPCertInfo &forCert)
388 {
389 StLock<Mutex> _(SecTrustKeychainsGetMutex());
390
391 uint32 dbDex;
392 CSSM_HANDLE resultHand;
393 CSSM_DATA crl;
394 CSSM_DL_DB_HANDLE dlDb;
395 CSSM_DB_UNIQUE_RECORD_PTR record;
396 TPCrlInfo *issuerCrl = NULL;
397 CSSM_DL_DB_LIST_PTR dbList = vfyCtx.dbList;
398 CSSM_RETURN crtn;
399
400 if(dbList == NULL) {
401 return NULL;
402 }
403 for(dbDex=0; dbDex<dbList->NumHandles; dbDex++) {
404 dlDb = dbList->DLDBHandle[dbDex];
405 crl.Data = NULL;
406 crl.Length = 0;
407 record = tpCrlLookup(dlDb,
408 &issuer,
409 vfyCtx.verifyTime,
410 &resultHand,
411 &crl);
412 /* remember we have to:
413 * -- abort this query regardless, and
414 * -- free the CSSM_DATA crl regardless, and
415 * -- free the unique record if we don't use it
416 * (by placing it in issuerCert)...
417 */
418 if(record != NULL) {
419 /* Found one */
420 assert(crl.Data != NULL);
421 issuerCrl = new TPCrlInfo(vfyCtx.clHand,
422 vfyCtx.cspHand,
423 &crl,
424 TIC_CopyData,
425 vfyCtx.verifyTime);
426 /* we're done with raw CRL data */
427 /* FIXME this assumes that vfyCtx.alloc is the same as the
428 * allocator associated with DlDB...OK? */
429 tpFreeCssmData(vfyCtx.alloc, &crl, CSSM_FALSE);
430 crl.Data = NULL;
431 crl.Length = 0;
432
433 /* and we're done with the record */
434 CSSM_DL_FreeUniqueRecord(dlDb, record);
435
436 /* Does it verify with specified context? */
437 crtn = issuerCrl->verifyWithContextNow(vfyCtx, &forCert);
438 if(crtn) {
439
440 delete issuerCrl;
441 issuerCrl = NULL;
442
443 /*
444 * Verify fail. Continue searching this DB. Break on
445 * finding the holy grail or no more records found.
446 */
447 for(;;) {
448 crl.Data = NULL;
449 crl.Length = 0;
450 crtn = CSSM_DL_DataGetNext(dlDb,
451 resultHand,
452 NULL, // no attrs
453 &crl,
454 &record);
455 if(crtn) {
456 /* no more, done with this DB */
457 assert(crl.Data == NULL);
458 break;
459 }
460 assert(crl.Data != NULL);
461
462 /* found one - is it any good? */
463 issuerCrl = new TPCrlInfo(vfyCtx.clHand,
464 vfyCtx.cspHand,
465 &crl,
466 TIC_CopyData,
467 vfyCtx.verifyTime);
468 /* we're done with raw CRL data */
469 /* FIXME this assumes that vfyCtx.alloc is the same as the
470 * allocator associated with DlDB...OK? */
471 tpFreeCssmData(vfyCtx.alloc, &crl, CSSM_FALSE);
472 crl.Data = NULL;
473 crl.Length = 0;
474
475 CSSM_DL_FreeUniqueRecord(dlDb, record);
476
477 crtn = issuerCrl->verifyWithContextNow(vfyCtx, &forCert);
478 if(crtn == CSSM_OK) {
479 /* yes! */
480 break;
481 }
482 delete issuerCrl;
483 issuerCrl = NULL;
484 } /* searching subsequent records */
485 } /* verify fail */
486 /* else success! */
487
488 if(issuerCrl != NULL) {
489 /* successful return */
490 CSSM_DL_DataAbortQuery(dlDb, resultHand);
491 tpDebug("tpDbFindIssuerCrl: found CRL record %p", record);
492 return issuerCrl;
493 }
494 } /* tpCrlLookup, i.e., CSSM_DL_DataGetFirst, succeeded */
495 else {
496 assert(crl.Data == NULL);
497 }
498 /* in any case, abort the query for this db */
499 CSSM_DL_DataAbortQuery(dlDb, resultHand);
500
501 } /* main loop searching dbList */
502
503 /* issuer not found */
504 return NULL;
505 }
506