cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ca_misc.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib DBMS CA Certificate Misc Interface *
4 * Copyright Peter Gutmann 1996-2007 *
5 * *
6 ****************************************************************************/
7 
8 #include <stdio.h> /* For snprintf() */
9 #if defined( INC_ALL )
10  #include "crypt.h"
11  #include "asn1.h"
12  #include "keyset.h"
13  #include "dbms.h"
14 #else
15  #include "crypt.h"
16  #include "enc_dec/asn1.h"
17  #include "keyset/keyset.h"
18  #include "keyset/dbms.h"
19 #endif /* Compiler-specific includes */
20 
21 #ifdef USE_DBMS
22 
23 /****************************************************************************
24 * *
25 * Utility Functions *
26 * *
27 ****************************************************************************/
28 
29 #if 0
30 
31 /* Get the ultimate successor certificate for one that's been superseded */
32 
33 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
34 static int getSuccessorCert( INOUT DBMS_INFO *dbmsInfo,
36  IN_BUFFER( initialCertIDlength ) \
37  const char *initialCertID,
38  IN_LENGTH_SHORT const int initialCertIDlength )
39  {
40  char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
41  int chainingLevel, status;
42 
43  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
44  assert( isWritePtr( *iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
45  assert( isReadPtr( initialCertID, initialCertIDlength ) );
46 
47  /* Walk through the chain of renewals in the certificate store log until
48  we find the ultimate successor certificate to the current one */
49  memcpy( certID, initialCertID, initialCertIDlength );
50  for( chainingLevel = 0, status = CRYPT_ERROR_NOTFOUND;
51  status == CRYPT_ERROR_NOTFOUND && \
52  chainingLevel < FAILSAFE_ITERATIONS_MED;
53  chainingLevel++ )
54  {
55  BYTE keyCertID[ DBXKEYID_SIZE + 8 ];
56  char certData[ MAX_QUERY_RESULT_SIZE + 8 ];
58 
59  /* Find the request to renew this certificate */
60  status = dbmsQuery(
61  "SELECT certID FROM certLog WHERE subjCertID = ? "
62  "AND action = " TEXT_CERTACTION_REQUEST_RENEWAL,
63  certData, &certDataLength, certID,
64  strlen( certID ), 0, DBMS_CACHEDQUERY_NONE,
66  if( cryptStatusError( status ) )
67  return( status );
68 
69  /* Find the resulting certificate */
70  memcpy( certID, certData,
71  min( certDataLength, ENCODED_DBXKEYID_SIZE + 1 ) );
72  certID[ MAX_ENCODED_DBXKEYID_SIZE ] = '\0';
73  status = dbmsQuery(
74  "SELECT certID FROM certLog WHERE reqCertID = ? "
75  "AND action = " TEXT_CERTACTION_CERT_CREATION,
76  certData, &certDataLength, certID,
77  strlen( certID ), 0, DBMS_CACHEDQUERY_NONE,
79  if( cryptStatusOK( status ) )
80  {
81  status = length = \
82  base64decode( keyCertID, certData,
83  min( certDataLength, ENCODED_DBXKEYID_SIZE ),
85  assert( !cryptStatusError( status ) );
86  }
87  if( cryptStatusError( status ) )
88  return( status );
89 
90  /* Try and get the replacement certificate */
91  status = getItemData( dbmsInfo, iCertificate, &dummy,
92  getKeyName( CRYPT_IKEYID_CERTID ),
93  keyCertID, length, KEYMGMT_ITEM_PUBLICKEY,
95  }
96  if( chainingLevel >= FAILSAFE_ITERATIONS_MED )
97  {
98  /* We've chained through too many entries, bail out */
99  return( CRYPT_ERROR_OVERFLOW );
100  }
101 
102  return( status );
103  }
104 #endif /* 0 */
105 
106 /****************************************************************************
107 * *
108 * Logging Functions *
109 * *
110 ****************************************************************************/
111 
112 /* Add an entry to the CA log */
113 
114 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
115 int updateCertLog( INOUT DBMS_INFO *dbmsInfo,
116  IN_ENUM( CRYPT_CERTACTION ) const CRYPT_CERTACTION_TYPE action,
117  IN_BUFFER_OPT( certIDlength ) const char *certID,
119  IN_BUFFER_OPT( reqCertIDlength ) const char *reqCertID,
121  IN_BUFFER_OPT( subjCertIDlength ) const char *subjCertID,
123  IN_BUFFER_OPT( dataLength ) const void *data,
124  IN_LENGTH_SHORT_Z const int dataLength,
125  IN_ENUM( DBMS_UPDATE ) const DBMS_UPDATE_TYPE updateType )
126  {
127  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
128  char sqlBuffer[ MAX_SQL_QUERY_SIZE + 8 ];
129  char certIDbuffer[ ENCODED_DBXKEYID_SIZE + 8 ];
130  char encodedCertData[ MAX_ENCODED_CERT_SIZE + 8 ];
131  const time_t boundDate = getApproxTime();
132  int localCertIDlength = certIDlength, sqlOffset, sqlLength, boundDataIndex;
133 
134  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
135  assert( ( certID == NULL && certIDlength == 0 ) || \
136  isReadPtr( certID, certIDlength ) );
137  assert( ( reqCertID == NULL && reqCertIDlength == 0 ) || \
138  isReadPtr( reqCertID, reqCertIDlength ) );
139  assert( ( subjCertID == NULL && subjCertIDlength == 0 ) || \
140  isReadPtr( subjCertID, subjCertIDlength ) );
141  assert( ( data == NULL && dataLength == 0 ) || \
142  isReadPtr( data, dataLength ) );
143 
144  REQUIRES( action > CRYPT_CERTACTION_NONE && \
145  action < CRYPT_CERTACTION_LAST );
146  REQUIRES( ( certID == NULL && certIDlength == 0 ) || \
147  ( certID != NULL && \
148  certIDlength > 0 && \
149  certIDlength < MAX_INTLENGTH_SHORT ) );
150  REQUIRES( ( reqCertID == NULL && reqCertIDlength == 0 ) || \
151  ( reqCertID != NULL && \
152  reqCertIDlength > 0 && \
153  reqCertIDlength < MAX_INTLENGTH_SHORT ) );
154  REQUIRES( ( subjCertID == NULL && subjCertIDlength == 0 ) || \
155  ( subjCertID != NULL && \
156  subjCertIDlength > 0 && \
157  subjCertIDlength < MAX_INTLENGTH_SHORT ) );
158  REQUIRES( ( data == NULL && dataLength == 0 ) || \
159  ( data != NULL && \
160  dataLength > 0 && \
161  dataLength < MAX_INTLENGTH_SHORT ) );
162  REQUIRES( updateType > DBMS_UPDATE_NONE && \
163  updateType < DBMS_UPDATE_LAST );
164 
165  /* Build up the necessary SQL format string required to insert the log
166  entry. This is complicated somewhat by the fact that some of the
167  values may be NULL so we have to insert them by naming the columns
168  (some databases allow the use of the DEFAULT keyword but this isn't
169  standardised enough to be safe) */
170  strlcpy_s( sqlBuffer, MAX_SQL_QUERY_SIZE,
171  "INSERT INTO certLog (action, actionTime, certID" );
172  if( reqCertID != NULL )
173  strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ", reqCertID" );
174  if( subjCertID != NULL )
175  strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ", subjCertID" );
176  if( data != NULL )
177  strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ", certData" );
178  strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ") VALUES (" );
179  sqlOffset = strlen( sqlBuffer );
180  sqlLength = MAX_SQL_QUERY_SIZE - sqlOffset;
181  sprintf_s( sqlBuffer + sqlOffset, sqlLength, "%d, ?, ?", action );
182  if( reqCertID != NULL )
183  strlcat_s( sqlBuffer + sqlOffset, sqlLength, ", ?" );
184  if( subjCertID != NULL )
185  strlcat_s( sqlBuffer + sqlOffset, sqlLength, ", ?" );
186  if( data != NULL )
187  strlcat_s( sqlBuffer + sqlOffset, sqlLength, ", ?" );
188  strlcat_s( sqlBuffer + sqlOffset, sqlLength, ")" );
189 
190  /* If we're not worried about the certID we just insert a nonce value
191  which is used to meet the constraints for a unique entry. In order
192  to ensure that it doesn't clash with a real certID we set the first
193  four characters to an out-of-band value */
194  if( certID == NULL )
195  {
197  BYTE nonce[ KEYID_SIZE + 8 ];
198  int status;
199 
200  setMessageData( &msgData, nonce, KEYID_SIZE );
202  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
203  if( cryptStatusOK( status ) )
204  status = base64encode( certIDbuffer, ENCODED_DBXKEYID_SIZE,
205  &localCertIDlength, nonce, DBXKEYID_SIZE,
207  if( cryptStatusError( status ) )
208  {
209  /* Normally this is a should-never-occur error, however if
210  cryptlib has been shut down from another thread the kernel
211  will fail all non shutdown-related calls with a permission
212  error. To avoid false alarms, we mask out failures due to
213  permission errors */
214  assert( ( status == CRYPT_ERROR_PERMISSION ) || DEBUG_WARN );
215  return( status );
216  }
217  memset( certIDbuffer, '-', 4 );
218  certID = certIDbuffer;
219  }
220 
221  /* Set up the parameter information and update the certificate store
222  log */
223  initBoundData( boundDataPtr );
224  setBoundDataDate( boundDataPtr, 0, &boundDate );
225  setBoundData( boundDataPtr, 1, certID, localCertIDlength );
226  boundDataIndex = 2;
227  if( reqCertID != NULL )
228  setBoundData( boundDataPtr, boundDataIndex++, reqCertID,
229  reqCertIDlength );
230  if( subjCertID != NULL )
231  setBoundData( boundDataPtr, boundDataIndex++, subjCertID,
232  subjCertIDlength );
233  if( data != NULL )
234  {
235  if( hasBinaryBlobs( dbmsInfo ) )
236  {
237  setBoundDataBlob( boundDataPtr, boundDataIndex,
238  data, dataLength );
239  }
240  else
241  {
242  int encodedDataLength, status;
243 
244  status = base64encode( encodedCertData, MAX_ENCODED_CERT_SIZE,
245  &encodedDataLength, data, dataLength,
247  if( cryptStatusError( status ) )
248  {
249  DEBUG_DIAG(( "Couldn't base64-encode data" ));
250  assert( DEBUG_WARN );
251  return( status );
252  }
253  setBoundData( boundDataPtr, boundDataIndex,
254  encodedCertData, encodedDataLength );
255  }
256  }
257  return( dbmsUpdate( sqlBuffer, boundDataPtr, updateType ) );
258  }
259 
260 RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
261 int updateCertErrorLog( INOUT DBMS_INFO *dbmsInfo,
262  IN_ERROR const int errorStatus,
263  IN_STRING const char *errorString,
264  IN_BUFFER_OPT( certIDlength ) const char *certID,
265  IN_LENGTH_SHORT_Z const int certIDlength,
266  IN_BUFFER_OPT( reqCertIDlength ) const char *reqCertID,
267  IN_LENGTH_SHORT_Z const int reqCertIDlength,
268  IN_BUFFER_OPT( subjCertIDlength ) const char *subjCertID,
269  IN_LENGTH_SHORT_Z const int subjCertIDlength,
270  IN_BUFFER_OPT( dataLength ) const void *data,
271  IN_LENGTH_SHORT_Z const int dataLength )
272  {
273  STREAM stream;
274  BYTE errorData[ 64 + MAX_CERT_SIZE + 8 ];
275  const int errorStringLength = strlen( errorString );
276  int errorDataLength = DUMMY_INIT, status;
277 
278  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
279  assert( ( certID == NULL && certIDlength == 0 ) || \
280  isReadPtr( certID, certIDlength ) );
281  assert( ( reqCertID == NULL && reqCertIDlength == 0 ) || \
282  isReadPtr( reqCertID, reqCertIDlength ) );
283  assert( ( subjCertID == NULL && subjCertIDlength == 0 ) || \
284  isReadPtr( subjCertID, subjCertIDlength ) );
285  assert( ( data == NULL && dataLength == 0 ) || \
286  isReadPtr( data, dataLength ) );
287 
288  REQUIRES( cryptStatusError( errorStatus ) );
289  REQUIRES( errorString != NULL );
290  REQUIRES( ( certID == NULL && certIDlength == 0 ) || \
291  ( certID != NULL && \
292  certIDlength > 0 && \
293  certIDlength < MAX_INTLENGTH_SHORT ) );
294  REQUIRES( ( reqCertID == NULL && reqCertIDlength == 0 ) || \
295  ( reqCertID != NULL && \
296  reqCertIDlength > 0 && \
297  reqCertIDlength < MAX_INTLENGTH_SHORT ) );
298  REQUIRES( ( subjCertID == NULL && subjCertIDlength == 0 ) || \
299  ( subjCertID != NULL && \
300  subjCertIDlength > 0 && \
301  subjCertIDlength < MAX_INTLENGTH_SHORT ) );
302  REQUIRES( ( data == NULL && dataLength == 0 ) || \
303  ( data != NULL && \
304  dataLength > 0 && \
305  dataLength < MAX_INTLENGTH_SHORT ) );
306 
307  /* Encode the error information:
308 
309  SEQUENCE {
310  errorStatus INTEGER,
311  errorString UTF8String,
312  certData ANY OPTIONAL
313  }
314 
315  Note that the buffer we use is slightly larger than MAX_CERT_SIZE in
316  order to accomodate the error status information alongside the
317  largest possible certificate, in theory this means that if the database
318  back-end doesn't support binary blobs there won't be enough room in
319  the logging code to text-encode this worst-case scenario, but the use
320  of non-binary-blob capable database should be fairly rare so it's
321  easier to just rely on the logging code to catch this unlikely
322  scenario than to try and special-case around it */
323  sMemOpen( &stream, errorData, 64 + MAX_CERT_SIZE );
324  writeSequence( &stream, sizeofShortInteger( -errorStatus ) + \
325  ( int ) sizeofObject( errorStringLength ) + \
326  dataLength );
327  writeShortInteger( &stream, -errorStatus, DEFAULT_TAG );
328  status = writeCharacterString( &stream, errorString, errorStringLength,
329  BER_STRING_UTF8 );
330  if( dataLength > 0 )
331  status = swrite( &stream, data, dataLength );
332  if( cryptStatusOK( status ) )
333  errorDataLength = stell( &stream );
334  sMemDisconnect( &stream );
335  if( cryptStatusError( status ) )
336  {
337  sMemOpen( &stream, errorData, MAX_CERT_SIZE );
338  writeSequence( &stream, sizeofObject( 1 ) + sizeofObject( 31 ) );
339  writeShortInteger( &stream, -( CRYPT_ERROR_FAILED ), DEFAULT_TAG );
340  status = writeCharacterString( &stream,
341  "Error writing error information", 31,
342  BER_STRING_UTF8 );
343  if( cryptStatusOK( status ) )
344  errorDataLength = stell( &stream );
345  sMemDisconnect( &stream );
346  }
347  ENSURES( cryptStatusOK( status ) );
348 
349  /* Update the certificate store log with the error information as the
350  data value */
351  return( updateCertLog( dbmsInfo, CRYPT_CERTACTION_ERROR,
352  certID, certIDlength, reqCertID, reqCertIDlength,
353  subjCertID, subjCertIDlength,
354  errorData, errorDataLength, DBMS_UPDATE_NORMAL ) );
355  }
356 
357 RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
358 int updateCertErrorLogMsg( INOUT DBMS_INFO *dbmsInfo,
359  IN_ERROR const int errorStatus,
360  IN_STRING const char *errorString )
361  {
362  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
363 
364  return( updateCertErrorLog( dbmsInfo, errorStatus, errorString,
365  NULL, 0, NULL, 0, NULL, 0, NULL, 0 ) );
366  }
367 
368 /****************************************************************************
369 * *
370 * Miscellaneous CA Functions *
371 * *
372 ****************************************************************************/
373 
374 /* Get the PKI user that originally authorised the issuance of a certificate.
375  This can involve chaining back through multiple generations of
376  certificates, for example to check authorisation on a revocation request
377  we might have to go through:
378 
379  rev_req: get reqCertID = update_req
380  update_req: get reqCertID = cert_req
381  cert_req: get reqCertID = init_req
382  init_req: get reqCertID = pki_user */
383 
384 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 5 ) ) \
385 int caGetIssuingUser( INOUT DBMS_INFO *dbmsInfo,
387  IN_BUFFER( initialCertIDlength ) const char *initialCertID,
389  const int initialCertIDlength,
391  {
392  char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
393  int certIDlength, chainingLevel, dummy, status;
394 
395  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
396  assert( isWritePtr( iPkiUser, sizeof( CRYPT_CERTIFICATE ) ) );
397  assert( isReadPtr( initialCertID, ENCODED_DBXKEYID_SIZE ) );
398 
399  REQUIRES( initialCertIDlength == ENCODED_DBXKEYID_SIZE );
400  REQUIRES( errorInfo != NULL );
401 
402  /* Clear return value */
403  *iPkiUser = CRYPT_ERROR;
404 
405  /* Walk through the chain of updates in the certificate store log until
406  we find the PKI user that authorised the first certificate issue */
407  memcpy( certID, initialCertID, initialCertIDlength );
408  certIDlength = initialCertIDlength;
409  for( chainingLevel = 0; chainingLevel < FAILSAFE_ITERATIONS_MED;
410  chainingLevel++ )
411  {
412  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
413  BYTE certData[ MAX_QUERY_RESULT_SIZE + 8 ];
414  int certDataLength;
415 
416  /* Find out whether this is a PKI user. The comparison for the
417  action type is a bit odd since some back-ends will return the
418  action as text and some as a binary numeric value. Rather than
419  relying on the back-end glue code to perform the appropriate
420  conversion we just check for either value type */
421  initBoundData( boundDataPtr );
422  setBoundData( boundDataPtr, 0, certID, certIDlength );
423  status = dbmsQuery(
424  "SELECT action FROM certLog WHERE certID = ?",
425  certData, MAX_QUERY_RESULT_SIZE, &certDataLength,
426  boundDataPtr, DBMS_CACHEDQUERY_NONE,
428  if( cryptStatusError( status ) )
429  return( status );
430  if( certData[ 0 ] == CRYPT_CERTACTION_ADDUSER || \
431  certData[ 0 ] == TEXTCH_CERTACTION_ADDUSER )
432  {
433  /* We've found the PKI user, we're done */
434  break;
435  }
436 
437  /* Find the certificate that was issued, recorded either as a
438  CERTACTION_CERT_CREATION for a multi-phase CMP-based certificate
439  creation or a CERTACTION_ISSUE_CERT for a one-step creation */
440  status = dbmsQuery(
441  "SELECT reqCertID FROM certLog WHERE certID = ?",
442  certData, MAX_QUERY_RESULT_SIZE, &certDataLength,
443  boundDataPtr, DBMS_CACHEDQUERY_NONE,
445  if( cryptStatusError( status ) )
446  return( status );
447  certIDlength = min( certDataLength, ENCODED_DBXKEYID_SIZE );
448  memcpy( certID, certData, certIDlength );
449 
450  /* Find the request to issue this certificate. For a CMP-based issue
451  this will have an authorising object (found in the next iteration
452  through the loop), for a one-step issue it won't */
453  initBoundData( boundDataPtr );
454  setBoundData( boundDataPtr, 0, certID, certIDlength );
455  status = dbmsQuery(
456  "SELECT reqCertID FROM certLog WHERE certID = ?",
457  certData, MAX_QUERY_RESULT_SIZE, &certDataLength,
458  boundDataPtr, DBMS_CACHEDQUERY_NONE,
460  if( cryptStatusError( status ) )
461  return( status );
462  certIDlength = min( certDataLength, ENCODED_DBXKEYID_SIZE );
463  memcpy( certID, certData, certIDlength );
464  }
465  if( chainingLevel >= FAILSAFE_ITERATIONS_MED )
466  {
467  /* We've chained through too many entries, bail out */
468  return( CRYPT_ERROR_OVERFLOW );
469  }
470 
471  /* We've found the original PKI user, get the user information */
472  return( getItemData( dbmsInfo, iPkiUser, &dummy, KEYMGMT_ITEM_PKIUSER,
473  CRYPT_IKEYID_CERTID, certID, certIDlength,
474  KEYMGMT_FLAG_NONE, errorInfo ) );
475  }
476 
477 /****************************************************************************
478 * *
479 * CA Certificate Management Interface *
480 * *
481 ****************************************************************************/
482 
483 /* Perform a certificate management operation */
484 
485 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
486 static int certMgmtFunction( INOUT KEYSET_INFO *keysetInfoPtr,
489  IN_HANDLE_OPT const CRYPT_CERTIFICATE request,
490  IN_ENUM( CRYPT_CERTACTION ) \
491  const CRYPT_CERTACTION_TYPE action )
492  {
493  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
494  DBMS_INFO *dbmsInfo = keysetInfoPtr->keysetDBMS;
495  char reqCertID[ ENCODED_DBXKEYID_SIZE + 8 ];
496  int reqCertIDlength, status;
497 
498  assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
499  assert( ( iCertificate == NULL ) || \
500  isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
501 
502  REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
503  REQUIRES( ( caKey == CRYPT_UNUSED ) || isHandleRangeValid( caKey ) );
504  REQUIRES( ( request == CRYPT_UNUSED ) || isHandleRangeValid( request ) );
505  REQUIRES( action > CRYPT_CERTACTION_NONE && \
506  action < CRYPT_CERTACTION_LAST );
507 
508  /* In order for various SQL query strings to use the correct values the
509  type values have to match their text equivalents defined at the start
510  of this file. Since we can't check this at compile time we have to
511  do it here via an assertion */
514  "SQL data type" );
517  "SQL data type" );
520  "SQL data type" );
523  "SQL data type" );
526  "SQL data type" );
529  "SQL data type" );
532  "SQL data type" );
535  "SQL data type" );
538  "SQL data type" );
541  "SQL data type" );
542 
543  /* Clear return value */
544  if( iCertificate != NULL )
545  *iCertificate = CRYPT_ERROR;
546 
547  /* If it's a simple certificate expire or cleanup, there are no
548  parameters to check so we can perform the action immediately */
549  if( action == CRYPT_CERTACTION_EXPIRE_CERT || \
550  action == CRYPT_CERTACTION_CLEANUP )
551  {
552  REQUIRES( caKey == CRYPT_UNUSED );
553  REQUIRES( request == CRYPT_UNUSED );
554 
555  return( caCleanup( dbmsInfo, action, KEYSET_ERRINFO ) );
556  }
557 
558  /* If it's the completion of a certificate creation, process it */
559  if( action == CRYPT_CERTACTION_CERT_CREATION_COMPLETE || \
562  {
563  REQUIRES( caKey == CRYPT_UNUSED );
564 
565  return( caIssueCertComplete( dbmsInfo, request, action,
566  KEYSET_ERRINFO ) );
567  }
568 
569  /* Check that the CA key that we've been passed is in order. These
570  checks are performed automatically during the issue process by the
571  kernel when we try and convert the request into a certificate,
572  however we perform them explicitly here so that we can return a more
573  meaningful error message to the caller */
574  if( action == CRYPT_CERTACTION_ISSUE_CRL )
575  {
576  int value;
577 
578  /* If we're issuing a CRL, the key must be capable of CRL signing */
579  status = krnlSendMessage( caKey, IMESSAGE_GETATTRIBUTE, &value,
581  if( cryptStatusError( status ) || \
582  !( value & CRYPT_KEYUSAGE_CRLSIGN ) )
583  {
586  "CA certificate isn't valid for CRL signing" ) );
587  }
588  }
589  else
590  {
591  /* For anything other than a revocation action (which just updates
592  the certificate store without doing anything else) the key must
593  be a CA key */
594  if( action != CRYPT_CERTACTION_REVOKE_CERT && \
596  krnlSendMessage( caKey, IMESSAGE_CHECK, NULL,
597  MESSAGE_CHECK_CA ) ) )
598  {
601  "CA certificate isn't valid for certificate "
602  "signing" ) );
603  }
604  }
605 
606  /* If it's a CRL issue it's a read-only operation on the CRL store for
607  which we only need the CA certificate (there's no request involved) */
608  if( action == CRYPT_CERTACTION_ISSUE_CRL )
609  {
610  REQUIRES( request == CRYPT_UNUSED );
611 
612  return( caIssueCRL( dbmsInfo, iCertificate, caKey, KEYSET_ERRINFO ) );
613  }
614 
615  /* We're processing an action that requires an explicit certificate
616  request, perform further checks on the request */
617  if( !checkRequest( request, action ) )
618  {
621  "Certificate request information "
622  "inconsistent/invalid" ) );
623  }
624 
625  /* Make sure that the request is present in the request table in order
626  to issue a certificate for it. Again, this will be checked later but
627  we can return a more meaningful error here */
628  status = getKeyID( reqCertID, ENCODED_DBXKEYID_SIZE, &reqCertIDlength,
630  if( cryptStatusError( status ) )
631  return( CAMGMT_ARGERROR_REQUEST );
632  initBoundData( boundDataPtr );
633  setBoundData( boundDataPtr, 0, reqCertID, reqCertIDlength );
634  status = dbmsQuery(
635  "SELECT certData FROM certRequests WHERE certID = ?",
636  NULL, 0, NULL, boundDataPtr,
638  if( cryptStatusError( status ) )
639  {
642  "Certificate request doesn't correspond to any existing "
643  "request in the certificate store" ) );
644  }
645 
646  /* If it's a revocation request, process it */
647  if( action == CRYPT_CERTACTION_REVOKE_CERT )
648  {
649  REQUIRES( caKey == CRYPT_UNUSED );
650 
651  return( caRevokeCert( dbmsInfo, request, CRYPT_UNUSED,
653  }
654 
655  /* It's a certificate issue request, issue the certificate */
656  REQUIRES( action == CRYPT_CERTACTION_ISSUE_CERT || \
657  action == CRYPT_CERTACTION_CERT_CREATION );
658  REQUIRES( isHandleRangeValid( caKey ) );
659 
660  return( caIssueCert( dbmsInfo, iCertificate, caKey, request, action,
661  KEYSET_ERRINFO ) );
662  }
663 
664 /* Set up the function pointers to the keyset methods */
665 
667 int initDBMSCA( INOUT KEYSET_INFO *keysetInfoPtr )
668  {
669  assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
670 
671  REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
672 
673  keysetInfoPtr->keysetDBMS->certMgmtFunction = certMgmtFunction;
674 
675  return( CRYPT_OK );
676  }
677 #endif /* USE_DBMS */