/**************************************************************************** * * * cryptlib Configuration Routines * * Copyright Peter Gutmann 1994-2005 * * * ****************************************************************************/ #include "crypt.h" #ifdef INC_ALL #include "trustmgr.h" #include "asn1.h" #else #include "cert/trustmgr.h" #include "misc/asn1.h" #endif /* Compiler-specific includes */ /**************************************************************************** * * * Configuration Options * * * ****************************************************************************/ /* Configuration option types */ typedef enum { OPTION_NONE, /* Non-option */ OPTION_STRING, /* Literal string */ OPTION_NUMERIC, /* Numeric value */ OPTION_BOOLEAN /* Boolean flag */ } OPTION_TYPE; /* The configuration options. These are broken up into two parts, the fixed default values that are shared across all cryptlib operations and the variable values that are variable for each user object. Alongside the CRYPT_ATTRIBUTE_TYPE we store a persistant index value for the option that always stays the same even if the attribute type changes. This avoids the need to change the config file every time an attribute is added or deleted. Some options can't be made persistent, for these the index value is set to CRYPT_UNUSED */ typedef struct { const CRYPT_ATTRIBUTE_TYPE option;/* Attribute ID */ const OPTION_TYPE type; /* Option type */ const int index; /* Index value for this option */ const char FAR_BSS *strDefault; /* Default if it's a string option */ const int intDefault; /* Default if it's a numeric/boolean */ } FIXED_OPTION_INFO; typedef struct { char *strValue; /* Value if it's a string option */ int intValue; /* Value if it's a numeric/boolean */ BOOLEAN dirty; /* Whether option has been changed */ } OPTION_INFO; #define MK_OPTION( option, value, index ) \ { option, OPTION_NUMERIC, index, NULL, value } #define MK_OPTION_B( option, value, index ) \ { option, OPTION_BOOLEAN, index, NULL, value } #define MK_OPTION_S( option, value, index ) \ { option, OPTION_STRING, index, value, 0 } #define MK_OPTION_NONE() \ { CRYPT_ATTRIBUTE_NONE, OPTION_NONE, CRYPT_UNUSED, NULL, 0 } static const FIXED_OPTION_INFO FAR_BSS fixedOptionInfo[] = { /* Dummy entry for CRYPT_ATTRIBUTE_NONE */ MK_OPTION_NONE(), /* cryptlib information (read-only) */ MK_OPTION_S( CRYPT_OPTION_INFO_DESCRIPTION, "cryptlib security toolkit", CRYPT_UNUSED ), MK_OPTION_S( CRYPT_OPTION_INFO_COPYRIGHT, "Copyright Peter Gutmann, Eric Young, OpenSSL, 1994-2007", CRYPT_UNUSED ), MK_OPTION( CRYPT_OPTION_INFO_MAJORVERSION, 3, CRYPT_UNUSED ), MK_OPTION( CRYPT_OPTION_INFO_MINORVERSION, 3, CRYPT_UNUSED ), MK_OPTION( CRYPT_OPTION_INFO_STEPPING, 1, CRYPT_UNUSED ), /* Context options, base = 0 */ /* Algorithm = Conventional encryption/hash/MAC options */ MK_OPTION( CRYPT_OPTION_ENCR_ALGO, CRYPT_ALGO_3DES, 0 ), MK_OPTION( CRYPT_OPTION_ENCR_HASH, CRYPT_ALGO_SHA, 1 ), MK_OPTION( CRYPT_OPTION_ENCR_MAC, CRYPT_ALGO_HMAC_SHA, 2 ), /* Algorithm = PKC options */ MK_OPTION( CRYPT_OPTION_PKC_ALGO, CRYPT_ALGO_RSA, 3 ), MK_OPTION( CRYPT_OPTION_PKC_KEYSIZE, bitsToBytes( 1024 ), 4 ), /* Algorithm = Signature options */ MK_OPTION( CRYPT_OPTION_SIG_ALGO, CRYPT_ALGO_RSA, 5 ), MK_OPTION( CRYPT_OPTION_SIG_KEYSIZE, bitsToBytes( 1024 ), 6 ), /* Algorithm = Key derivation options */ MK_OPTION( CRYPT_OPTION_KEYING_ALGO, CRYPT_ALGO_SHA, 7 ), MK_OPTION( CRYPT_OPTION_KEYING_ITERATIONS, 500, 8 ), /* Certificate options, base = 100 */ MK_OPTION_B( CRYPT_OPTION_CERT_SIGNUNRECOGNISEDATTRIBUTES, FALSE, 100 ), MK_OPTION( CRYPT_OPTION_CERT_VALIDITY, 365, 101 ), MK_OPTION( CRYPT_OPTION_CERT_UPDATEINTERVAL, 90, 102 ), MK_OPTION( CRYPT_OPTION_CERT_COMPLIANCELEVEL, CRYPT_COMPLIANCELEVEL_STANDARD, 103 ), MK_OPTION_B( CRYPT_OPTION_CERT_REQUIREPOLICY, TRUE, 104 ), /* CMS options */ MK_OPTION_B( CRYPT_OPTION_CMS_DEFAULTATTRIBUTES, TRUE, 105 ), /* Keyset options, base = 200 */ /* Keyset = LDAP options */ MK_OPTION_S( CRYPT_OPTION_KEYS_LDAP_OBJECTCLASS, "inetOrgPerson", 200 ), MK_OPTION( CRYPT_OPTION_KEYS_LDAP_OBJECTTYPE, CRYPT_CERTTYPE_NONE, 201 ), MK_OPTION_S( CRYPT_OPTION_KEYS_LDAP_FILTER, "(objectclass=*)", 202 ), MK_OPTION_S( CRYPT_OPTION_KEYS_LDAP_CACERTNAME, "cACertificate;binary", 203 ), MK_OPTION_S( CRYPT_OPTION_KEYS_LDAP_CERTNAME, "userCertificate;binary", 204 ), MK_OPTION_S( CRYPT_OPTION_KEYS_LDAP_CRLNAME, "certificateRevocationList;binary", 205 ), MK_OPTION_S( CRYPT_OPTION_KEYS_LDAP_EMAILNAME, "mail", 206 ), /* Device options, base = 300 */ /* Device = PKCS #11 token options */ MK_OPTION_S( CRYPT_OPTION_DEVICE_PKCS11_DVR01, NULL, 300 ), MK_OPTION_S( CRYPT_OPTION_DEVICE_PKCS11_DVR02, NULL, 301 ), MK_OPTION_S( CRYPT_OPTION_DEVICE_PKCS11_DVR03, NULL, 302 ), MK_OPTION_S( CRYPT_OPTION_DEVICE_PKCS11_DVR04, NULL, 303 ), MK_OPTION_S( CRYPT_OPTION_DEVICE_PKCS11_DVR05, NULL, 304 ), MK_OPTION_B( CRYPT_OPTION_DEVICE_PKCS11_HARDWAREONLY, FALSE, 305 ), /* Session options, base = 400 */ /* Miscellaneous options, base = 500. The network options are mostly used by sessions, but also apply to other object types like network keysets, so they're classed as miscellaneous options */ MK_OPTION_S( CRYPT_OPTION_NET_SOCKS_SERVER, NULL, 500 ), MK_OPTION_S( CRYPT_OPTION_NET_SOCKS_USERNAME, NULL, 501 ), MK_OPTION_S( CRYPT_OPTION_NET_HTTP_PROXY, NULL, 502 ), MK_OPTION( CRYPT_OPTION_NET_CONNECTTIMEOUT, 30, 503 ), MK_OPTION( CRYPT_OPTION_NET_READTIMEOUT, 0, 504 ), MK_OPTION( CRYPT_OPTION_NET_WRITETIMEOUT, 2, 505 ), MK_OPTION_B( CRYPT_OPTION_MISC_ASYNCINIT, TRUE, 506 ), MK_OPTION_B( CRYPT_OPTION_MISC_SIDECHANNELPROTECTION, FALSE, 507 ), /* All options beyond this point are ephemeral and aren't stored to disk. Remember to update the LAST_STORED_OPTION define below when adding new options here */ /* cryptlib state information. These are special-case options that record state information rather than a static config value. The config-option-changed status value is updated dynamically, being set to TRUE if any config option is changed. Writing it to FALSE commits the changes to disk. The self-test status value is initially set to FALSE, writing it to TRUE triggers a self-test for which the value remains at TRUE if the test succeeds. Writing it to a particular algorithm value tests only that algorithm */ MK_OPTION_B( CRYPT_OPTION_CONFIGCHANGED, FALSE, CRYPT_UNUSED ), MK_OPTION( CRYPT_OPTION_SELFTESTOK, FALSE, CRYPT_UNUSED ), /* End-of-list marker */ MK_OPTION_NONE(), MK_OPTION_NONE() }; /* The last option that's written to disk. Further options beyond this one are ephemeral and are never written to disk */ #define LAST_STORED_OPTION CRYPT_OPTION_MISC_SIDECHANNELPROTECTION /* The size of the variable-length config data */ #define OPTION_INFO_SIZE ( sizeof( OPTION_INFO ) * \ CRYPT_OPTION_CONFIGCHANGED - CRYPT_OPTION_FIRST ) /**************************************************************************** * * * Set/Query Library-wide Config Options * * * ****************************************************************************/ /* Set the value of a numeric or string option */ int setOption( OPTION_INFO *optionList, const CRYPT_ATTRIBUTE_TYPE option, const int value ) { const FIXED_OPTION_INFO *fixedOptionInfoPtr; OPTION_INFO *optionInfoPtr; /* The update of the selt-test status is performed in two phases, when we begin the self-test it's set to an undefined value, once the self-test completes it's set to the test result. Since there's no direct way to differentiate an internal status update from an external attempt to do the same thing, we disallow any attempt to update the value when it's in the undefined state (see the comment for CRYPT_OPTION_SELFTESTOK below), and use a write of CRYPT_OPTION_LAST to indicate an update of the self-test status */ if( option == CRYPT_OPTION_LAST ) { assert( optionList[ CRYPT_OPTION_SELFTESTOK - \ CRYPT_OPTION_FIRST ].intValue == CRYPT_ERROR ); optionList[ CRYPT_OPTION_SELFTESTOK - \ CRYPT_OPTION_FIRST ].intValue = value; return( CRYPT_OK ); } /* Get a pointer to the option information and make sure that everything is OK */ assert( option > CRYPT_OPTION_FIRST && option < CRYPT_OPTION_LAST ); optionInfoPtr = &optionList[ option - CRYPT_OPTION_FIRST ]; fixedOptionInfoPtr = &fixedOptionInfo[ option - CRYPT_OPTION_FIRST ]; assert( fixedOptionInfoPtr->type == OPTION_NUMERIC || \ fixedOptionInfoPtr->type == OPTION_BOOLEAN ); /* If the value is the same as the current one, there's nothing to do */ if( optionInfoPtr->intValue == value ) return( CRYPT_OK ); /* If we're forcing a commit by returning the config.changed flag to its ground state, write any changed options to backing store */ if( option == CRYPT_OPTION_CONFIGCHANGED ) { /* When a non-config option (for example a cert trust option) is changed, then we need to write the updated config data to backing store, but there's no way to tell that this is required because the config options are unchanged. To allow the caller to signal this change, they can explicitly set the config-changed setting to TRUE (normally this is done implicitly by when another config setting is changed). This explicit setting can only be done by the higher-level config-update code, because the kernel blocks any attempts to set it to a value other than FALSE */ if( value ) { optionInfoPtr->intValue = TRUE; return( CRYPT_OK ); } /* Make sure that there's something to write. We do this to avoid problems with programs that always try to update the config (whether it's necessary or not), which can cause problems with media with limited writeability */ if( !optionInfoPtr->intValue ) return( CRYPT_OK ); /* We don't do anything to write the config data at this level since we currently have the user object locked and don't want to stall all operations that depend on it while we're updating the config data, so all we do is tell the user object to perform the necessary operations */ return( OK_SPECIAL ); } /* If we're forcing a self-test by changing the value of the self-test status, perform an algorithm test */ if( option == CRYPT_OPTION_SELFTESTOK ) { /* The self-test can take some time to complete. While it's running we don't want to leave the user object locked since this will block most other threads, which all eventually read some sort of config option. To get around this problem we set the result value to an undefined status and unlock the user object around the call, then re-lock it and set its actual value via an update of the pseudo-option CRYPT_OPTION_LAST once the self-test is done */ if( optionInfoPtr->intValue == CRYPT_ERROR ) return( CRYPT_ERROR_TIMEOUT ); optionInfoPtr->intValue = CRYPT_ERROR; return( OK_SPECIAL ); } /* Set the value and remember that the config options have been changed */ if( fixedOptionInfoPtr->type == OPTION_BOOLEAN ) /* Turn a generic zero/nonzero boolean into TRUE or FALSE */ optionInfoPtr->intValue = ( value ) ? TRUE : FALSE; else optionInfoPtr->intValue = value; optionInfoPtr->dirty = TRUE; optionList[ CRYPT_OPTION_CONFIGCHANGED - \ CRYPT_OPTION_FIRST ].intValue = TRUE; return( CRYPT_OK ); } int setOptionString( OPTION_INFO *optionList, const CRYPT_ATTRIBUTE_TYPE option, const char *value, const int valueLength ) { const FIXED_OPTION_INFO *fixedOptionInfoPtr; OPTION_INFO *optionInfoPtr; char *valuePtr; /* Get a pointer to the option information and make sure that everything is OK */ assert( option > CRYPT_OPTION_FIRST && option < CRYPT_OPTION_LAST ); optionInfoPtr = &optionList[ option - CRYPT_OPTION_FIRST ]; fixedOptionInfoPtr = &fixedOptionInfo[ option - CRYPT_OPTION_FIRST ]; assert( fixedOptionInfoPtr->type == OPTION_STRING ); /* If there's no value given, we're deleting the option rather than setting it. These options don't have default values so we check for a setting of NULL rather than equivalence to a default string value */ if( value == NULL ) { assert( fixedOptionInfoPtr->strDefault == NULL ); if( optionInfoPtr->strValue == NULL ) return( CRYPT_ERROR_NOTFOUND ); zeroise( optionInfoPtr->strValue, strlen( optionInfoPtr->strValue ) ); clFree( "setOptionString", optionInfoPtr->strValue ); optionInfoPtr->strValue = NULL; optionInfoPtr->dirty = TRUE; optionList[ CRYPT_OPTION_CONFIGCHANGED - \ CRYPT_OPTION_FIRST ].intValue = TRUE; return( CRYPT_OK ); } assert( value != NULL && valueLength > 0 ); /* If the value is the same as the current one, there's nothing to do */ if( optionInfoPtr->strValue != NULL && \ strlen( optionInfoPtr->strValue ) == valueLength && \ !memcmp( optionInfoPtr->strValue, value, valueLength ) ) return( CRYPT_OK ); /* If we're resetting a value to its default setting, just reset the string pointers rather than storing the value */ if( fixedOptionInfoPtr->strDefault != NULL && \ strlen( fixedOptionInfoPtr->strDefault ) == valueLength && \ !memcmp( fixedOptionInfoPtr->strDefault, value, valueLength ) ) { if( optionInfoPtr->strValue != fixedOptionInfoPtr->strDefault ) { zeroise( optionInfoPtr->strValue, strlen( optionInfoPtr->strValue ) ); clFree( "setOptionString", optionInfoPtr->strValue ); } optionInfoPtr->strValue = ( char * ) fixedOptionInfoPtr->strDefault; optionInfoPtr->dirty = TRUE; optionList[ CRYPT_OPTION_CONFIGCHANGED - \ CRYPT_OPTION_FIRST ].intValue = TRUE; return( CRYPT_OK ); } /* Try and allocate room for the new option */ if( ( valuePtr = clAlloc( "setOptionString", valueLength + 1 ) ) == NULL ) return( CRYPT_ERROR_MEMORY ); memcpy( valuePtr, value, valueLength ); valuePtr[ valueLength ] = '\0'; /* If the string value that's currently set isn't the default setting, clear and free it */ if( optionInfoPtr->strValue != fixedOptionInfoPtr->strDefault ) { zeroise( optionInfoPtr->strValue, strlen( optionInfoPtr->strValue ) ); clFree( "setOptionString", optionInfoPtr->strValue ); } /* Set the value and remember that the config options have been changed */ optionInfoPtr->strValue = valuePtr; optionInfoPtr->dirty = TRUE; optionList[ CRYPT_OPTION_CONFIGCHANGED - \ CRYPT_OPTION_FIRST ].intValue = TRUE; return( CRYPT_OK ); } /* Query the value of a numeric or string option */ int getOption( OPTION_INFO *optionList, const CRYPT_ATTRIBUTE_TYPE option ) { assert( option > CRYPT_OPTION_FIRST && option < CRYPT_OPTION_LAST ); assert( fixedOptionInfo[ option - \ CRYPT_OPTION_FIRST ].type == OPTION_NUMERIC || \ fixedOptionInfo[ option - \ CRYPT_OPTION_FIRST ].type == OPTION_BOOLEAN ); return( optionList[ option - CRYPT_OPTION_FIRST ].intValue ); } const char *getOptionString( OPTION_INFO *optionList, const CRYPT_ATTRIBUTE_TYPE option ) { assert( option > CRYPT_OPTION_FIRST && option < CRYPT_OPTION_LAST ); assert( fixedOptionInfo[ option - \ CRYPT_OPTION_FIRST ].type == OPTION_STRING ); return( optionList[ option - CRYPT_OPTION_FIRST ].strValue ); } /* Initialise/shut down the config option handling */ int initOptions( OPTION_INFO **optionListPtr ) { OPTION_INFO *optionList; int i; /* Perform a consistency check on the options */ FORALL( i, 1, CRYPT_OPTION_LAST - CRYPT_OPTION_FIRST, fixedOptionInfo[ i ].option == i + CRYPT_OPTION_FIRST ); /* Allocate storage for the variable config data */ if( ( optionList = clAlloc( "initOptions", OPTION_INFO_SIZE ) ) == NULL ) return( CRYPT_ERROR_MEMORY ); memset( optionList, 0, OPTION_INFO_SIZE ); /* Walk through the config table setting up each option to contain its default value */ for( i = 1; fixedOptionInfo[ i ].option != CRYPT_ATTRIBUTE_NONE && \ i < FAILSAFE_ARRAYSIZE(fixedOptionInfo, FIXED_OPTION_INFO ); i++ ) { if( fixedOptionInfo[ i ].type == OPTION_STRING ) optionList[ i ].strValue = \ ( char * ) fixedOptionInfo[ i ].strDefault; else optionList[ i ].intValue = fixedOptionInfo[ i ].intDefault; } if( i >= FAILSAFE_ARRAYSIZE(fixedOptionInfo, FIXED_OPTION_INFO ) ) retIntError(); *optionListPtr = optionList; return( CRYPT_OK ); } void endOptions( OPTION_INFO *optionList ) { int i; /* Walk through the config table clearing and freeing each option */ for( i = 1; fixedOptionInfo[ i ].option != CRYPT_ATTRIBUTE_NONE && \ i < FAILSAFE_ARRAYSIZE(fixedOptionInfo, FIXED_OPTION_INFO ); i++ ) { const FIXED_OPTION_INFO *fixedOptionInfoPtr = &fixedOptionInfo[ i ]; OPTION_INFO *optionInfoPtr = &optionList[ i ]; if( fixedOptionInfoPtr->type == OPTION_STRING ) { /* If the string value that's currently set isn't the default setting, clear and free it */ if( optionInfoPtr->strValue != fixedOptionInfoPtr->strDefault ) { zeroise( optionInfoPtr->strValue, strlen( optionInfoPtr->strValue ) ); clFree( "endOptions", optionInfoPtr->strValue ); } } } if( i >= FAILSAFE_ARRAYSIZE(fixedOptionInfo, FIXED_OPTION_INFO ) ) retIntError_Void(); /* Clear and free the config table */ memset( optionList, 0, OPTION_INFO_SIZE ); clFree( "endOptions", optionList ); } /**************************************************************************** * * * Read and Write the Config Options * * * ****************************************************************************/ /* Read any user-defined configuration options. Since the config file is an untrusted source, we set the values in it via external messages rather than manipulating the config info directly, which means that everything read is subject to the usual ACL checks */ static int readTrustedCerts( const CRYPT_KEYSET iCryptKeyset, void *trustInfoPtr ) { MESSAGE_DATA msgData; BYTE buffer[ CRYPT_MAX_PKCSIZE + 1536 + 8 ]; int iterationCount = 0, status; /* Read each trusted cert from the keyset */ setMessageData( &msgData, buffer, CRYPT_MAX_PKCSIZE + 1536 ); status = krnlSendMessage( iCryptKeyset, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_TRUSTEDCERT ); while( cryptStatusOK( status ) && \ iterationCount++ < FAILSAFE_ITERATIONS_LARGE ) { /* Add the cert data as a trusted cert item and look for the next one */ addTrustEntry( trustInfoPtr, CRYPT_UNUSED, msgData.data, msgData.length, TRUE ); setMessageData( &msgData, buffer, CRYPT_MAX_PKCSIZE + 1536 ); status = krnlSendMessage( iCryptKeyset, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_TRUSTEDCERT_NEXT ); } if( iterationCount >= FAILSAFE_ITERATIONS_LARGE ) retIntError(); return( ( status == CRYPT_ERROR_NOTFOUND ) ? CRYPT_OK : status ); } int readConfig( const CRYPT_USER iCryptUser, const char *fileName, void *trustInfoPtr ) { CRYPT_KEYSET iCryptKeyset; MESSAGE_CREATEOBJECT_INFO createInfo; MESSAGE_DATA msgData; STREAM stream; DYNBUF configDB; char configFilePath[ MAX_PATH_LENGTH + 8 ]; int configFilePathLen, iterationCount = 0, status; /* Try and open the config file. If we can't open it, it means the that file doesn't exist, which isn't an error */ status = fileBuildCryptlibPath( configFilePath, MAX_PATH_LENGTH, &configFilePathLen, fileName, strlen( fileName ), BUILDPATH_GETPATH ); if( cryptStatusError( status ) ) return( CRYPT_OK ); /* Can't build config path */ setMessageCreateObjectInfo( &createInfo, CRYPT_KEYSET_FILE ); createInfo.arg2 = CRYPT_KEYOPT_READONLY; createInfo.strArg1 = configFilePath; createInfo.strArgLen1 = configFilePathLen; status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT, &createInfo, OBJECT_TYPE_KEYSET ); if( cryptStatusError( status ) ) return( CRYPT_OK ); /* No config data present */ iCryptKeyset = createInfo.cryptHandle; /* Get the config info from the keyset */ status = dynCreate( &configDB, iCryptKeyset, CRYPT_IATTRIBUTE_CONFIGDATA ); if( status == CRYPT_ERROR_NOTFOUND ) { /* No config options present, there may still be trusted certs */ status = readTrustedCerts( iCryptKeyset, trustInfoPtr ); krnlSendNotifier( iCryptKeyset, IMESSAGE_DECREFCOUNT ); return( status ); } if( cryptStatusOK( status ) ) status = readTrustedCerts( iCryptKeyset, trustInfoPtr ); krnlSendNotifier( iCryptKeyset, IMESSAGE_DECREFCOUNT ); if( cryptStatusError( status ) ) { dynDestroy( &configDB ); return( status ); } /* Read each config option */ sMemConnect( &stream, dynData( configDB ), dynLength( configDB ) ); while( cryptStatusOK( status ) && \ stell( &stream ) < dynLength( configDB ) && \ iterationCount++ < FAILSAFE_ITERATIONS_LARGE ) { CRYPT_ATTRIBUTE_TYPE attributeType; long option; int value, tag, i; /* Read the wrapper and option index and map it to the actual option. If we find an unknown index or one that shouldn't be writeable to persistent storage, we skip it and continue. This is done to handle new options that may have been added after this version of cryptlib was built (for unknown indices) and because the stored config options are an untrusted source so we have to check for attempts to feed in bogus values (for non-writeable options) */ readSequence( &stream, NULL ); status = readShortInteger( &stream, &option ); if( cryptStatusError( status ) ) continue; for( i = 1; fixedOptionInfo[ i ].option <= LAST_STORED_OPTION && \ i < FAILSAFE_ARRAYSIZE(fixedOptionInfo, FIXED_OPTION_INFO ); i++ ) { if( fixedOptionInfo[ i ].index == option ) break; } if( i >= FAILSAFE_ITERATIONS_LARGE ) retIntError(); if( fixedOptionInfo[ i ].option > LAST_STORED_OPTION || \ fixedOptionInfo[ i ].index == CRYPT_UNUSED ) { readUniversal( &stream ); continue; } attributeType = fixedOptionInfo[ i ].option; /* Read the option value and set the option. We don't treat a failure to set the option as a problem since the user probably doesn't want the entire system to fail because of a bad config option, and in any case we'll fall back to a safe default value */ tag = peekTag( &stream ); if( tag == BER_BOOLEAN || tag == BER_INTEGER ) { /* It's a numeric value, read the appropriate type and try and set the option */ if( tag == BER_BOOLEAN ) status = readBoolean( &stream, &value ); else { long integer; status = readShortInteger( &stream, &integer ); value = ( int ) integer; } if( cryptStatusOK( status ) ) krnlSendMessage( iCryptUser, IMESSAGE_SETATTRIBUTE, &value, attributeType ); } else { int length; /* It's a string value, set the option straight from the encoded data */ status = readGenericHole( &stream, &length, 1, BER_STRING_UTF8 ); if( cryptStatusError( status ) ) continue; setMessageData( &msgData, sMemBufPtr( &stream ), length ); krnlSendMessage( iCryptUser, IMESSAGE_SETATTRIBUTE_S, &msgData, attributeType ); status = sSkip( &stream, length ); } } if( iterationCount >= FAILSAFE_ITERATIONS_LARGE ) retIntError(); sMemDisconnect( &stream ); /* Clean up */ dynDestroy( &configDB ); return( status ); } /* Write any user-defined configuration options. This is performed in two phases, a first phase that encodes the config data and a second phase that writes the data to disk. The reason for the split is that the second phase doesn't require the use of the user object data any more and can be a somewhat lengthy process due to disk accesses and other bits and pieces. Because of this the caller is expected to unlock the user object between the two phases to ensure that the second phase doesn't stall all other operations that require it */ int prepareConfigData( OPTION_INFO *optionList, const char *fileName, void *trustInfoPtr, void **data, int *length ) { STREAM stream; const BOOLEAN trustedCertsPresent = \ cryptStatusOK( \ enumTrustedCerts( trustInfoPtr, CRYPT_UNUSED, CRYPT_UNUSED ) ) ? \ TRUE : FALSE; int i; /* Clear the return values */ *data = NULL; *length = 0; /* If neither the config options nor any cert trust settings have changed, there's nothing to do */ for( i = 1; fixedOptionInfo[ i ].option <= LAST_STORED_OPTION && \ i < FAILSAFE_ARRAYSIZE(fixedOptionInfo, FIXED_OPTION_INFO ); i++ ) { if( optionList[ i ].dirty ) break; } if( i >= FAILSAFE_ITERATIONS_LARGE ) retIntError(); if( fixedOptionInfo[ i ].option >= LAST_STORED_OPTION && \ !trustedCertsPresent ) return( CRYPT_OK ); /* Make a first pass through the config options to determine the total encoded length of the ones that don't match the default setting. We can't just check the isDirty flag because if a value is reset to its default setting the encoded size will be zero even though the isDirty flag is set */ for( i = 1; fixedOptionInfo[ i ].option <= LAST_STORED_OPTION && \ i < FAILSAFE_ARRAYSIZE(fixedOptionInfo, FIXED_OPTION_INFO ); i++ ) { const FIXED_OPTION_INFO *fixedOptionInfoPtr = &fixedOptionInfo[ i ]; const OPTION_INFO *optionInfoPtr = &optionList[ i ]; /* If it's an option that can't be written to disk, skip it */ if( fixedOptionInfoPtr->index == CRYPT_UNUSED ) continue; if( fixedOptionInfoPtr->type == OPTION_STRING ) { /* If the string value is the same as the default, there's nothing to do */ if( optionInfoPtr->strValue == NULL || \ optionInfoPtr->strValue == fixedOptionInfoPtr->strDefault ) continue; *length += ( int ) sizeofObject( \ sizeofShortInteger( fixedOptionInfoPtr->index ) + \ sizeofObject( strlen( optionInfoPtr->strValue ) ) ); } else { /* If the integer/boolean value that's currently set isn't the default setting, update it */ if( optionInfoPtr->intValue == fixedOptionInfoPtr->intDefault ) continue; *length += ( int ) sizeofObject( \ sizeofShortInteger( fixedOptionInfoPtr->index ) + \ ( fixedOptionInfoPtr->type == OPTION_NUMERIC ? \ sizeofShortInteger( optionInfoPtr->intValue ) : \ sizeofBoolean() ) ); } } if( i >= FAILSAFE_ITERATIONS_LARGE ) retIntError(); /* If we've gone back to all default values from having non-default ones stored, we either have to write only trusted certs or nothing at all */ if( *length <= 0 ) { char configFilePath[ MAX_PATH_LENGTH + 1 + 8 ]; int configFilePathLen, status; /* There's no data to write, if there are trusted certs present notify the caller */ if( trustedCertsPresent ) return( OK_SPECIAL ); /* There's nothing to write, delete the config file */ status = fileBuildCryptlibPath( configFilePath, MAX_PATH_LENGTH, &configFilePathLen, fileName, strlen( fileName ), BUILDPATH_GETPATH ); if( cryptStatusOK( status ) ) { configFilePath[ configFilePathLen ] = '\0'; fileErase( configFilePath ); } return( CRYPT_OK ); } assert( *length > 0 ); /* Allocate a buffer to hold the encoded values */ if( ( *data = clAlloc( "prepareConfigData", *length ) ) == NULL ) return( CRYPT_ERROR_MEMORY ); /* Write the config options */ sMemOpen( &stream, *data, *length ); for( i = 1; fixedOptionInfo[ i ].option <= LAST_STORED_OPTION && \ i < FAILSAFE_ARRAYSIZE(fixedOptionInfo, FIXED_OPTION_INFO ); i++ ) { const FIXED_OPTION_INFO *fixedOptionInfoPtr = &fixedOptionInfo[ i ]; const OPTION_INFO *optionInfoPtr = &optionList[ i ]; /* If it's an option that can't be written to disk, skip it */ if( fixedOptionInfoPtr->index == CRYPT_UNUSED ) continue; if( fixedOptionInfoPtr->type == OPTION_STRING ) { if( optionInfoPtr->strValue == NULL || \ optionInfoPtr->strValue == fixedOptionInfoPtr->strDefault ) continue; writeSequence( &stream, sizeofShortInteger( fixedOptionInfoPtr->index ) + \ sizeofObject( strlen( optionInfoPtr->strValue ) ) ); writeShortInteger( &stream, fixedOptionInfoPtr->index, DEFAULT_TAG ); writeCharacterString( &stream, optionInfoPtr->strValue, strlen( optionInfoPtr->strValue ), BER_STRING_UTF8 ); continue; } if( optionInfoPtr->intValue == fixedOptionInfoPtr->intDefault ) continue; if( fixedOptionInfoPtr->type == OPTION_NUMERIC ) { writeSequence( &stream, sizeofShortInteger( fixedOptionInfoPtr->index ) + \ sizeofShortInteger( optionInfoPtr->intValue ) ); writeShortInteger( &stream, fixedOptionInfoPtr->index, DEFAULT_TAG ); writeShortInteger( &stream, optionInfoPtr->intValue, DEFAULT_TAG ); } else { writeSequence( &stream, sizeofShortInteger( fixedOptionInfoPtr->index ) + \ sizeofBoolean() ); writeShortInteger( &stream, fixedOptionInfoPtr->index, DEFAULT_TAG ); writeBoolean( &stream, optionInfoPtr->intValue, DEFAULT_TAG ); } } if( i >= FAILSAFE_ITERATIONS_LARGE ) retIntError(); assert( sGetStatus( &stream ) == CRYPT_OK ); sMemDisconnect( &stream ); /* We've written the config data to the memory buffer, let the caller know that they can unlock it and commit it to permanent storage */ return( OK_SPECIAL ); } int commitConfigData( const CRYPT_USER cryptUser, const char *fileName, const void *data, const int length ) { MESSAGE_CREATEOBJECT_INFO createInfo; MESSAGE_DATA msgData; char configFilePath[ MAX_PATH_LENGTH + 8 ]; int configFilePathLen, status; /* Build the path to the config file and try and create it */ status = fileBuildCryptlibPath( configFilePath, MAX_PATH_LENGTH, &configFilePathLen, fileName, strlen( fileName ), BUILDPATH_CREATEPATH ); if( cryptStatusError( status ) ) /* Map the lower-level filesystem-specific error into a more meaningful generic error */ return( CRYPT_ERROR_OPEN ); setMessageCreateObjectInfo( &createInfo, CRYPT_KEYSET_FILE ); createInfo.arg2 = CRYPT_KEYOPT_CREATE; createInfo.strArg1 = configFilePath; createInfo.strArgLen1 = configFilePathLen; status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT, &createInfo, OBJECT_TYPE_KEYSET ); if( cryptStatusError( status ) ) /* Map the lower-level keyset-specific error into a more meaningful generic error */ return( CRYPT_ERROR_OPEN ); /* Send the config data (if there is any) and any trusted certs to the keyset */ if( length > 0 ) { setMessageData( &msgData, ( void * ) data, length ); status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_CONFIGDATA ); } if( cryptStatusOK( status ) ) status = krnlSendMessage( cryptUser, IMESSAGE_SETATTRIBUTE, &createInfo.cryptHandle, CRYPT_IATTRUBUTE_CERTKEYSET ); krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT ); if( cryptStatusError( status ) ) { fileErase( configFilePath ); return( CRYPT_ERROR_WRITE ); } return( CRYPT_OK ); }