cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ca_issue.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib DBMS CA Certificate Issue Interface *
4 * Copyright Peter Gutmann 1996-2007 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "keyset.h"
11  #include "dbms.h"
12 #else
13  #include "crypt.h"
14  #include "keyset/keyset.h"
15  #include "keyset/dbms.h"
16 #endif /* Compiler-specific includes */
17 
18 #ifdef USE_DBMS
19 
20 /****************************************************************************
21 * *
22 * Utility Functions *
23 * *
24 ****************************************************************************/
25 
26 /* Get the issue type (new request, renewal, etc) for a particular
27  certificate request or certificate */
28 
30 static int getCertIssueType( INOUT DBMS_INFO *dbmsInfo,
32  OUT_ENUM_OPT( CERTADD ) CERTADD_TYPE *issueType,
33  const BOOLEAN isCert )
34  {
35  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
36  BYTE certData[ MAX_QUERY_RESULT_SIZE + 8 ];
37  char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
39 
40  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
41  assert( isWritePtr( issueType, sizeof( CERTADD_TYPE ) ) );
42 
43  REQUIRES( isHandleRangeValid( iCertificate ) );
44 
45  /* Clear return value */
46  *issueType = CERTADD_NONE;
47 
48  /* Get the certID of the request that resulted in the certificate
49  creation */
50  status = getKeyID( certID, ENCODED_DBXKEYID_SIZE, &certIDlength,
51  iCertificate, CRYPT_CERTINFO_FINGERPRINT_SHA1 );
52  if( cryptStatusOK( status ) && isCert )
53  {
54  /* If it's a certificate we have to apply an extra level of
55  indirection to get the request that resulted in its creation */
56  initBoundData( boundDataPtr );
57  setBoundData( boundDataPtr, 0, certID, certIDlength );
58  status = dbmsQuery(
59  "SELECT reqCertID FROM certLog WHERE certID = ?",
60  certData, MAX_QUERY_RESULT_SIZE, &length,
61  boundDataPtr, DBMS_CACHEDQUERY_NONE,
63  if( cryptStatusOK( status ) )
64  {
65  if( length > ENCODED_DBXKEYID_SIZE )
66  length = ENCODED_DBXKEYID_SIZE;
67  memcpy( certID, certData, length );
68  certIDlength = length;
69  }
70  }
71  if( cryptStatusError( status ) )
72  return( status );
73 
74  /* Find out whether this was a certificate update by checking whether it
75  was added as a standard or renewal request, then set the update type
76  appropriately. The comparison for the action type is a bit odd since
77  some back-ends will return the action as text and some as a binary
78  numeric value, rather than relying on the back-end glue code to
79  perform the appropriate conversion we just check for either value
80  type.
81 
82  In addition to performing this basic check in order to figure out how
83  the replacement process should be handled we could in theory also
84  check that an update really is a pure update with all other portions
85  of the certificate being identical, however there are two problems
86  with this. The first is that we don't know when we get the request
87  how much of what's in the final certificate will be added by the CA
88  (in other words we'd have to check that the request is a proper
89  subset of the certificate that it's supposedly updating, which isn't
90  easily feasible). The second, more important one is that if a client
91  really wants to change the certificate details then they can just use
92  a CRYPT_CERTACTION_REQUEST_CERT in place of a
93  CRYPT_CERTACTION_REQUEST_RENEWAL and change whatever details they
94  want. The distinction between the two is really just an artefact of
95  CMP's messaging, and as usual the CMP spec confuses the issue
96  completely, with varied discussions of updating a key vs. updating a
97  certificate vs. obtaining a certificate. Because of this there
98  doesn't seem to be anything to be gained by performing whatever it is
99  that we could be checking for here */
100  initBoundData( boundDataPtr );
101  setBoundData( boundDataPtr, 0, certID, certIDlength );
102  status = dbmsQuery(
103  "SELECT action FROM certLog WHERE certID = ?",
104  certData, MAX_QUERY_RESULT_SIZE, &length,
105  boundDataPtr, DBMS_CACHEDQUERY_NONE,
107  if( cryptStatusError( status ) )
108  return( status );
109  switch( certData[ 0 ] )
110  {
113  *issueType = CERTADD_PARTIAL;
114  return( CRYPT_OK );
115 
118  *issueType = CERTADD_PARTIAL_RENEWAL;
119  return( CRYPT_OK );
120 
121  default:
122  DEBUG_DIAG(( "Unknown certificate action type %d",
123  certData[ 0 ] ));
124  assert( DEBUG_WARN );
125  }
126 
127  return( CRYPT_ERROR_NOTFOUND );
128  }
129 
130 /* Sanitise a new certificate of potentially dangerous attributes by
131  creating a template of the disallowed values and setting them as blocked
132  attributes. For our use we clear all CA and CA-equivalent attributes to
133  prevent users from submitting requests that turn them into CAs */
134 
135 CHECK_RETVAL \
136 static int sanitiseCertAttributes( IN_HANDLE const CRYPT_CERTIFICATE iCertificate )
137  {
138  CRYPT_CERTIFICATE iTemplateCertificate;
139  MESSAGE_CREATEOBJECT_INFO createInfo;
140  int status;
141 
142  REQUIRES( isHandleRangeValid( iCertificate ) );
143 
146  &createInfo, OBJECT_TYPE_CERTIFICATE );
147  if( cryptStatusError( status ) )
148  return( status );
149  iTemplateCertificate = createInfo.cryptHandle;
150 
151  /* Add as disallowed values the CA flag and and CA keyUsages, and any
152  CA-equivalent values, in this case the old Netscape usage flags which
153  (incredibly) are still used today by some CAs in place of the X.509
154  keyUsage extension. We can only do the latter if use of the obsolete
155  Netscape extensions is enabled, by default these are disabled so they
156  can still be sneaked in as blob attributes, but only if the user has
157  enabled the (disabled-by-default)
158  CRYPT_OPTION_CERT_SIGNUNRECOGNISEDATTRIBUTES option.
159 
160  The disabling of the CA flag isn't really necessary since the entire
161  basicConstraints extension is disallowed in certificate requests,
162  we only have it here for belt-and-suspenders security.
163 
164  Note that the blocked-attributes check currently assumes that the
165  value being checked for non-permitted values is a keyUsage-style
166  bitflag (since almost no attributes are allowed in certificate
167  requests for security purposes), if any other data types are blocked
168  then the checking will have to be extended to handle these */
169  status = krnlSendMessage( iTemplateCertificate, IMESSAGE_SETATTRIBUTE,
171 #ifdef USE_CERT_OBSOLETE
172  if( cryptStatusOK( status ) )
173  {
174  static const int value = CRYPT_NS_CERTTYPE_SSLCA | \
175  CRYPT_NS_CERTTYPE_SMIMECA | \
176  CRYPT_NS_CERTTYPE_OBJECTSIGNINGCA;
177 
178  status = krnlSendMessage( iTemplateCertificate, IMESSAGE_SETATTRIBUTE,
179  ( MESSAGE_CAST ) &value,
181  }
182 #endif /* USE_CERT_OBSOLETE */
183  if( cryptStatusOK( status ) )
184  {
185  static const int value = KEYUSAGE_CA;
186 
187  status = krnlSendMessage( iTemplateCertificate, IMESSAGE_SETATTRIBUTE,
188  ( MESSAGE_CAST ) &value,
190  }
191  if( cryptStatusOK( status ) )
192  status = krnlSendMessage( iCertificate, IMESSAGE_SETATTRIBUTE,
193  ( MESSAGE_CAST ) &iTemplateCertificate,
194  CRYPT_IATTRIBUTE_BLOCKEDATTRS );
195  if( status == CRYPT_ERROR_INVALID )
196  {
197  /* If the request would have resulted in the creation of an invalid
198  certificate, report it as an error with the request */
199  status = CAMGMT_ARGERROR_REQUEST;
200  }
201  krnlSendNotifier( iTemplateCertificate, IMESSAGE_DECREFCOUNT );
202 
203  return( status );
204  }
205 
206 /* Make sure that an about-to-be-issued certificate hasn't been added to the
207  certificate store yet. In theory we wouldn't need to do this since the
208  keyID uniqueness constraint will catch duplicates, however duplicates are
209  allowed for updates and won't automatically be caught for partial adds
210  because the keyID has to be added in a special form to enable the
211  completion of the partial add to work. What we therefore need to check
212  for is that a partial add (which will add the keyID in special form)
213  won't in the future clash with a keyID in standard form. The checking
214  for a keyID clash in special form happens automagically through the
215  uniqueness constraint.
216 
217  There are two special cases in which the issue can fail during the
218  completion rather than initial add phase, one is during an update (which
219  can't be avoided, since clashes are legal for this and we can't resolve
220  things until the completion phase) and the other is through a race
221  condition caused by the following sequence of updates:
222 
223  1: check keyID -> OK
224  2: check keyID -> OK
225  1: add as ESC1+keyID
226  1: issue as keyID
227  2: add as ESC1+keyID
228  2: issue -> fails
229 
230  This condition will be fairly rare. Note that in neither case are the
231  integrity constraints of the certificate issuing process violated, the
232  only thing that happens is that a failure due to duplicates is detected
233  at a later stage than it normally would be */
234 
236 static int checkDuplicateAdd( INOUT DBMS_INFO *dbmsInfo,
237  IN_HANDLE const CRYPT_CERTIFICATE iLocalCertificate,
238  IN_ENUM( CERTADD ) const CERTADD_TYPE issueType )
239  {
240  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
241  char keyID[ ENCODED_DBXKEYID_SIZE + 8 ];
242  int keyIDlength, status;
243 
244  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
245 
246  REQUIRES( isHandleRangeValid( iLocalCertificate ) );
247  REQUIRES( issueType > CERTADD_NONE && issueType < CERTADD_LAST );
248 
249  /* If it's a normal certificate issue then there's no problem with the
250  potential presence of pseudo-duplicates */
251  if( issueType != CERTADD_PARTIAL )
252  return( CRYPT_OK );
253 
254  /* Check whether a certificate with this keyID is already present in the
255  store */
256  status = getCertKeyID( keyID, ENCODED_DBXKEYID_SIZE, &keyIDlength,
257  iLocalCertificate );
258  if( cryptStatusError( status ) )
259  return( status );
260  initBoundData( boundDataPtr );
261  setBoundData( boundDataPtr, 0, keyID, keyIDlength );
262  status = dbmsQuery( \
263  "SELECT certData FROM certificates WHERE keyID = ?",
264  NULL, 0, NULL, boundDataPtr,
266  resetErrorInfo( dbmsInfo );
267  return( cryptStatusOK( status ) ? CRYPT_ERROR_DUPLICATE : CRYPT_OK );
268  }
269 
270 /* Replace one certificate (usually a partially-issued one) with another
271  (usually its completed form). The types of operations and their
272  corresponding add-type values are:
273 
274  ESC1 -> std CERTADD_PARTIAL Completion of partial
275  ESC1 -> ESC2 CERTADD_PARTIAL_RENEWAL First half of renewal
276  ESC2 -> std CERTADD_RENEWAL_COMPLETE Second half of renewal */
277 
279 static int completeCert( INOUT DBMS_INFO *dbmsInfo,
280  IN_HANDLE const CRYPT_CERTIFICATE iCertificate,
281  IN_ENUM( CERTADD ) const CERTADD_TYPE addType,
283  {
284  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
285  char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
286  int certIDlength, status;
287 
288  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
289 
290  REQUIRES( isHandleRangeValid( iCertificate ) );
291  REQUIRES( addType == CERTADD_PARTIAL || \
292  addType == CERTADD_PARTIAL_RENEWAL || \
293  addType == CERTADD_RENEWAL_COMPLETE );
294  REQUIRES( errorInfo != NULL );
295 
296  status = getKeyID( certID, ENCODED_DBXKEYID_SIZE, &certIDlength,
297  iCertificate, CRYPT_CERTINFO_FINGERPRINT_SHA1 );
298  if( cryptStatusError( status ) )
299  return( status );
300  status = addCert( dbmsInfo, iCertificate, CRYPT_CERTTYPE_CERTIFICATE,
301  ( addType == CERTADD_PARTIAL_RENEWAL ) ? \
303  DBMS_UPDATE_BEGIN, errorInfo );
304  if( cryptStatusOK( status ) )
305  {
306  char specialCertID[ ENCODED_DBXKEYID_SIZE + 8 ];
307 
308  /* Turn the general certID into the form required for special-case
309  certificate data */
310  memcpy( specialCertID, certID, certIDlength );
311  memcpy( specialCertID,
312  ( addType == CERTADD_RENEWAL_COMPLETE ) ? \
314  initBoundData( boundDataPtr );
315  setBoundData( boundDataPtr, 0, specialCertID, certIDlength );
316  status = dbmsUpdate(
317  "DELETE FROM certificates WHERE certID = ?",
318  boundDataPtr,
319  ( addType == CERTADD_PARTIAL_RENEWAL ) ? \
321  }
322  if( cryptStatusOK( status ) )
323  {
324  if( addType != CERTADD_PARTIAL_RENEWAL )
325  {
326  status = updateCertLog( dbmsInfo,
328  NULL, 0, NULL, 0, certID, certIDlength,
329  NULL, 0, DBMS_UPDATE_COMMIT );
330  }
331  }
332  else
333  {
334  /* Something went wrong, abort the transaction */
335  dbmsUpdate( NULL, NULL, DBMS_UPDATE_ABORT );
336  }
337 
338  /* If the operation failed, record the details */
339  if( cryptStatusError( status ) )
340  {
341  updateCertErrorLog( dbmsInfo, status,
342  "Certificate creation - completion operation "
343  "failed", NULL, 0, NULL, 0,
344  certID, certIDlength, NULL, 0 );
345  retExtErr( status,
346  ( status, errorInfo, getDbmsErrorInfo( dbmsInfo ),
347  "Certificate creation - completion operation "
348  "failed: " ) );
349  }
350 
351  return( CRYPT_OK );
352  }
353 
354 /****************************************************************************
355 * *
356 * Certificate Issue Functions *
357 * *
358 ****************************************************************************/
359 
360 /* Complete a certificate renewal operation by revoking the certificate to
361  be replaced and replacing it with the newly-issued certificate */
362 
363 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
364 int completeCertRenewal( INOUT DBMS_INFO *dbmsInfo,
366  INOUT ERROR_INFO *errorInfo )
367  {
368  CRYPT_CERTIFICATE iOrigCertificate = DUMMY_INIT;
369  char keyID[ ENCODED_DBXKEYID_SIZE + 8 ];
370  int keyIDlength, dummy, status;
371 
372  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
373 
374  REQUIRES( isHandleRangeValid( iReplaceCertificate ) );
375  REQUIRES( errorInfo != NULL );
376 
377  /* Extract the key ID from the new certificate and use it to fetch the
378  existing certificate issued for the same key */
379  status = getCertKeyID( keyID, ENCODED_DBXKEYID_SIZE, &keyIDlength,
380  iReplaceCertificate );
381  if( cryptStatusOK( status ) )
382  {
383  status = getItemData( dbmsInfo, &iOrigCertificate, &dummy,
384  KEYMGMT_ITEM_PUBLICKEY, CRYPT_IKEYID_KEYID,
385  keyID, keyIDlength, KEYMGMT_FLAG_NONE,
386  errorInfo );
387  if( status == CRYPT_ERROR_NOTFOUND )
388  {
389  /* If the original certificate fetch fails with a notfound error
390  this is OK since we may be resuming from a point where the
391  revocation has already occurred or the certificate may have
392  already expired or been otherwise replaced, so we just slide
393  in the new certificate */
394  return( completeCert( dbmsInfo, iReplaceCertificate,
395  CERTADD_RENEWAL_COMPLETE, errorInfo ) );
396  }
397  }
398  if( cryptStatusError( status ) )
399  {
400  retExtErr( status,
401  ( status, errorInfo, getDbmsErrorInfo( dbmsInfo ),
402  "Couldn't get information for the certificate to be "
403  "renewed: " ) );
404  }
405 
406  /* Replace the original certificate with the new one */
407  status = revokeCertDirect( dbmsInfo, iOrigCertificate,
408  CRYPT_CERTACTION_REVOKE_CERT, errorInfo );
409  if( cryptStatusOK( status ) )
410  status = completeCert( dbmsInfo, iReplaceCertificate,
411  CERTADD_RENEWAL_COMPLETE, errorInfo );
412  krnlSendNotifier( iOrigCertificate, IMESSAGE_DECREFCOUNT );
413 
414  return( status );
415  }
416 
417 /* Issue a certificate from a certificate request */
418 
419 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 6 ) ) \
420 int caIssueCert( INOUT DBMS_INFO *dbmsInfo,
424  IN_ENUM( CRYPT_CERTACTION ) const CRYPT_CERTACTION_TYPE action,
425  INOUT ERROR_INFO *errorInfo )
426  {
427  CRYPT_CERTIFICATE iLocalCertificate;
428  MESSAGE_CREATEOBJECT_INFO createInfo;
429  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
430  BYTE certData[ MAX_CERT_SIZE + 8 ];
431  char issuerID[ ENCODED_DBXKEYID_SIZE + 8 ];
432  char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
433  char reqCertID[ ENCODED_DBXKEYID_SIZE + 8 ];
434  CERTADD_TYPE addType = CERTADD_NORMAL, issueType;
435  int certDataLength = DUMMY_INIT, issuerIDlength, certIDlength;
437 
438  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
439  assert( ( iCertificate == NULL ) || \
440  isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
441 
442  REQUIRES( isHandleRangeValid( caKey ) );
443  REQUIRES( isHandleRangeValid( iCertRequest ) );
444  REQUIRES( action == CRYPT_CERTACTION_ISSUE_CERT || \
445  action == CRYPT_CERTACTION_CERT_CREATION );
446  REQUIRES( errorInfo != NULL );
447 
448  /* Clear return value */
449  if( iCertificate != NULL )
450  *iCertificate = CRYPT_ERROR;
451 
452  /* Extract the information that we need from the certificate request */
453  status = getCertIssueType( dbmsInfo, iCertRequest, &issueType, FALSE );
454  if( cryptStatusOK( status ) )
455  status = getKeyID( reqCertID, ENCODED_DBXKEYID_SIZE, &reqCertIDlength,
456  iCertRequest, CRYPT_CERTINFO_FINGERPRINT_SHA1 );
457  if( cryptStatusError( status ) )
458  {
459  if( cryptArgError( status ) )
460  status = CAMGMT_ARGERROR_REQUEST;
461  retExtArg( status,
462  ( status, errorInfo,
463  "Couldn't extract certificate request information "
464  "needed to issue certificate" ) );
465  }
466 
467  /* We're ready to perform the certificate issue transaction. First we
468  turn the request into a certificate */
471  &createInfo, OBJECT_TYPE_CERTIFICATE );
472  if( cryptStatusError( status ) )
473  return( status );
474  iLocalCertificate = createInfo.cryptHandle;
475  status = krnlSendMessage( iLocalCertificate, IMESSAGE_SETATTRIBUTE,
476  ( MESSAGE_CAST ) &iCertRequest,
478  if( cryptStatusError( status ) )
479  {
480  krnlSendNotifier( iLocalCertificate, IMESSAGE_DECREFCOUNT );
481  retExt( status,
482  ( status, errorInfo,
483  "Couldn't create certificate from certificate request "
484  "data" ) );
485  }
486 
487  /* Sanitise the new certificate of potentially dangerous attributes */
488  status = sanitiseCertAttributes( iLocalCertificate );
489  if( cryptStatusError( status ) )
490  {
491  krnlSendNotifier( iLocalCertificate, IMESSAGE_DECREFCOUNT );
492  retExtArg( status,
493  ( status, errorInfo,
494  "Certificate request contains attributes that would "
495  "result in the creation of a CA rather than a normal "
496  "user certificate" ) );
497  }
498 
499  /* Finally, sign the certificate */
500  status = krnlSendMessage( iLocalCertificate, IMESSAGE_CRT_SIGN, NULL,
501  caKey );
502  if( cryptStatusError( status ) )
503  {
504  krnlSendNotifier( iLocalCertificate, IMESSAGE_DECREFCOUNT );
505  if( status == CRYPT_ARGERROR_VALUE )
506  status = CAMGMT_ARGERROR_CAKEY;
507  retExtArg( status,
508  ( status, errorInfo,
509  "Couldn't sign certificate created from certificate "
510  "request" ) );
511  }
512 
513  /* Extract the information that we need from the newly-created
514  certificate */
515  status = getKeyID( certID, ENCODED_DBXKEYID_SIZE, &certIDlength,
516  iLocalCertificate, CRYPT_CERTINFO_FINGERPRINT_SHA1 );
517  if( cryptStatusOK( status ) )
518  status = getKeyID( issuerID, ENCODED_DBXKEYID_SIZE, &issuerIDlength,
519  iLocalCertificate,
520  CRYPT_IATTRIBUTE_ISSUERANDSERIALNUMBER );
521  if( cryptStatusOK( status ) )
522  status = extractCertData( iLocalCertificate,
523  CRYPT_CERTFORMAT_CERTIFICATE, certData,
524  MAX_CERT_SIZE, &certDataLength );
525  if( cryptStatusError( status ) )
526  {
527  krnlSendNotifier( iLocalCertificate, IMESSAGE_DECREFCOUNT );
528  retExt( status,
529  ( status, errorInfo,
530  "Couldn't extract new certificate data to add to "
531  "certificate store" ) );
532  }
533 
534  /* If we're doing a partial certificate creation, handle the
535  complexities created by things like certificate renewals that create
536  pseudo-duplicates while the update is taking place */
537  if( action == CRYPT_CERTACTION_CERT_CREATION )
538  {
539  status = checkDuplicateAdd( dbmsInfo, iLocalCertificate, issueType );
540  if( cryptStatusError( status ) )
541  {
542  krnlSendNotifier( iLocalCertificate, IMESSAGE_DECREFCOUNT );
543  retExt( status,
544  ( status, errorInfo,
545  "Certificate already exists in certificate store" ) );
546  }
547 
548  /* This is a partial add, make sure that the certificate is added in
549  the appropriate manner */
550  addType = CERTADD_PARTIAL;
551  }
552 
553  /* Update the certificate store */
554  status = addCert( dbmsInfo, iLocalCertificate, CRYPT_CERTTYPE_CERTIFICATE,
555  addType, DBMS_UPDATE_BEGIN, errorInfo );
556  if( cryptStatusOK( status ) )
557  status = updateCertLog( dbmsInfo, action, certID, certIDlength,
558  reqCertID, reqCertIDlength, NULL, 0,
559  certData, certDataLength,
561  if( cryptStatusOK( status ) )
562  {
563  initBoundData( boundDataPtr );
564  setBoundData( boundDataPtr, 0, reqCertID, reqCertIDlength );
565  status = dbmsUpdate(
566  "DELETE FROM certRequests WHERE certID = ?",
567  boundDataPtr, DBMS_UPDATE_COMMIT );
568  }
569  else
570  {
571  /* Something went wrong, abort the transaction */
572  dbmsUpdate( NULL, NULL, DBMS_UPDATE_ABORT );
573  }
574 
575  /* If the operation failed, record the details */
576  if( cryptStatusError( status ) )
577  {
578  updateCertErrorLog( dbmsInfo, status,
579  ( action == CRYPT_CERTACTION_ISSUE_CERT ) ? \
580  "Certificate issue operation failed" : \
581  "Certificate creation operation failed",
582  NULL, 0, reqCertID, reqCertIDlength, NULL, 0,
583  NULL, 0 );
584  krnlSendNotifier( iLocalCertificate, IMESSAGE_DECREFCOUNT );
585  retExtErr( status,
586  ( status, errorInfo, getDbmsErrorInfo( dbmsInfo ),
587  ( action == CRYPT_CERTACTION_ISSUE_CERT ) ? \
588  "Certificate issue operation failed: " : \
589  "Certificate creation operation failed: " ) );
590  }
591 
592  /* The certificate has been successfully issued, return it to the caller
593  if necessary */
594  if( iCertificate != NULL )
595  *iCertificate = iLocalCertificate;
596  else
597  {
598  /* The caller isn't interested in the certificate, destroy it */
599  krnlSendNotifier( iLocalCertificate, IMESSAGE_DECREFCOUNT );
600  }
601  return( CRYPT_OK );
602  }
603 
604 /* Complete a previously-started certificate issue */
605 
606 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
607 int caIssueCertComplete( INOUT DBMS_INFO *dbmsInfo,
608  IN_HANDLE const CRYPT_CERTIFICATE iCertificate,
609  IN_ENUM( CRYPT_CERTACTION ) \
610  const CRYPT_CERTACTION_TYPE action,
611  INOUT ERROR_INFO *errorInfo )
612  {
613  char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
614  int certIDlength, status;
615 
616  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
617 
618  REQUIRES( isHandleRangeValid( iCertificate ) );
622  REQUIRES( errorInfo != NULL );
623 
624  /* Extract the information that we need from the certificate */
625  status = getKeyID( certID, ENCODED_DBXKEYID_SIZE, &certIDlength,
626  iCertificate, CRYPT_CERTINFO_FINGERPRINT_SHA1 );
627  if( cryptStatusError( status ) )
628  return( status );
629 
630  /* If we're completing the certificate issue process, replace the
631  incomplete certificate with the completed one and exit */
633  {
634  CERTADD_TYPE issueType;
635 
636  status = getCertIssueType( dbmsInfo, iCertificate, &issueType, TRUE );
637  if( cryptStatusError( status ) )
638  {
639  retExt( status,
640  ( status, errorInfo,
641  "Couldn't get original certificate issue type to "
642  "complete certificate issue operation: " ) );
643  }
644  status = completeCert( dbmsInfo, iCertificate, issueType, errorInfo );
645  if( cryptStatusError( status ) )
646  return( status );
647 
648  /* If we're doing a certificate renewal, complete the multi-phase
649  update required to replace an existing certificate */
650  if( issueType == CERTADD_PARTIAL_RENEWAL )
651  {
652  return( completeCertRenewal( dbmsInfo, iCertificate,
653  errorInfo ) );
654  }
655 
656  return( CRYPT_OK );
657  }
658 
659  /* If we're abandoning the certificate issue process, delete the
660  incomplete certificate and exit */
662  {
663  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
664  char incompleteCertID[ ENCODED_DBXKEYID_SIZE + 8 ];
665 
666  memcpy( incompleteCertID, certID, certIDlength );
667  memcpy( incompleteCertID, KEYID_ESC1, KEYID_ESC_SIZE );
668  initBoundData( boundDataPtr );
669  setBoundData( boundDataPtr, 0, incompleteCertID, certIDlength );
670  status = dbmsUpdate(
671  "DELETE FROM certificates WHERE certID = ?",
672  boundDataPtr, DBMS_UPDATE_BEGIN );
673  if( cryptStatusOK( status ) )
674  status = updateCertLog( dbmsInfo, action, NULL, 0, NULL, 0,
675  certID, certIDlength, NULL, 0,
677  else
678  {
679  /* Something went wrong, abort the transaction */
680  dbmsUpdate( NULL, NULL, DBMS_UPDATE_ABORT );
681  }
682  if( cryptStatusOK( status ) )
683  return( CRYPT_OK );
684 
685  /* The drop operation failed, record the details and fall back to a
686  straight delete */
687  updateCertErrorLog( dbmsInfo, status,
688  "Certificate creation - drop operation failed, "
689  "performing straight delete", NULL, 0, NULL, 0,
690  certID, certIDlength, NULL, 0 );
691  status = dbmsUpdate(
692  "DELETE FROM certificates WHERE certID = ?",
693  boundDataPtr, DBMS_UPDATE_NORMAL );
694  if( cryptStatusError( status ) )
695  {
696  updateCertErrorLogMsg( dbmsInfo, status, "Fallback straight "
697  "delete failed" );
698  retExtErr( status,
699  ( status, errorInfo, getDbmsErrorInfo( dbmsInfo ),
700  "Certificate creation - drop operation failed: " ) );
701  }
702  return( CRYPT_OK );
703  }
704 
705  /* We're reversing a certificate creation as a compensating transaction
706  for an aborted certificate issue, we need to explicitly revoke the
707  certificate rather than just deleting it */
709 
710  return( revokeCertDirect( dbmsInfo, iCertificate,
712  errorInfo ) );
713  }
714 #endif /* USE_DBMS */