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
) // optional
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 /* now the optional attributes */
381 if(crlNumberPresent
) {
382 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
383 attr
->Info
.Label
.AttributeName
= (char*) "CrlNumber";
384 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
385 attr
->NumberOfValues
= 1;
386 attr
->Value
= &crlNumberData
;
389 if(deltaCrlPresent
) {
390 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
391 attr
->Info
.Label
.AttributeName
= (char*) "DeltaCrlNumber";
392 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
393 attr
->NumberOfValues
= 1;
394 attr
->Value
= &deltaCrlNumberData
;
398 /* ensure URI string does not contain NULL */
400 if((attrUri
.Length
!= 0) &&
401 (attrUri
.Data
[attrUri
.Length
- 1] == 0)) {
404 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
405 attr
->Info
.Label
.AttributeName
= (char*) "URI";
406 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
407 attr
->NumberOfValues
= 1;
408 attr
->Value
= &attrUri
;
411 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_X509_CRL
;
412 recordAttrs
.SemanticInformation
= 0;
413 recordAttrs
.NumberOfAttributes
= (uint32
)(attr
- attrs
);
414 recordAttrs
.AttributeData
= attrs
;
416 crtn
= CSSM_DL_DataInsert(dlDbHand
,
417 CSSM_DL_DB_RECORD_X509_CRL
,
421 if(crtn
== CSSMERR_DL_INVALID_RECORDTYPE
) {
422 /* gross hack of inserting this "new" schema that Keychain didn't specify */
423 crtn
= cuAddCrlSchema(dlDbHand
);
424 if(crtn
== CSSM_OK
) {
425 /* Retry with a fully capable DLDB */
426 crtn
= CSSM_DL_DataInsert(dlDbHand
,
427 CSSM_DL_DB_RECORD_X509_CRL
,
433 if(crtn
== CSSM_OK
) {
434 CSSM_DL_FreeUniqueRecord(dlDbHand
, recordPtr
);
438 /* free all the stuff we allocated to get here */
440 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_X509V1IssuerName
, issuer
);
443 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_X509V2CRLSignedCrlCStruct
, crlValue
);
456 * Update an existing DLDB to be CRL-capable.
458 static CSSM_RETURN
cuAddCrlSchema(
459 CSSM_DL_DB_HANDLE dlDbHand
)
461 return CSSM_DL_CreateRelation(dlDbHand
,
462 CSSM_DL_DB_RECORD_X509_CRL
,
463 "CSSM_DL_DB_RECORD_X509_CRL",
464 Security::KeychainCore::Schema::X509CrlSchemaAttributeCount
,
465 Security::KeychainCore::Schema::X509CrlSchemaAttributeList
,
466 Security::KeychainCore::Schema::X509CrlSchemaIndexCount
,
467 Security::KeychainCore::Schema::X509CrlSchemaIndexList
);
471 * Search DB for all records of type CRL or cert, calling appropriate
472 * parse/print routine for each record.
474 CSSM_RETURN
cuDumpCrlsCerts(
475 CSSM_DL_DB_HANDLE dlDbHand
,
476 CSSM_CL_HANDLE clHand
,
478 unsigned &numItems
, // returned
482 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
483 CSSM_HANDLE resultHand
;
489 itemStr
= isCert
? "Certificate" : "CRL";
491 /* just search by recordType, no predicates, no attributes */
493 query
.RecordType
= CSSM_DL_DB_RECORD_X509_CERTIFICATE
;
496 query
.RecordType
= CSSM_DL_DB_RECORD_X509_CRL
;
498 query
.Conjunctive
= CSSM_DB_NONE
;
499 query
.NumSelectionPredicates
= 0;
500 query
.SelectionPredicate
= NULL
;
501 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
502 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
503 query
.QueryFlags
= 0; // CSSM_QUERY_RETURN_DATA...FIXME - used?
507 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
516 case CSSMERR_DL_ENDOFDATA
:
517 /* no data, otherwise OK */
519 case CSSMERR_DL_INVALID_RECORDTYPE
:
520 /* invalid record type just means "this hasn't been set up
524 cuPrintError("DataGetFirst", crtn
);
528 /* got one; print it */
529 dprintf("%s %u:\n", itemStr
, numItems
);
531 printCert(certCrl
.Data
, (unsigned)certCrl
.Length
, verbose
);
534 printCrl(certCrl
.Data
, (unsigned)certCrl
.Length
, verbose
);
536 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
537 APP_FREE(certCrl
.Data
);
544 crtn
= CSSM_DL_DataGetNext(dlDbHand
,
551 dprintf("%s %u:\n", itemStr
, numItems
);
553 printCert(certCrl
.Data
, (unsigned)certCrl
.Length
, verbose
);
556 printCrl(certCrl
.Data
, (unsigned)certCrl
.Length
, verbose
);
558 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
559 APP_FREE(certCrl
.Data
);
563 break; // and go again
564 case CSSMERR_DL_ENDOFDATA
:
565 /* normal termination */
568 cuPrintError("DataGetNext", crtn
);