cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
dbx_wr.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib DBMS 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 /* A structure to store ID information extracted from a certificate before
19  it's added to the certificate store */
20 
21 typedef struct {
22  /* User identification data */
23  BUFFER( CRYPT_MAX_TEXTSIZE, Clength ) \
24  char C[ CRYPT_MAX_TEXTSIZE + 8 ];
25  BUFFER( CRYPT_MAX_TEXTSIZE, SPlength ) \
26  char SP[ CRYPT_MAX_TEXTSIZE + 8 ];
27  BUFFER( CRYPT_MAX_TEXTSIZE, Llength ) \
28  char L[ CRYPT_MAX_TEXTSIZE + 8 ];
29  BUFFER( CRYPT_MAX_TEXTSIZE, Olength ) \
30  char O[ CRYPT_MAX_TEXTSIZE + 8 ];
31  BUFFER( CRYPT_MAX_TEXTSIZE, OUlength ) \
32  char OU[ CRYPT_MAX_TEXTSIZE + 8 ];
33  BUFFER( CRYPT_MAX_TEXTSIZE, CNlength ) \
34  char CN[ CRYPT_MAX_TEXTSIZE + 8 ];
35  BUFFER( CRYPT_MAX_TEXTSIZE, uriLength ) \
36  char uri[ CRYPT_MAX_TEXTSIZE + 8 ];
37  int Clength, SPlength, Llength, Olength, OUlength, CNlength, uriLength;
38 
39  /* Certificate identification data */
41  char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
42  BUFFER( ENCODED_DBXKEYID_SIZE, nameIDlength ) \
43  char nameID[ ENCODED_DBXKEYID_SIZE + 8 ];
44  BUFFER( ENCODED_DBXKEYID_SIZE, issuerIDlength ) \
45  char issuerID[ ENCODED_DBXKEYID_SIZE + 8 ];
47  char keyID[ ENCODED_DBXKEYID_SIZE + 8 ];
48  int certIDlength, nameIDlength, issuerIDlength, keyIDlength;
49  } CERT_ID_DATA;
50 
51 #ifdef USE_DBMS
52 
53 /****************************************************************************
54 * *
55 * Utility Routines *
56 * *
57 ****************************************************************************/
58 
59 /* Get the SQL string to delete data from a given table */
60 
61 CHECK_RETVAL_PTR \
62 static char *getDeleteString( IN_ENUM( KEYMGMT_ITEM ) \
63  const KEYMGMT_ITEM_TYPE itemType )
64  {
65  REQUIRES_N( itemType == KEYMGMT_ITEM_PKIUSER || \
66  itemType == KEYMGMT_ITEM_PUBLICKEY );
67 
68  switch( itemType )
69  {
71  return( "DELETE FROM pkiUsers WHERE " );
72 
74  return( "DELETE FROM certificates WHERE " );
75  }
76 
78  }
79 
80 /****************************************************************************
81 * *
82 * Extract ID Information *
83 * *
84 ****************************************************************************/
85 
86 /* Extract user identification data from a certificate */
87 
89 static int extractCertNameData( IN_HANDLE const CRYPT_CERTIFICATE iCryptHandle,
90  IN_ENUM( CRYPT_CERTTYPE ) \
92  OUT CERT_ID_DATA *certIdData )
93  {
94  static const int nameValue = CRYPT_CERTINFO_SUBJECTNAME;
95  static const int altNameValue = CRYPT_CERTINFO_SUBJECTALTNAME;
97  int status;
98 
99  assert( isWritePtr( certIdData, sizeof( CERT_ID_DATA ) ) );
100 
101  REQUIRES( isHandleRangeValid( iCryptHandle ) );
102  REQUIRES( certType == CRYPT_CERTTYPE_CERTIFICATE || \
103  certType == CRYPT_CERTTYPE_REQUEST_CERT || \
104  certType == CRYPT_CERTTYPE_PKIUSER );
105 
106  /* Clear return value */
107  memset( certIdData, 0, sizeof( CERT_ID_DATA ) );
108 
109  /* Extract the DN and altName (URI) components. This changes the
110  currently selected DN components but this is OK because we've got the
111  certificate locked and the prior state will be restored when we
112  unlock it */
113  status = krnlSendMessage( iCryptHandle, IMESSAGE_SETATTRIBUTE,
114  ( MESSAGE_CAST ) &nameValue,
116  if( cryptStatusError( status ) )
117  return( status );
118  setMessageData( &msgData, certIdData->C, CRYPT_MAX_TEXTSIZE );
119  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
120  &msgData, CRYPT_CERTINFO_COUNTRYNAME );
121  if( cryptStatusOK( status ) )
122  certIdData->Clength = msgData.length;
123  else
124  {
125  if( status != CRYPT_ERROR_NOTFOUND )
126  return( status );
127  }
128  setMessageData( &msgData, certIdData->SP, CRYPT_MAX_TEXTSIZE );
129  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
131  if( cryptStatusOK( status ) )
132  certIdData->SPlength = msgData.length;
133  else
134  {
135  if( status != CRYPT_ERROR_NOTFOUND )
136  return( status );
137  }
138  setMessageData( &msgData, certIdData->L, CRYPT_MAX_TEXTSIZE );
139  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
140  &msgData, CRYPT_CERTINFO_LOCALITYNAME );
141  if( cryptStatusOK( status ) )
142  certIdData->Llength = msgData.length;
143  else
144  {
145  if( status != CRYPT_ERROR_NOTFOUND )
146  return( status );
147  }
148  setMessageData( &msgData, certIdData->O, CRYPT_MAX_TEXTSIZE );
149  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
151  if( cryptStatusOK( status ) )
152  certIdData->Olength = msgData.length;
153  else
154  {
155  if( status != CRYPT_ERROR_NOTFOUND )
156  return( status );
157  }
158  setMessageData( &msgData, certIdData->OU, CRYPT_MAX_TEXTSIZE );
159  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
161  if( cryptStatusOK( status ) )
162  certIdData->OUlength = msgData.length;
163  else
164  {
165  if( status != CRYPT_ERROR_NOTFOUND )
166  return( status );
167  }
168 
169  /* The CommonName component is the generic "name" associated with the
170  certificate, to make sure that there's always at least something
171  useful present to identify it we fetch the certificate holder name
172  rather than the specific common name */
173  setMessageData( &msgData, certIdData->CN, CRYPT_MAX_TEXTSIZE );
174  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
175  &msgData, CRYPT_IATTRIBUTE_HOLDERNAME );
176  if( cryptStatusOK( status ) )
177  certIdData->CNlength = msgData.length;
178  else
179  {
180  if( status != CRYPT_ERROR_NOTFOUND )
181  return( status );
182  }
183 
184  /* PKI user objects don't have URI information so if we're processing
185  one of these we're done */
186  if( certType == CRYPT_CERTTYPE_PKIUSER )
187  return( CRYPT_OK );
188 
189  /* Get the URI for this certificate, in order of likelihood of
190  occurrence */
191  setMessageData( &msgData, certIdData->uri, CRYPT_MAX_TEXTSIZE );
192  krnlSendMessage( iCryptHandle, IMESSAGE_SETATTRIBUTE,
193  ( MESSAGE_CAST ) &altNameValue,
195  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
196  &msgData, CRYPT_CERTINFO_RFC822NAME );
197  if( status == CRYPT_ERROR_NOTFOUND )
198  {
199  setMessageData( &msgData, certIdData->uri, CRYPT_MAX_TEXTSIZE );
200  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
201  &msgData,
203  }
204  if( status == CRYPT_ERROR_NOTFOUND )
205  {
206  setMessageData( &msgData, certIdData->uri, CRYPT_MAX_TEXTSIZE );
207  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
208  &msgData, CRYPT_CERTINFO_DNSNAME );
209  }
210  if( cryptStatusOK( status ) )
211  {
212  int i;
213 
214  /* Force the URI (as stored) to lowercase to make case-insensitive
215  matching easier. In most cases we could ask the back-end to do
216  this but this complicates indexing and there's no reason why we
217  can't do it here */
218  for( i = 0; i < msgData.length; i++ )
219  certIdData->uri[ i ] = intToByte( toLower( certIdData->uri[ i ] ) );
220  certIdData->uriLength = msgData.length;
221  }
222 
223  return( status );
224  }
225 
226 /* Extract certificate identification data from a certificate */
227 
229 static int extractCertIdData( IN_HANDLE const CRYPT_CERTIFICATE iCryptHandle,
230  IN_ENUM( CRYPT_CERTTYPE ) \
231  const CRYPT_CERTTYPE_TYPE certType,
232  INOUT CERT_ID_DATA *certIdData )
233  {
235  int status;
236 
237  assert( isWritePtr( certIdData, sizeof( CERT_ID_DATA ) ) );
238 
239  REQUIRES( isHandleRangeValid( iCryptHandle ) );
240  REQUIRES( certType == CRYPT_CERTTYPE_CERTIFICATE || \
241  certType == CRYPT_CERTTYPE_REQUEST_CERT || \
242  certType == CRYPT_CERTTYPE_PKIUSER );
243 
244  /* Get general ID information */
245  status = getKeyID( certIdData->certID, ENCODED_DBXKEYID_SIZE,
246  &certIdData->certIDlength, iCryptHandle,
248  if( cryptStatusError( status ) )
249  return( status );
250 
251  /* Get object-specific ID information */
252  if( certType == CRYPT_CERTTYPE_CERTIFICATE )
253  {
254  status = getKeyID( certIdData->nameID, ENCODED_DBXKEYID_SIZE,
255  &certIdData->nameIDlength, iCryptHandle,
256  CRYPT_IATTRIBUTE_SUBJECT );
257  if( cryptStatusOK( status ) )
258  status = getKeyID( certIdData->issuerID, ENCODED_DBXKEYID_SIZE,
259  &certIdData->issuerIDlength, iCryptHandle,
260  CRYPT_IATTRIBUTE_ISSUERANDSERIALNUMBER );
261  if( cryptStatusOK( status ) )
262  status = getCertKeyID( certIdData->keyID, ENCODED_DBXKEYID_SIZE,
263  &certIdData->keyIDlength, iCryptHandle );
264  return( status );
265  }
266  if( certType == CRYPT_CERTTYPE_PKIUSER )
267  {
268  BYTE binaryKeyID[ 64 + 8 ];
269  char encKeyID[ CRYPT_MAX_TEXTSIZE + 8 ];
270  int binaryKeyIDlength;
271 
272  /* Get the PKI user ID. We can't read this directly since it's
273  returned in text form for use by end users so we have to read the
274  encoded form, decode it, and then turn the decoded binary value
275  into a key ID. We identify the result as a keyID,
276  (== subjectKeyIdentifier, which it isn't really) but we need to
277  use this to ensure that it's hashed/expanded out to the correct
278  size */
279  setMessageData( &msgData, encKeyID, CRYPT_MAX_TEXTSIZE );
280  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
281  &msgData, CRYPT_CERTINFO_PKIUSER_ID );
282  if( cryptStatusError( status ) )
283  return( status );
284  status = decodePKIUserValue( binaryKeyID, 64, &binaryKeyIDlength,
285  encKeyID, msgData.length );
286  if( cryptStatusOK( status ) )
287  status = makeKeyID( certIdData->keyID, ENCODED_DBXKEYID_SIZE,
288  &certIdData->keyIDlength, CRYPT_IKEYID_KEYID,
289  binaryKeyID, binaryKeyIDlength );
290  if( cryptStatusOK( status ) )
291  status = getKeyID( certIdData->nameID, ENCODED_DBXKEYID_SIZE,
292  &certIdData->nameIDlength, iCryptHandle,
293  CRYPT_IATTRIBUTE_SUBJECT );
294  return( status );
295  }
296 
297  return( CRYPT_OK );
298  }
299 
300 /* Extract certificate identification data from a CRL */
301 
303 static int extractCrlIdData( IN_HANDLE const CRYPT_CERTIFICATE iCryptCRL,
304  IN_HANDLE_OPT \
306  OUT CERT_ID_DATA *crlIdData )
307  {
308  int status;
309 
310  assert( isWritePtr( crlIdData, sizeof( CERT_ID_DATA ) ) );
311 
312  REQUIRES( isHandleRangeValid( iCryptCRL ) );
313  REQUIRES( iCryptRevokeCert == CRYPT_UNUSED || \
314  isHandleRangeValid( iCryptRevokeCert ) );
315 
316  /* Clear return value */
317  memset( crlIdData, 0, sizeof( CERT_ID_DATA ) );
318 
319  /* Get general ID information */
320  status = getKeyID( crlIdData->issuerID, ENCODED_DBXKEYID_SIZE,
321  &crlIdData->issuerIDlength, iCryptCRL,
322  CRYPT_IATTRIBUTE_ISSUERANDSERIALNUMBER );
323  if( cryptStatusError( status ) )
324  return( status );
325 
326  /* If there's no certificate being revoked present (i.e. we're just
327  adding a set of CRL entries), we're done */
328  if( iCryptRevokeCert == CRYPT_UNUSED )
329  return( CRYPT_OK );
330 
331  /* Get the certificate ID and the name ID of the issuer from the
332  certificate being revoked */
333  status = getKeyID( crlIdData->certID, ENCODED_DBXKEYID_SIZE,
334  &crlIdData->certIDlength, iCryptRevokeCert,
336  if( cryptStatusOK( status ) )
337  status = getKeyID( crlIdData->nameID, ENCODED_DBXKEYID_SIZE,
338  &crlIdData->nameIDlength, iCryptRevokeCert,
339  CRYPT_IATTRIBUTE_ISSUER );
340  return( status );
341  }
342 
343 /****************************************************************************
344 * *
345 * Database Add Routines *
346 * *
347 ****************************************************************************/
348 
349 /* Add a certificate object (certificate, certificate request, PKI user) to
350  a certificate store. Normally existing rows would be overwritten if we
351  added duplicate entries but the UNIQUE constraint on the indices will
352  catch this */
353 
354 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 6 ) ) \
355 int addCert( INOUT DBMS_INFO *dbmsInfo,
356  IN_HANDLE const CRYPT_HANDLE iCryptHandle,
357  IN_ENUM( CRYPT_CERTTYPE ) const CRYPT_CERTTYPE_TYPE certType,
358  IN_ENUM( CERTADD ) const CERTADD_TYPE addType,
359  IN_ENUM( DBMS_UPDATE ) const DBMS_UPDATE_TYPE updateType,
361  {
363  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
364  CERT_ID_DATA certIdData;
365  BYTE certData[ MAX_CERT_SIZE + 8 ];
366  char encodedCertData[ MAX_ENCODED_CERT_SIZE + 8 ];
367  const char *sqlString;
368  time_t boundDate;
369  int certDataLength = DUMMY_INIT, boundDataIndex, status;
370 
371  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
372 
373  REQUIRES( isHandleRangeValid( iCryptHandle ) );
374  REQUIRES( certType == CRYPT_CERTTYPE_CERTIFICATE || \
375  certType == CRYPT_CERTTYPE_REQUEST_CERT || \
376  certType == CRYPT_CERTTYPE_PKIUSER );
377  REQUIRES( addType > CERTADD_NONE && addType < CERTADD_LAST );
378  REQUIRES( updateType > DBMS_UPDATE_NONE && \
379  updateType < DBMS_UPDATE_LAST );
380  REQUIRES( errorInfo != NULL );
381 
382  /* Extract name-related information from the certificate */
383  status = extractCertNameData( iCryptHandle, certType, &certIdData );
384  if( ( cryptStatusOK( status ) || status == CRYPT_ERROR_NOTFOUND ) && \
385  ( certType == CRYPT_CERTTYPE_CERTIFICATE ) )
386  {
387  setMessageData( &msgData, &boundDate, sizeof( time_t ) );
388  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
389  &msgData, CRYPT_CERTINFO_VALIDTO );
390  }
391  else
392  {
393  if( status == CRYPT_ERROR_NOTFOUND )
394  status = CRYPT_OK;
395  }
396  if( cryptStatusError( status ) )
397  {
398  /* Convert any low-level certificate-specific error into something
399  generic that makes a bit more sense to the caller */
401  ( CRYPT_ARGERROR_NUM1, errorInfo,
402  "Couldn't extract user identification information "
403  "from certificate" ) );
404  }
405 
406  /* Get the ID information and certificate data from the certificate */
407  status = extractCertIdData( iCryptHandle, certType, &certIdData );
408  if( cryptStatusOK( status ) )
409  {
410  status = extractCertData( iCryptHandle,
411  ( certType == CRYPT_CERTTYPE_PKIUSER ) ? \
412  CRYPT_ICERTFORMAT_DATA : \
414  certData, MAX_CERT_SIZE, &certDataLength );
415  }
416  if( cryptStatusError( status ) )
417  {
418  /* Convert any low-level certificate-specific error into something
419  generic that makes a bit more sense to the caller */
421  ( CRYPT_ARGERROR_NUM1, errorInfo,
422  "Couldn't extract certificate data from "
423  "certificate" ) );
424  }
425 
426  /* If this is a partial add (in which we add a certificate item which is
427  in the initial stages of the creation process so that although the
428  item may be physically present in the store it can't be accessed
429  directly) we set the first byte to 0xFF to indicate this. In
430  addition we set the first two bytes of the IDs that have uniqueness
431  constraints to an out-of-band value to prevent a clash with the
432  finished entry when we complete the issue process and replace the
433  partial version with the full version */
434  if( addType == CERTADD_PARTIAL || addType == CERTADD_PARTIAL_RENEWAL )
435  {
436  const char *escapeStr = ( addType == CERTADD_PARTIAL ) ? \
438 
439  certData[ 0 ] = 0xFF;
440  memcpy( certIdData.issuerID, escapeStr, KEYID_ESC_SIZE );
441  memcpy( certIdData.keyID, escapeStr, KEYID_ESC_SIZE );
442  memcpy( certIdData.certID, escapeStr, KEYID_ESC_SIZE );
443  }
444 
445  /* Set up the certificate object data to be written and send it to the
446  database */
447  initBoundData( boundDataPtr );
448  setBoundData( boundDataPtr, 0, certIdData.C, certIdData.Clength );
449  setBoundData( boundDataPtr, 1, certIdData.SP, certIdData.SPlength );
450  setBoundData( boundDataPtr, 2, certIdData.L, certIdData.Llength );
451  setBoundData( boundDataPtr, 3, certIdData.O, certIdData.Olength );
452  setBoundData( boundDataPtr, 4, certIdData.OU, certIdData.OUlength );
453  setBoundData( boundDataPtr, 5, certIdData.CN, certIdData.CNlength );
454  switch( certType )
455  {
457  setBoundData( boundDataPtr, 6, certIdData.uri,
458  certIdData.uriLength );
459  setBoundDataDate( boundDataPtr, 7, &boundDate );
460  setBoundData( boundDataPtr, 8, certIdData.nameID,
461  certIdData.nameIDlength );
462  setBoundData( boundDataPtr, 9, certIdData.issuerID,
463  certIdData.issuerIDlength );
464  setBoundData( boundDataPtr, 10, certIdData.keyID,
465  certIdData.keyIDlength );
466  boundDataIndex = 11;
467  sqlString = \
468  "INSERT INTO certificates VALUES (?, ?, ?, ?, ?, ?, ?,"
469  "?, ?, ?, ?, ?, ?)";
470  break;
471 
473  setBoundData( boundDataPtr, 6, certIdData.uri,
474  certIdData.uriLength );
475  boundDataIndex = 7;
476  sqlString = \
477  "INSERT INTO certRequests VALUES ('" TEXT_CERTTYPE_REQUEST_CERT "', "
478  "?, ?, ?, ?, ?, ?, ?, ?, ?)";
479  break;
480 
482  setBoundData( boundDataPtr, 6, certIdData.nameID,
483  certIdData.nameIDlength );
484  setBoundData( boundDataPtr, 7, certIdData.keyID,
485  certIdData.keyIDlength );
486  boundDataIndex = 8;
487  sqlString = \
488  "INSERT INTO pkiUsers VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
489  break;
490 
491  default:
492  retIntError();
493  }
494  setBoundData( boundDataPtr, boundDataIndex++, certIdData.certID,
495  certIdData.certIDlength );
496  if( hasBinaryBlobs( dbmsInfo ) )
497  {
498  setBoundDataBlob( boundDataPtr, boundDataIndex,
499  certData, certDataLength );
500  }
501  else
502  {
503  int encodedCertDataLength;
504 
505  status = base64encode( encodedCertData, MAX_ENCODED_CERT_SIZE,
506  &encodedCertDataLength, certData,
507  certDataLength, CRYPT_CERTTYPE_NONE );
508  if( cryptStatusError( status ) )
509  {
510  DEBUG_DIAG(( "Couldn't base64-encode data" ));
511  assert( DEBUG_WARN );
512  retIntError();
513  }
514  setBoundData( boundDataPtr, boundDataIndex, encodedCertData,
515  encodedCertDataLength );
516  }
517  status = dbmsUpdate( sqlString, boundDataPtr, updateType );
518  if( cryptStatusError( status ) )
519  {
520  retExtErr( status,
521  ( status, errorInfo, getDbmsErrorInfo( dbmsInfo ),
522  "Certificate add operation failed: " ) );
523  }
524  return( CRYPT_OK );
525  }
526 
527 /* Add a CRL to a certificate store */
528 
529 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 5 ) ) \
530 int addCRL( INOUT DBMS_INFO *dbmsInfo,
531  IN_HANDLE const CRYPT_CERTIFICATE iCryptCRL,
532  IN_HANDLE_OPT const CRYPT_CERTIFICATE iCryptRevokeCert,
533  IN_ENUM( DBMS_UPDATE ) const DBMS_UPDATE_TYPE updateType,
534  INOUT ERROR_INFO *errorInfo )
535  {
536  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
537  CERT_ID_DATA crlIdData;
538  BYTE certData[ MAX_CERT_SIZE + 8 ];
539  char encodedCertData[ MAX_ENCODED_CERT_SIZE + 8 ];
540  const char *sqlString;
541  time_t expiryDate = 0;
542  int certDataLength = DUMMY_INIT, boundDataIndex, status;
543 
544  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
545 
546  REQUIRES( isHandleRangeValid( iCryptCRL ) );
547  REQUIRES( ( isCertStore( dbmsInfo ) && \
548  isHandleRangeValid( iCryptRevokeCert ) ) || \
549  ( !isCertStore( dbmsInfo ) && \
550  iCryptRevokeCert == CRYPT_UNUSED ) );
551  REQUIRES( updateType > DBMS_UPDATE_NONE && \
552  updateType < DBMS_UPDATE_LAST );
553  REQUIRES( errorInfo != NULL );
554 
555  /* Get the ID information for the current CRL entry */
556  status = extractCrlIdData( iCryptCRL, iCryptRevokeCert, &crlIdData );
557  if( cryptStatusOK( status ) )
558  status = extractCertData( iCryptCRL, CRYPT_IATTRIBUTE_CRLENTRY,
559  certData, MAX_CERT_SIZE, &certDataLength );
560  if( cryptStatusOK( status ) && isCertStore( dbmsInfo ) )
561  {
563 
564  setMessageData( &msgData, &expiryDate, sizeof( time_t ) );
565  status = krnlSendMessage( iCryptRevokeCert, IMESSAGE_GETATTRIBUTE_S,
566  &msgData, CRYPT_CERTINFO_VALIDTO );
567  }
568  if( cryptStatusError( status ) )
569  {
570  /* Convert any low-level certificate-specific error into something
571  generic that makes a bit more sense to the caller */
573  ( CRYPT_ARGERROR_NUM1, errorInfo,
574  "Couldn't extract CRL data from CRL" ) );
575  }
576 
577  /* Set up the certificate object data to be written and send it to the
578  database. Certificate stores contain extra inforomation that's
579  needed to build a CRL so we have to vary the SQL string depending on
580  the keyset type */
581  initBoundData( boundDataPtr );
582  if( isCertStore( dbmsInfo ) )
583  {
584  setBoundDataDate( boundDataPtr, 0, &expiryDate );
585  setBoundData( boundDataPtr, 1, crlIdData.nameID,
586  crlIdData.nameIDlength );
587  setBoundData( boundDataPtr, 2, crlIdData.issuerID,
588  crlIdData.issuerIDlength );
589  setBoundData( boundDataPtr, 3, crlIdData.certID,
590  crlIdData.certIDlength );
591  boundDataIndex = 4;
592  sqlString = "INSERT INTO CRLs VALUES (?, ?, ?, ?, ?)";
593 
594  }
595  else
596  {
597  setBoundData( boundDataPtr, 0, crlIdData.issuerID,
598  crlIdData.issuerIDlength );
599  boundDataIndex = 1;
600  sqlString = "INSERT INTO CRLs VALUES (?, ?)";
601  }
602  if( hasBinaryBlobs( dbmsInfo ) )
603  {
604  setBoundDataBlob( boundDataPtr, boundDataIndex, certData,
605  certDataLength );
606  }
607  else
608  {
609  int encodedCertDataLength;
610 
611  status = base64encode( encodedCertData, MAX_ENCODED_CERT_SIZE,
612  &encodedCertDataLength, certData,
613  certDataLength, CRYPT_CERTTYPE_NONE );
614  if( cryptStatusError( status ) )
615  {
616  DEBUG_DIAG(( "Couldn't base64-encode data" ));
617  assert( DEBUG_WARN );
618  retIntError();
619  }
620  setBoundData( boundDataPtr, boundDataIndex, encodedCertData,
621  encodedCertDataLength );
622  }
623  status = dbmsUpdate( sqlString, boundDataPtr, updateType );
624  if( cryptStatusError( status ) )
625  {
626  retExtErr( status,
627  ( status, errorInfo, getDbmsErrorInfo( dbmsInfo ),
628  "CRL add operation failed: " ) );
629  }
630 
631  return( CRYPT_OK );
632  }
633 
634 /* Add an item to the certificate store */
635 
637 static int setItemFunction( INOUT KEYSET_INFO *keysetInfoPtr,
638  IN_HANDLE const CRYPT_HANDLE iCryptHandle,
639  IN_ENUM( KEYMGMT_ITEM ) \
640  const KEYMGMT_ITEM_TYPE itemType,
641  STDC_UNUSED const char *password,
642  STDC_UNUSED const int passwordLength,
643  IN_FLAGS( KEYMGMT ) const int flags )
644  {
645  DBMS_INFO *dbmsInfo = keysetInfoPtr->keysetDBMS;
646  BOOLEAN seenNonDuplicate = FALSE;
647  int type, iterationCount = 0, status;
648 
649  assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
650 
651  REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
652  REQUIRES( isHandleRangeValid( iCryptHandle ) );
653  REQUIRES( itemType == KEYMGMT_ITEM_PUBLICKEY || \
654  itemType == KEYMGMT_ITEM_REVOCATIONINFO || \
655  itemType == KEYMGMT_ITEM_REQUEST || \
656  itemType == KEYMGMT_ITEM_REVREQUEST || \
657  itemType == KEYMGMT_ITEM_PKIUSER );
658  REQUIRES( password == NULL && passwordLength == 0 );
659  REQUIRES( flags >= KEYMGMT_FLAG_NONE && flags < KEYMGMT_FLAG_MAX );
660 
661  /* Make sure that we've been given a certificate, certificate chain, or
662  CRL (or a PKI user if it's a CA certificate store). We can't do any
663  more specific checking against the itemType because if it's coming
664  from outside cryptlib it'll just be passed in as a generic
665  certificate object with no distinction between object subtypes */
666  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE,
667  &type, CRYPT_CERTINFO_CERTTYPE );
668  if( cryptStatusError( status ) )
669  return( CRYPT_ARGERROR_NUM1 );
670  if( isCertStore( dbmsInfo ) )
671  {
672  /* The only item that can be inserted directly into a CA certificate
673  store is a CA request or PKI user information */
674  if( type != CRYPT_CERTTYPE_CERTREQUEST && \
675  type != CRYPT_CERTTYPE_REQUEST_CERT && \
677  type != CRYPT_CERTTYPE_PKIUSER )
678  {
681  "Invalid item type for CA certificate store" ) );
682  }
683 
684  if( itemType == KEYMGMT_ITEM_PKIUSER )
685  {
686  REQUIRES( type == CRYPT_CERTTYPE_PKIUSER );
687  return( caAddPKIUser( dbmsInfo, iCryptHandle, KEYSET_ERRINFO ) );
688  }
689 
690  /* It's a certificate request being added to a CA certificate
691  store */
692  REQUIRES( ( itemType == KEYMGMT_ITEM_REQUEST && \
693  ( type == CRYPT_CERTTYPE_CERTREQUEST || \
694  type == CRYPT_CERTTYPE_REQUEST_CERT ) ) || \
695  ( itemType == KEYMGMT_ITEM_REVREQUEST && \
697  return( caAddCertRequest( dbmsInfo, iCryptHandle, type,
698  ( flags & KEYMGMT_FLAG_UPDATE ) ? \
699  TRUE : FALSE,
700  ( flags & KEYMGMT_FLAG_INITIALOP ) ? \
701  TRUE : FALSE, KEYSET_ERRINFO ) );
702  }
703  if( type != CRYPT_CERTTYPE_CERTIFICATE && \
704  type != CRYPT_CERTTYPE_CERTCHAIN && \
705  type != CRYPT_CERTTYPE_CRL )
706  {
709  "Item being added must be a CRL or certificate" ) );
710  }
711 
712  REQUIRES( itemType == KEYMGMT_ITEM_PUBLICKEY || \
713  itemType == KEYMGMT_ITEM_REVOCATIONINFO );
714 
715  /* Lock the certificate or CRL for our exclusive use and select the
716  first sub-item (certificate in a certificate chain, entry in a CRL),
717  update the keyset with the certificate(s)/CRL entries, and unlock it
718  to allow others access */
719  status = krnlSendMessage( iCryptHandle, IMESSAGE_SETATTRIBUTE,
720  MESSAGE_VALUE_TRUE, CRYPT_IATTRIBUTE_LOCKED );
721  if( cryptStatusError( status ) )
722  return( status );
723  status = krnlSendMessage( iCryptHandle, IMESSAGE_SETATTRIBUTE,
726  if( cryptStatusError( status ) )
727  {
728  ( void ) krnlSendMessage( iCryptHandle, IMESSAGE_SETATTRIBUTE,
730  CRYPT_IATTRIBUTE_LOCKED );
731  return( status );
732  }
733  do
734  {
735  /* Add the certificate or CRL */
736  if( type == CRYPT_CERTTYPE_CRL )
737  status = addCRL( dbmsInfo, iCryptHandle, CRYPT_UNUSED,
739  else
740  status = addCert( dbmsInfo, iCryptHandle,
743 
744  /* An item being added may already be present but we can't fail
745  immediately because what's being added may be a chain containing
746  further certificates or a CRL containing further entries so we
747  keep track of whether we've successfully added at least one item
748  and clear data duplicate errors */
749  if( status == CRYPT_OK )
750  seenNonDuplicate = TRUE;
751  else
752  {
753  if( status == CRYPT_ERROR_DUPLICATE )
754  status = CRYPT_OK;
755  }
756  }
757  while( cryptStatusOK( status ) && \
758  krnlSendMessage( iCryptHandle, IMESSAGE_SETATTRIBUTE,
761  iterationCount++ < FAILSAFE_ITERATIONS_MED );
762  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
763  ( void ) krnlSendMessage( iCryptHandle, IMESSAGE_SETATTRIBUTE,
764  MESSAGE_VALUE_FALSE, CRYPT_IATTRIBUTE_LOCKED );
765  if( cryptStatusOK( status ) && !seenNonDuplicate )
766  {
767  /* We reached the end of the certificate chain/CRL without finding
768  anything that we could add, return a data duplicate error */
771  "No new %s were found to add to the certificate store",
772  ( type == CRYPT_CERTTYPE_CRL ) ? \
773  "CRL entries" : "certificates" ) );
774  }
775 
776  return( status );
777  }
778 
779 /* Delete an item from the certificate store */
780 
781 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
782 static int deleteItemFunction( INOUT KEYSET_INFO *keysetInfoPtr,
783  IN_ENUM( KEYMGMT_ITEM ) \
784  const KEYMGMT_ITEM_TYPE itemType,
786  IN_BUFFER( keyIDlength ) const void *keyID,
787  IN_LENGTH_KEYID const int keyIDlength )
788  {
789  DBMS_INFO *dbmsInfo = keysetInfoPtr->keysetDBMS;
790  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
791  char sqlBuffer[ MAX_SQL_QUERY_SIZE + 8 ];
792  char encodedKeyID[ ( CRYPT_MAX_TEXTSIZE * 2 ) + 8 ];
793  const char *keyName = getKeyName( keyIDtype );
794  const char *deleteString = getDeleteString( itemType );
795  int encodedKeyIDlength, status;
796 
797  assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
798  assert( isReadPtr( keyID, keyIDlength ) );
799 
800  REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
801  REQUIRES( itemType == KEYMGMT_ITEM_PUBLICKEY || \
802  itemType == KEYMGMT_ITEM_PKIUSER );
803  REQUIRES( ( !isCertStore( dbmsInfo ) && \
804  itemType == KEYMGMT_ITEM_PUBLICKEY ) || \
805  ( isCertStore( dbmsInfo ) && \
806  itemType == KEYMGMT_ITEM_PKIUSER ) );
807  REQUIRES( keyIDtype > CRYPT_KEYID_NONE && \
808  keyIDtype < CRYPT_KEYID_LAST );
809  REQUIRES( keyIDlength >= MIN_NAME_LENGTH && \
810  keyIDlength < MAX_ATTRIBUTE_SIZE );
811 
812  /* Delete the item from the certificate store */
813  status = makeKeyID( encodedKeyID, CRYPT_MAX_TEXTSIZE * 2,
814  &encodedKeyIDlength, keyIDtype, keyID, keyIDlength );
815  if( cryptStatusError( status ) )
816  return( CRYPT_ARGERROR_STR1 );
817  if( isCertStore( dbmsInfo ) )
818  {
819  /* The only item that can be deleted from a CA certificate store is
820  PKI user information */
821  if( itemType != KEYMGMT_ITEM_PKIUSER )
822  {
825  "Invalid operation for CA certificate store" ) );
826  }
827 
828  return( caDeletePKIUser( dbmsInfo, keyIDtype, keyID, keyIDlength,
829  KEYSET_ERRINFO ) );
830  }
831  ENSURES( keyName != NULL && deleteString != NULL );
832  strlcpy_s( sqlBuffer, MAX_SQL_QUERY_SIZE, deleteString );
833  strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, keyName );
834  strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, " = ?" );
835  initBoundData( boundDataPtr );
836  setBoundData( boundDataPtr, 0, encodedKeyID, encodedKeyIDlength );
837  status = dbmsUpdate( sqlBuffer, boundDataPtr, DBMS_UPDATE_NORMAL );
838  if( cryptStatusError( status ) )
839  {
840  retExtErr( status,
841  ( status, KEYSET_ERRINFO, getDbmsErrorInfo( dbmsInfo ),
842  "Certificate delete operation failed: " ) );
843  }
844  return( CRYPT_OK );
845  }
846 
847 /****************************************************************************
848 * *
849 * Database Access Routines *
850 * *
851 ****************************************************************************/
852 
854 int initDBMSwrite( INOUT KEYSET_INFO *keysetInfoPtr )
855  {
856  assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
857 
858  REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
859 
860  keysetInfoPtr->setItemFunction = setItemFunction;
861  keysetInfoPtr->deleteItemFunction = deleteItemFunction;
862 
863  return( CRYPT_OK );
864  }
865 #endif /* USE_DBMS */