2 * Copyright (c) 2002-2003,2011-2012,2014 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.
7 * Please obtain a copy of the License at http://www.apple.com/publicsource
8 * and read it before using this file.
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
12 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
13 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
15 * Please see the License for the specific language governing rights
16 * and limitations under the License.
22 Description: CDSA DB access utilities
27 #include "cuCdsaUtils.h"
28 #include "cuTimeStr.h"
29 #include "cuDbUtils.h"
30 #include "cuPrintCert.h"
33 #include <Security/SecCertificate.h>
34 #include <Security/SecCertificatePriv.h> /* private SecInferLabelFromX509Name() */
35 #include <Security/cssmapple.h> /* for cssmPerror() */
36 #include <Security/oidscert.h>
37 #include <Security/oidscrl.h>
38 #include <Security/oidsattr.h>
40 #include <security_cdsa_utilities/Schema.h> /* private API */
43 #define dprintf(args...) printf(args)
45 #define dprintf(args...)
49 * Add a certificate to an open DLDB.
51 CSSM_RETURN
cuAddCertToDb(
52 CSSM_DL_DB_HANDLE dlDbHand
,
53 const CSSM_DATA
*cert
,
54 CSSM_CERT_TYPE certType
,
55 CSSM_CERT_ENCODING certEncoding
,
56 const char *printName
, // C string
57 const CSSM_DATA
*publicKeyHash
)
59 CSSM_DB_ATTRIBUTE_DATA attrs
[6];
60 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
61 CSSM_DB_ATTRIBUTE_DATA_PTR attr
= &attrs
[0];
62 CSSM_DATA certTypeData
;
63 CSSM_DATA certEncData
;
64 CSSM_DATA printNameData
;
66 CSSM_DB_UNIQUE_RECORD_PTR recordPtr
;
68 /* issuer and serial number required, fake 'em */
69 CSSM_DATA issuer
= {6, (uint8
*)"issuer"};
70 CSSM_DATA serial
= {6, (uint8
*)"serial"};
72 /* we spec six attributes, skipping alias */
73 certTypeData
.Data
= (uint8
*)&certType
;
74 certTypeData
.Length
= sizeof(CSSM_CERT_TYPE
);
75 certEncData
.Data
= (uint8
*)&certEncoding
;
76 certEncData
.Length
= sizeof(CSSM_CERT_ENCODING
);
77 printNameData
.Data
= (uint8
*)printName
;
78 printNameData
.Length
= strlen(printName
) + 1;
80 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
81 attr
->Info
.Label
.AttributeName
= (char*) "CertType";
82 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
83 attr
->NumberOfValues
= 1;
84 attr
->Value
= &certTypeData
;
87 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
88 attr
->Info
.Label
.AttributeName
= (char*) "CertEncoding";
89 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
90 attr
->NumberOfValues
= 1;
91 attr
->Value
= &certEncData
;
94 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
95 attr
->Info
.Label
.AttributeName
= (char*) "PrintName";
96 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
97 attr
->NumberOfValues
= 1;
98 attr
->Value
= &printNameData
;
101 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
102 attr
->Info
.Label
.AttributeName
= (char*) "PublicKeyHash";
103 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
104 attr
->NumberOfValues
= 1;
105 attr
->Value
= (CSSM_DATA_PTR
)publicKeyHash
;
108 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
109 attr
->Info
.Label
.AttributeName
= (char*) "Issuer";
110 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
111 attr
->NumberOfValues
= 1;
112 attr
->Value
= &issuer
;
115 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
116 attr
->Info
.Label
.AttributeName
= (char*) "SerialNumber";
117 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
118 attr
->NumberOfValues
= 1;
119 attr
->Value
= &serial
;
121 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_X509_CERTIFICATE
;
122 recordAttrs
.SemanticInformation
= 0;
123 recordAttrs
.NumberOfAttributes
= 6;
124 recordAttrs
.AttributeData
= attrs
;
126 crtn
= CSSM_DL_DataInsert(dlDbHand
,
127 CSSM_DL_DB_RECORD_X509_CERTIFICATE
,
132 cuPrintError("CSSM_DL_DataInsert", crtn
);
135 CSSM_DL_FreeUniqueRecord(dlDbHand
, recordPtr
);
140 static CSSM_RETURN
cuAddCrlSchema(
141 CSSM_DL_DB_HANDLE dlDbHand
);
143 static void cuInferCrlLabel(
144 const CSSM_X509_NAME
*x509Name
,
145 CSSM_DATA
*label
) // not mallocd; contents are from the x509Name
147 /* use private API for common "infer label" logic */
148 const CSSM_DATA
*printValue
= SecInferLabelFromX509Name(x509Name
);
149 if(printValue
== NULL
) {
151 label
->Data
= (uint8
*)"X509 CRL";
155 *label
= *printValue
;
160 * Search extensions for specified OID, assumed to have underlying
161 * value type of uint32; returns the value and true if found.
163 static bool cuSearchNumericExtension(
164 const CSSM_X509_EXTENSIONS
*extens
,
168 for(uint32 dex
=0; dex
<extens
->numberOfExtensions
; dex
++) {
169 const CSSM_X509_EXTENSION
*exten
= &extens
->extensions
[dex
];
170 if(!cuCompareOid(&exten
->extnId
, oid
)) {
173 if(exten
->format
!= CSSM_X509_DATAFORMAT_PARSED
) {
174 dprintf("***Malformed extension\n");
177 *val
= *((uint32
*)exten
->value
.parsedValue
);
184 * Add a CRL to an existing DL/DB.
186 #define MAX_CRL_ATTRS 9
188 CSSM_RETURN
cuAddCrlToDb(
189 CSSM_DL_DB_HANDLE dlDbHand
,
190 CSSM_CL_HANDLE clHand
,
191 const CSSM_DATA
*crl
,
192 const CSSM_DATA
*URI
)
194 CSSM_DB_ATTRIBUTE_DATA attrs
[MAX_CRL_ATTRS
];
195 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
196 CSSM_DB_ATTRIBUTE_DATA_PTR attr
= &attrs
[0];
197 CSSM_DATA crlTypeData
;
198 CSSM_DATA crlEncData
;
199 CSSM_DATA printNameData
;
201 CSSM_DB_UNIQUE_RECORD_PTR recordPtr
;
202 CSSM_DATA_PTR issuer
= NULL
; // mallocd by CL
203 CSSM_DATA_PTR crlValue
= NULL
; // ditto
206 CSSM_CRL_ENCODING crlEnc
= CSSM_CRL_ENCODING_DER
;
207 const CSSM_X509_SIGNED_CRL
*signedCrl
;
208 const CSSM_X509_TBS_CERTLIST
*tbsCrl
;
209 CSSM_CRL_TYPE crlType
;
210 CSSM_DATA thisUpdateData
= {0, NULL
};
211 CSSM_DATA nextUpdateData
= {0, NULL
};
212 char *thisUpdate
= NULL
;
213 char *nextUpdate
= NULL
;
216 uint32 deltaCrlNumber
;
217 CSSM_DATA crlNumberData
;
218 CSSM_DATA deltaCrlNumberData
;
219 bool crlNumberPresent
= false;
220 bool deltaCrlPresent
= false;
223 /* get normalized issuer name as Issuer attr */
224 crtn
= CSSM_CL_CrlGetFirstFieldValue(clHand
,
226 &CSSMOID_X509V1IssuerName
,
231 cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn
);
234 CSSM_CL_CrlAbortQuery(clHand
, result
);
236 /* get parsed CRL from the CL */
237 crtn
= CSSM_CL_CrlGetFirstFieldValue(clHand
,
239 &CSSMOID_X509V2CRLSignedCrlCStruct
,
244 cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn
);
247 CSSM_CL_CrlAbortQuery(clHand
, result
);
248 if(crlValue
== NULL
) {
249 dprintf("***CSSM_CL_CrlGetFirstFieldValue: value error (1)\n");
250 crtn
= CSSMERR_CL_INVALID_CRL_POINTER
;
253 if((crlValue
->Data
== NULL
) ||
254 (crlValue
->Length
!= sizeof(CSSM_X509_SIGNED_CRL
))) {
255 dprintf("***CSSM_CL_CrlGetFirstFieldValue: value error (2)\n");
256 crtn
= CSSMERR_CL_INVALID_CRL_POINTER
;
259 signedCrl
= (const CSSM_X509_SIGNED_CRL
*)crlValue
->Data
;
260 tbsCrl
= &signedCrl
->tbsCertList
;
262 /* CrlType inferred from version */
263 if(tbsCrl
->version
.Length
== 0) {
264 /* should never happen... */
265 crlType
= CSSM_CRL_TYPE_X_509v1
;
268 uint8 vers
= tbsCrl
->version
.Data
[tbsCrl
->version
.Length
- 1];
271 crlType
= CSSM_CRL_TYPE_X_509v1
;
274 crlType
= CSSM_CRL_TYPE_X_509v2
;
277 dprintf("***Unknown version in CRL (%u)\n", vers
);
278 crlType
= CSSM_CRL_TYPE_X_509v1
;
282 crlTypeData
.Data
= (uint8
*)&crlType
;
283 crlTypeData
.Length
= sizeof(CSSM_CRL_TYPE
);
284 /* encoding more-or-less assumed here */
285 crlEncData
.Data
= (uint8
*)&crlEnc
;
286 crlEncData
.Length
= sizeof(CSSM_CRL_ENCODING
);
288 /* printName inferred from issuer */
289 cuInferCrlLabel(&tbsCrl
->issuer
, &printNameData
);
291 /* cook up CSSM_TIMESTRING versions of this/next update */
292 thisUpdate
= cuX509TimeToCssmTimestring(&tbsCrl
->thisUpdate
, &timeLen
);
293 if(thisUpdate
== NULL
) {
294 dprintf("***Badly formatted thisUpdate\n");
297 thisUpdateData
.Data
= (uint8
*)thisUpdate
;
298 thisUpdateData
.Length
= timeLen
;
300 if(tbsCrl
->nextUpdate
.time
.Data
!= NULL
) {
301 nextUpdate
= cuX509TimeToCssmTimestring(&tbsCrl
->nextUpdate
, &timeLen
);
302 if(nextUpdate
== NULL
) {
303 dprintf("***Badly formatted nextUpdate\n");
306 nextUpdateData
.Data
= (uint8
*)nextUpdate
;
307 nextUpdateData
.Length
= timeLen
;
312 * NextUpdate not present; fake it by using "virtual end of time"
314 CSSM_X509_TIME tempTime
= { 0, // timeType, not used
315 { strlen(CSSM_APPLE_CRL_END_OF_TIME
),
316 (uint8
*)CSSM_APPLE_CRL_END_OF_TIME
} };
317 nextUpdate
= cuX509TimeToCssmTimestring(&tempTime
, &timeLen
);
318 nextUpdateData
.Data
= (uint8
*)nextUpdate
;
319 nextUpdateData
.Length
= CSSM_TIME_STRLEN
;
322 /* optional CrlNumber and DeltaCrlNumber */
323 if(cuSearchNumericExtension(&tbsCrl
->extensions
,
326 crlNumberData
.Data
= (uint8
*)&crlNumber
;
327 crlNumberData
.Length
= sizeof(uint32
);
328 crlNumberPresent
= true;
330 if(cuSearchNumericExtension(&tbsCrl
->extensions
,
331 &CSSMOID_DeltaCrlIndicator
,
333 deltaCrlNumberData
.Data
= (uint8
*)&deltaCrlNumber
;
334 deltaCrlNumberData
.Length
= sizeof(uint32
);
335 deltaCrlPresent
= true;
338 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
339 attr
->Info
.Label
.AttributeName
= (char*) "CrlType";
340 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
341 attr
->NumberOfValues
= 1;
342 attr
->Value
= &crlTypeData
;
345 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
346 attr
->Info
.Label
.AttributeName
= (char*) "CrlEncoding";
347 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
348 attr
->NumberOfValues
= 1;
349 attr
->Value
= &crlEncData
;
352 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
353 attr
->Info
.Label
.AttributeName
= (char*) "PrintName";
354 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
355 attr
->NumberOfValues
= 1;
356 attr
->Value
= &printNameData
;
359 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
360 attr
->Info
.Label
.AttributeName
= (char*) "Issuer";
361 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
362 attr
->NumberOfValues
= 1;
363 attr
->Value
= issuer
;
366 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
367 attr
->Info
.Label
.AttributeName
= (char*) "ThisUpdate";
368 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
369 attr
->NumberOfValues
= 1;
370 attr
->Value
= &thisUpdateData
;
373 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
374 attr
->Info
.Label
.AttributeName
= (char*) "NextUpdate";
375 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
376 attr
->NumberOfValues
= 1;
377 attr
->Value
= &nextUpdateData
;
380 /* ensure URI string does not contain NULL */
382 if((attrUri
.Length
!= 0) &&
383 (attrUri
.Data
[attrUri
.Length
- 1] == 0)) {
386 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
387 attr
->Info
.Label
.AttributeName
= (char*) "URI";
388 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
389 attr
->NumberOfValues
= 1;
390 attr
->Value
= &attrUri
;
393 /* now the optional attributes */
394 if(crlNumberPresent
) {
395 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
396 attr
->Info
.Label
.AttributeName
= (char*) "CrlNumber";
397 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
398 attr
->NumberOfValues
= 1;
399 attr
->Value
= &crlNumberData
;
402 if(deltaCrlPresent
) {
403 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
404 attr
->Info
.Label
.AttributeName
= (char*) "DeltaCrlNumber";
405 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
406 attr
->NumberOfValues
= 1;
407 attr
->Value
= &deltaCrlNumberData
;
410 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_X509_CRL
;
411 recordAttrs
.SemanticInformation
= 0;
412 recordAttrs
.NumberOfAttributes
= (uint32
)(attr
- attrs
);
413 recordAttrs
.AttributeData
= attrs
;
415 crtn
= CSSM_DL_DataInsert(dlDbHand
,
416 CSSM_DL_DB_RECORD_X509_CRL
,
420 if(crtn
== CSSMERR_DL_INVALID_RECORDTYPE
) {
421 /* gross hack of inserting this "new" schema that Keychain didn't specify */
422 crtn
= cuAddCrlSchema(dlDbHand
);
423 if(crtn
== CSSM_OK
) {
424 /* Retry with a fully capable DLDB */
425 crtn
= CSSM_DL_DataInsert(dlDbHand
,
426 CSSM_DL_DB_RECORD_X509_CRL
,
432 if(crtn
== CSSM_OK
) {
433 CSSM_DL_FreeUniqueRecord(dlDbHand
, recordPtr
);
437 /* free all the stuff we allocated to get here */
439 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_X509V1IssuerName
, issuer
);
442 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_X509V2CRLSignedCrlCStruct
, crlValue
);
455 * Update an existing DLDB to be CRL-capable.
457 static CSSM_RETURN
cuAddCrlSchema(
458 CSSM_DL_DB_HANDLE dlDbHand
)
460 return CSSM_DL_CreateRelation(dlDbHand
,
461 CSSM_DL_DB_RECORD_X509_CRL
,
462 "CSSM_DL_DB_RECORD_X509_CRL",
463 Security::KeychainCore::Schema::X509CrlSchemaAttributeCount
,
464 Security::KeychainCore::Schema::X509CrlSchemaAttributeList
,
465 Security::KeychainCore::Schema::X509CrlSchemaIndexCount
,
466 Security::KeychainCore::Schema::X509CrlSchemaIndexList
);
470 * Search DB for all records of type CRL or cert, calling appropriate
471 * parse/print routine for each record.
473 CSSM_RETURN
cuDumpCrlsCerts(
474 CSSM_DL_DB_HANDLE dlDbHand
,
475 CSSM_CL_HANDLE clHand
,
477 unsigned &numItems
, // returned
481 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
482 CSSM_HANDLE resultHand
;
488 itemStr
= isCert
? "Certificate" : "CRL";
490 /* just search by recordType, no predicates, no attributes */
492 query
.RecordType
= CSSM_DL_DB_RECORD_X509_CERTIFICATE
;
495 query
.RecordType
= CSSM_DL_DB_RECORD_X509_CRL
;
497 query
.Conjunctive
= CSSM_DB_NONE
;
498 query
.NumSelectionPredicates
= 0;
499 query
.SelectionPredicate
= NULL
;
500 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
501 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
502 query
.QueryFlags
= 0; // CSSM_QUERY_RETURN_DATA...FIXME - used?
506 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
515 case CSSMERR_DL_ENDOFDATA
:
516 /* no data, otherwise OK */
518 case CSSMERR_DL_INVALID_RECORDTYPE
:
519 /* invalid record type just means "this hasn't been set up
523 cuPrintError("DataGetFirst", crtn
);
527 /* got one; print it */
528 dprintf("%s %u:\n", itemStr
, numItems
);
530 printCert(certCrl
.Data
, (unsigned)certCrl
.Length
, verbose
);
533 printCrl(certCrl
.Data
, (unsigned)certCrl
.Length
, verbose
);
535 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
536 APP_FREE(certCrl
.Data
);
543 crtn
= CSSM_DL_DataGetNext(dlDbHand
,
550 dprintf("%s %u:\n", itemStr
, numItems
);
552 printCert(certCrl
.Data
, (unsigned)certCrl
.Length
, verbose
);
555 printCrl(certCrl
.Data
, (unsigned)certCrl
.Length
, verbose
);
557 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
558 APP_FREE(certCrl
.Data
);
562 break; // and go again
563 case CSSMERR_DL_ENDOFDATA
:
564 /* normal termination */
567 cuPrintError("DataGetNext", crtn
);