2 * Copyright (c) 2002-2009 Apple Inc. All Rights Reserved.
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
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.
20 * TPDatabase.cpp - TP's DL/DB access functions.
22 * Created 10/9/2002 by Doug Mitchell.
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"
41 * Given a DL/DB, look up cert by subject name. Subsequent
42 * certs can be found using the returned result handle.
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
51 CSSM_SELECTION_PREDICATE predicate
;
52 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
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;
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?
74 CSSM_DL_DataGetFirst(dlDb
,
77 NULL
, // don't fetch attributes
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.
93 TPCertInfo
*tpDbFindIssuerCert(
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
102 StLock
<Mutex
> _(SecTrustKeychainsGetMutex());
105 CSSM_HANDLE resultHand
;
107 CSSM_DL_DB_HANDLE dlDb
;
108 CSSM_DB_UNIQUE_RECORD_PTR record
;
109 TPCertInfo
*issuerCert
= NULL
;
111 TPCertInfo
*expiredIssuer
= NULL
;
113 partialIssuerKey
= false;
117 for(dbDex
=0; dbDex
<dbList
->NumHandles
; dbDex
++) {
118 dlDb
= dbList
->DLDBHandle
[dbDex
];
122 record
= tpCertLookup(dlDb
,
123 subjectItem
->issuerName(),
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)...
134 assert(cert
.Data
!= NULL
);
135 tpDbDebug("tpDbFindIssuerCert: found cert record %p", record
);
137 CSSM_RETURN crtn
= CSSM_OK
;
139 issuerCert
= new TPCertInfo(clHand
, cspHand
, &cert
, TIC_CopyData
, verifyTime
);
142 crtn
= CSSMERR_TP_INVALID_CERTIFICATE
;
145 /* we're done with raw cert data */
146 tpFreePluginMemory(dlDb
.DLHandle
, cert
.Data
);
150 /* Does it verify the subject cert? */
151 if(crtn
== CSSM_OK
) {
152 crtn
= subjectItem
->verifyWithIssuer(issuerCert
);
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.
159 if((crtn
== CSSM_OK
) && (expiredIssuer
== NULL
)) {
160 if(issuerCert
->isExpired() || issuerCert
->isNotValidYet()) {
162 * Exact value not important here, this just uniquely identifies
163 * this situation in the switch below.
165 tpDbDebug("tpDbFindIssuerCert: holding expired cert (1)");
166 crtn
= CSSM_CERT_STATUS_EXPIRED
;
167 expiredIssuer
= issuerCert
;
168 expiredIssuer
->dlDbHandle(dlDb
);
169 expiredIssuer
->uniqueRecord(record
);
175 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:
176 partialIssuerKey
= true;
180 if(crtn
!= CSSM_CERT_STATUS_EXPIRED
) {
182 CSSM_DL_FreeUniqueRecord(dlDb
, record
);
186 * Continue searching this DB. Break on finding the holy
187 * grail or no more records found.
193 CSSM_RETURN crtn
= CSSM_DL_DataGetNext(dlDb
,
199 /* no more, done with this DB */
200 assert(cert
.Data
== NULL
);
203 assert(cert
.Data
!= NULL
);
204 tpDbDebug("tpDbFindIssuerCert: found cert record %p", record
);
206 /* found one - does it verify subject? */
208 issuerCert
= new TPCertInfo(clHand
, cspHand
, &cert
, TIC_CopyData
,
212 crtn
= CSSMERR_TP_INVALID_CERTIFICATE
;
214 /* we're done with raw cert data */
215 tpFreePluginMemory(dlDb
.DLHandle
, cert
.Data
);
219 if(crtn
== CSSM_OK
) {
220 crtn
= subjectItem
->verifyWithIssuer(issuerCert
);
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
);
239 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
:
240 partialIssuerKey
= true;
250 if(crtn
!= CSSM_CERT_STATUS_EXPIRED
) {
252 CSSM_DL_FreeUniqueRecord(dlDb
, record
);
255 } /* searching subsequent records */
256 } /* switch verify */
259 /* NULL record --> end of search --> DB auto-aborted */
260 crtn
= CSSM_DL_DataAbortQuery(dlDb
, resultHand
);
261 assert(crtn
== CSSM_OK
);
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
;
276 } /* tpCertLookup, i.e., CSSM_DL_DataGetFirst, succeeded */
278 assert(cert
.Data
== NULL
);
279 assert(resultHand
== 0);
281 } /* main loop searching dbList */
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
;
289 /* issuer not found */
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.
297 #define SEARCH_BY_DATE 1
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
307 CSSM_SELECTION_PREDICATE pred
[3];
308 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
309 char timeStr
[CSSM_TIME_STRLEN
+ 1];
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;
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
);
328 tpErrorLog("tpCrlLookup: Invalid VerifyTime string\n");
334 StLock
<Mutex
> _(tpTimeLock());
335 timeAtNowPlus(0, TIME_CSSM
, timeStr
);
338 timeData
.Data
= (uint8
*)timeStr
;
339 timeData
.Length
= CSSM_TIME_STRLEN
;
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;
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;
357 query
.RecordType
= CSSM_DL_DB_RECORD_X509_CRL
;
358 query
.Conjunctive
= CSSM_DB_AND
;
360 query
.NumSelectionPredicates
= 3;
362 query
.NumSelectionPredicates
= 1;
364 query
.SelectionPredicate
= pred
;
365 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
366 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
367 query
.QueryFlags
= 0; // FIXME - used?
369 CSSM_DL_DataGetFirst(dlDb
,
372 NULL
, // don't fetch attributes
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.
384 TPCrlInfo
*tpDbFindIssuerCrl(
385 TPVerifyContext
&vfyCtx
,
386 const CSSM_DATA
&issuer
,
389 StLock
<Mutex
> _(SecTrustKeychainsGetMutex());
392 CSSM_HANDLE resultHand
;
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
;
403 for(dbDex
=0; dbDex
<dbList
->NumHandles
; dbDex
++) {
404 dlDb
= dbList
->DLDBHandle
[dbDex
];
407 record
= tpCrlLookup(dlDb
,
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)...
420 assert(crl
.Data
!= NULL
);
421 issuerCrl
= new TPCrlInfo(vfyCtx
.clHand
,
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
);
433 /* and we're done with the record */
434 CSSM_DL_FreeUniqueRecord(dlDb
, record
);
436 /* Does it verify with specified context? */
437 crtn
= issuerCrl
->verifyWithContextNow(vfyCtx
, &forCert
);
444 * Verify fail. Continue searching this DB. Break on
445 * finding the holy grail or no more records found.
450 crtn
= CSSM_DL_DataGetNext(dlDb
,
456 /* no more, done with this DB */
457 assert(crl
.Data
== NULL
);
460 assert(crl
.Data
!= NULL
);
462 /* found one - is it any good? */
463 issuerCrl
= new TPCrlInfo(vfyCtx
.clHand
,
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
);
475 CSSM_DL_FreeUniqueRecord(dlDb
, record
);
477 crtn
= issuerCrl
->verifyWithContextNow(vfyCtx
, &forCert
);
478 if(crtn
== CSSM_OK
) {
484 } /* searching subsequent records */
488 if(issuerCrl
!= NULL
) {
489 /* successful return */
490 CSSM_DL_DataAbortQuery(dlDb
, resultHand
);
491 tpDebug("tpDbFindIssuerCrl: found CRL record %p", record
);
494 } /* tpCrlLookup, i.e., CSSM_DL_DataGetFirst, succeeded */
496 assert(crl
.Data
== NULL
);
498 /* in any case, abort the query for this db */
499 CSSM_DL_DataAbortQuery(dlDb
, resultHand
);
501 } /* main loop searching dbList */
503 /* issuer not found */