]> git.saurik.com Git - apple/security.git/blob - CdsaUtils/cuDbUtils.cpp
Security-176.tar.gz
[apple/security.git] / CdsaUtils / cuDbUtils.cpp
1 /*
2 * Copyright (c) 2002-2003 Apple Computer, 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.
7 * Please obtain a copy of the License at http://www.apple.com/publicsource
8 * and read it before 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
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.
17 */
18
19 /*
20 File: cuDbUtils.cpp
21
22 Description: CDSA DB access utilities
23
24 Author: dmitch
25 */
26
27 #include "cuCdsaUtils.h"
28 #include "cuTimeStr.h"
29 #include "cuDbUtils.h"
30 #include "cuPrintCert.h"
31 #include <stdlib.h>
32 #include <stdio.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>
39 #include <strings.h>
40 #include <Security/Schema.h> /* private API */
41
42 /*
43 * Add a certificate to an open DLDB.
44 */
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)
52 {
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;
59 CSSM_RETURN crtn;
60 CSSM_DB_UNIQUE_RECORD_PTR recordPtr;
61
62 /* issuer and serial number required, fake 'em */
63 CSSM_DATA issuer = {6, (uint8 *)"issuer"};
64 CSSM_DATA serial = {6, (uint8 *)"serial"};
65
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;
73
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;
79
80 attr++;
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;
86
87 attr++;
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;
93
94 attr++;
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;
100
101 attr++;
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;
107
108 attr++;
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;
114
115 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
116 recordAttrs.SemanticInformation = 0;
117 recordAttrs.NumberOfAttributes = 6;
118 recordAttrs.AttributeData = attrs;
119
120 crtn = CSSM_DL_DataInsert(dlDbHand,
121 CSSM_DL_DB_RECORD_X509_CERTIFICATE,
122 &recordAttrs,
123 cert,
124 &recordPtr);
125 if(crtn) {
126 cuPrintError("CSSM_DL_DataInsert", crtn);
127 }
128 else {
129 CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr);
130 }
131 return crtn;
132 }
133
134 static CSSM_RETURN cuAddCrlSchema(
135 CSSM_DL_DB_HANDLE dlDbHand);
136
137 static void cuInferCrlLabel(
138 const CSSM_X509_NAME *x509Name,
139 CSSM_DATA *label) // not mallocd; contents are from the x509Name
140 {
141 /* use private API for common "infer label" logic */
142 const CSSM_DATA *printValue = SecInferLabelFromX509Name(x509Name);
143 if(printValue == NULL) {
144 /* punt! */
145 label->Data = (uint8 *)"X509 CRL";
146 label->Length = 8;
147 }
148 else {
149 *label = *printValue;
150 }
151 }
152
153 /*
154 * Search extensions for specified OID, assumed to have underlying
155 * value type of uint32; returns the value and true if found.
156 */
157 static bool cuSearchNumericExtension(
158 const CSSM_X509_EXTENSIONS *extens,
159 const CSSM_OID *oid,
160 uint32 *val)
161 {
162 for(uint32 dex=0; dex<extens->numberOfExtensions; dex++) {
163 const CSSM_X509_EXTENSION *exten = &extens->extensions[dex];
164 if(!cuCompareOid(&exten->extnId, oid)) {
165 continue;
166 }
167 if(exten->format != CSSM_X509_DATAFORMAT_PAIR) {
168 printf("***Malformed extension\n");
169 continue;
170 }
171 *val = *((uint32 *)exten->value.parsedValue);
172 return true;
173 }
174 return false;
175 }
176
177 /*
178 * Add a CRL to an existing DL/DB.
179 */
180 #define MAX_CRL_ATTRS 9
181
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
187 {
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;
194 CSSM_RETURN crtn;
195 CSSM_DB_UNIQUE_RECORD_PTR recordPtr;
196 CSSM_DATA_PTR issuer; // mallocd by CL
197 CSSM_DATA_PTR crlValue; // ditto
198 uint32 numFields;
199 CSSM_HANDLE result;
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;
207 unsigned timeLen;
208 uint32 crlNumber;
209 uint32 deltaCrlNumber;
210 CSSM_DATA crlNumberData;
211 CSSM_DATA deltaCrlNumberData;
212 bool crlNumberPresent = false;
213 bool deltaCrlPresent = false;
214 CSSM_DATA attrUri;
215
216 /* get normalized issuer name as Issuer attr */
217 crtn = CSSM_CL_CrlGetFirstFieldValue(clHand,
218 crl,
219 &CSSMOID_X509V1IssuerName,
220 &result,
221 &numFields,
222 &issuer);
223 if(crtn) {
224 cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn);
225 return crtn;
226 }
227 CSSM_CL_CrlAbortQuery(clHand, result);
228
229 /* get parsed CRL from the CL */
230 crtn = CSSM_CL_CrlGetFirstFieldValue(clHand,
231 crl,
232 &CSSMOID_X509V2CRLSignedCrlCStruct,
233 &result,
234 &numFields,
235 &crlValue);
236 if(crtn) {
237 cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn);
238 return crtn;
239 }
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;
244 }
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;
249 }
250 signedCrl = (const CSSM_X509_SIGNED_CRL *)crlValue->Data;
251 tbsCrl = &signedCrl->tbsCertList;
252
253 /* CrlType inferred from version */
254 if(tbsCrl->version.Length == 0) {
255 /* should never happen... */
256 crlType = CSSM_CRL_TYPE_X_509v1;
257 }
258 else {
259 uint8 vers = tbsCrl->version.Data[tbsCrl->version.Length - 1];
260 switch(vers) {
261 case 0:
262 crlType = CSSM_CRL_TYPE_X_509v1;
263 break;
264 case 1:
265 crlType = CSSM_CRL_TYPE_X_509v2;
266 break;
267 default:
268 printf("***Unknown version in CRL (%u)\n", vers);
269 crlType = CSSM_CRL_TYPE_X_509v1;
270 break;
271 }
272 }
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);
278
279 /* printName inferred from issuer */
280 cuInferCrlLabel(&tbsCrl->issuer, &printNameData);
281
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");
286 }
287 else {
288 thisUpdateData.Data = (uint8 *)thisUpdate;
289 thisUpdateData.Length = timeLen;
290 }
291 if(tbsCrl->nextUpdate.time.Data != NULL) {
292 nextUpdate = cuX509TimeToCssmTimestring(&tbsCrl->nextUpdate, &timeLen);
293 if(nextUpdate == NULL) {
294 printf("***Badly formatted nextUpdate\n");
295 }
296 else {
297 nextUpdateData.Data = (uint8 *)nextUpdate;
298 nextUpdateData.Length = timeLen;
299 }
300 }
301 else {
302 /*
303 * NextUpdate not present; fake it by using "virtual end of time"
304 */
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;
311 }
312
313 /* optional CrlNumber and DeltaCrlNumber */
314 if(cuSearchNumericExtension(&tbsCrl->extensions,
315 &CSSMOID_CrlNumber,
316 &crlNumber)) {
317 crlNumberData.Data = (uint8 *)&crlNumber;
318 crlNumberData.Length = sizeof(uint32);
319 crlNumberPresent = true;
320 }
321 if(cuSearchNumericExtension(&tbsCrl->extensions,
322 &CSSMOID_DeltaCrlIndicator,
323 &deltaCrlNumber)) {
324 deltaCrlNumberData.Data = (uint8 *)&deltaCrlNumber;
325 deltaCrlNumberData.Length = sizeof(uint32);
326 deltaCrlPresent = true;
327 }
328
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;
334 attr++;
335
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;
341 attr++;
342
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;
348 attr++;
349
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;
355 attr++;
356
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;
362 attr++;
363
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;
369 attr++;
370
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;
378 attr++;
379 }
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;
386 attr++;
387 }
388 if(URI) {
389 /* ensure URI string does not contain NULL */
390 attrUri = *URI;
391 if((attrUri.Length != 0) &&
392 (attrUri.Data[attrUri.Length - 1] == 0)) {
393 attrUri.Length--;
394 }
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;
400 attr++;
401 }
402 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CRL;
403 recordAttrs.SemanticInformation = 0;
404 recordAttrs.NumberOfAttributes = attr - attrs;
405 recordAttrs.AttributeData = attrs;
406
407 crtn = CSSM_DL_DataInsert(dlDbHand,
408 CSSM_DL_DB_RECORD_X509_CRL,
409 &recordAttrs,
410 crl,
411 &recordPtr);
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,
419 &recordAttrs,
420 crl,
421 &recordPtr);
422 }
423 }
424 if(crtn) {
425 cuPrintError("CSSM_DL_DataInsert", crtn);
426 }
427 else {
428 CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr);
429 }
430
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);
434 free(thisUpdate);
435 free(nextUpdate);
436 return crtn;
437 }
438
439
440 /*
441 * Update an existing DLDB to be CRL-capable.
442 */
443 static CSSM_RETURN cuAddCrlSchema(
444 CSSM_DL_DB_HANDLE dlDbHand)
445 {
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);
453 }
454
455 /*
456 * Search DB for all records of type CRL or cert, calling appropriate
457 * parse/print routine for each record.
458 */
459 CSSM_RETURN cuDumpCrlsCerts(
460 CSSM_DL_DB_HANDLE dlDbHand,
461 CSSM_CL_HANDLE clHand,
462 CSSM_BOOL isCert,
463 unsigned &numItems, // returned
464 CSSM_BOOL verbose)
465 {
466 CSSM_QUERY query;
467 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
468 CSSM_HANDLE resultHand;
469 CSSM_RETURN crtn;
470 CSSM_DATA certCrl;
471 const char *itemStr;
472
473 numItems = 0;
474 itemStr = isCert ? "Certificate" : "CRL";
475
476 /* just search by recordType, no predicates, no attributes */
477 if(isCert) {
478 query.RecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
479 }
480 else {
481 query.RecordType = CSSM_DL_DB_RECORD_X509_CRL;
482 }
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?
489
490 certCrl.Data = NULL;
491 certCrl.Length = 0;
492 crtn = CSSM_DL_DataGetFirst(dlDbHand,
493 &query,
494 &resultHand,
495 NULL, // no attrs
496 &certCrl,
497 &record);
498 switch(crtn) {
499 case CSSM_OK:
500 break; // proceed
501 case CSSMERR_DL_ENDOFDATA:
502 /* no data, otherwise OK */
503 return CSSM_OK;
504 case CSSMERR_DL_INVALID_RECORDTYPE:
505 /* invalid record type just means "this hasn't been set up
506 * for certs yet". */
507 return crtn;
508 default:
509 cuPrintError("DataGetFirst", crtn);
510 return crtn;
511 }
512
513 /* got one; print it */
514 printf("%s %u:\n", itemStr, numItems);
515 if(isCert) {
516 printCert(certCrl.Data, certCrl.Length, verbose);
517 }
518 else {
519 printCrl(certCrl.Data, certCrl.Length, verbose);
520 }
521 CSSM_DL_FreeUniqueRecord(dlDbHand, record);
522 APP_FREE(certCrl.Data);
523 certCrl.Data = NULL;
524 certCrl.Length = 0;
525 numItems++;
526
527 /* get the rest */
528 for(;;) {
529 crtn = CSSM_DL_DataGetNext(dlDbHand,
530 resultHand,
531 NULL,
532 &certCrl,
533 &record);
534 switch(crtn) {
535 case CSSM_OK:
536 printf("%s %u:\n", itemStr, numItems);
537 if(isCert) {
538 printCert(certCrl.Data, certCrl.Length, verbose);
539 }
540 else {
541 printCrl(certCrl.Data, certCrl.Length, verbose);
542 }
543 CSSM_DL_FreeUniqueRecord(dlDbHand, record);
544 APP_FREE(certCrl.Data);
545 certCrl.Data = NULL;
546 certCrl.Length = 0;
547 numItems++;
548 break; // and go again
549 case CSSMERR_DL_ENDOFDATA:
550 /* normal termination */
551 return CSSM_OK;
552 default:
553 cuPrintError("DataGetNext", crtn);
554 return crtn;
555 }
556 }
557 /* NOT REACHED */
558 }
559