home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / crypl200.zip / CRYPTDBX.C < prev    next >
Text File  |  1996-10-10  |  18KB  |  563 lines

  1. /****************************************************************************
  2. *                                                                            *
  3. *                         cryptlib Key Database Routines                        *
  4. *                        Copyright Peter Gutmann 1995-1996                    *
  5. *                                                                            *
  6. ****************************************************************************/
  7.  
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include "crypt.h"
  12. #ifdef __WINDOWS__
  13.   #include <sql.h>
  14.   #include <sqlext.h>
  15. #endif /* __WINDOWS__ */
  16. #ifdef INC_ALL
  17.   #include "keymgmt.h"
  18. #else
  19.   #include "keymgmt/keymgmt.h"
  20. #endif /* Compiler-specific includes */
  21.  
  22. /* The structure which stores information on a keyset */
  23.  
  24. typedef struct KI {
  25.     /* General keyset information */
  26.     CRYPT_KEYSET_TYPE type;            /* Keyset type (native, PGP, X.509, etc) */
  27.     KEYSET_SUBTYPE subType;            /* Keyset subtype (public, private, etc) */
  28.     int accessMode;                    /* Keyset access mode */
  29.     BOOLEAN needsUpdate;            /* Whether the keyset has been updated */
  30.  
  31.     /* The I/O stream and type if the keyset is implemented as a file.  We
  32.        also remember the last known good position in the file in case we need
  33.        to retry an operation (for example rereading a private key if
  34.        decryption of encrypted fields fails) */
  35.     FILE *filePtr;
  36.     long filePos;                    /* Last known good position in file */
  37.  
  38.     /* The username and password needed to access the database */
  39.     char *user;
  40.     char *password;
  41.  
  42. #ifdef __WINDOWS__
  43.     /* ODBC access information */
  44.     HENV hEnv;
  45.     HDBC hDbc;
  46.     HSTMT hStmt;
  47. #endif /* __WINDOWS__ */
  48.  
  49.     /* A check value so we can determine whether the keyset context has been
  50.        initialised or not.  This is needed because passing in an
  51.        uninitialised block of memory as a keyset context can lead to problems
  52.        when we try to dereference wild pointers */
  53.     LONG checkValue;
  54.  
  55.     /* The next and previous keyset context in the linked list of contexts */
  56.     struct KI *next, *prev;
  57.     } KEYSET_INFO;
  58.  
  59. /* Macros to convert to/from keyset contexts.  These are analogous to the
  60.    encryption context macros in crypt.h */
  61.  
  62. #define KEYSET_TO_INFO( x )        ( KEYSET_INFO * ) ( ( BYTE * ) x - cryptContextConversionOffset )
  63. #define INFO_TO_KEYSET( x )        ( CRYPT_KEYSET ) ( ( BYTE * ) x + cryptContextConversionOffset )
  64.  
  65. /****************************************************************************
  66. *                                                                            *
  67. *                Memory Management Functions for Database Contexts            *
  68. *                                                                            *
  69. ****************************************************************************/
  70.  
  71. /* The linked list of key database contexts */
  72.  
  73. static KEYSET_INFO *keysetInfoListHead = NULL, *keysetInfoListTail;
  74.  
  75. /* Create a keyset context and add it to the list */
  76.  
  77. static int createKeysetContext( KEYSET_INFO **keysetInfo )
  78.     {
  79.     KEYSET_INFO *newElement;
  80.  
  81.     /* Allocate memory for the new keyset context */
  82.     if( ( newElement  = malloc( sizeof( KEYSET_INFO ) ) ) == NULL )
  83.         return( CRYPT_NOMEM );
  84.     memset( newElement, 0, sizeof( KEYSET_INFO ) );
  85.  
  86.     /* Link it into the list */
  87.     if( keysetInfoListHead == NULL )
  88.         keysetInfoListHead = newElement;
  89.     else
  90.         {
  91.         keysetInfoListTail->next = newElement;
  92.         newElement->prev = keysetInfoListTail;
  93.         }
  94.     keysetInfoListTail = newElement;
  95.  
  96.     *keysetInfo = newElement;
  97.     return( CRYPT_OK );
  98.     }
  99.  
  100. /* Delete a keyset context from the list */
  101.  
  102. static void deleteKeysetContext( KEYSET_INFO *keysetInfo )
  103.     {
  104.     KEYSET_INFO *keysetInfoPrevPtr = keysetInfo->prev, *keysetInfoNextPtr = keysetInfo->next;
  105.  
  106.     /* Remove the keyset context from the list of contexts */
  107.     if( keysetInfo == keysetInfoListHead )
  108.         {
  109.         /* Special case for first keyset context */
  110.         keysetInfoListHead = keysetInfoNextPtr;
  111.         if( keysetInfoNextPtr != NULL )
  112.             keysetInfoNextPtr->prev = NULL;
  113.         }
  114.     else
  115.         {
  116.         /* Delete from the middle or the end of the chain */
  117.         keysetInfoPrevPtr->next = keysetInfoNextPtr;
  118.         if( keysetInfoNextPtr != NULL )
  119.             keysetInfoNextPtr->prev = keysetInfoPrevPtr;
  120.         }
  121.  
  122.     /* If this was the last element, move the tail pointer back */
  123.     if( keysetInfoListTail == keysetInfo )
  124.         keysetInfoListTail = keysetInfoPrevPtr;
  125.  
  126.     /* Clear all data in the keyset context and free the memory */
  127.     zeroise( keysetInfo, sizeof( KEYSET_INFO ) );
  128.     free( keysetInfo );
  129.     }
  130.  
  131. /* Delete all keyset contexts */
  132.  
  133. int deleteAllKeysetContexts( void )
  134.     {
  135.     KEYSET_INFO *keysetInfoListPtr = keysetInfoListHead;
  136.  
  137.     /* If there are no remaining allocated keyset contexts, return now */
  138.     if( keysetInfoListPtr == NULL )
  139.         return( CRYPT_OK );
  140.  
  141.     /* Destroy any remaining keyset contexts */
  142.     while( keysetInfoListPtr != NULL )
  143.         {
  144.         KEYSET_INFO *keysetInfoToFree = keysetInfoListPtr;
  145.  
  146.         keysetInfoListPtr = keysetInfoListPtr->next;
  147.         cryptKeysetClose( INFO_TO_KEYSET( keysetInfoToFree ) );
  148.         }
  149.  
  150.     /* If there were keyset contexts still allocated, warn the user about
  151.        them */
  152.     return( CRYPT_ORPHAN );
  153.     }
  154.  
  155. /****************************************************************************
  156. *                                                                            *
  157. *                    System-specific Database Access Functions                *
  158. *                                                                            *
  159. ****************************************************************************/
  160.  
  161. #ifdef __WINDOWS__
  162.  
  163. /* Open a connection to a data source using ODBC.  We don't check the return
  164.    codes for many of the functions since the worst that can happen is that
  165.    performance will be somewhat suboptimal */
  166.  
  167. static int odbcOpen( KEYSET_INFO *keysetInfo, const char *server )
  168.     {
  169.     RETCODE retCode;
  170.  
  171.     /* Allocate environment and connection handles */
  172.     SQLAllocEnv( &keysetInfo->hEnv );
  173.     SQLAllocConnect( keysetInfo->hEnv, &keysetInfo->hDbc );
  174.  
  175.     /* Set the access mode to readonly if we can.  The default is R/W, but
  176.        setting it to readonly optimises transaction management.  We also
  177.        set cursor concurrency to readonly (which should be the default
  178.        anyway) */
  179.     if( !( keysetInfo->accessMode & CRYPT_ACCESS_WRITE ) )
  180.         {
  181.         SQLSetConnectOption( keysetInfo->hDbc, SQL_ACCESS_MODE,
  182.                              SQL_MODE_READ_ONLY );
  183.         SQLSetConnectOption( keysetInfo->hDbc, SQL_CONCURRENCY,
  184.                              SQL_CONCUR_READ_ONLY );
  185.         }
  186.  
  187.     /* Set the cursor type to forward-only (which should be the default) */
  188.     SQLSetConnectOption( keysetInfo->hDbc, SQL_CURSOR_TYPE,
  189.                          SQL_CURSOR_FORWARD_ONLY );
  190.  
  191.     /* Turn off scanning for escape clauses in the SQL strings, which lets
  192.        the driver pass the string directly to the data source */
  193.     SQLSetConnectOption( keysetInfo->hDbc, SQL_NOSCAN, SQL_NOSCAN_ON );
  194.  
  195.     /* Once everything is set up the way we want it, try to connect to a data
  196.        source and allocate a statement handle */
  197.     retCode = SQLConnect( keysetInfo->hDbc, ( char * ) server, SQL_NTS,
  198.                           keysetInfo->user, SQL_NTS,
  199.                           keysetInfo->password, SQL_NTS );
  200.     if( retCode != SQL_SUCCESS && retCode != SQL_SUCCESS_WITH_INFO )
  201.         {
  202.         SQLFreeConnect( keysetInfo->hDbc );
  203.         SQLFreeEnv( keysetInfo->hEnv );
  204.         return( CRYPT_KEYSET_OPEN );
  205.         }
  206.     SQLAllocStmt( keysetInfo->hDbc, &keysetInfo->hStmt );
  207.  
  208.     return( CRYPT_OK );
  209.     }
  210.  
  211. /* Close the previously-opened ODBC connection */
  212.  
  213. static int odbcClose( KEYSET_INFO *keysetInfo )
  214.     {
  215.     /* Commit the transaction (the default transaction mode for drivers which
  216.        support SQLSetConnectOption() is auto-commit so the SQLTransact() call
  217.        isn't strictly necessary, but we play it safe anyway) */
  218.     if( keysetInfo->needsUpdate )
  219.         SQLTransact( keysetInfo->hEnv, keysetInfo->hDbc, SQL_COMMIT );
  220.  
  221.     /* Clean up */
  222.     SQLFreeStmt( keysetInfo->hStmt, SQL_DROP );
  223.     SQLDisconnect( keysetInfo->hDbc );
  224.     SQLFreeConnect( keysetInfo->hDbc );
  225.     SQLFreeEnv( keysetInfo->hEnv );
  226.  
  227.     return( CRYPT_OK );
  228.     }
  229. #endif /* __WINDOWS__ */
  230.  
  231. /****************************************************************************
  232. *                                                                            *
  233. *                            Low-level Key Access Functions                    *
  234. *                                                                            *
  235. ****************************************************************************/
  236.  
  237. /* Get a key from a keyset */
  238.  
  239. static int getKey( KEYSET_INFO *keysetInfoPtr, CRYPT_CONTEXT *cryptContext,
  240.                    const GETKEY_INFO *getkeyInfo )
  241.     {
  242.     int status = CRYPT_OK;
  243.  
  244.     /* If we don't know what the key file type is, check it now.  This is
  245.        done when we get a key rather than when we open the keyset to avoid
  246.        unnecessary keyset accesses */
  247.     if( keysetInfoPtr->subType == KEYSET_SUBTYPE_NONE )
  248.         switch( keysetInfoPtr->type )
  249.             {
  250.             case CRYPT_KEYSET_NONE:
  251.                 /* The native format isn't supported yet */
  252.                 keysetInfoPtr->subType = KEYSET_SUBTYPE_ERROR;
  253.                 break;
  254.  
  255.             case CRYPT_KEYSET_PGP:
  256.                 keysetInfoPtr->subType = pgpGetKeysetType( keysetInfoPtr->filePtr );
  257.                 break;
  258.  
  259.             case CRYPT_KEYSET_X509:
  260.                 keysetInfoPtr->subType = x509GetKeysetType( keysetInfoPtr->filePtr );
  261.                 break;
  262.             }
  263.     if( keysetInfoPtr->subType == KEYSET_SUBTYPE_ERROR )
  264.         return( CRYPT_BADDATA );
  265.  
  266.     /* Make sure we've got the right type of key file.  We can get a public
  267.        key from a private key file, but not a private key from a public key
  268.        file */
  269.     if( !getkeyInfo->isPublicKey && \
  270.         keysetInfoPtr->subType == KEYSET_SUBTYPE_PUBLIC )
  271.         return( CRYPT_KEYSET_NOTFOUND );
  272.  
  273.     /* If it's a stream-type keyset, keep track of the position in the
  274.        stream */
  275.     if( keysetInfoPtr->filePtr != NULL )
  276.         {
  277.         /* If we're doing a getFirst(), go back to the start of the file */
  278.         if( getkeyInfo->keyID == CRYPT_KEYSET_GETFIRST )
  279.             fseek( keysetInfoPtr->filePtr, 0L, SEEK_SET );
  280.  
  281.         /* Remember the last known good position */
  282.         keysetInfoPtr->filePos = ftell( keysetInfoPtr->filePtr );
  283.         }
  284.  
  285.     /* Get the key from the key collection */
  286.     switch( keysetInfoPtr->type )
  287.         {
  288.         case CRYPT_KEYSET_NONE:
  289.             /* The native format isn't supported yet */
  290.             status = CRYPT_BADDATA;
  291.             break;
  292.  
  293.         case CRYPT_KEYSET_PGP:
  294.             status = pgpGetKey( keysetInfoPtr->filePtr, cryptContext,
  295.                                 getkeyInfo );
  296.             break;
  297.  
  298.         case CRYPT_KEYSET_X509:
  299.             status = x509GetKey( keysetInfoPtr->filePtr, cryptContext,
  300.                                  getkeyInfo );
  301.             break;
  302.  
  303.         default:
  304.             /* Internal error, should never happen */
  305.             status = CRYPT_ERROR;
  306.         }
  307.  
  308.     /* If the problem was a failed decrypt of private-key fields, move back
  309.        to the last known good position */
  310.     if( status == CRYPT_WRONGKEY && keysetInfoPtr->filePtr != NULL )
  311.         fseek( keysetInfoPtr->filePtr, keysetInfoPtr->filePos, SEEK_SET );
  312.  
  313.     return( status );
  314.     }
  315.  
  316. /* Set the names of the tables which contain key compoents */
  317.  
  318. int setKeysetNames( void *keySet,CRYPT_IOCTLINFO_KEYSETNAMES *ioctlInfo )
  319.     {
  320.     KEYSET_INFO *keysetInfoPtr = KEYSET_TO_INFO( keySet );
  321.  
  322.     UNUSED( keysetInfoPtr );
  323.     UNUSED( ioctlInfo );
  324.  
  325.     return( CRYPT_OK );
  326.     }
  327.  
  328. /****************************************************************************
  329. *                                                                            *
  330. *                                Keyset API Functions                        *
  331. *                                                                            *
  332. ****************************************************************************/
  333.  
  334. /* Open and close a keyset */
  335.  
  336. CRET cryptKeysetOpenEx( CRYPT_KEYSET CPTR keySet, const char CPTR name, \
  337.                         const int accessMode, const char CPTR user, \
  338.                         const char CPTR password )
  339.     {
  340.     KEYSET_INFO *keysetInfoPtr;
  341.     int status;
  342.  
  343.     /* Perform basic error checking */
  344.     if( keySet == NULL )
  345.         return( CRYPT_BADPARM1 );
  346.     if( name == NULL )
  347.         return( CRYPT_BADPARM2 );
  348.     if( accessMode < 0 || \
  349.         accessMode > ( CRYPT_ACCESS_READ | CRYPT_ACCESS_WRITE | CRYPT_ACCESS_CREATE ) )
  350.         return( CRYPT_BADPARM3 );
  351.     if( user == NULL )
  352.         return( CRYPT_BADPARM4 );
  353.     if( password == NULL )
  354.         return( CRYPT_BADPARM5 );
  355.  
  356.     /* Create the keyset context */
  357.     if( ( status = createKeysetContext( &keysetInfoPtr ) ) != CRYPT_OK )
  358.         return( status );
  359.     keysetInfoPtr->accessMode = accessMode;
  360.     keysetInfoPtr->type = CRYPT_KEYSET_NONE;    /* Default internal format */
  361.  
  362.     /* Set up the user name and password if necessary */
  363.     if( user != ( char * ) CRYPT_UNUSED )
  364.         {
  365.         if( ( keysetInfoPtr->user = malloc( strlen( user ) + 1 ) ) == NULL )
  366.             {
  367.             deleteKeysetContext( keysetInfoPtr );
  368.             return( CRYPT_NOMEM );
  369.             }
  370.         strcpy( keysetInfoPtr->user, user );
  371.         }
  372.     if( password != ( char * ) CRYPT_UNUSED )
  373.         {
  374.         if( ( keysetInfoPtr->password = malloc( strlen( password ) + 1 ) ) == NULL )
  375.             {
  376.             deleteKeysetContext( keysetInfoPtr );
  377.             return( CRYPT_NOMEM );
  378.             }
  379.         strcpy( keysetInfoPtr->password, password );
  380.         }
  381.  
  382. #ifdef __WINDOWS__
  383.     /* Try and establish the ODBC session */
  384.     if( ( status = odbcOpen( keysetInfoPtr, name ) ) != CRYPT_OK )
  385.         {
  386.         deleteKeysetContext( keysetInfoPtr );
  387.         return( status );
  388.         }
  389. #endif /* __WINDOWS__ */
  390.  
  391.     /* Set the check value */
  392.     keysetInfoPtr->checkValue = CRYPT_MAGIC;
  393.  
  394.     /* Convert the keyset information pointer to a keyset context and return
  395.        it to the user */
  396.     *keySet = INFO_TO_KEYSET( keysetInfoPtr );
  397.  
  398.     return( CRYPT_OK );
  399.     }
  400.  
  401. CRET cryptKeysetClose( CRYPT_KEYSET keySet )
  402.     {
  403.     KEYSET_INFO *keysetInfoPtr = KEYSET_TO_INFO( keySet );
  404.  
  405.     /* Perform basic error checking */
  406.     if( isBadCookie( keySet ) || keysetInfoPtr->checkValue != CRYPT_MAGIC )
  407.         return( CRYPT_BADPARM1 );
  408.  
  409.     /* If the keyset is implemented as a file, close it */
  410.     if( keysetInfoPtr->filePtr != NULL )
  411.         fclose( keysetInfoPtr->filePtr );
  412.  
  413.     /* Clear the user name and password if necessary */
  414.     if( keysetInfoPtr->user != NULL )
  415.         {
  416.         zeroise( keysetInfoPtr->user, strlen( keysetInfoPtr->user ) );
  417.         free( keysetInfoPtr->user );
  418.         }
  419.     if( keysetInfoPtr->password != NULL )
  420.         {
  421.         zeroise( keysetInfoPtr->password, strlen( keysetInfoPtr->password ) );
  422.         free( keysetInfoPtr->password );
  423.         }
  424.  
  425. #ifdef __WINDOWS__
  426.     /* Close down the ODBC session if necessary */
  427.     if( keysetInfoPtr->hDbc )
  428.         odbcClose( keysetInfoPtr );
  429. #endif /* __WINDOWS__ */
  430.  
  431.     /* Delete the keyset context */
  432.     deleteKeysetContext( keysetInfoPtr );
  433.  
  434.     return( CRYPT_OK );
  435.     }
  436.  
  437. /* Open an external keyset */
  438.  
  439. CRET cryptExtKeysetOpen( CRYPT_KEYSET CPTR keySet, const char CPTR name,
  440.                          const CRYPT_KEYSET_TYPE type )
  441.     {
  442.     KEYSET_INFO *keysetInfoPtr;
  443.     FILE *filePtr;
  444.     int status;
  445.  
  446.     /* Perform basic error checking */
  447.     if( keySet == NULL )
  448.         return( CRYPT_BADPARM1 );
  449.     if( name == NULL )
  450.         return( CRYPT_BADPARM2 );
  451.     if( type <= CRYPT_KEYSET_NONE || type > CRYPT_KEYSET_LAST )
  452.         return( CRYPT_BADPARM3 );
  453.  
  454.     /* Open the file containing the keyset */
  455.     if( ( filePtr = fopen( name, "rb" ) ) == NULL )
  456.         return( CRYPT_KEYSET_OPEN );
  457.  
  458.     /* Create the keyset context */
  459.     if( ( status = createKeysetContext( &keysetInfoPtr ) ) != CRYPT_OK )
  460.         {
  461.         fclose( filePtr );
  462.         return( status );
  463.         }
  464.     keysetInfoPtr->type = type;
  465.     keysetInfoPtr->filePtr = filePtr;
  466.     keysetInfoPtr->accessMode = CRYPT_ACCESS_READ;
  467.  
  468.     /* Set the check value */
  469.     keysetInfoPtr->checkValue = CRYPT_MAGIC;
  470.  
  471.     /* Convert the keyset information pointer to a keyset context and return
  472.        it to the user */
  473.     *keySet = INFO_TO_KEYSET( keysetInfoPtr );
  474.  
  475.     return( CRYPT_OK );
  476.     }
  477.  
  478. /* Retrieve a key from a keyset based on a cryptlib object */
  479.  
  480. CRET cryptGetKeyFromObjectEx( CRYPT_KEYSET keySet, CRYPT_CONTEXT CPTR cryptContext,
  481.                               const void CPTR object, const void CPTR password )
  482.     {
  483.     KEYSET_INFO *keysetInfoPtr = KEYSET_TO_INFO( keySet );
  484.     CRYPT_OBJECT_INFO cryptObjectInfo;
  485.     GETKEY_INFO getkeyInfo;
  486.     int status;
  487.  
  488.     /* Perform basic error checking */
  489.     if( isBadCookie( keySet ) || keysetInfoPtr->checkValue != CRYPT_MAGIC )
  490.         return( CRYPT_BADPARM1 );
  491.     if( cryptContext == NULL )
  492.         return( CRYPT_BADPARM2 );
  493.     if( object == NULL )
  494.         return( CRYPT_BADPARM3 );
  495.     *cryptContext = NULL;
  496.  
  497.     /* Make sure we've got a public-key object and determine the key ID */
  498.     status = cryptQueryObject( object, &cryptObjectInfo );
  499.     if( cryptStatusError( status ) || \
  500.         ( cryptObjectInfo.type != CRYPT_OBJECT_SIGNATURE && \
  501.           cryptObjectInfo.type != CRYPT_OBJECT_PKCENCRYPTED_KEY ) || \
  502.         ( cryptObjectInfo.type == CRYPT_OBJECT_PKCENCRYPTED_KEY && \
  503.           password == ( void * ) CRYPT_UNUSED ) )
  504.         {
  505.         zeroise( &cryptObjectInfo, sizeof( CRYPT_OBJECT_INFO ) );
  506.         return( CRYPT_BADPARM3 );
  507.         }
  508.  
  509.     /* Get the key */
  510.     memset( &getkeyInfo, 0, sizeof( GETKEY_INFO ) );
  511.     getkeyInfo.keyID = cryptObjectInfo.keyID;
  512.     getkeyInfo.keyIDsize = cryptObjectInfo.keyIDsize;
  513.     getkeyInfo.isKeyID = TRUE;
  514.     if( cryptObjectInfo.type == CRYPT_OBJECT_SIGNATURE )
  515.         getkeyInfo.isPublicKey = TRUE;
  516.     else
  517.         if( password != NULL )
  518.             {
  519.             getkeyInfo.password = ( void * ) password;
  520.             getkeyInfo.passwordSize = strlen( password );
  521.             }
  522.     status = getKey( keysetInfoPtr, cryptContext, &getkeyInfo );
  523.     zeroise( &cryptObjectInfo, sizeof( CRYPT_OBJECT_INFO ) );
  524.     zeroise( &getkeyInfo, sizeof( GETKEY_INFO ) );
  525.     return( status );
  526.     }
  527.  
  528. /* Retrieve a key from a keyset based on a user ID */
  529.  
  530. CRET cryptGetKeyEx( CRYPT_KEYSET keySet, CRYPT_CONTEXT CPTR cryptContext,
  531.                     const void CPTR userID, const void CPTR password )
  532.     {
  533.     KEYSET_INFO *keysetInfoPtr = KEYSET_TO_INFO( keySet );
  534.     GETKEY_INFO getkeyInfo;
  535.     int status;
  536.  
  537.     /* Perform basic error checking */
  538.     if( isBadCookie( keySet ) || keysetInfoPtr->checkValue != CRYPT_MAGIC )
  539.         return( CRYPT_BADPARM1 );
  540.     if( cryptContext == NULL )
  541.         return( CRYPT_BADPARM2 );
  542.     if( userID == NULL )
  543.         return( CRYPT_BADPARM3 );
  544.     *cryptContext = NULL;
  545.  
  546.     /* Get the key */
  547.     memset( &getkeyInfo, 0, sizeof( GETKEY_INFO ) );
  548.     getkeyInfo.keyID = ( void * ) userID;
  549.     if( userID != CRYPT_KEYSET_GETFIRST && userID != CRYPT_KEYSET_GETNEXT )
  550.         getkeyInfo.keyIDsize = strlen( userID );
  551.     if( password == ( void * ) CRYPT_UNUSED )
  552.         getkeyInfo.isPublicKey = TRUE;
  553.     else
  554.         if( password != NULL )
  555.             {
  556.             getkeyInfo.password = ( void * ) password;
  557.             getkeyInfo.passwordSize = strlen( password );
  558.             }
  559.     status = getKey( keysetInfoPtr, cryptContext, &getkeyInfo );
  560.     zeroise( &getkeyInfo, sizeof( GETKEY_INFO ) );
  561.     return( status );
  562.     }
  563.