]> git.saurik.com Git - apple/security.git/blob - Security/libsecurity_cdsa_utils/lib/cuDbUtils.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_cdsa_utils / lib / cuDbUtils.cpp
1 /*
2 * Copyright (c) 2002-2003,2011-2012,2014 Apple 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_cdsa_utilities/Schema.h> /* private API */
41
42 #ifndef NDEBUG
43 #define dprintf(args...) printf(args)
44 #else
45 #define dprintf(args...)
46 #endif
47
48 /*
49 * Add a certificate to an open DLDB.
50 */
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)
58 {
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;
65 CSSM_RETURN crtn;
66 CSSM_DB_UNIQUE_RECORD_PTR recordPtr;
67
68 /* issuer and serial number required, fake 'em */
69 CSSM_DATA issuer = {6, (uint8 *)"issuer"};
70 CSSM_DATA serial = {6, (uint8 *)"serial"};
71
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;
79
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;
85
86 attr++;
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;
92
93 attr++;
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;
99
100 attr++;
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;
106
107 attr++;
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;
113
114 attr++;
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;
120
121 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
122 recordAttrs.SemanticInformation = 0;
123 recordAttrs.NumberOfAttributes = 6;
124 recordAttrs.AttributeData = attrs;
125
126 crtn = CSSM_DL_DataInsert(dlDbHand,
127 CSSM_DL_DB_RECORD_X509_CERTIFICATE,
128 &recordAttrs,
129 cert,
130 &recordPtr);
131 if(crtn) {
132 cuPrintError("CSSM_DL_DataInsert", crtn);
133 }
134 else {
135 CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr);
136 }
137 return crtn;
138 }
139
140 static CSSM_RETURN cuAddCrlSchema(
141 CSSM_DL_DB_HANDLE dlDbHand);
142
143 static void cuInferCrlLabel(
144 const CSSM_X509_NAME *x509Name,
145 CSSM_DATA *label) // not mallocd; contents are from the x509Name
146 {
147 /* use private API for common "infer label" logic */
148 const CSSM_DATA *printValue = SecInferLabelFromX509Name(x509Name);
149 if(printValue == NULL) {
150 /* punt! */
151 label->Data = (uint8 *)"X509 CRL";
152 label->Length = 8;
153 }
154 else {
155 *label = *printValue;
156 }
157 }
158
159 /*
160 * Search extensions for specified OID, assumed to have underlying
161 * value type of uint32; returns the value and true if found.
162 */
163 static bool cuSearchNumericExtension(
164 const CSSM_X509_EXTENSIONS *extens,
165 const CSSM_OID *oid,
166 uint32 *val)
167 {
168 for(uint32 dex=0; dex<extens->numberOfExtensions; dex++) {
169 const CSSM_X509_EXTENSION *exten = &extens->extensions[dex];
170 if(!cuCompareOid(&exten->extnId, oid)) {
171 continue;
172 }
173 if(exten->format != CSSM_X509_DATAFORMAT_PARSED) {
174 dprintf("***Malformed extension\n");
175 continue;
176 }
177 *val = *((uint32 *)exten->value.parsedValue);
178 return true;
179 }
180 return false;
181 }
182
183 /*
184 * Add a CRL to an existing DL/DB.
185 */
186 #define MAX_CRL_ATTRS 9
187
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
193 {
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;
200 CSSM_RETURN crtn;
201 CSSM_DB_UNIQUE_RECORD_PTR recordPtr;
202 CSSM_DATA_PTR issuer = NULL; // mallocd by CL
203 CSSM_DATA_PTR crlValue = NULL; // ditto
204 uint32 numFields;
205 CSSM_HANDLE result;
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;
214 unsigned timeLen;
215 uint32 crlNumber;
216 uint32 deltaCrlNumber;
217 CSSM_DATA crlNumberData;
218 CSSM_DATA deltaCrlNumberData;
219 bool crlNumberPresent = false;
220 bool deltaCrlPresent = false;
221 CSSM_DATA attrUri;
222
223 /* get normalized issuer name as Issuer attr */
224 crtn = CSSM_CL_CrlGetFirstFieldValue(clHand,
225 crl,
226 &CSSMOID_X509V1IssuerName,
227 &result,
228 &numFields,
229 &issuer);
230 if(crtn) {
231 cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn);
232 return crtn;
233 }
234 CSSM_CL_CrlAbortQuery(clHand, result);
235
236 /* get parsed CRL from the CL */
237 crtn = CSSM_CL_CrlGetFirstFieldValue(clHand,
238 crl,
239 &CSSMOID_X509V2CRLSignedCrlCStruct,
240 &result,
241 &numFields,
242 &crlValue);
243 if(crtn) {
244 cuPrintError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn);
245 goto errOut;
246 }
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;
251 goto errOut;
252 }
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;
257 goto errOut;
258 }
259 signedCrl = (const CSSM_X509_SIGNED_CRL *)crlValue->Data;
260 tbsCrl = &signedCrl->tbsCertList;
261
262 /* CrlType inferred from version */
263 if(tbsCrl->version.Length == 0) {
264 /* should never happen... */
265 crlType = CSSM_CRL_TYPE_X_509v1;
266 }
267 else {
268 uint8 vers = tbsCrl->version.Data[tbsCrl->version.Length - 1];
269 switch(vers) {
270 case 0:
271 crlType = CSSM_CRL_TYPE_X_509v1;
272 break;
273 case 1:
274 crlType = CSSM_CRL_TYPE_X_509v2;
275 break;
276 default:
277 dprintf("***Unknown version in CRL (%u)\n", vers);
278 crlType = CSSM_CRL_TYPE_X_509v1;
279 break;
280 }
281 }
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);
287
288 /* printName inferred from issuer */
289 cuInferCrlLabel(&tbsCrl->issuer, &printNameData);
290
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");
295 }
296 else {
297 thisUpdateData.Data = (uint8 *)thisUpdate;
298 thisUpdateData.Length = timeLen;
299 }
300 if(tbsCrl->nextUpdate.time.Data != NULL) {
301 nextUpdate = cuX509TimeToCssmTimestring(&tbsCrl->nextUpdate, &timeLen);
302 if(nextUpdate == NULL) {
303 dprintf("***Badly formatted nextUpdate\n");
304 }
305 else {
306 nextUpdateData.Data = (uint8 *)nextUpdate;
307 nextUpdateData.Length = timeLen;
308 }
309 }
310 else {
311 /*
312 * NextUpdate not present; fake it by using "virtual end of time"
313 */
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;
320 }
321
322 /* optional CrlNumber and DeltaCrlNumber */
323 if(cuSearchNumericExtension(&tbsCrl->extensions,
324 &CSSMOID_CrlNumber,
325 &crlNumber)) {
326 crlNumberData.Data = (uint8 *)&crlNumber;
327 crlNumberData.Length = sizeof(uint32);
328 crlNumberPresent = true;
329 }
330 if(cuSearchNumericExtension(&tbsCrl->extensions,
331 &CSSMOID_DeltaCrlIndicator,
332 &deltaCrlNumber)) {
333 deltaCrlNumberData.Data = (uint8 *)&deltaCrlNumber;
334 deltaCrlNumberData.Length = sizeof(uint32);
335 deltaCrlPresent = true;
336 }
337
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;
343 attr++;
344
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;
350 attr++;
351
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;
357 attr++;
358
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;
364 attr++;
365
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;
371 attr++;
372
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;
378 attr++;
379
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;
387 attr++;
388 }
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;
395 attr++;
396 }
397 if(URI) {
398 /* ensure URI string does not contain NULL */
399 attrUri = *URI;
400 if((attrUri.Length != 0) &&
401 (attrUri.Data[attrUri.Length - 1] == 0)) {
402 attrUri.Length--;
403 }
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;
409 attr++;
410 }
411 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CRL;
412 recordAttrs.SemanticInformation = 0;
413 recordAttrs.NumberOfAttributes = (uint32)(attr - attrs);
414 recordAttrs.AttributeData = attrs;
415
416 crtn = CSSM_DL_DataInsert(dlDbHand,
417 CSSM_DL_DB_RECORD_X509_CRL,
418 &recordAttrs,
419 crl,
420 &recordPtr);
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,
428 &recordAttrs,
429 crl,
430 &recordPtr);
431 }
432 }
433 if(crtn == CSSM_OK) {
434 CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr);
435 }
436
437 errOut:
438 /* free all the stuff we allocated to get here */
439 if(issuer) {
440 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1IssuerName, issuer);
441 }
442 if(crlValue) {
443 CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V2CRLSignedCrlCStruct, crlValue);
444 }
445 if(thisUpdate) {
446 free(thisUpdate);
447 }
448 if(nextUpdate) {
449 free(nextUpdate);
450 }
451 return crtn;
452 }
453
454
455 /*
456 * Update an existing DLDB to be CRL-capable.
457 */
458 static CSSM_RETURN cuAddCrlSchema(
459 CSSM_DL_DB_HANDLE dlDbHand)
460 {
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);
468 }
469
470 /*
471 * Search DB for all records of type CRL or cert, calling appropriate
472 * parse/print routine for each record.
473 */
474 CSSM_RETURN cuDumpCrlsCerts(
475 CSSM_DL_DB_HANDLE dlDbHand,
476 CSSM_CL_HANDLE clHand,
477 CSSM_BOOL isCert,
478 unsigned &numItems, // returned
479 CSSM_BOOL verbose)
480 {
481 CSSM_QUERY query;
482 CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
483 CSSM_HANDLE resultHand;
484 CSSM_RETURN crtn;
485 CSSM_DATA certCrl;
486 const char *itemStr;
487
488 numItems = 0;
489 itemStr = isCert ? "Certificate" : "CRL";
490
491 /* just search by recordType, no predicates, no attributes */
492 if(isCert) {
493 query.RecordType = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
494 }
495 else {
496 query.RecordType = CSSM_DL_DB_RECORD_X509_CRL;
497 }
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?
504
505 certCrl.Data = NULL;
506 certCrl.Length = 0;
507 crtn = CSSM_DL_DataGetFirst(dlDbHand,
508 &query,
509 &resultHand,
510 NULL, // no attrs
511 &certCrl,
512 &record);
513 switch(crtn) {
514 case CSSM_OK:
515 break; // proceed
516 case CSSMERR_DL_ENDOFDATA:
517 /* no data, otherwise OK */
518 return CSSM_OK;
519 case CSSMERR_DL_INVALID_RECORDTYPE:
520 /* invalid record type just means "this hasn't been set up
521 * for certs yet". */
522 return crtn;
523 default:
524 cuPrintError("DataGetFirst", crtn);
525 return crtn;
526 }
527
528 /* got one; print it */
529 dprintf("%s %u:\n", itemStr, numItems);
530 if(isCert) {
531 printCert(certCrl.Data, (unsigned)certCrl.Length, verbose);
532 }
533 else {
534 printCrl(certCrl.Data, (unsigned)certCrl.Length, verbose);
535 }
536 CSSM_DL_FreeUniqueRecord(dlDbHand, record);
537 APP_FREE(certCrl.Data);
538 certCrl.Data = NULL;
539 certCrl.Length = 0;
540 numItems++;
541
542 /* get the rest */
543 for(;;) {
544 crtn = CSSM_DL_DataGetNext(dlDbHand,
545 resultHand,
546 NULL,
547 &certCrl,
548 &record);
549 switch(crtn) {
550 case CSSM_OK:
551 dprintf("%s %u:\n", itemStr, numItems);
552 if(isCert) {
553 printCert(certCrl.Data, (unsigned)certCrl.Length, verbose);
554 }
555 else {
556 printCrl(certCrl.Data, (unsigned)certCrl.Length, verbose);
557 }
558 CSSM_DL_FreeUniqueRecord(dlDbHand, record);
559 APP_FREE(certCrl.Data);
560 certCrl.Data = NULL;
561 certCrl.Length = 0;
562 numItems++;
563 break; // and go again
564 case CSSMERR_DL_ENDOFDATA:
565 /* normal termination */
566 return CSSM_OK;
567 default:
568 cuPrintError("DataGetNext", crtn);
569 return crtn;
570 }
571 }
572 /* NOT REACHED */
573 }
574