2 * Copyright (c) 2002-2003 Apple Computer, 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/Schema.h> /* private API */
43 * Add a certificate to an open DLDB.
45 CSSM_RETURN
cuAddCertToDb(
46 CSSM_DL_DB_HANDLE dlDbHand
,
47 const CSSM_DATA
*cert
,
48 CSSM_CERT_TYPE certType
,
49 CSSM_CERT_ENCODING certEncoding
,
50 const char *printName
, // C string
51 const CSSM_DATA
*publicKeyHash
)
53 CSSM_DB_ATTRIBUTE_DATA attrs
[6];
54 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
55 CSSM_DB_ATTRIBUTE_DATA_PTR attr
= &attrs
[0];
56 CSSM_DATA certTypeData
;
57 CSSM_DATA certEncData
;
58 CSSM_DATA printNameData
;
60 CSSM_DB_UNIQUE_RECORD_PTR recordPtr
;
62 /* issuer and serial number required, fake 'em */
63 CSSM_DATA issuer
= {6, (uint8
*)"issuer"};
64 CSSM_DATA serial
= {6, (uint8
*)"serial"};
66 /* we spec six attributes, skipping alias */
67 certTypeData
.Data
= (uint8
*)&certType
;
68 certTypeData
.Length
= sizeof(CSSM_CERT_TYPE
);
69 certEncData
.Data
= (uint8
*)&certEncoding
;
70 certEncData
.Length
= sizeof(CSSM_CERT_ENCODING
);
71 printNameData
.Data
= (uint8
*)printName
;
72 printNameData
.Length
= strlen(printName
) + 1;
74 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
75 attr
->Info
.Label
.AttributeName
= "CertType";
76 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
77 attr
->NumberOfValues
= 1;
78 attr
->Value
= &certTypeData
;
81 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
82 attr
->Info
.Label
.AttributeName
= "CertEncoding";
83 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
84 attr
->NumberOfValues
= 1;
85 attr
->Value
= &certEncData
;
88 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
89 attr
->Info
.Label
.AttributeName
= "PrintName";
90 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
91 attr
->NumberOfValues
= 1;
92 attr
->Value
= &printNameData
;
95 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
96 attr
->Info
.Label
.AttributeName
= "PublicKeyHash";
97 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
98 attr
->NumberOfValues
= 1;
99 attr
->Value
= (CSSM_DATA_PTR
)publicKeyHash
;
102 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
103 attr
->Info
.Label
.AttributeName
= "Issuer";
104 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
105 attr
->NumberOfValues
= 1;
106 attr
->Value
= &issuer
;
109 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
110 attr
->Info
.Label
.AttributeName
= "SerialNumber";
111 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
112 attr
->NumberOfValues
= 1;
113 attr
->Value
= &serial
;
115 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_X509_CERTIFICATE
;
116 recordAttrs
.SemanticInformation
= 0;
117 recordAttrs
.NumberOfAttributes
= 6;
118 recordAttrs
.AttributeData
= attrs
;
120 crtn
= CSSM_DL_DataInsert(dlDbHand
,
121 CSSM_DL_DB_RECORD_X509_CERTIFICATE
,
126 cuPrintError("CSSM_DL_DataInsert", crtn
);
129 CSSM_DL_FreeUniqueRecord(dlDbHand
, recordPtr
);
134 static CSSM_RETURN
cuAddCrlSchema(
135 CSSM_DL_DB_HANDLE dlDbHand
);
137 static void cuInferCrlLabel(
138 const CSSM_X509_NAME
*x509Name
,
139 CSSM_DATA
*label
) // not mallocd; contents are from the x509Name
141 /* use private API for common "infer label" logic */
142 const CSSM_DATA
*printValue
= SecInferLabelFromX509Name(x509Name
);
143 if(printValue
== NULL
) {
145 label
->Data
= (uint8
*)"X509 CRL";
149 *label
= *printValue
;
154 * Search extensions for specified OID, assumed to have underlying
155 * value type of uint32; returns the value and true if found.
157 static bool cuSearchNumericExtension(
158 const CSSM_X509_EXTENSIONS
*extens
,
162 for(uint32 dex
=0; dex
<extens
->numberOfExtensions
; dex
++) {
163 const CSSM_X509_EXTENSION
*exten
= &extens
->extensions
[dex
];
164 if(!cuCompareOid(&exten
->extnId
, oid
)) {
167 if(exten
->format
!= CSSM_X509_DATAFORMAT_PAIR
) {
168 printf("***Malformed extension\n");
171 *val
= *((uint32
*)exten
->value
.parsedValue
);
178 * Add a CRL to an existing DL/DB.
180 #define MAX_CRL_ATTRS 9
182 CSSM_RETURN
cuAddCrlToDb(
183 CSSM_DL_DB_HANDLE dlDbHand
,
184 CSSM_CL_HANDLE clHand
,
185 const CSSM_DATA
*crl
,
186 const CSSM_DATA
*URI
) // optional
188 CSSM_DB_ATTRIBUTE_DATA attrs
[MAX_CRL_ATTRS
];
189 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
190 CSSM_DB_ATTRIBUTE_DATA_PTR attr
= &attrs
[0];
191 CSSM_DATA crlTypeData
;
192 CSSM_DATA crlEncData
;
193 CSSM_DATA printNameData
;
195 CSSM_DB_UNIQUE_RECORD_PTR recordPtr
;
196 CSSM_DATA_PTR issuer
; // mallocd by CL
197 CSSM_DATA_PTR crlValue
; // ditto
200 CSSM_CRL_ENCODING crlEnc
= CSSM_CRL_ENCODING_DER
;
201 const CSSM_X509_SIGNED_CRL
*signedCrl
;
202 const CSSM_X509_TBS_CERTLIST
*tbsCrl
;
203 CSSM_CRL_TYPE crlType
;
204 CSSM_DATA thisUpdateData
= {0, NULL
};
205 CSSM_DATA nextUpdateData
= {0, NULL
};
206 char *thisUpdate
, *nextUpdate
;
209 uint32 deltaCrlNumber
;
210 CSSM_DATA crlNumberData
;
211 CSSM_DATA deltaCrlNumberData
;
212 bool crlNumberPresent
= false;
213 bool deltaCrlPresent
= false;
216 /* get normalized issuer name as Issuer attr */
217 crtn
= CSSM_CL_CrlGetFirstFieldValue(clHand
,
219 &CSSMOID_X509V1IssuerName
,
224 cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn
);
227 CSSM_CL_CrlAbortQuery(clHand
, result
);
229 /* get parsed CRL from the CL */
230 crtn
= CSSM_CL_CrlGetFirstFieldValue(clHand
,
232 &CSSMOID_X509V2CRLSignedCrlCStruct
,
237 cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn
);
240 CSSM_CL_CrlAbortQuery(clHand
, result
);
241 if(crlValue
== NULL
) {
242 printf("***CSSM_CL_CrlGetFirstFieldValue: value error (1)\n");
243 return CSSMERR_CL_INVALID_CRL_POINTER
;
245 if((crlValue
->Data
== NULL
) ||
246 (crlValue
->Length
!= sizeof(CSSM_X509_SIGNED_CRL
))) {
247 printf("***CSSM_CL_CrlGetFirstFieldValue: value error (2)\n");
248 return CSSMERR_CL_INVALID_CRL_POINTER
;
250 signedCrl
= (const CSSM_X509_SIGNED_CRL
*)crlValue
->Data
;
251 tbsCrl
= &signedCrl
->tbsCertList
;
253 /* CrlType inferred from version */
254 if(tbsCrl
->version
.Length
== 0) {
255 /* should never happen... */
256 crlType
= CSSM_CRL_TYPE_X_509v1
;
259 uint8 vers
= tbsCrl
->version
.Data
[tbsCrl
->version
.Length
- 1];
262 crlType
= CSSM_CRL_TYPE_X_509v1
;
265 crlType
= CSSM_CRL_TYPE_X_509v2
;
268 printf("***Unknown version in CRL (%u)\n", vers
);
269 crlType
= CSSM_CRL_TYPE_X_509v1
;
273 crlTypeData
.Data
= (uint8
*)&crlType
;
274 crlTypeData
.Length
= sizeof(CSSM_CRL_TYPE
);
275 /* encoding more-or-less assumed here */
276 crlEncData
.Data
= (uint8
*)&crlEnc
;
277 crlEncData
.Length
= sizeof(CSSM_CRL_ENCODING
);
279 /* printName inferred from issuer */
280 cuInferCrlLabel(&tbsCrl
->issuer
, &printNameData
);
282 /* cook up CSSM_TIMESTRING versions of this/next update */
283 thisUpdate
= cuX509TimeToCssmTimestring(&tbsCrl
->thisUpdate
, &timeLen
);
284 if(thisUpdate
== NULL
) {
285 printf("***Badly formatted thisUpdate\n");
288 thisUpdateData
.Data
= (uint8
*)thisUpdate
;
289 thisUpdateData
.Length
= timeLen
;
291 if(tbsCrl
->nextUpdate
.time
.Data
!= NULL
) {
292 nextUpdate
= cuX509TimeToCssmTimestring(&tbsCrl
->nextUpdate
, &timeLen
);
293 if(nextUpdate
== NULL
) {
294 printf("***Badly formatted nextUpdate\n");
297 nextUpdateData
.Data
= (uint8
*)nextUpdate
;
298 nextUpdateData
.Length
= timeLen
;
303 * NextUpdate not present; fake it by using "virtual end of time"
305 CSSM_X509_TIME tempTime
= { 0, // timeType, not used
306 { strlen(CSSM_APPLE_CRL_END_OF_TIME
),
307 (uint8
*)CSSM_APPLE_CRL_END_OF_TIME
} };
308 nextUpdate
= cuX509TimeToCssmTimestring(&tempTime
, &timeLen
);
309 nextUpdateData
.Data
= (uint8
*)nextUpdate
;
310 nextUpdateData
.Length
= CSSM_TIME_STRLEN
;
313 /* optional CrlNumber and DeltaCrlNumber */
314 if(cuSearchNumericExtension(&tbsCrl
->extensions
,
317 crlNumberData
.Data
= (uint8
*)&crlNumber
;
318 crlNumberData
.Length
= sizeof(uint32
);
319 crlNumberPresent
= true;
321 if(cuSearchNumericExtension(&tbsCrl
->extensions
,
322 &CSSMOID_DeltaCrlIndicator
,
324 deltaCrlNumberData
.Data
= (uint8
*)&deltaCrlNumber
;
325 deltaCrlNumberData
.Length
= sizeof(uint32
);
326 deltaCrlPresent
= true;
329 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
330 attr
->Info
.Label
.AttributeName
= "CrlType";
331 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
332 attr
->NumberOfValues
= 1;
333 attr
->Value
= &crlTypeData
;
336 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
337 attr
->Info
.Label
.AttributeName
= "CrlEncoding";
338 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
339 attr
->NumberOfValues
= 1;
340 attr
->Value
= &crlEncData
;
343 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
344 attr
->Info
.Label
.AttributeName
= "PrintName";
345 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
346 attr
->NumberOfValues
= 1;
347 attr
->Value
= &printNameData
;
350 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
351 attr
->Info
.Label
.AttributeName
= "Issuer";
352 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
353 attr
->NumberOfValues
= 1;
354 attr
->Value
= issuer
;
357 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
358 attr
->Info
.Label
.AttributeName
= "ThisUpdate";
359 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
360 attr
->NumberOfValues
= 1;
361 attr
->Value
= &thisUpdateData
;
364 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
365 attr
->Info
.Label
.AttributeName
= "NextUpdate";
366 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
367 attr
->NumberOfValues
= 1;
368 attr
->Value
= &nextUpdateData
;
371 /* now the optional attributes */
372 if(crlNumberPresent
) {
373 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
374 attr
->Info
.Label
.AttributeName
= "CrlNumber";
375 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
376 attr
->NumberOfValues
= 1;
377 attr
->Value
= &crlNumberData
;
380 if(deltaCrlPresent
) {
381 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
382 attr
->Info
.Label
.AttributeName
= "DeltaCrlNumber";
383 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_UINT32
;
384 attr
->NumberOfValues
= 1;
385 attr
->Value
= &deltaCrlNumberData
;
389 /* ensure URI string does not contain NULL */
391 if((attrUri
.Length
!= 0) &&
392 (attrUri
.Data
[attrUri
.Length
- 1] == 0)) {
395 attr
->Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
396 attr
->Info
.Label
.AttributeName
= "URI";
397 attr
->Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
398 attr
->NumberOfValues
= 1;
399 attr
->Value
= &attrUri
;
402 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_X509_CRL
;
403 recordAttrs
.SemanticInformation
= 0;
404 recordAttrs
.NumberOfAttributes
= attr
- attrs
;
405 recordAttrs
.AttributeData
= attrs
;
407 crtn
= CSSM_DL_DataInsert(dlDbHand
,
408 CSSM_DL_DB_RECORD_X509_CRL
,
412 if(crtn
== CSSMERR_DL_INVALID_RECORDTYPE
) {
413 /* gross hack of inserting this "new" schema that Keychain didn't specify */
414 crtn
= cuAddCrlSchema(dlDbHand
);
415 if(crtn
== CSSM_OK
) {
416 /* Retry with a fully capable DLDB */
417 crtn
= CSSM_DL_DataInsert(dlDbHand
,
418 CSSM_DL_DB_RECORD_X509_CRL
,
425 cuPrintError("CSSM_DL_DataInsert", crtn
);
428 CSSM_DL_FreeUniqueRecord(dlDbHand
, recordPtr
);
431 /* free all the stuff we allocated to get here */
432 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_X509V1IssuerName
, issuer
);
433 CSSM_CL_FreeFieldValue(clHand
, &CSSMOID_X509V2CRLSignedCrlCStruct
, crlValue
);
441 * Update an existing DLDB to be CRL-capable.
443 static CSSM_RETURN
cuAddCrlSchema(
444 CSSM_DL_DB_HANDLE dlDbHand
)
446 return CSSM_DL_CreateRelation(dlDbHand
,
447 CSSM_DL_DB_RECORD_X509_CRL
,
448 "CSSM_DL_DB_RECORD_X509_CRL",
449 Security::KeychainCore::Schema::X509CrlSchemaAttributeCount
,
450 Security::KeychainCore::Schema::X509CrlSchemaAttributeList
,
451 Security::KeychainCore::Schema::X509CrlSchemaIndexCount
,
452 Security::KeychainCore::Schema::X509CrlSchemaIndexList
);
456 * Search DB for all records of type CRL or cert, calling appropriate
457 * parse/print routine for each record.
459 CSSM_RETURN
cuDumpCrlsCerts(
460 CSSM_DL_DB_HANDLE dlDbHand
,
461 CSSM_CL_HANDLE clHand
,
463 unsigned &numItems
, // returned
467 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
468 CSSM_HANDLE resultHand
;
474 itemStr
= isCert
? "Certificate" : "CRL";
476 /* just search by recordType, no predicates, no attributes */
478 query
.RecordType
= CSSM_DL_DB_RECORD_X509_CERTIFICATE
;
481 query
.RecordType
= CSSM_DL_DB_RECORD_X509_CRL
;
483 query
.Conjunctive
= CSSM_DB_NONE
;
484 query
.NumSelectionPredicates
= 0;
485 query
.SelectionPredicate
= NULL
;
486 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
487 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
488 query
.QueryFlags
= 0; // CSSM_QUERY_RETURN_DATA...FIXME - used?
492 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
501 case CSSMERR_DL_ENDOFDATA
:
502 /* no data, otherwise OK */
504 case CSSMERR_DL_INVALID_RECORDTYPE
:
505 /* invalid record type just means "this hasn't been set up
509 cuPrintError("DataGetFirst", crtn
);
513 /* got one; print it */
514 printf("%s %u:\n", itemStr
, numItems
);
516 printCert(certCrl
.Data
, certCrl
.Length
, verbose
);
519 printCrl(certCrl
.Data
, certCrl
.Length
, verbose
);
521 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
522 APP_FREE(certCrl
.Data
);
529 crtn
= CSSM_DL_DataGetNext(dlDbHand
,
536 printf("%s %u:\n", itemStr
, numItems
);
538 printCert(certCrl
.Data
, certCrl
.Length
, verbose
);
541 printCrl(certCrl
.Data
, certCrl
.Length
, verbose
);
543 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
544 APP_FREE(certCrl
.Data
);
548 break; // and go again
549 case CSSMERR_DL_ENDOFDATA
:
550 /* normal termination */
553 cuPrintError("DataGetNext", crtn
);