cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ca_clean.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib DBMS CA Cleanup Interface *
4 * Copyright Peter Gutmann 1996-2007 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "asn1.h"
11  #include "keyset.h"
12  #include "dbms.h"
13 #else
14  #include "crypt.h"
15  #include "enc_dec/asn1.h"
16  #include "keyset/keyset.h"
17  #include "keyset/dbms.h"
18 #endif /* Compiler-specific includes */
19 
20 /* When we iterate through the entries in the certificate store we have to
21  protect ourselves against repeatedly fetching the same value over and
22  over again if there's a problem with the certificate store, which we do
23  by remembering data from the previous certificate that we fetched. The
24  following value defines the amount of certificate data that we record
25  (since this doesn't protect against Byzantine failures, we also maintain
26  an iteration counter that bails out after a certain number of iterations
27  are exceeded) */
28 
29 #define MAX_PREVCERT_DATA 128
30 
31 #ifdef USE_DBMS
32 
33 /****************************************************************************
34 * *
35 * Utility Functions *
36 * *
37 ****************************************************************************/
38 
39 /* Delete a certificate request or partially-issued certificated if a
40  cleanup operation for it failed, recording the failure details in the
41  log */
42 
43 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 5 ) ) \
44 static int deleteIncompleteRequest( INOUT DBMS_INFO *dbmsInfo,
46  const char *reqCertID,
48  IN_ERROR const int errorStatus,
49  IN_STRING const char *reasonMessage )
50  {
51  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
52  int status;
53 
54  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
55  assert( isReadPtr( reqCertID, reqCertIDlength ) );
56 
57  REQUIRES( reqCertIDlength > 0 && reqCertIDlength < MAX_INTLENGTH_SHORT );
58  REQUIRES( cryptStatusError( errorStatus ) );
59 
60  /* Delete the request and record the cause */
61  initBoundData( boundDataPtr );
62  setBoundData( boundDataPtr, 0, reqCertID, reqCertIDlength );
63  status = dbmsUpdate(
64  "DELETE FROM certRequests WHERE certID = ?",
65  boundDataPtr, DBMS_UPDATE_NORMAL );
66  updateCertErrorLog( dbmsInfo, errorStatus, reasonMessage, NULL, 0,
67  NULL, 0, reqCertID, reqCertIDlength, NULL, 0 );
68  return( status );
69  }
70 
71 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
72 static int deleteIncompleteCert( INOUT DBMS_INFO *dbmsInfo,
73  const BOOLEAN isRenewal,
74  IN_ERROR const int errorStatus,
75  IN_STRING const char *reasonMessage )
76  {
77  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
78  BYTE certID[ MAX_QUERY_RESULT_SIZE + 8 ];
79  int certIDlength, status;
80 
81  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
82 
83  REQUIRES( cryptStatusError( errorStatus ) );
84 
85  /* Get the certID for the incomplete certificate to delete */
86  status = dbmsQuery( isRenewal ? \
87  "SELECT certID FROM certificates WHERE keyID LIKE '" KEYID_ESC2 "%'" : \
88  "SELECT certID FROM certificates WHERE keyID LIKE '" KEYID_ESC1 "%'",
89  certID, MAX_QUERY_RESULT_SIZE, &certIDlength, NULL,
91  if( cryptStatusError( status ) )
92  return( status );
93 
94  /* Delete the request and record the cause */
95  initBoundData( boundDataPtr );
96  setBoundData( boundDataPtr, 0, certID, certIDlength );
97  status = dbmsUpdate(
98  "DELETE FROM certificates WHERE certID = ?",
99  boundDataPtr, DBMS_UPDATE_NORMAL );
100  updateCertErrorLog( dbmsInfo, errorStatus, reasonMessage, NULL, 0,
101  NULL, 0, certID, certIDlength, NULL, 0 );
102  return( status );
103  }
104 
105 /* Get a partially-issued certificate. We have to perform the import
106  ourselves since it's marked as an incompletely-issued certificate and so
107  is invisible to accesses performed via the standard certificate fetch
108  routines */
109 
110 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
111 static int getNextPartialCert( INOUT DBMS_INFO *dbmsInfo,
113  INOUT_BUFFER_FIXED( prevCertDataMaxLen ) \
114  BYTE *prevCertData,
115  IN_LENGTH_SHORT const int prevCertDataMaxLen,
116  const BOOLEAN isRenewal )
117  {
118  MESSAGE_CREATEOBJECT_INFO createInfo;
120  char encodedCertData[ MAX_QUERY_RESULT_SIZE + 8 ];
121  void *certPtr = hasBinaryBlobs( dbmsInfo ) ? \
122  ( void * ) certificate : encodedCertData;
123  int certSize, status; /* Cast needed for gcc */
124 
125  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
126  assert( isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
127  assert( isWritePtr( prevCertData, prevCertDataMaxLen ) );
128 
129  REQUIRES( prevCertDataMaxLen > 0 && \
130  prevCertDataMaxLen < MAX_INTLENGTH_SHORT );
131 
132  /* Clear return value */
133  *iCertificate = CRYPT_ERROR;
134 
135  /* Find the next certificate and import it. Although this would appear
136  to be fetching the same certificate over and over again, the caller
137  will be deleting the currently-fetched certificate after we return it
138  to them so in practice it fetches a new certificate each time (we
139  also perform a loop check on the prevCertData once we've fetched the
140  next certificate so we'll detect any looping fairly quickly) */
141  status = dbmsQuery( isRenewal ? \
142  "SELECT certData FROM certificates WHERE keyID LIKE '" KEYID_ESC2 "%'" : \
143  "SELECT certData FROM certificates WHERE keyID LIKE '" KEYID_ESC1 "%'",
144  certPtr, MAX_QUERY_RESULT_SIZE, &certSize, NULL,
146  if( cryptStatusError( status ) )
147  {
148  if( status != CRYPT_ERROR_NOTFOUND )
149  return( status );
150 
151  /* We've processed all of the entries, this isn't an error */
152  resetErrorInfo( dbmsInfo );
153  return( OK_SPECIAL );
154  }
155  if( !hasBinaryBlobs( dbmsInfo ) )
156  {
157  status = base64decode( certificate, MAX_CERT_SIZE, &certSize,
158  encodedCertData, certSize,
160  if( cryptStatusError( status ) )
161  {
162  DEBUG_DIAG(( "Couldn't base64-decode data" ));
163  assert( DEBUG_WARN );
164  return( status );
165  }
166  }
167  ENSURES( certSize > 0 && certSize <= MAX_CERT_SIZE );
168 
169  /* If we're stuck in a loop fetching the same value over and over, make
170  an emergency exit */
171  if( !memcmp( prevCertData, certificate, \
172  min( certSize, prevCertDataMaxLen ) ) )
173  {
174  DEBUG_DIAG(( "Certificate-fetch loop detected" ));
175  assert( DEBUG_WARN );
176  return( CRYPT_ERROR_DUPLICATE );
177  }
178  memcpy( prevCertData, certificate,
179  min( certSize, prevCertDataMaxLen ) );
180 
181  /* Reset the first byte of the certificate data from the not-present
182  magic value to allow it to be imported and create a certificate from
183  it */
184  certificate[ 0 ] = BER_SEQUENCE;
185  setMessageCreateObjectIndirectInfo( &createInfo, certificate, certSize,
189  &createInfo, OBJECT_TYPE_CERTIFICATE );
190  if( cryptStatusOK( status ) )
191  *iCertificate = createInfo.cryptHandle;
192  return( status );
193  }
194 
195 /****************************************************************************
196 * *
197 * CA Cleanup Functions *
198 * *
199 ****************************************************************************/
200 
201 /* Perform a cleanup operation on the certificate store, removing
202  incomplete, expired, and otherwise leftover certificates. Since we're
203  cleaning up the certificate store we try and continue even if an error
204  occurs, at least up to a limit. In addition to protect against Byzantine
205  failures we include an iteration counter and bail out after a certain
206  number of iterations. This is a tradeoff between DoS protection and
207  bailing out too early, using FAILSAFE_ITERATIONS_LARGE is a reasonable
208  compromise */
209 
210 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
211 int caCleanup( INOUT DBMS_INFO *dbmsInfo,
212  IN_ENUM( CRYPT_CERTACTION ) const CRYPT_CERTACTION_TYPE action,
214  {
215  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
216  BYTE prevCertData[ MAX_PREVCERT_DATA + 8 ];
217  BYTE certID[ MAX_QUERY_RESULT_SIZE + 8 ];
218  const time_t currentTime = getTime();
219  int certIDlength, errorCount, iterationCount, status;
220 
221  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
222 
223  REQUIRES( action == CRYPT_CERTACTION_EXPIRE_CERT || \
224  action == CRYPT_CERTACTION_CLEANUP );
225  REQUIRES( errorInfo != NULL );
226 
227  /* If the time is screwed up we can't perform time-based cleanup
228  actions */
229  if( action == CRYPT_CERTACTION_EXPIRE_CERT && \
230  currentTime <= MIN_TIME_VALUE )
231  retIntError();
232 
233  /* Rumble through the certificate store either deleting leftover
234  requests or expiring every certificate which is no longer current */
235  memset( prevCertData, 0, MAX_PREVCERT_DATA );
236  for( status = CRYPT_OK, errorCount = 0, iterationCount = 0;
237  status != CRYPT_ERROR_NOTFOUND && \
238  errorCount < FAILSAFE_ITERATIONS_SMALL && \
239  iterationCount < FAILSAFE_ITERATIONS_LARGE;
240  iterationCount++ )
241  {
242  /* Find the certificate ID of the next expired certificate or next
243  certificate request (revocation requests are handled later by
244  completing the revocation). Note that the select requires that
245  the database glue code be capable of returning a single result
246  and then finishing the query, for some back-ends there may be a
247  need to explicitly cancel the query after the first result is
248  returned if the database returns an entire result set */
249  if( action == CRYPT_CERTACTION_EXPIRE_CERT )
250  {
251  initBoundData( boundDataPtr );
252  setBoundDataDate( boundDataPtr, 0, &currentTime );
253  status = dbmsQuery(
254  "SELECT certID FROM certificates WHERE validTo < ?",
255  certID, MAX_QUERY_RESULT_SIZE, &certIDlength,
256  boundDataPtr, DBMS_CACHEDQUERY_NONE,
258  }
259  else
260  {
261  status = dbmsQuery(
262  "SELECT certID FROM certRequests WHERE type = "
264  certID, MAX_QUERY_RESULT_SIZE, &certIDlength,
265  NULL, DBMS_CACHEDQUERY_NONE,
267  }
268  if( cryptStatusError( status ) )
269  {
270  /* If we've processed all of the entries this isn't an error */
271  if( status == CRYPT_ERROR_NOTFOUND )
272  resetErrorInfo( dbmsInfo );
273  else
274  errorCount++;
275  continue;
276  }
277  if( certIDlength > MAX_PREVCERT_DATA )
278  {
279  DEBUG_DIAG(( "Certificate ID data too large" ));
280  assert( DEBUG_WARN );
281  certIDlength = MAX_PREVCERT_DATA;
282  }
283  if( !memcmp( prevCertData, certID, certIDlength ) )
284  {
285  /* We're stuck in a loop fetching the same value over and over,
286  make an emergency exit */
287  DEBUG_DIAG(( "Certificate-fetch loop detected" ));
288  assert( DEBUG_WARN );
289  break;
290  }
291  memcpy( prevCertData, certID, certIDlength );
292 
293  /* Clean up/expire the certificate. Since CRYPT_CERTACTION_CLEANUP
294  is a composite action that encompasses a whole series of
295  operations we replace it with a more specific action code */
296  status = updateCertLog( dbmsInfo,
297  ( action == CRYPT_CERTACTION_CLEANUP ) ? \
299  NULL, 0, NULL, 0, certID, certIDlength,
300  NULL, 0, DBMS_UPDATE_BEGIN );
301  if( cryptStatusOK( status ) )
302  {
303  initBoundData( boundDataPtr );
304  setBoundData( boundDataPtr, 0, certID, certIDlength );
305  status = dbmsUpdate( ( action == CRYPT_CERTACTION_EXPIRE_CERT ) ? \
306  "DELETE FROM certificates WHERE certID = ?" : \
307  "DELETE FROM certRequests WHERE certID = ?",
308  boundDataPtr, DBMS_UPDATE_COMMIT );
309  }
310  else
311  {
312  /* Something went wrong, abort the transaction */
313  dbmsUpdate( NULL, NULL, DBMS_UPDATE_ABORT );
314  errorCount++;
315  }
316  }
317  if( errorCount >= FAILSAFE_ITERATIONS_SMALL || \
318  iterationCount >= FAILSAFE_ITERATIONS_LARGE )
319  {
320  /* It's hard to tell what type of error an iterationCount-exceeded
321  situation really is, in theory it's a software error (either in
322  cryptlib's fetch logic or in the dabatase) but because we could be
323  cleaning up a large number of entries (for example if it's
324  cleaning up the result of an app going into and endless loop and
325  requesting a large number of certificates) it's entirely possible
326  that this is isn't an abnormal situation. Because of this we
327  don't flag it as an internal error but simply warn in the debug
328  build, although we do bail out after a fixed limit */
329  DEBUG_DIAG(( "Certificate-fetch loop detected" ));
330  assert( DEBUG_WARN );
331  }
332 
333  /* If we ran into a problem, perform a fallback general delete of
334  entries that caused the problem */
335  if( status != CRYPT_ERROR_NOTFOUND )
336  {
337  if( action == CRYPT_CERTACTION_EXPIRE_CERT )
338  {
339  updateCertErrorLogMsg( dbmsInfo, status, "Expire operation "
340  "failed, performing fallback straight "
341  "delete" );
342  initBoundData( boundDataPtr );
343  setBoundDataDate( boundDataPtr, 0, &currentTime );
344  status = dbmsUpdate(
345  "DELETE FROM certificates WHERE validTo < ?",
346  boundDataPtr, DBMS_UPDATE_NORMAL );
347  }
348  else
349  {
350  updateCertErrorLogMsg( dbmsInfo, status, "Certificate request "
351  "cleanup operation failed, performing "
352  "fallback straight delete" );
353  status = dbmsStaticUpdate(
354  "DELETE FROM certRequests WHERE type = "
356  }
357  if( cryptStatusError( status ) )
358  updateCertErrorLogMsg( dbmsInfo, status, "Fallback straight "
359  "delete failed" );
360  }
361 
362  /* If it's an expiry action we've done the expired certificates, now
363  remove any stale CRL entries and exit. If there are no CRL entries
364  in the expiry period this isn't an error, so we remap the error code
365  if necessary */
366  if( action == CRYPT_CERTACTION_EXPIRE_CERT )
367  {
368  initBoundData( boundDataPtr );
369  setBoundDataDate( boundDataPtr, 0, &currentTime );
370  status = dbmsUpdate(
371  "DELETE FROM CRLs WHERE expiryDate < ?",
372  boundDataPtr, DBMS_UPDATE_NORMAL );
373  if( status == CRYPT_ERROR_NOTFOUND )
374  {
375  resetErrorInfo( dbmsInfo );
376  return( CRYPT_OK );
377  }
378  if( cryptStatusError( status ) )
379  {
380  retExtErr( status,
381  ( status, errorInfo, getDbmsErrorInfo( dbmsInfo ),
382  "Couldn't delete stale CRL entries from "
383  "certificate store: " ) );
384  }
385  return( CRYPT_OK );
386  }
387 
388  /* It's a restart, process any incompletely-issued certificates in the
389  same manner as the expiry/cleanup is handled. Since we don't know at
390  what stage the issue process was interrupted we have to make a worst-
391  case assumption and do a full reversal as a compensating transaction
392  for an aborted certificate issue */
393  memset( prevCertData, 0, MAX_PREVCERT_DATA );
394  for( status = CRYPT_OK, errorCount = 0, iterationCount = 0;
395  status != CRYPT_ERROR_NOTFOUND && \
396  errorCount < FAILSAFE_ITERATIONS_SMALL && \
397  iterationCount < FAILSAFE_ITERATIONS_LARGE;
398  iterationCount++ )
399  {
401 
402  /* Get the next partially-issued certificate */
403  status = getNextPartialCert( dbmsInfo, &iCertificate,
404  prevCertData, MAX_PREVCERT_DATA, FALSE );
405  if( cryptStatusError( status ) )
406  {
407  /* If we've processed all of the entries, we're done */
408  if( status == OK_SPECIAL )
409  break;
410 
411  /* If we're stuck in a loop fetching the same value over and
412  over, make an emergency exit */
413  if( status == CRYPT_ERROR_DUPLICATE )
414  {
415  DEBUG_DIAG(( "Certificate-fetch loop detected" ));
416  assert( DEBUG_WARN );
417  break;
418  }
419 
420  /* It's some other type of problem, clear it and continue */
421  status = deleteIncompleteCert( dbmsInfo, FALSE, status,
422  "Couldn't get partially-issued "
423  "certificate to complete issue, "
424  "deleting and continuing" );
425  errorCount++;
426  continue;
427  }
428 
429  /* We found a certificate to revoke, complete the revocation */
430  status = revokeCertDirect( dbmsInfo, iCertificate,
432  errorInfo );
433  krnlSendNotifier( iCertificate, IMESSAGE_DECREFCOUNT );
434  }
435  if( errorCount >= FAILSAFE_ITERATIONS_SMALL || \
436  iterationCount >= FAILSAFE_ITERATIONS_LARGE )
437  {
438  /* See note with earlier code */
439  DEBUG_DIAG(( "Certificate-fetch loop detected" ));
440  assert( DEBUG_WARN );
441  }
442 
443  /* If we ran into a problem, perform a fallback general delete of
444  entries that caused the problem */
445  if( status != CRYPT_ERROR_NOTFOUND )
446  {
447  updateCertErrorLogMsg( dbmsInfo, status, "Partially-issued "
448  "certificate cleanup operation failed, "
449  "performing fallback straight delete" );
450  status = dbmsStaticUpdate(
451  "DELETE FROM certificates WHERE keyID LIKE '" KEYID_ESC1 "%'" );
452  if( cryptStatusError( status ) )
453  updateCertErrorLogMsg( dbmsInfo, status, "Fallback straight "
454  "delete failed" );
455  }
456 
457  /* Now process any partially-completed renewals */
458  memset( prevCertData, 0, MAX_PREVCERT_DATA );
459  for( status = CRYPT_OK, errorCount = 0, iterationCount = 0;
460  status != CRYPT_ERROR_NOTFOUND && \
461  errorCount < FAILSAFE_ITERATIONS_SMALL && \
462  iterationCount < FAILSAFE_ITERATIONS_LARGE;
463  iterationCount++ )
464  {
466 
467  /* Get the next partially-completed certificate */
468  status = getNextPartialCert( dbmsInfo, &iCertificate,
469  prevCertData, MAX_PREVCERT_DATA, TRUE );
470  if( cryptStatusError( status ) )
471  {
472  /* If we've processed all of the entries, we're done */
473  if( status == OK_SPECIAL )
474  break;
475 
476  /* If we're stuck in a loop fetching the same value over and
477  over, make an emergency exit */
478  if( status == CRYPT_ERROR_DUPLICATE )
479  {
480  DEBUG_DIAG(( "Certificate-fetch loop detected" ));
481  assert( DEBUG_WARN );
482  break;
483  }
484 
485  /* It's some other type of problem, clear it and continue */
486  status = deleteIncompleteCert( dbmsInfo, TRUE, status,
487  "Couldn't get partially-renewed "
488  "certificate to complete renewal, "
489  "deleting and continuing" );
490  errorCount++;
491  continue;
492  }
493 
494  /* We found a partially-completed certificate, complete the renewal */
495  status = completeCertRenewal( dbmsInfo, iCertificate, errorInfo );
496  krnlSendNotifier( iCertificate, IMESSAGE_DECREFCOUNT );
497  }
498  if( errorCount >= FAILSAFE_ITERATIONS_SMALL || \
499  iterationCount >= FAILSAFE_ITERATIONS_LARGE )
500  {
501  /* See note with earlier code */
502  DEBUG_DIAG(( "Certificate-fetch loop detected" ));
503  assert( DEBUG_WARN );
504  }
505 
506  /* Finally, process any pending revocations */
507  memset( prevCertData, 0, MAX_PREVCERT_DATA );
508  for( status = CRYPT_OK, errorCount = 0, iterationCount = 0;
509  status != CRYPT_ERROR_NOTFOUND && \
510  errorCount < FAILSAFE_ITERATIONS_SMALL && \
511  iterationCount < FAILSAFE_ITERATIONS_LARGE;
512  iterationCount++ )
513  {
515  int dummy;
516 
517  /* Find the next revocation request and import it. This is slightly
518  ugly since we could grab it directly by fetching the data based on
519  the request type field, but there's no way to easily get to the
520  low-level import functions from here so we have to first fetch the
521  certificate ID and then pass that down to the lower-level
522  functions to fetch the actual request */
523  status = dbmsQuery(
524  "SELECT certID FROM certRequests WHERE type = "
526  certID, MAX_QUERY_RESULT_SIZE, &certIDlength,
527  NULL, DBMS_CACHEDQUERY_NONE,
529  if( cryptStatusError( status ) )
530  {
531  /* If we've processed all of the entries this isn't an error */
532  if( status == CRYPT_ERROR_NOTFOUND )
533  {
534  resetErrorInfo( dbmsInfo );
535  break;
536  }
537 
538  errorCount++;
539  continue;
540  }
541  if( certIDlength > MAX_PREVCERT_DATA )
542  {
543  DEBUG_DIAG(( "Certificate ID data too large" ));
544  assert( DEBUG_WARN );
545  certIDlength = MAX_PREVCERT_DATA;
546  }
547  if( !memcmp( prevCertData, certID, certIDlength ) )
548  {
549  /* We're stuck in a loop fetching the same value over and over,
550  make an emergency exit */
551  DEBUG_DIAG(( "Certificate-fetch loop detected" ));
552  assert( DEBUG_WARN );
553  break;
554  }
555  memcpy( prevCertData, certID, certIDlength );
556  status = getItemData( dbmsInfo, &iCertRequest, &dummy,
557  KEYMGMT_ITEM_REVREQUEST, CRYPT_IKEYID_CERTID,
558  certID, certIDlength, KEYMGMT_FLAG_NONE,
559  errorInfo );
560  if( cryptStatusError( status ) )
561  {
562  status = \
563  deleteIncompleteRequest( dbmsInfo, certID, certIDlength, status,
564  "Couldn't instantiate revocation "
565  "request from stored data, deleting "
566  "request and continuing" );
567  errorCount++;
568  continue;
569  }
570 
571  /* Complete the revocation */
572  status = caRevokeCert( dbmsInfo, iCertRequest, CRYPT_UNUSED,
574  errorInfo );
575  if( cryptStatusError( status ) )
576  {
577  /* If we get a not-found error this is an allowable condition
578  since the certificate may have expired or been otherwise
579  removed after the revocation request was received, so we
580  just delete the entry */
581  status = \
582  deleteIncompleteRequest( dbmsInfo, certID, certIDlength, status,
583  ( status == CRYPT_ERROR_NOTFOUND ) ? \
584  "Deleted revocation request for "
585  "non-present certificate" : \
586  "Couldn't revoke certificate from "
587  "revocation request, deleting "
588  "request and continuing" );
589  }
590  krnlSendNotifier( iCertRequest, IMESSAGE_DECREFCOUNT );
591  }
592  if( errorCount >= FAILSAFE_ITERATIONS_SMALL || \
593  iterationCount >= FAILSAFE_ITERATIONS_LARGE )
594  {
595  /* See note with earlier code */
596  DEBUG_DIAG(( "Certificate-fetch loop detected" ));
597  assert( DEBUG_WARN );
598  }
599 
600  /* If we ran into a problem, perform a fallback general delete of
601  entries that caused the problem */
602  if( status != CRYPT_ERROR_NOTFOUND )
603  {
604  updateCertErrorLogMsg( dbmsInfo, status, "Revocation request "
605  "cleanup operation failed, performing "
606  "fallback straight delete" );
607  status = dbmsStaticUpdate(
608  "DELETE FROM certRequests WHERE type = "
610  if( cryptStatusError( status ) )
611  {
612  updateCertErrorLogMsg( dbmsInfo, status, "Fallback straight "
613  "delete failed" );
614  retExtErr( status,
615  ( status, errorInfo, getDbmsErrorInfo( dbmsInfo ),
616  "Revocation request cleanup operation failed: " ) );
617  }
618  }
619 
620  return( resetErrorInfo( dbmsInfo ) );
621  }
622 #endif /* USE_DBMS */