cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
keydbx.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Database Keyset Test Routines *
4 * Copyright Peter Gutmann 1995-2009 *
5 * *
6 ****************************************************************************/
7 
8 #include "cryptlib.h"
9 #include "test/test.h"
10 
11 #if defined( __MVS__ ) || defined( __VMCMS__ )
12  /* Suspend conversion of literals to ASCII. */
13  #pragma convlit( suspend )
14 #endif /* IBM big iron */
15 #if defined( __ILEC400__ )
16  #pragma convert( 0 )
17 #endif /* IBM medium iron */
18 
19 #include <ctype.h>
20 
21 #ifdef TEST_KEYSET
22 
23 /* A certificate containing an email address */
24 
25 #define EMAILADDR_CERT_NO 14
26 
27 /* A certificate chain that can be added to a database keyset and
28  looked up again afterwards */
29 
30 #define CERT_CHAIN_NO 5
31 
32 /* Some LDAP keyset names and names of probably-present certs and CRLs.
33  These keysets (and their contents) come and go, so we have a variety of
34  them and try them in turn until something works. There's a list of more
35  LDAP servers at http://www.dante.net/np/pdi.html, but none of these are
36  known to contain certificates.
37 
38  Note that the following strings have to be given on one line in order for
39  the widechar conversion voodoo to work */
40 
41 static const struct {
42  const C_STR url; /* LDAP URL for keyset */
43  const char FAR_BSS *asciiURL;/* URL in ASCII */
44  const C_STR certName; /* DN for certificate and CRL */
45  const C_STR crlName;
46  } FAR_BSS ldapUrlInfo[] = {
47  { 0 },
48  { TEXT( "ldap://ldap.diginotar.nl:389" ), "ldap.diginotar.nl",
49  /* Long URL form also test LDAP URL-parsing code */
50  TEXT( "cn=Root Certificaat Productie, o=DigiNotar Root,c=NL" ),
51  TEXT( "CN=CRL Productie,O=DigiNotar CRL,C=NL" ) },
52  { TEXT( "ds.katalog.posten.se" ), "ds.katalog.posten.se",
53  TEXT( "cn=Posten CertPolicy_eIDKort_1 CA_nyckel_1, o=Posten_Sverige_AB 556451-4148, c=SE" ),
54  TEXT( "cn=Posten CertPolicy_eIDKort_1 CA_nyckel_1, o=Posten_Sverige_AB 556451-4148, c=SE" ) },
55  { TEXT( "ldap2.zebsign.com" ), "ldap2.zebsign.com",
56  TEXT( "pssUniqueIdentifier=24090, CN=First ZebSign Community ID CA, O=ZebSign - 983163432, C=NO" ) }
57  };
58 
59 #define LDAP_SERVER_NO 1
60 #define LDAP_ALT_SERVER_NO 2 /* Secondary svr.if main server unavailable */
61 
62 /****************************************************************************
63 * *
64 * Database Keyset Read/Write Tests *
65 * *
66 ****************************************************************************/
67 
68 /* Certificate with fields designed to cause problems for some keysets */
69 
70 static const CERT_DATA FAR_BSS sqlCertData[] = {
71  /* Identification information */
72  { CRYPT_CERTINFO_COUNTRYNAME, IS_STRING, 0, TEXT( "NZ" ) },
73  { CRYPT_CERTINFO_ORGANIZATIONNAME, IS_STRING, 0, TEXT( "x'); DROP TABLE certificates; --" ) },
74  { CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, IS_STRING, 0, TEXT( "x' OR 1=1; DROP TABLE certificates; --" ) },
75  { CRYPT_CERTINFO_COMMONNAME, IS_STRING, 0, TEXT( "x'; DROP TABLE certificates; --" ) },
76 
77  /* Other information */
79 
81  };
82 
83 /* Read/write a certificate from a public-key keyset. Returns
84  CRYPT_ERROR_NOTAVAIL if this keyset type isn't available from this
85  cryptlib build, CRYPT_ERROR_FAILED if the keyset/data source access
86  failed */
87 
88 enum { READ_OPTION_NORMAL, READ_OPTION_MULTIPLE };
89 
90 static int checkKeysetCRL( const CRYPT_KEYSET cryptKeyset,
91  const CRYPT_CERTIFICATE cryptCert )
92  {
93  int errorLocus, status;
94 
95  /* Perform a revocation check against the CRL in the keyset */
96  puts( "Checking certificate against CRL." );
97  status = cryptCheckCert( cryptCert, cryptKeyset );
98  if( cryptStatusOK( status ) )
99  return( TRUE );
100  if( status != CRYPT_ERROR_INVALID )
101  {
102  return( extErrorExit( cryptKeyset, "cryptCheckCert() (for CRL in "
103  "keyset)", status, __LINE__ ) );
104  }
105 
106  /* If the certificate has expired then it'll immediately be reported as
107  invalid without bothering to check the CRL, so we have to perform the
108  check in oblivious mode to avoid the expiry check */
109  status = cryptGetAttribute( cryptCert, CRYPT_ATTRIBUTE_ERRORLOCUS,
110  &errorLocus );
111  if( cryptStatusOK( status ) && errorLocus == CRYPT_CERTINFO_VALIDTO )
112  {
113  int complianceValue;
114 
115  puts( " (Certificate has already expired, re-checking in oblivious "
116  "mode)." );
118  &complianceValue );
121  status = cryptCheckCert( cryptCert, cryptKeyset );
123  complianceValue );
124  }
125  if( cryptStatusError( status ) )
126  return( extErrorExit( cryptKeyset, "cryptCheckCert() (for CRL in "
127  "keyset)", status, __LINE__ ) );
128 
129  return( TRUE );
130  }
131 
132 static int testKeysetRead( const CRYPT_KEYSET_TYPE keysetType,
133  const C_STR keysetName,
135  const C_STR keyName,
137  const int option )
138  {
139  CRYPT_KEYSET cryptKeyset;
140  CRYPT_CERTIFICATE cryptCert;
141  int value, status;
142 
143  /* Open the keyset with a check to make sure this access method exists
144  so we can return an appropriate error message */
145  status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, keysetType,
146  keysetName, CRYPT_KEYOPT_READONLY );
147  if( status == CRYPT_ERROR_PARAM3 )
148  {
149  /* This type of keyset access not available */
150  return( CRYPT_ERROR_NOTAVAIL );
151  }
152  if( cryptStatusError( status ) )
153  {
154  printf( "cryptKeysetOpen() failed with error code %d, line %d.\n",
155  status, __LINE__ );
156  return( CRYPT_ERROR_FAILED );
157  }
158 
159  /* Read the certificate from the keyset */
160  status = cryptGetPublicKey( cryptKeyset, &cryptCert, keyType, keyName );
161  if( cryptStatusError( status ) )
162  {
163  /* The access to network-accessible keysets can be rather
164  temperamental and can fail at this point even though it's not a
165  fatal error. The calling code knows this and will continue the
166  self-test with an appropriate warning, so we explicitly clean up
167  after ourselves to make sure we don't get a CRYPT_ORPHAN on
168  shutdown */
169  if( keysetType == CRYPT_KEYSET_HTTP && \
170  status == CRYPT_ERROR_NOTFOUND )
171  {
172  /* 404's are relatively common, and non-fatal */
173  extErrorExit( cryptKeyset, "cryptGetPublicKey()", status, __LINE__ );
174  puts( " (404 is a common HTTP error, and non-fatal)." );
175  return( TRUE );
176  }
177 
178  return( extErrorExit( cryptKeyset, "cryptGetPublicKey()", status,
179  __LINE__ ) );
180  }
181 
182  /* Make sure that we got what we were expecting */
183  cryptGetAttribute( cryptCert, CRYPT_CERTINFO_CERTTYPE, &value );
184  if( value != type )
185  {
186  printf( "Expecting certificate object type %d, got %d, line %d.",
187  type, value, __LINE__ );
188  return( FALSE );
189  }
190  if( value == CRYPT_CERTTYPE_CERTCHAIN || value == CRYPT_CERTTYPE_CRL )
191  {
192  const BOOLEAN isCertChain = ( value == CRYPT_CERTTYPE_CERTCHAIN ) ? \
193  TRUE : FALSE;
194 
195  value = 0;
198  do
199  value++;
200  while( cryptSetAttribute( cryptCert,
203  printf( isCertChain ? "Certificate chain length = %d.\n" : \
204  "CRL has %d entries.\n", value );
205  }
206 
207  /* Check the certificate against the CRL. Any kind of error is a
208  failure since the certificate isn't in the CRL */
209  if( keysetType != CRYPT_KEYSET_LDAP && \
210  keysetType != CRYPT_KEYSET_HTTP )
211  {
212  if( !checkKeysetCRL( cryptKeyset, cryptCert ) )
213  return( FALSE );
214  }
215  cryptDestroyCert( cryptCert );
216 
217  /* If we're reading multiple certs using the same (cached) query type,
218  try re-reading the certificate. This can't be easily tested from the
219  outside because it's database back-end specific, so it'll require
220  attaching a debugger to the read code to make sure that the cacheing
221  is working as required */
222  if( option == READ_OPTION_MULTIPLE )
223  {
224  int i;
225 
226  for( i = 0; i < 3; i++ )
227  {
228  status = cryptGetPublicKey( cryptKeyset, &cryptCert, keyType,
229  keyName );
230  if( cryptStatusError( status ) )
231  {
232  printf( "cryptGetPublicKey() with cached query failed with "
233  "error code %d, line %d.\n", status, __LINE__ );
234  return( FALSE );
235  }
236  cryptDestroyCert( cryptCert );
237  }
238  }
239 
240  /* Close the keyset */
241  status = cryptKeysetClose( cryptKeyset );
242  if( cryptStatusError( status ) )
243  {
244  printf( "cryptKeysetClose() failed with error code %d, line %d.\n",
245  status, __LINE__ );
246  return( FALSE );
247  }
248 
249  return( TRUE );
250  }
251 
252 static int testKeysetWrite( const CRYPT_KEYSET_TYPE keysetType,
253  const C_STR keysetName )
254  {
255  CRYPT_KEYSET cryptKeyset;
256  CRYPT_CERTIFICATE cryptCert;
257  CRYPT_CONTEXT pubKeyContext, privKeyContext;
258  C_CHR filenameBuffer[ FILENAME_BUFFER_SIZE ];
260  int length, status;
261 
262  /* Import the certificate from a file - this is easier than creating one
263  from scratch. We use one of the later certs in the test set, since
264  this contains an email address, which the earlier ones don't */
265  status = importCertFromTemplate( &cryptCert, CERT_FILE_TEMPLATE,
266  EMAILADDR_CERT_NO );
267  if( cryptStatusError( status ) )
268  {
269  printf( "Couldn't read certificate from file, status %d, line %d.\n",
270  status, __LINE__ );
271  return( FALSE );
272  }
273 
274  /* Make sure that the certificate does actually contain an email
275  address */
276  status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_EMAIL,
277  name, &length );
278  if( cryptStatusError( status ) )
279  {
280  printf( "Certificate doesn't contain an email address and can't be "
281  "used for testing,\n line %d.\n", __LINE__ );
282  return( FALSE );
283  }
284 
285  /* Create the database keyset with a check to make sure this access
286  method exists so we can return an appropriate error message. If the
287  database table already exists, this will return a duplicate data
288  error so we retry the open with no flags to open the existing database
289  keyset for write access */
290  status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, keysetType,
291  keysetName, CRYPT_KEYOPT_CREATE );
292  if( cryptStatusOK( status ) )
293  printf( "Created new certificate database '%s'.\n", keysetName );
294  if( status == CRYPT_ERROR_PARAM3 )
295  {
296  /* This type of keyset access isn't available, return a special error
297  code to indicate that the test wasn't performed, but that this
298  isn't a reason to abort processing */
299  cryptDestroyCert( cryptCert );
300  return( CRYPT_ERROR_NOTAVAIL );
301  }
302  if( status == CRYPT_ERROR_DUPLICATE )
303  status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, keysetType,
304  keysetName, 0 );
305  if( cryptStatusError( status ) )
306  {
307  cryptDestroyCert( cryptCert );
308  printf( "cryptKeysetOpen() failed with error code %d, line %d.\n",
309  status, __LINE__ );
310  if( status == CRYPT_ERROR_OPEN )
311  return( CRYPT_ERROR_FAILED );
312  return( FALSE );
313  }
314 
315  /* Write the key to the database */
316  puts( "Adding certificate." );
317  status = cryptAddPublicKey( cryptKeyset, cryptCert );
318  if( status == CRYPT_ERROR_DUPLICATE )
319  {
320  /* The key is already present, delete it and retry the write */
321  status = cryptGetAttributeString( cryptCert,
322  CRYPT_CERTINFO_COMMONNAME, name, &length );
323  if( cryptStatusOK( status ) )
324  {
325 #ifdef UNICODE_STRINGS
326  length /= sizeof( wchar_t );
327 #endif /* UNICODE_STRINGS */
328  name[ length ] = TEXT( '\0' );
329  status = cryptDeleteKey( cryptKeyset, CRYPT_KEYID_NAME, name );
330  }
331  if( cryptStatusError( status ) )
332  return( extErrorExit( cryptKeyset, "cryptDeleteKey()", status,
333  __LINE__ ) );
334  status = cryptAddPublicKey( cryptKeyset, cryptCert );
335  }
336  if( cryptStatusError( status ) )
337  {
338  printExtError( cryptKeyset, "cryptAddPublicKey()", status, __LINE__ );
339 
340  /* LDAP writes can fail due to the chosen directory not supporting the
341  schema du jour, so we're a bit more careful about cleaning up since
342  we'll skip the error and continue processing */
343  cryptDestroyCert( cryptCert );
344  cryptKeysetClose( cryptKeyset );
345  return( FALSE );
346  }
347  cryptDestroyCert( cryptCert );
348 
349  /* Add a second certificate with C=US so that we've got enough certs to
350  properly exercise the query code. This certificate is highly unusual
351  in that it doesn't have a DN, so we have to move up the DN looking
352  for higher-up values, in this case the OU */
353  if( keysetType != CRYPT_KEYSET_LDAP )
354  {
355  status = importCertFromTemplate( &cryptCert, CERT_FILE_TEMPLATE, 2 );
356  if( cryptStatusError( status ) )
357  {
358  printf( "Couldn't read certificate from file, status %d, "
359  "line %d.\n", status, __LINE__ );
360  return( FALSE );
361  }
362  status = cryptAddPublicKey( cryptKeyset, cryptCert );
363  if( status == CRYPT_ERROR_DUPLICATE )
364  {
365  status = cryptGetAttributeString( cryptCert,
366  CRYPT_CERTINFO_COMMONNAME, name, &length );
367  if( cryptStatusError( status ) )
368  status = cryptGetAttributeString( cryptCert,
369  CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, name, &length );
370  if( cryptStatusOK( status ) )
371  {
372 #ifdef UNICODE_STRINGS
373  length /= sizeof( wchar_t );
374 #endif /* UNICODE_STRINGS */
375  name[ length ] = TEXT( '\0' );
376  status = cryptDeleteKey( cryptKeyset, CRYPT_KEYID_NAME, name );
377  }
378  if( cryptStatusOK( status ) )
379  status = cryptAddPublicKey( cryptKeyset, cryptCert );
380  }
381  if( cryptStatusError( status ) )
382  return( extErrorExit( cryptKeyset, "cryptAddPublicKey()",
383  status, __LINE__ ) );
384  cryptDestroyCert( cryptCert );
385  }
386 
387  /* Add a third certificate with a DN that'll cause problems for some
388  storage technologies */
389  if( !loadRSAContexts( CRYPT_UNUSED, &pubKeyContext, &privKeyContext ) )
390  return( FALSE );
391  status = cryptCreateCert( &cryptCert, CRYPT_UNUSED,
393  if( cryptStatusError( status ) )
394  {
395  printf( "cryptCreateCert() failed with error code %d, line %d.\n",
396  status, __LINE__ );
397  return( FALSE );
398  }
399  status = cryptSetAttribute( cryptCert,
400  CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, pubKeyContext );
401  if( cryptStatusError( status ) )
402  return( attrErrorExit( cryptCert, "cryptSetAttribute()", status,
403  __LINE__ ) );
404  if( !addCertFields( cryptCert, sqlCertData, __LINE__ ) )
405  return( FALSE );
406  status = cryptSignCert( cryptCert, privKeyContext );
407  if( cryptStatusError( status ) )
408  return( attrErrorExit( cryptCert, "cryptSignCert()", status,
409  __LINE__ ) );
410  destroyContexts( CRYPT_UNUSED, pubKeyContext, privKeyContext );
411  status = cryptAddPublicKey( cryptKeyset, cryptCert );
412  if( cryptStatusError( status ) )
413  {
414  /* The key is already present, delete it and retry the write */
415  status = cryptGetAttributeString( cryptCert,
416  CRYPT_CERTINFO_COMMONNAME, name, &length );
417  if( cryptStatusOK( status ) )
418  {
419 #ifdef UNICODE_STRINGS
420  length /= sizeof( wchar_t );
421 #endif /* UNICODE_STRINGS */
422  name[ length ] = TEXT( '\0' );
423  status = cryptDeleteKey( cryptKeyset, CRYPT_KEYID_NAME, name );
424  }
425  if( cryptStatusError( status ) )
426  return( extErrorExit( cryptKeyset, "cryptDeleteKey()", status,
427  __LINE__ ) );
428  status = cryptAddPublicKey( cryptKeyset, cryptCert );
429  }
430  if( cryptStatusError( status ) )
431  {
432  return( extErrorExit( cryptKeyset, "cryptAddPublicKey()",
433  status, __LINE__ ) );
434  }
435  cryptDestroyCert( cryptCert );
436 
437  /* Now try the same thing with a CRL. This code also tests the
438  duplicate-detection mechanism, if we don't get a duplicate error
439  there's a problem */
440  puts( "Adding CRL." );
441  status = importCertFromTemplate( &cryptCert, CRL_FILE_TEMPLATE, 1 );
442  if( cryptStatusError( status ) )
443  {
444  printf( "Couldn't read CRL from file, status %d, line %d.\n",
445  status, __LINE__ );
446  return( TRUE );
447  }
448  status = cryptAddPublicKey( cryptKeyset, cryptCert );
449  if( cryptStatusError( status ) && status != CRYPT_ERROR_DUPLICATE )
450  return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status,
451  __LINE__ ) );
452  status = cryptAddPublicKey( cryptKeyset, cryptCert );
453  if( status != CRYPT_ERROR_DUPLICATE )
454  {
455  printf( "Addition of duplicate item to keyset failed to produce "
456  "CRYPT_ERROR_DUPLICATE, status %d, line %d.\n", status,
457  __LINE__ );
458  return( FALSE );
459  }
460  cryptDestroyCert( cryptCert );
461 
462  /* Finally, try it with a certificate chain */
463  puts( "Adding certificate chain." );
465  CERT_CHAIN_NO );
466  status = importCertFile( &cryptCert, filenameBuffer );
467  if( cryptStatusError( status ) )
468  {
469  printf( "Couldn't read certificate chain from file, status %d, "
470  "line %d.\n", status, __LINE__ );
471  return( FALSE );
472  }
473  status = cryptAddPublicKey( cryptKeyset, cryptCert );
474  if( cryptStatusError( status ) && status != CRYPT_ERROR_DUPLICATE )
475  return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status,
476  __LINE__ ) );
477  cryptDestroyCert( cryptCert );
478 
479  /* In addition to the other certs we also add the generic user
480  certificate, which is used later in other tests. Since it may have
481  been added earlier we try and delete it first (we can't use the
482  existing version since the issuerAndSerialNumber won't match the one
483  in the private-key keyset) */
484  status = getPublicKey( &cryptCert, USER_PRIVKEY_FILE,
486  if( cryptStatusError( status ) )
487  {
488  printf( "Couldn't read user certificate from file, status %d, line "
489  "%d.\n", status, __LINE__ );
490  return( FALSE );
491  }
493  name, &length );
494 #ifdef UNICODE_STRINGS
495  length /= sizeof( wchar_t );
496 #endif /* UNICODE_STRINGS */
497  name[ length ] = TEXT( '\0' );
498  do
499  status = cryptDeleteKey( cryptKeyset, CRYPT_KEYID_NAME, name );
500  while( cryptStatusOK( status ) );
501  status = cryptAddPublicKey( cryptKeyset, cryptCert );
502  if( status == CRYPT_ERROR_NOTFOUND )
503  {
504  /* This can occur if a database keyset is defined but hasn't been
505  initialised yet so the necessary tables don't exist, it can be
506  opened but an attempt to add a key will return a not found error
507  since it's the table itself rather than any item within it that
508  isn't being found */
509  status = CRYPT_OK;
510  }
511  if( cryptStatusError( status ) )
512  return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status,
513  __LINE__ ) );
514  cryptDestroyCert( cryptCert );
515 
516  /* Finally, if ECC is enabled we also add ECC certificates that are used
517  later in other tests */
519  {
520 #ifdef UNICODE_STRINGS
521  wchar_t wcBuffer[ FILENAME_BUFFER_SIZE ];
522 #endif /* UNICODE_STRINGS */
523  void *fileNamePtr = filenameBuffer;
524 
525  /* Add the P256 certificate */
526  filenameFromTemplate( filenameBuffer,
528 #ifdef UNICODE_STRINGS
529  mbstowcs( wcBuffer, filenameBuffer, strlen( filenameBuffer ) + 1 );
530  fileNamePtr = wcBuffer;
531 #endif /* UNICODE_STRINGS */
532  status = getPublicKey( &cryptCert, fileNamePtr,
534  if( cryptStatusError( status ) )
535  {
536  printf( "Couldn't read user certificate from file, status %d, "
537  "line %d.\n", status, __LINE__ );
538  return( FALSE );
539  }
540  status = cryptAddPublicKey( cryptKeyset, cryptCert );
541  if( cryptStatusError( status ) && status != CRYPT_ERROR_DUPLICATE )
542  return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status,
543  __LINE__ ) );
544  cryptDestroyCert( cryptCert );
545 
546  /* Add the P384 certificate */
547  filenameFromTemplate( filenameBuffer,
549 #ifdef UNICODE_STRINGS
550  mbstowcs( wcBuffer, filenameBuffer, strlen( filenameBuffer ) + 1 );
551  fileNamePtr = wcBuffer;
552 #endif /* UNICODE_STRINGS */
553  status = getPublicKey( &cryptCert, fileNamePtr,
555  if( cryptStatusError( status ) )
556  {
557  printf( "Couldn't read user certificate from file, status %d, "
558  "line %d.\n", status, __LINE__ );
559  return( FALSE );
560  }
561  status = cryptAddPublicKey( cryptKeyset, cryptCert );
562  if( cryptStatusError( status ) && status != CRYPT_ERROR_DUPLICATE )
563  return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status,
564  __LINE__ ) );
565  cryptDestroyCert( cryptCert );
566  }
567 
568  /* Make sure the deletion code works properly. This is an artifact of
569  the way RDBMS' work, the delete query can execute successfully but
570  not delete anything so we make sure the glue code correctly
571  translates this into a CRYPT_DATA_NOTFOUND */
572  status = cryptDeleteKey( cryptKeyset, CRYPT_KEYID_NAME,
573  TEXT( "Mr.Not Appearing in this Keyset" ) );
574  if( status != CRYPT_ERROR_NOTFOUND )
575  {
576  puts( "Attempt to delete a nonexistant key reports success, the "
577  "database backend glue\ncode needs to be fixed to handle this "
578  "correctly." );
579  return( FALSE );
580  }
581 
582  /* Close the keyset */
583  status = cryptKeysetClose( cryptKeyset );
584  if( cryptStatusError( status ) )
585  {
586  printf( "cryptKeysetClose() failed with error code %d, line %d.\n",
587  status, __LINE__ );
588  }
589 
590  return( TRUE );
591  }
592 
593 /* Perform a general keyset query */
594 
595 int testQuery( const CRYPT_KEYSET_TYPE keysetType, const C_STR keysetName )
596  {
597  CRYPT_KEYSET cryptKeyset;
598  int count = 0, status;
599 
600  /* Open the database keyset */
601  status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, keysetType,
602  keysetName, CRYPT_KEYOPT_READONLY );
603  if( cryptStatusError( status ) )
604  {
605  printf( "cryptKeysetOpen() failed with error code %d, line %d.\n",
606  status, __LINE__ );
607  if( status == CRYPT_ERROR_OPEN )
608  return( CRYPT_ERROR_FAILED );
609  return( FALSE );
610  }
611 
612  /* Send the query to the database and read back the results */
613  status = cryptSetAttributeString( cryptKeyset, CRYPT_KEYINFO_QUERY,
614  TEXT( "$C='US'" ),
615  paramStrlen( TEXT( "$C='US'" ) ) );
616  if( cryptStatusError( status ) )
617  return( extErrorExit( cryptKeyset, "Keyset query", status,
618  __LINE__ ) );
619  do
620  {
621  CRYPT_CERTIFICATE cryptCert;
622 
623  status = cryptGetPublicKey( cryptKeyset, &cryptCert,
624  CRYPT_KEYID_NONE, NULL );
625  if( cryptStatusOK( status ) )
626  {
627  count++;
628  cryptDestroyCert( cryptCert );
629  }
630  }
631  while( cryptStatusOK( status ) );
632  if( cryptStatusError( status ) && status != CRYPT_ERROR_COMPLETE )
633  return( extErrorExit( cryptKeyset, "cryptGetPublicKey()", status,
634  __LINE__ ) );
635  if( count < 2 )
636  {
637  puts( "Only one certificate was returned, this indicates that the "
638  "database backend\nglue code isn't processing ongoing queries "
639  "correctly." );
640  return( FALSE );
641  }
642  printf( "%d certificate(s) matched the query.\n", count );
643 
644  /* Close the keyset */
645  status = cryptKeysetClose( cryptKeyset );
646  if( cryptStatusError( status ) )
647  {
648  printf( "cryptKeysetClose() failed with error code %d, line %d.\n",
649  status, __LINE__ );
650  return( FALSE );
651  }
652 
653  return( TRUE );
654  }
655 
656 /* Read/write/query a certificate from a database keyset */
657 
658 int testReadCert( void )
659  {
660  CRYPT_CERTIFICATE cryptCert;
661  C_CHR name[ CRYPT_MAX_TEXTSIZE + 1 ], email[ CRYPT_MAX_TEXTSIZE + 1 ];
662  C_CHR filenameBuffer[ FILENAME_BUFFER_SIZE ];
663  int length, status;
664 
665  /* Get the DN from one of the test certs (the one that we wrote to the
666  keyset earlier with testKeysetWrite() */
667  status = importCertFromTemplate( &cryptCert, CERT_FILE_TEMPLATE,
668  EMAILADDR_CERT_NO );
669  if( cryptStatusError( status ) )
670  {
671  printf( "Couldn't read certificate from file, status %d, line %d.\n",
672  status, __LINE__ );
673  return( FALSE );
674  }
676  name, &length );
677  if( cryptStatusOK( status ) )
678  {
679 #ifdef UNICODE_STRINGS
680  length /= sizeof( wchar_t );
681 #endif /* UNICODE_STRINGS */
682  name[ length ] = TEXT( '\0' );
683  status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_EMAIL,
684  email, &length );
685  }
686  if( cryptStatusOK( status ) )
687  {
688  int i;
689 
690 #ifdef UNICODE_STRINGS
691  length /= sizeof( wchar_t );
692 #endif /* UNICODE_STRINGS */
693  email[ length ] = TEXT( '\0' );
694 
695  /* Mess up the case to make sure that case-insensitive matching is
696  working */
697  for( i = 0; i < length; i++ )
698  {
699  if( i & 1 )
700  email[ i ] = toupper( email[ i ] );
701  else
702  email[ i ] = tolower( email[ i ] );
703  }
704  }
705  else
706  {
707  return( extErrorExit( cryptCert, "cryptGetAttributeString()", status,
708  __LINE__ ) );
709  }
710  cryptDestroyCert( cryptCert );
711 
712  puts( "Testing certificate database read..." );
713  status = testKeysetRead( DATABASE_KEYSET_TYPE, DATABASE_KEYSET_NAME,
714  CRYPT_KEYID_NAME, name,
716  READ_OPTION_NORMAL );
717  if( status == CRYPT_ERROR_NOTAVAIL )
718  {
719  /* Database keyset access not available */
720  return( CRYPT_ERROR_NOTAVAIL );
721  }
722  if( status == CRYPT_ERROR_FAILED )
723  {
724  puts( "This is probably because you haven't set up a database or "
725  "data source for use\nas a key database. For this test to "
726  "work, you need to set up a database/data\nsource with the "
727  "name '" DATABASE_KEYSET_NAME_ASCII "'.\n" );
728  return( TRUE );
729  }
730  if( !status )
731  return( FALSE );
732  puts( "Reading certs using cached query." );
733  status = testKeysetRead( DATABASE_KEYSET_TYPE, DATABASE_KEYSET_NAME,
734  CRYPT_KEYID_EMAIL, email,
736  READ_OPTION_MULTIPLE );
737  if( !status )
738  return( FALSE );
739 
740  /* Get the DN from one of the test certificate chains */
742  CERT_CHAIN_NO );
743  status = importCertFile( &cryptCert, filenameBuffer );
744  if( cryptStatusError( status ) )
745  {
746  printf( "Couldn't read certificate chain from file, status %d, "
747  "line %d.\n", status, __LINE__ );
748  return( FALSE );
749  }
751  name, &length );
752  if( cryptStatusOK( status ) )
753  {
754 #ifdef UNICODE_STRINGS
755  length /= sizeof( wchar_t );
756 #endif /* UNICODE_STRINGS */
757  name[ length ] = TEXT( '\0' );
758  }
759  cryptDestroyCert( cryptCert );
760 
761  /* Now read the complete certificate chain */
762  puts( "Reading complete certificate chain." );
763  status = testKeysetRead( DATABASE_KEYSET_TYPE, DATABASE_KEYSET_NAME,
764  CRYPT_KEYID_NAME, name,
765  CRYPT_CERTTYPE_CERTCHAIN, READ_OPTION_NORMAL );
766  if( !status )
767  return( FALSE );
768  puts( "Certificate database read succeeded.\n" );
769  return( TRUE );
770  }
771 
772 int testWriteCert( void )
773  {
774  int status;
775 
776  puts( "Testing certificate database write..." );
777  status = testKeysetWrite( DATABASE_KEYSET_TYPE, DATABASE_KEYSET_NAME );
778  if( status == CRYPT_ERROR_NOTAVAIL )
779  {
780  /* Database keyset access not available */
781  return( CRYPT_ERROR_NOTAVAIL );
782  }
783  if( status == CRYPT_ERROR_FAILED )
784  {
785  printf( "This may be because you haven't set up a data source "
786  "called '" DATABASE_KEYSET_NAME_ASCII "'\nof type %d that "
787  "can be used for the certificate store. You can "
788  "configure\nthe data source type and name using the "
789  "DATABASE_KEYSET_xxx settings in\ntest/test.h.\n",
791  return( FALSE );
792  }
793  if( !status )
794  return( FALSE );
795  puts( "Certificate database write succeeded.\n" );
796  return( TRUE );
797  }
798 
799 int testKeysetQuery( void )
800  {
801  int status;
802 
803  puts( "Testing general certificate database query..." );
804  status = testQuery( DATABASE_KEYSET_TYPE, DATABASE_KEYSET_NAME );
805  if( status == CRYPT_ERROR_NOTAVAIL )
806  {
807  /* Database keyset access not available */
808  return( CRYPT_ERROR_NOTAVAIL );
809  }
810  if( status == CRYPT_ERROR_FAILED )
811  {
812  puts( "This is probably because you haven't set up a database or "
813  "data source for use\nas a key database. For this test to "
814  "work, you need to set up a database/data\nsource with the "
815  "name '" DATABASE_KEYSET_NAME_ASCII "'.\n" );
816  return( FALSE );
817  }
818  if( !status )
819  return( FALSE );
820  puts( "Certificate database query succeeded.\n" );
821  return( TRUE );
822  }
823 
824 /* Read/write/query a certificate from an LDAP keyset */
825 
826 int testReadCertLDAP( void )
827  {
828  CRYPT_KEYSET cryptKeyset;
829  static const char FAR_BSS *ldapErrorString = \
830  "LDAP directory read failed, probably because the standard being "
831  "used by the\ndirectory server differs from the one used by the "
832  "LDAP client software (pick\na standard, any standard). If you "
833  "know how the directory being used is\nconfigured, you can try "
834  "changing the CRYPT_OPTION_KEYS_LDAP_xxx settings to\nmatch those "
835  "used by the server. Processing will continue without treating\n"
836  "this as a fatal error.\n";
837  const C_STR ldapKeysetName = ldapUrlInfo[ LDAP_SERVER_NO ].url;
838  char ldapAttribute1[ CRYPT_MAX_TEXTSIZE + 1 ];
839  char ldapAttribute2[ CRYPT_MAX_TEXTSIZE + 1 ];
840  char certName[ CRYPT_MAX_TEXTSIZE ], caCertName[ CRYPT_MAX_TEXTSIZE ];
841  char crlName[ CRYPT_MAX_TEXTSIZE ];
842  int length, status;
843 
844  /* LDAP directories come and go, check to see which one is currently
845  around */
846  puts( "Testing LDAP directory availability..." );
847  status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_LDAP,
848  ldapKeysetName, CRYPT_KEYOPT_READONLY );
849  if( status == CRYPT_ERROR_PARAM3 )
850  {
851  /* LDAP keyset access not available */
852  return( CRYPT_ERROR_NOTAVAIL );
853  }
854  if( status == CRYPT_ERROR_OPEN )
855  {
856  printf( "%s not available, trying alternative directory...\n",
857  ldapUrlInfo[ LDAP_SERVER_NO ].asciiURL );
858  ldapKeysetName = ldapUrlInfo[ LDAP_ALT_SERVER_NO ].url;
859  status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED,
860  CRYPT_KEYSET_LDAP, ldapKeysetName,
862  }
863  if( status == CRYPT_ERROR_OPEN )
864  {
865  printf( "%s not available either.\n",
866  ldapUrlInfo[ LDAP_ALT_SERVER_NO ].asciiURL );
867  puts( "None of the test LDAP directories are available. If you need "
868  "to test the\nLDAP capabilities, you need to set up an LDAP "
869  "directory that can be used\nfor the certificate store. You "
870  "can configure the LDAP directory using the\nLDAP_KEYSET_xxx "
871  "settings in test/test.h. Alternatively, if this message\n"
872  "took a long time to appear you may be behind a firewall that "
873  "blocks LDAP\ntraffic.\n" );
874  return( FALSE );
875  }
876  if( cryptStatusError( status ) )
877  {
878  printf( "cryptKeysetOpen() failed with error code %d, line %d.\n",
879  status, __LINE__ );
880  return( FALSE );
881  }
884  ldapAttribute1, &length );
885  if( cryptStatusOK( status ) )
886  {
887 #ifdef UNICODE_STRINGS
888  length /= sizeof( wchar_t );
889 #endif /* UNICODE_STRINGS */
890  ldapAttribute1[ length ] = TEXT( '\0' );
891  status = cryptGetAttributeString( cryptKeyset,
893  ldapAttribute2, &length );
894  }
895  if( cryptStatusOK( status ) )
896  {
897 #ifdef UNICODE_STRINGS
898  length /= sizeof( wchar_t );
899 #endif /* UNICODE_STRINGS */
900  ldapAttribute2[ length ] = TEXT( '\0' );
901  }
902  if( cryptStatusError( status ) || \
903  strcmp( ldapAttribute1, ldapAttribute2 ) )
904  {
905  printf( "Failed to get/set keyset attribute via equivalent global "
906  "attribute, error\ncode %d, value '%s', should be\n'%s', "
907  "line %d.\n", status, ldapAttribute2, ldapAttribute1,
908  __LINE__ );
909  return( FALSE );
910  }
911  cryptKeysetClose( cryptKeyset );
912  printf( " LDAP directory %s seems to be up, using that for read test.\n",
913  ldapKeysetName );
914 
915  /* Now it gets tricky, we have to jump through all sorts of hoops to
916  run the tests in an automated manner since the innate incompatibility
917  of LDAP directory setups means that even though cryptlib's LDAP access
918  code retries failed queries with various options, we still need to
919  manually override some settings here. The simplest option is a direct
920  read with no special-case handling */
921  if( !paramStrcmp( ldapKeysetName, ldapUrlInfo[ LDAP_SERVER_NO ].url ) )
922  {
923  puts( "Testing LDAP certificate read..." );
924  status = testKeysetRead( CRYPT_KEYSET_LDAP, ldapKeysetName,
926  ldapUrlInfo[ LDAP_SERVER_NO ].certName,
928  READ_OPTION_NORMAL );
929  if( !status )
930  {
931  /* Since we can never be sure about the LDAP schema du jour, we
932  don't treat a failure as a fatal error */
933  puts( ldapErrorString );
934  return( FALSE );
935  }
936 
937  /* This directory doesn't contain CRLs (or at least not at any known
938  location) so we skip the CRL read test */
939  puts( "LDAP certificate read succeeded (CRL read skipped).\n" );
940  return( TRUE );
941  }
942 
943  /* The secondary LDAP directory that we're using for these tests doesn't
944  recognise the ';binary' modifier which is required by LDAP servers in
945  order to get them to work properly, we have to change the attribute
946  name around the read calls to the format expected by the server.
947 
948  In addition because the magic formula for fetching a CRL doesn't seem
949  to work for certificates, the CRL read is done first */
950  puts( "Testing LDAP CRL read..." );
952  crlName, &length );
953 #ifdef UNICODE_STRINGS
954  length /= sizeof( wchar_t );
955 #endif /* UNICODE_STRINGS */
956  certName[ length ] = TEXT( '\0' );
958  "certificateRevocationList", 25 );
959  status = testKeysetRead( CRYPT_KEYSET_LDAP, ldapKeysetName,
961  ldapUrlInfo[ LDAP_ALT_SERVER_NO ].crlName,
962  CRYPT_CERTTYPE_CRL, READ_OPTION_NORMAL );
964  crlName, strlen( crlName ) );
965  if( !status )
966  {
967  /* Since we can never be sure about the LDAP schema du jour, we
968  don't treat a failure as a fatal error */
969  puts( ldapErrorString );
970  return( FALSE );
971  }
972 
973  puts( "Testing LDAP certificate read..." );
975  certName, &length );
976 #ifdef UNICODE_STRINGS
977  length /= sizeof( wchar_t );
978 #endif /* UNICODE_STRINGS */
979  certName[ length ] = TEXT( '\0' );
981  "userCertificate", 15 );
983  caCertName, &length );
984 #ifdef UNICODE_STRINGS
985  length /= sizeof( wchar_t );
986 #endif /* UNICODE_STRINGS */
987  certName[ length ] = TEXT( '\0' );
989  "cACertificate", 13 );
990  status = testKeysetRead( CRYPT_KEYSET_LDAP, ldapKeysetName,
992  ldapUrlInfo[ LDAP_ALT_SERVER_NO ].certName,
993  CRYPT_CERTTYPE_CERTIFICATE, READ_OPTION_NORMAL );
995  certName, strlen( certName ) );
997  caCertName, strlen( caCertName ) );
998  if( !status )
999  {
1000  /* Since we can never be sure about the LDAP schema du jour, we
1001  don't treat a failure as a fatal error */
1002  puts( "LDAP directory read failed, probably due to the magic "
1003  "incantatation to fetch\na certificate from this server not "
1004  "matching the one used to fetch a CRL.\nProcessing will "
1005  "continue without treating this as a fatal error.\n" );
1006  return( FALSE );
1007  }
1008  puts( "LDAP certificate/CRL read succeeded.\n" );
1009 
1010  return( TRUE );
1011  }
1012 
1013 int testWriteCertLDAP( void )
1014  {
1015  int status;
1016 
1017  puts( "Testing LDAP directory write..." );
1018  status = testKeysetWrite( CRYPT_KEYSET_LDAP,
1019  ldapUrlInfo[ LDAP_SERVER_NO ].url );
1020  if( status == CRYPT_ERROR_NOTAVAIL )
1021  {
1022  /* LDAP keyset access not available */
1023  return( CRYPT_ERROR_NOTAVAIL );
1024  }
1025  if( status == CRYPT_ERROR_FAILED )
1026  {
1027  printf( "This is probably because you haven't set up an LDAP "
1028  "directory for use as the\nkey store. For this test to "
1029  "work,you need to set up a directory with the\nname "
1030  "'%s'.\n\n", ldapUrlInfo[ LDAP_SERVER_NO ].asciiURL );
1031  return( FALSE );
1032  }
1033  if( !status )
1034  {
1035  /* Since we can never be sure about the LDAP schema du jour, we
1036  don't treat a failure as a fatal error */
1037  puts( "LDAP directory write failed, probably due to the standard "
1038  "being used by the\ndirectory differing from the one used "
1039  "by cryptlib (pick a standard, any\nstandard). Processing "
1040  "will continue without treating this as a fatal error.\n" );
1041  return( FALSE );
1042  }
1043  puts( "LDAP directory write succeeded.\n" );
1044  return( TRUE );
1045  }
1046 
1047 /* Read a certificate from a web page */
1048 
1049 int testReadCertURL( void )
1050  {
1051  int status;
1052 
1053  /* Test fetching a certificate from a URL via an HTTP proxy. This
1054  requires a random open proxy for testing (unless the site happens to
1055  be running an HTTP proxy), www.openproxies.com will provide this.
1056 
1057  Alternatively, for testing from a NZ-local ISP, proxy.xtra.co.nz:8080
1058  can also be used */
1059 #if 0 /* This is usually disabled because most people aren't behind HTTP
1060  proxies, and abusing other people's misconfigured HTTP caches/
1061  proxies for testing isn't very nice */
1062  puts( "Testing HTTP certificate read from URL via proxy..." );
1064  "proxy.zetwuinwest.com.pl:80", 27 );
1065  status = testKeysetRead( CRYPT_KEYSET_HTTP, HTTP_KEYSET_CERT_NAME,
1066  CRYPT_KEYID_NAME, "[none]",
1067  CRYPT_CERTTYPE_CERTIFICATE, READ_OPTION_NORMAL );
1068  if( status == CRYPT_ERROR_NOTAVAIL ) /* HTTP keyset access not avail.*/
1069  return( CRYPT_ERROR_NOTAVAIL );
1070  if( !status )
1071  return( FALSE );
1072 #endif /* 0 */
1073 
1074  /* Test fetching a certificate from a URL */
1075  puts( "Testing HTTP certificate read from URL..." );
1076  status = testKeysetRead( CRYPT_KEYSET_HTTP, HTTP_KEYSET_CERT_NAME,
1077  CRYPT_KEYID_NAME, TEXT( "[none]" ),
1078  CRYPT_CERTTYPE_CERTIFICATE, READ_OPTION_NORMAL );
1079  if( status == CRYPT_ERROR_NOTAVAIL ) /* HTTP keyset access not avail.*/
1080  return( CRYPT_ERROR_NOTAVAIL );
1081  if( status == CRYPT_ERROR_FAILED )
1082  {
1083  puts( "This is probably because the server isn't available or "
1084  "inaccessible.\n" );
1085  return( TRUE );
1086  }
1087  if( !status )
1088  {
1089  puts( "If this message took a long time to appear, you may be "
1090  "behind a firewall\nthat blocks HTTP traffic.\n" );
1091  return( FALSE );
1092  }
1093 
1094  /* Test fetching a CRL from a URL */
1095  puts( "Testing HTTP CRL read from URL..." );
1096  status = testKeysetRead( CRYPT_KEYSET_HTTP, HTTP_KEYSET_CRL_NAME,
1097  CRYPT_KEYID_NAME, TEXT( "[none]" ),
1098  CRYPT_CERTTYPE_CRL, READ_OPTION_NORMAL );
1099  if( status == CRYPT_ERROR_NOTAVAIL ) /* HTTP keyset access not avail.*/
1100  return( CRYPT_ERROR_NOTAVAIL );
1101  if( !status )
1102  return( FALSE );
1103 
1104  /* Test fetching a huge CRL from a URL, to check the ability to read
1105  arbitrary-length HTTP data */
1106 #if 1 /* We allow this to be disabled because of the CRL size */
1107  puts( "Testing HTTP mega-CRL read from URL..." );
1108  status = testKeysetRead( CRYPT_KEYSET_HTTP, HTTP_KEYSET_HUGECRL_NAME,
1109  CRYPT_KEYID_NAME, TEXT( "[none]" ),
1110  CRYPT_CERTTYPE_CRL, READ_OPTION_NORMAL );
1111  if( status == CRYPT_ERROR_NOTAVAIL ) /* HTTP keyset access not avail.*/
1112  return( CRYPT_ERROR_NOTAVAIL );
1113  if( !status )
1114  return( FALSE );
1115 #endif /* 0 */
1116 
1117  puts( "HTTP certificate/CRL read from URL succeeded.\n" );
1118  return( TRUE );
1119  }
1120 
1121 /* Read a certificate from an HTTP certificate store */
1122 
1123 int testReadCertHTTP( void )
1124  {
1125  int status;
1126 
1127  puts( "Testing HTTP certificate store read..." );
1128  status = testKeysetRead( CRYPT_KEYSET_HTTP, HTTP_KEYSET_CERT_NAME,
1129  CRYPT_KEYID_NAME, TEXT( "Verisign" ),
1130  CRYPT_CERTTYPE_CERTIFICATE, READ_OPTION_NORMAL );
1131  if( status == CRYPT_ERROR_NOTAVAIL ) /* HTTP keyset access not avail.*/
1132  return( CRYPT_ERROR_NOTAVAIL );
1133  if( status == CRYPT_ERROR_FAILED )
1134  {
1135  puts( "This is probably because the server isn't available or "
1136  "inaccessible.\n" );
1137  return( TRUE );
1138  }
1139  if( !status )
1140  {
1141  puts( "If this message took a long time to appear, you may be "
1142  "behind a firewall\nthat blocks HTTP traffic.\n" );
1143  return( FALSE );
1144  }
1145 
1146  puts( "HTTP certificate store read succeeded.\n" );
1147  return( TRUE );
1148  }
1149 
1150 #endif /* TEST_KEYSET */