cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
dbx_rd.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 "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 #ifdef USE_DBMS
21 
22 /****************************************************************************
23 * *
24 * Utility Routines *
25 * *
26 ****************************************************************************/
27 
28 /* The most common query types can be performed using cached access plans
29  and query data. The following function determines whether a particular
30  query can be performed using cached information, returning the cache
31  entry for the query if so */
32 
33 CHECK_RETVAL_ENUM( DBMS_CACHEDQUERY ) \
34 static DBMS_CACHEDQUERY_TYPE getCachedQueryType( IN_ENUM_OPT( KEYMGMT_ITEM ) \
35  const KEYMGMT_ITEM_TYPE itemType,
36  IN_KEYID_OPT \
38  {
39  REQUIRES_EXT( ( itemType == KEYMGMT_ITEM_NONE || \
40  itemType == KEYMGMT_ITEM_REQUEST || \
41  itemType == KEYMGMT_ITEM_REVREQUEST || \
42  itemType == KEYMGMT_ITEM_PKIUSER || \
43  itemType == KEYMGMT_ITEM_PUBLICKEY || \
44  itemType == KEYMGMT_ITEM_REVOCATIONINFO ),
46  /* KEYMGMT_ITEM_NONE is for ongoing queries */
47  REQUIRES_EXT( ( itemType == KEYMGMT_ITEM_NONE && \
48  keyIDtype == CRYPT_KEYID_NONE ) || \
49  ( keyIDtype > CRYPT_KEYID_NONE && \
50  keyIDtype <= CRYPT_KEYID_LAST ), DBMS_CACHEDQUERY_NONE );
51  /* { KEYMGMT_ITEM_NONE, CRYPT_KEYID_NONE } is for ongoing
52  queries */
53 
54  /* If we're not reading from the standard certificates table the query
55  won't be cached */
56  if( itemType != KEYMGMT_ITEM_PUBLICKEY )
57  return( DBMS_CACHEDQUERY_NONE );
58 
59  /* Check whether we're querying on a cacheable key value type. An ID
60  type of CRYPT_KEYID_LAST is a special case which denotes that we're
61  doing a query on a name ID, this is used for getNext() and is very
62  common (it follows most certificate reads and is used to see if we
63  can build a chain) so we make it cacheable */
64  switch( keyIDtype )
65  {
66  case CRYPT_KEYID_URI:
67  return( DBMS_CACHEDQUERY_URI );
68 
69  case CRYPT_IKEYID_ISSUERID:
70  return( DBMS_CACHEDQUERY_ISSUERID );
71 
72  case CRYPT_IKEYID_CERTID:
73  return( DBMS_CACHEDQUERY_CERTID );
74 
75  case CRYPT_KEYID_LAST:
76  return( DBMS_CACHEDQUERY_NAMEID );
77  }
78 
79  return( DBMS_CACHEDQUERY_NONE );
80  }
81 
82 /* Get the SQL string to fetch data from a given table */
83 
84 CHECK_RETVAL_PTR \
85 static char *getSelectString( IN_ENUM( KEYMGMT_ITEM ) \
86  const KEYMGMT_ITEM_TYPE itemType )
87  {
88  REQUIRES_N( itemType == KEYMGMT_ITEM_REQUEST || \
89  itemType == KEYMGMT_ITEM_REVREQUEST || \
90  itemType == KEYMGMT_ITEM_PKIUSER || \
91  itemType == KEYMGMT_ITEM_PUBLICKEY || \
92  itemType == KEYMGMT_ITEM_REVOCATIONINFO );
93 
94  switch( itemType )
95  {
98  return( "SELECT certData FROM certRequests WHERE " );
99 
101  return( "SELECT certData FROM pkiUsers WHERE " );
102 
104  return( "SELECT certData FROM certificates WHERE " );
105 
107  return( "SELECT certData FROM CRLs WHERE " );
108  }
109 
111  }
112 
113 /* Check an encoded certificate for a matching key usage. The semantics of
114  key usage flags are vague in the sense that the query "Is this key valid
115  for X" is easily resolved but the query "Which key is appropriate for X"
116  is NP-hard due to the potential existence of unbounded numbers of
117  certificates with usage semantics expressed in an arbitrary number of
118  ways. For now we distinguish between signing and encryption keys (this,
119  at least, is feasible) by doing a quick check for keyUsage if we get
120  multiple certificates with the same DN and choosing the one with the
121  appropriate key usage.
122 
123  Rather than performing a relatively expensive certificate import for each
124  certificate we find the keyUsage by doing an optimised search through the
125  certificate data for its encoded form. The pattern that we look for is:
126 
127  OID 06 03 55 1D 0F + SEQUENCE at n-2
128  BOOLEAN (optional)
129  OCTET STRING {
130  BIT STRING (value)
131 
132  Note that this function isn't security-critical because it doesn't perform
133  an actual usage check, all it needs to do is find the key that's most
134  likely to be usable for purpose X. If it gets it wrong the certificate-
135  level checking will catch the problem */
136 
138 static BOOLEAN checkCertUsage( IN_BUFFER( certLength ) const BYTE *certificate,
140  const int certLength,
141  IN_FLAGS( KEYMGMT ) const int requestedUsage )
142  {
143  const int keyUsageMask = ( requestedUsage & KEYMGMT_FLAG_USAGE_CRYPT ) ? \
145  int i;
146 
147  assert( isReadPtr( certificate, certLength ) );
148 
149  REQUIRES( certLength >= MIN_CRYPT_OBJECTSIZE && \
150  certLength < MAX_INTLENGTH_SHORT );
151  REQUIRES( requestedUsage > KEYMGMT_FLAG_NONE && \
152  requestedUsage < KEYMGMT_FLAG_MAX );
153  REQUIRES( requestedUsage & KEYMGMT_MASK_USAGEOPTIONS );
154 
155  /* Scan the payload portion of the certificate for the keyUsage
156  extension. The certificate is laid out approximately as:
157 
158  [ junk ][ DN ][ times ][ DN ][ pubKey ][ attrs ][ sig ]
159 
160  so we know there's at least 128 + MIN_PKCSIZE bytes at the start and
161  MIN_PKCSIZE bytes at the end that we don't have to bother poking
162  around in */
163  for( i = 128 + MIN_PKCSIZE; i < certLength - MIN_PKCSIZE; i++ )
164  {
165  STREAM stream;
166  int keyUsage, status;
167 
168  /* Look for the OID. This potentially skips two bytes at a time but
169  this is safe because the preceding bytes can never contain either
170  of these two values (they're 0x30 + 11...15) */
171  if( certificate[ i++ ] != BER_OBJECT_IDENTIFIER || \
172  certificate[ i++ ] != 3 )
173  continue;
174  if( certificate[ i - 4 ] != BER_SEQUENCE )
175  continue;
176  if( memcmp( certificate + i, "\x55\x1D\x0F", 3 ) )
177  continue;
178  i += 3;
179 
180  /* We've found the OID (with 2.8e-14 error probability), skip
181  the critical flag if necessary */
182  if( certificate[ i ] == BER_BOOLEAN )
183  i += 3;
184 
185  /* Read the OCTET STRING wrapper and BIT STRING */
186  sMemConnect( &stream, certificate + i,
187  certLength - ( i + MIN_PKCSIZE ) );
188  readOctetStringHole( &stream, NULL, 4, DEFAULT_TAG );
189  status = readBitString( &stream, &keyUsage );
190  sMemDisconnect( &stream );
191  if( cryptStatusError( status ) )
192  continue;
193 
194  /* Check whether the requested usage is allowed */
195  return( ( keyUsage & keyUsageMask ) ? TRUE : FALSE );
196  }
197 
198  /* No key usage found, assume that any usage is OK */
199  return( TRUE );
200  }
201 
202 /****************************************************************************
203 * *
204 * Database Fetch Routines *
205 * *
206 ****************************************************************************/
207 
208 /* Fetch a sequence of certificates from a data source. This is called in
209  one of two ways, either indirectly by the certificate code to fetch the
210  first and subsequent certificates in a chain or directly by the user
211  after submitting a query to the keyset (which doesn't return any data)
212  to read the results of the query. The schema for calls is:
213 
214  state = NULL: query( NULL, &data, CONTINUE );
215  state, point query: query( SQL, &data, NORMAL );
216  state, multi-cert: query( SQL, &data, START );
217  ( followed by 'query( NULL, &data, CONTINUE )') */
218 
219 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 9 ) ) \
220 int getItemData( INOUT DBMS_INFO *dbmsInfo,
222  OUT_OPT int *stateInfo,
223  IN_ENUM_OPT( KEYMGMT_ITEM ) const KEYMGMT_ITEM_TYPE itemType,
224  IN_KEYID_OPT const CRYPT_KEYID_TYPE keyIDtype,
225  IN_BUFFER_OPT( keyValueLength ) const char *keyValue,
227  IN_FLAGS_Z( KEYMGMT ) const int options,
229  {
230  MESSAGE_CREATEOBJECT_INFO createInfo;
231  const DBMS_CACHEDQUERY_TYPE cachedQueryType = \
232  getCachedQueryType( itemType, keyIDtype );
233  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
234  BYTE certificate[ MAX_CERT_SIZE + 8 ];
235  char sqlBuffer[ MAX_SQL_QUERY_SIZE + 8 ];
236  const char *queryString;
237  char certDataBuffer[ MAX_QUERY_RESULT_SIZE + 8 ];
238  void *certDataPtr = hasBinaryBlobs( dbmsInfo ) ? \
239  certificate : ( void * ) certDataBuffer;
240  /* Cast needed for gcc */
241  DBMS_QUERY_TYPE queryType;
242  BOOLEAN multiCertQuery = ( options & KEYMGMT_MASK_USAGEOPTIONS ) ? \
243  TRUE : FALSE;
244  int certDataLength = DUMMY_INIT, iterationCount, status;
245 
246  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
247  assert( isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
248  assert( ( stateInfo == NULL ) || \
249  isWritePtr( stateInfo, sizeof( int ) ) );
250  assert( ( keyValueLength > MIN_NAME_LENGTH && \
251  isReadPtr( keyValue, keyValueLength ) && \
252  ( keyIDtype > CRYPT_KEYID_NONE && \
253  keyIDtype <= CRYPT_KEYID_LAST ) ) || \
254  ( keyValueLength == 0 && keyValue == NULL && \
255  keyIDtype == CRYPT_KEYID_NONE ) );
256 
257  REQUIRES( ( itemType == KEYMGMT_ITEM_NONE && \
258  keyIDtype == CRYPT_KEYID_NONE && keyValue == NULL && \
259  keyValueLength == 0 && stateInfo == NULL ) ||
260  /* This variant is for ongoing queries, for which information
261  will have been submitted when the query was started */
262  ( itemType > KEYMGMT_ITEM_NONE && \
263  itemType < KEYMGMT_ITEM_LAST && \
264  keyIDtype > CRYPT_KEYID_NONE && \
265  keyIDtype <= CRYPT_KEYID_LAST && \
266  keyValue != NULL && \
267  keyValueLength >= MIN_NAME_LENGTH &&
268  keyValueLength < MAX_ATTRIBUTE_SIZE && \
269  stateInfo != NULL ) );
270  /* As well as the standard values we can also have the
271  special-case value CRYPT_KEYID_LAST (see the comment in
272  getItemFunction() for details) which is mapped to the
273  database-use-only lookup value "nameID", the hashed DN */
274  REQUIRES( itemType == KEYMGMT_ITEM_NONE || \
275  itemType == KEYMGMT_ITEM_PUBLICKEY || \
276  itemType == KEYMGMT_ITEM_REQUEST || \
277  itemType == KEYMGMT_ITEM_REVREQUEST || \
278  itemType == KEYMGMT_ITEM_PKIUSER || \
279  itemType == KEYMGMT_ITEM_REVOCATIONINFO );
280  REQUIRES( options >= KEYMGMT_FLAG_NONE && options < KEYMGMT_FLAG_MAX );
281  REQUIRES( errorInfo != NULL );
282 
283  /* Clear return values */
284  *iCertificate = CRYPT_ERROR;
285  if( stateInfo != NULL )
286  *stateInfo = CRYPT_ERROR;
287 
288  /* Make sure that we can never explicitly fetch anything with an ID that
289  indicates that it's physically but not logically present, for example
290  certificates that have been created but not fully issued yet,
291  certificate items that are on hold, and similar items */
292  if( keyValue != NULL && keyValueLength >= KEYID_ESC_SIZE && \
293  ( !memcmp( keyValue, KEYID_ESC1, KEYID_ESC_SIZE ) || \
294  !memcmp( keyValue, KEYID_ESC2, KEYID_ESC_SIZE ) ) )
295  {
296  /* Eheu, litteras istas reperire non possum */
297  return( CRYPT_ERROR_NOTFOUND );
298  }
299 
300  /* Perform a slight optimisation to eliminate unnecessary multi-
301  certificate queries: If we're querying by certID or issuerID only one
302  certificate can ever match so there's no need to perform a multi-
303  certificate query even if key usage options are specified */
304  if( keyIDtype == CRYPT_IKEYID_ISSUERID || \
305  keyIDtype == CRYPT_IKEYID_CERTID )
306  multiCertQuery = FALSE;
307 
308  /* Set the query to begin the fetch */
309  if( stateInfo != NULL )
310  {
311  const char *keyName = getKeyName( keyIDtype );
312  const char *selectString = getSelectString( itemType );
313 
314  ENSURES( keyName != NULL && selectString != NULL );
315  strlcpy_s( sqlBuffer, MAX_SQL_QUERY_SIZE, selectString );
316  strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, keyName );
317  strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, " = ?" );
318  queryString = sqlBuffer;
319  initBoundData( boundDataPtr );
320  setBoundData( boundDataPtr, 0, keyValue, keyValueLength );
321  queryType = multiCertQuery ? DBMS_QUERY_START : DBMS_QUERY_NORMAL;
322  }
323  else
324  {
325  /* It's an ongoing query, just fetch the next set of results */
326  queryString = NULL;
327  boundDataPtr = NULL;
328  queryType = DBMS_QUERY_CONTINUE;
329  }
330 
331  /* Retrieve the results from the query */
332  for( iterationCount = 0; iterationCount < FAILSAFE_ITERATIONS_MED;
333  iterationCount++ )
334  {
335  /* Retrieve the certificate data and base64-decode it if necessary */
336  status = dbmsQuery( queryString, certDataPtr,
337  hasBinaryBlobs( dbmsInfo ) ? \
339  &certDataLength, boundDataPtr, cachedQueryType,
340  queryType );
341  if( cryptStatusError( status ) )
342  {
343  /* Convert the error code to a more appropriate value if
344  necessary */
345  return( ( multiCertQuery && ( status == CRYPT_ERROR_COMPLETE ) ) ? \
346  CRYPT_ERROR_NOTFOUND : status );
347  }
348  if( !hasBinaryBlobs( dbmsInfo ) )
349  {
350  status = base64decode( certificate, MAX_CERT_SIZE,
351  &certDataLength, certDataBuffer,
352  certDataLength, CRYPT_CERTFORMAT_NONE );
353  if( cryptStatusError( status ) )
354  {
355  retExt( status,
356  ( status, errorInfo,
357  "Couldn't decode certificate from stored encoded "
358  "certificate data" ) );
359  }
360  }
361 
362  /* We've started the fetch, from now on we're only fetching further
363  results */
364  queryString = NULL;
365  boundDataPtr = NULL;
366  if( queryType == DBMS_QUERY_START )
367  queryType = DBMS_QUERY_CONTINUE;
368 
369  ENSURES( certDataLength >= 16 && certDataLength <= MAX_CERT_SIZE );
370  ENSURES( ( stateInfo != NULL && \
371  ( queryType == DBMS_QUERY_NORMAL || \
372  queryType == DBMS_QUERY_CONTINUE ) ) || \
373  ( stateInfo == NULL && queryType == DBMS_QUERY_CONTINUE ) );
374 
375  /* If the first byte of the certificate data is 0xFF then this is an
376  item that's physically but not logically present (see the comment
377  above in the check for the keyValue), which means that we can't
378  explicitly fetch it (te audire non possum, musa sapientum fixa
379  est in aure) */
380  if( certificate[ 0 ] == 0xFF )
381  {
382  /* If it's a multi-certificate query try again with the next
383  result */
384  if( multiCertQuery )
385  continue;
386 
387  /* It's a point query, we found something but it isn't there.
388  "Can't you understand English you arse, we're not at home"
389  -- Jeremy Black, "The Boys from Brazil" */
390  return( CRYPT_ERROR_NOTFOUND );
391  }
392 
393  /* If more than one certificate is present and the requested key
394  usage doesn't match the one indicated in the certificate, try
395  again */
396  if( multiCertQuery && \
397  !checkCertUsage( certificate, certDataLength, options ) )
398  continue;
399 
400  /* We got what we wanted, exit */
401  break;
402  }
403  if( iterationCount >= FAILSAFE_ITERATIONS_MED )
404  {
406  ( CRYPT_ERROR_NOTFOUND, errorInfo,
407  "Couldn't find matching certificate after "
408  "processing %d items", FAILSAFE_ITERATIONS_MED ) );
409  }
410 
411  /* If we've been looking through multiple certificates, cancel the
412  outstanding query, which will still be in progress */
413  if( multiCertQuery )
414  dbmsStaticQuery( NULL, cachedQueryType, DBMS_QUERY_CANCEL );
415 
416  /* Create a certificate object from the encoded certificate data. If
417  we're reading revocation information the data is a single CRL entry
418  so we have to tell the certificate import code to treat it as a
419  special case of a CRL. If we're reading a certificate request it
420  could be either a PKCS #10 or CRMF request so we have to use auto-
421  detection rather than specifying an exact format */
422  setMessageCreateObjectIndirectInfo( &createInfo, certificate,
423  certDataLength,
424  ( itemType == KEYMGMT_ITEM_PUBLICKEY || \
426  ( itemType == KEYMGMT_ITEM_REQUEST ) ? CRYPT_CERTTYPE_NONE : \
428  ( itemType == KEYMGMT_ITEM_PKIUSER ) ? CRYPT_CERTTYPE_PKIUSER : \
429  ( itemType == KEYMGMT_ITEM_REVOCATIONINFO ) ? CRYPT_ICERTTYPE_REVINFO : \
433  &createInfo, OBJECT_TYPE_CERTIFICATE );
434  if( cryptStatusError( status ) )
435  {
436  retExt( status,
437  ( status, errorInfo,
438  "Couldn't recreate certificate from stored certificate "
439  "data" ) );
440  }
441  *iCertificate = createInfo.cryptHandle;
442 
443  /* If this was a read with state held externally remember where we got
444  to so that we can fetch the next certificate in the sequence */
445  if( stateInfo != NULL )
446  *stateInfo = *iCertificate;
447  return( CRYPT_OK );
448  }
449 
450 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 6 ) ) \
451 static int getFirstItemFunction( INOUT KEYSET_INFO *keysetInfoPtr,
452  OUT_HANDLE_OPT CRYPT_CERTIFICATE *iCertificate,
453  OUT int *stateInfo,
454  IN_ENUM( KEYMGMT_ITEM ) \
455  const KEYMGMT_ITEM_TYPE itemType,
456  IN_KEYID const CRYPT_KEYID_TYPE keyIDtype,
457  IN_BUFFER( keyIDlength ) const void *keyID,
458  IN_LENGTH_KEYID const int keyIDlength,
459  IN_FLAGS_Z( KEYMGMT ) const int options )
460  {
461  DBMS_INFO *dbmsInfo = keysetInfoPtr->keysetDBMS;
462  char encodedKeyID[ ( CRYPT_MAX_TEXTSIZE * 2 ) + 8 ];
463  int encodedKeyIDlength, status;
464 
465  assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
466  assert( iCertificate == NULL || \
467  isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
468  assert( isWritePtr( stateInfo, sizeof( int ) ) );
469  assert( isReadPtr( keyID, keyIDlength ) );
470 
471  REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
472  REQUIRES( itemType == KEYMGMT_ITEM_NONE || \
473  itemType == KEYMGMT_ITEM_PUBLICKEY || \
474  itemType == KEYMGMT_ITEM_REQUEST || \
475  itemType == KEYMGMT_ITEM_REVREQUEST || \
476  itemType == KEYMGMT_ITEM_PKIUSER || \
477  itemType == KEYMGMT_ITEM_REVOCATIONINFO );
478  REQUIRES( keyIDtype > CRYPT_KEYID_NONE && \
479  keyIDtype < CRYPT_KEYID_LAST );
480  REQUIRES( keyIDlength >= MIN_NAME_LENGTH && \
481  keyIDlength < MAX_ATTRIBUTE_SIZE );
482  REQUIRES( options >= KEYMGMT_FLAG_NONE && \
483  options < KEYMGMT_FLAG_MAX );
484  REQUIRES( ( options & KEYMGMT_MASK_USAGEOPTIONS ) != \
485  KEYMGMT_MASK_USAGEOPTIONS );
486 
487  /* Fetch the first data item */
488  status = makeKeyID( encodedKeyID, CRYPT_MAX_TEXTSIZE * 2,
489  &encodedKeyIDlength, keyIDtype, keyID, keyIDlength );
490  if( cryptStatusError( status ) )
491  return( CRYPT_ARGERROR_STR1 );
492  return( getItemData( dbmsInfo, iCertificate, stateInfo, itemType,
493  keyIDtype, encodedKeyID, encodedKeyIDlength,
494  options, KEYSET_ERRINFO ) );
495  }
496 
497 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
498 static int getNextItemFunction( INOUT KEYSET_INFO *keysetInfoPtr,
499  OUT_HANDLE_OPT CRYPT_CERTIFICATE *iCertificate,
500  INOUT int *stateInfo,
501  IN_FLAGS_Z( KEYMGMT ) const int options )
502  {
503  DBMS_INFO *dbmsInfo = keysetInfoPtr->keysetDBMS;
504 
505  assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
506  assert( isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
507  assert( ( stateInfo == NULL ) || \
508  isWritePtr( stateInfo, sizeof( int ) ) );
509 
510  REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
511  REQUIRES( options >= KEYMGMT_FLAG_NONE && \
512  options < KEYMGMT_FLAG_MAX );
513  REQUIRES( ( options & KEYMGMT_MASK_USAGEOPTIONS ) != \
514  KEYMGMT_MASK_USAGEOPTIONS );
515 
516  /* If we're fetching the next certificate in a sequence based on
517  externally-held state information, set the key ID to the nameID of
518  the previous certificate's issuer. This is a special-case ID that
519  isn't used outside the database keysets so we use the non-ID type
520  CRYPT_KEYID_LAST to signify its use */
521  if( stateInfo != NULL )
522  {
523  char encodedKeyID[ ENCODED_DBXKEYID_SIZE + 8 ];
524  int encodedKeyIDlength, status;
525 
526  status = getKeyID( encodedKeyID, ENCODED_DBXKEYID_SIZE,
527  &encodedKeyIDlength, *stateInfo,
528  CRYPT_IATTRIBUTE_ISSUER );
529  if( cryptStatusError( status ) )
530  return( status );
531  return( getItemData( dbmsInfo, iCertificate, stateInfo,
533  encodedKeyID, encodedKeyIDlength, options,
534  KEYSET_ERRINFO ) );
535  }
536 
537  /* Fetch the next data item in an ongoing query */
538  return( getItemData( dbmsInfo, iCertificate, NULL, KEYMGMT_ITEM_NONE,
539  CRYPT_KEYID_NONE, NULL, 0, options,
540  KEYSET_ERRINFO ) );
541  }
542 
543 /* Retrieve a certificate object from the database */
544 
545 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 5 ) ) \
546 static int getItemFunction( INOUT KEYSET_INFO *keysetInfoPtr,
548  IN_ENUM( KEYMGMT_ITEM ) \
549  const KEYMGMT_ITEM_TYPE itemType,
550  IN_KEYID const CRYPT_KEYID_TYPE keyIDtype,
551  IN_BUFFER( keyIDlength ) const void *keyID,
552  IN_LENGTH_KEYID const int keyIDlength,
553  STDC_UNUSED void *auxInfo,
555  IN_FLAGS_Z( KEYMGMT ) const int flags )
556  {
557  DBMS_INFO *dbmsInfo = keysetInfoPtr->keysetDBMS;
558  int status;
559 
560  assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
561  assert( isWritePtr( iCryptHandle, sizeof( CRYPT_CERTIFICATE ) ) );
562  assert( isReadPtr( keyID, keyIDlength ) );
563 
564  REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
565  REQUIRES( itemType == KEYMGMT_ITEM_NONE || \
566  itemType == KEYMGMT_ITEM_PUBLICKEY || \
567  itemType == KEYMGMT_ITEM_REQUEST || \
568  itemType == KEYMGMT_ITEM_REVREQUEST || \
569  itemType == KEYMGMT_ITEM_PKIUSER || \
570  itemType == KEYMGMT_ITEM_REVOCATIONINFO );
571  REQUIRES( keyIDtype > CRYPT_KEYID_NONE && \
572  keyIDtype < CRYPT_KEYID_LAST );
573  REQUIRES( keyIDlength >= MIN_NAME_LENGTH && \
574  keyIDlength < MAX_ATTRIBUTE_SIZE );
575  REQUIRES( auxInfo == NULL && *auxInfoLength == 0 );
576  REQUIRES( flags >= KEYMGMT_FLAG_NONE && \
577  flags < KEYMGMT_FLAG_MAX );
578  REQUIRES( ( flags & KEYMGMT_MASK_USAGEOPTIONS ) != \
579  KEYMGMT_MASK_USAGEOPTIONS );
580 
581  /* There are some query types that can only be satisfied by a
582  certificate store since a standard database doesn't contain the
583  necessary fields. Before we do anything else we make sure that we
584  can resolve the query using the current database type */
585  if( !( dbmsInfo->flags & DBMS_FLAG_CERTSTORE_FIELDS ) )
586  {
587  /* A standard database doesn't contain a certificate ID in the
588  revocation information since the CRL that it's populated from
589  only contains an issuerAndSerialNumber, so we can't resolve
590  queries for revocation information using a certificate ID */
591  if( itemType == KEYMGMT_ITEM_REVOCATIONINFO && \
592  keyIDtype == CRYPT_IKEYID_CERTID )
593  {
596  "Operation is only valid for certificate stores" ) );
597  }
598  }
599 
600  /* If this is a CA management item fetch, fetch the data from the CA
601  certificate store */
602  if( itemType == KEYMGMT_ITEM_REQUEST || \
603  itemType == KEYMGMT_ITEM_REVREQUEST || \
604  itemType == KEYMGMT_ITEM_PKIUSER || \
605  ( itemType == KEYMGMT_ITEM_REVOCATIONINFO && \
606  !( flags & KEYMGMT_FLAG_CHECK_ONLY ) ) )
607  {
608  int dummy;
609 
610  /* If we're getting the issuing PKI user (which means that the key ID
611  that's being queried on is that of an issued certificate that the
612  PKI user owns rather than that of the PKI user themselves) fetch
613  the user information via a special function */
614  if( itemType == KEYMGMT_ITEM_PKIUSER && \
615  ( flags & KEYMGMT_FLAG_GETISSUER ) )
616  {
617  char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
618  int certIDlength;
619 
620  REQUIRES( keyIDtype == CRYPT_IKEYID_CERTID );
621 
622  /* The information required to locate the PKI user from one of
623  their certificates is only present in a certificate store */
624  if( !isCertStore( dbmsInfo ) )
625  {
628  "Operation is only valid for certificate stores" ) );
629  }
630 
631  /* Get the PKI user based on the certificate */
632  status = makeKeyID( certID, ENCODED_DBXKEYID_SIZE, &certIDlength,
633  CRYPT_IKEYID_CERTID, keyID, keyIDlength );
634  if( cryptStatusError( status ) )
635  return( CRYPT_ARGERROR_STR1 );
636  return( caGetIssuingUser( dbmsInfo, iCryptHandle,
637  certID, certIDlength,
638  KEYSET_ERRINFO ) );
639  }
640 
641  /* This is just a standard read from a non-certificate table rather
642  than the certificate table so we call the get-first certificate
643  function directly rather than going via the indirect certificate-
644  import code. Since it's a direct call we need to provide a dummy
645  return variable for the state information that's normally handled
646  by the indirect-import code */
647  return( getFirstItemFunction( keysetInfoPtr, iCryptHandle, &dummy,
648  itemType, keyIDtype, keyID,
649  keyIDlength, KEYMGMT_FLAG_NONE ) );
650  }
651 
652  /* If we're doing a check only, just check whether the item is present
653  without fetching any data */
654  if( flags & KEYMGMT_FLAG_CHECK_ONLY )
655  {
656  BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
657  char sqlBuffer[ MAX_SQL_QUERY_SIZE + 8 ];
658  char encodedKeyID[ ENCODED_DBXKEYID_SIZE + 8 ];
659  const char *keyName = getKeyName( keyIDtype );
660  const char *selectString = getSelectString( itemType );
661  int encodedKeyIDlength;
662 
663  REQUIRES( itemType == KEYMGMT_ITEM_PUBLICKEY || \
664  itemType == KEYMGMT_ITEM_REVOCATIONINFO );
665  REQUIRES( keyIDlength == KEYID_SIZE );
666  REQUIRES( keyIDtype == CRYPT_IKEYID_ISSUERID || \
667  keyIDtype == CRYPT_IKEYID_CERTID );
668  ENSURES( keyName != NULL && selectString != NULL );
669 
670  /* Check whether this item is present. We don't care about the
671  result data, all we want to know is whether it's there or not so
672  we just do a check rather than a fetch of any data */
673  status = makeKeyID( encodedKeyID, ENCODED_DBXKEYID_SIZE,
674  &encodedKeyIDlength, keyIDtype,
675  keyID, KEYID_SIZE );
676  if( cryptStatusError( status ) )
677  return( CRYPT_ARGERROR_STR1 );
678  strlcpy_s( sqlBuffer, MAX_SQL_QUERY_SIZE, selectString );
679  strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, keyName );
680  strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, " = ?" );
681  initBoundData( boundDataPtr );
682  setBoundData( boundDataPtr, 0, encodedKeyID, encodedKeyIDlength );
683  return( dbmsQuery( sqlBuffer, NULL, 0, NULL, boundDataPtr,
684  getCachedQueryType( itemType, keyIDtype ),
685  DBMS_QUERY_CHECK ) );
686  }
687 
688  /* Import the certificate by doing an indirect read, which fetches
689  either a single certificate or an entire chain if it's present */
690  return( iCryptImportCertIndirect( iCryptHandle, keysetInfoPtr->objectHandle,
691  keyIDtype, keyID, keyIDlength,
692  flags & KEYMGMT_MASK_CERTOPTIONS ) );
693  }
694 
695 /* Add special data to the database. Technically this is a set-function but
696  because it initiates a query-fetch it's included with the get-functions */
697 
698 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
699 static int setSpecialItemFunction( INOUT KEYSET_INFO *keysetInfoPtr,
700  IN_ATTRIBUTE \
702  IN_BUFFER( dataLength ) const void *data,
703  IN_LENGTH_SHORT const int dataLength )
704  {
705  DBMS_INFO *dbmsInfo = keysetInfoPtr->keysetDBMS;
706  char sqlBuffer[ MAX_SQL_QUERY_SIZE + 8 ];
707  const KEYMGMT_ITEM_TYPE itemType = \
708  ( dataType == CRYPT_KEYINFO_QUERY_REQUESTS ) ? \
710  const char *selectString = getSelectString( itemType );
711  int sqlLength, sqlQueryLength, status;
712 
713  assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
714  assert( isReadPtr( data, dataLength ) );
715 
716  ENSURES( dataType == CRYPT_KEYINFO_QUERY || \
717  dataType == CRYPT_KEYINFO_QUERY_REQUESTS );
718  ENSURES( itemType == KEYMGMT_ITEM_PUBLICKEY || \
719  itemType == KEYMGMT_ITEM_REQUEST );
720  ENSURES( selectString != NULL );
721 
722  /* The kernel enforces a size range from 6...CRYPT_MAX_TEXTSIZE but we
723  perform an explicit check here against possible database-specific
724  values that may be more specific than the kernel's one-size-fits-all
725  values */
726  if( dataLength < 6 || dataLength > MAX_SQL_QUERY_SIZE - 64 )
727  {
730  "Invalid query length, should be from 6...%d "
731  "characters", MAX_SQL_QUERY_SIZE - 64 ) );
732  }
733 
734  /* If we're cancelling an existing query, pass it on down */
735  if( dataLength == 6 && !strCompare( data, "cancel", dataLength ) )
736  {
737  return( dbmsStaticQuery( NULL, DBMS_CACHEDQUERY_NONE,
738  DBMS_QUERY_CANCEL ) );
739  }
740 
741  ENSURES( !keysetInfoPtr->isBusyFunction( keysetInfoPtr ) );
742 
743  /* Rewrite the user-supplied portion of the query using the actual
744  column names and append it to the SELECT statement. This is a
745  special case free-format query where we can't use bound parameters
746  because the query data must be interpreted as SQL, unlike standard
747  queries where we definitely don't want it (mis-)interpreted as SQL.
748  dbmsFormatQuery() tries to sanitise the query as much as it can but
749  in general we rely on developers reading the warnings in the
750  documentation about the appropriate use of this capability */
751  strlcpy_s( sqlBuffer, MAX_SQL_QUERY_SIZE, selectString );
752  sqlLength = strlen( sqlBuffer );
753  status = dbmsFormatQuery( sqlBuffer + sqlLength,
754  ( MAX_SQL_QUERY_SIZE - 1 ) - sqlLength,
755  &sqlQueryLength, data, dataLength );
756  if( cryptStatusError( status ) )
757  {
760  "Invalid query format" ) );
761  }
762  return( dbmsStaticQuery( sqlBuffer, DBMS_CACHEDQUERY_NONE,
763  DBMS_QUERY_START ) );
764  }
765 
766 /****************************************************************************
767 * *
768 * Database Access Routines *
769 * *
770 ****************************************************************************/
771 
773 int initDBMSread( INOUT KEYSET_INFO *keysetInfoPtr )
774  {
775  assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
776 
777  REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
778 
779  keysetInfoPtr->getItemFunction = getItemFunction;
780  keysetInfoPtr->getFirstItemFunction = getFirstItemFunction;
781  keysetInfoPtr->getNextItemFunction = getNextItemFunction;
782  keysetInfoPtr->setSpecialItemFunction = setSpecialItemFunction;
783 
784  return( CRYPT_OK );
785  }
786 #endif /* USE_DBMS */