/**************************************************************************** * * * Kernel Message Dispatcher * * Copyright Peter Gutmann 1997-2006 * * * ****************************************************************************/ #if defined( INC_ALL ) #include "crypt.h" #include "acl.h" #include "kernel.h" #else #include "crypt.h" #include "kernel/acl.h" #include "kernel/kernel.h" #endif /* Compiler-specific includes */ /* A pointer to the kernel data block */ static KERNEL_DATA *krnlData = NULL; /* The ACL used to check objects passed as message parameters, in this case for cert sign/sig-check messages */ static const MESSAGE_ACL FAR_BSS messageParamACLTbl[] = { /* Certs can only be signed by (private-key) PKC contexts */ { MESSAGE_CRT_SIGN, { ST_CTX_PKC, ST_NONE } }, /* Signatures can be checked with a raw PKC context or a cert or cert chain. The object being checked can also be checked against a CRL, against revocation data in a cert store, or against an RTCS or OCSP responder */ { MESSAGE_CRT_SIGCHECK, { ST_CTX_PKC | ST_CERT_CERT | ST_CERT_CERTCHAIN | ST_CERT_CRL | \ ST_KEYSET_DBMS, ST_SESS_RTCS | ST_SESS_OCSP } }, { MESSAGE_NONE, { ST_NONE, ST_NONE } }, { MESSAGE_NONE, { ST_NONE, ST_NONE } } }; /**************************************************************************** * * * Utility Functions * * * ****************************************************************************/ /* Sometimes a message is explicitly non-routable (i.e. it has to be sent directly to the appropriate target object). The following function checks that the target object is one of the required types */ int checkTargetType( const int objectHandle, const long targets ) { const OBJECT_TYPE target = targets & 0xFF; const OBJECT_TYPE altTarget = targets >> 8; OBJECT_INFO *objectTable = krnlData->objectTable; /* Precondition: Source is a valid object, destination(s) are valid target(s) */ PRE( isValidObject( objectHandle ) ); PRE( isValidType( target ) ); PRE( altTarget == OBJECT_TYPE_NONE || isValidType( altTarget ) ); /* Check whether the object matches the required type. We don't have to check whether the alternative target has a value or not since the object can never be a OBJECT_TYPE_NONE */ if( !isValidObject( objectHandle ) || \ ( objectTable[ objectHandle ].type != target && \ objectTable[ objectHandle ].type != altTarget ) ) return( CRYPT_ERROR ); /* Postcondition */ POST( objectTable[ objectHandle ].type == target || \ objectTable[ objectHandle ].type == altTarget ); return( objectHandle ); } /* Find the ACL for a parameter object */ static const MESSAGE_ACL *findParamACL( const MESSAGE_TYPE message ) { int i; /* Precondition: It's a message that takes an object parameter */ PRE( isParamMessage( message ) ); /* Find the ACL entry for this message type. There's no need to explicitly handle the internal-error condition since any loop exit is treated as an error */ for( i = 0; messageParamACLTbl[ i ].type != MESSAGE_NONE && \ i < FAILSAFE_ARRAYSIZE( messageParamACLTbl, MESSAGE_ACL ); i++ ) { if( messageParamACLTbl[ i ].type == message ) return( &messageParamACLTbl[ i ] ); } /* Postcondition: We found a matching ACL entry */ POST( NOTREACHED ); /* Return a no-permission ACL in case of error */ return( &messageParamACLTbl[ i ] ); } /* Wait for an object to become available so that we can use it, with a timeout for blocked objects. This is an internal function which is used when mapping an object handle to object data, and is never called directly. As an aid in identifying objects acting as bottlenecks, we provide a function to warn about excessive waiting, along with information on the object that was waited on, in debug mode. A wait count threshold of 100 is generally high enough to avoid false positives caused by (for example) network subsystem delays */ #define MAX_WAITCOUNT 10000 #define WAITCOUNT_WARN_THRESHOLD 100 #if !defined( NDEBUG ) && !defined( __WIN16__ ) #include static void waitWarn( const int objectHandle, const int waitCount ) { static const char *objectTypeNames[] = { "None", "Context", "Keyset", "Envelope", "Certificate", "Device", "Session", "User", "None", "None" }; const OBJECT_INFO *objectInfoPtr = &krnlData->objectTable[ objectHandle ]; char buffer[ 128 + 8 ]; if( objectHandle == SYSTEM_OBJECT_HANDLE ) strlcpy_s( buffer, 128, "system object" ); else if( objectHandle == DEFAULTUSER_OBJECT_HANDLE ) strlcpy_s( buffer, 128, "default user object" ); else sprintf_s( buffer, 128, "object %d (%s, subtype %lX)", objectHandle, objectTypeNames[ objectInfoPtr->type ], objectInfoPtr->subType ); fprintf( stderr, "\nWarning: Thread %X waited %d iteration%s for %s.\n", THREAD_SELF(), waitCount, ( waitCount == 1 ) ? "" : "s", buffer ); } #endif /* Debug mode only */ int waitForObject( const int objectHandle, OBJECT_INFO **objectInfoPtrPtr ) { OBJECT_INFO *objectTable = krnlData->objectTable; const int uniqueID = objectTable[ objectHandle ].uniqueID; int waitCount = 0; /* Preconditions: The object is in use by another thread */ PRE( isValidObject( objectHandle ) ); PRE( isInUse( objectHandle ) && !isObjectOwner( objectHandle ) ); /* While the object is busy, put the thread to sleep ("Pauzele lungi si dese; Cheia marilor succese"). This is the only really portable way to wait on the resource, which gives up this thread's timeslice to allow other threads (including the one using the object) to run. Somewhat better methods methods such as mutexes with timers are difficult to manage portably across different platforms */ while( objectTable[ objectHandle ].uniqueID == uniqueID && \ isInUse( objectHandle ) && waitCount < MAX_WAITCOUNT && \ krnlData->shutdownLevel < SHUTDOWN_LEVEL_MESSAGES ) { MUTEX_UNLOCK( objectTable ); waitCount++; THREAD_YIELD(); MUTEX_LOCK( objectTable ); } #if !defined( NDEBUG ) && !defined( __WIN16__ ) if( waitCount > WAITCOUNT_WARN_THRESHOLD ) /* If we waited more than WAITCOUNT_WARN_THRESHOLD iterations for something this could be a sign of a resource usage bottleneck (typically caused by users who don't understand threading), warn the user that there's a potential problem */ waitWarn( objectHandle, waitCount ); #endif /* NDEBUG on systems with stdio */ /* If cryptlib is shutting down, exit */ if( krnlData->shutdownLevel >= SHUTDOWN_LEVEL_MESSAGES ) return( CRYPT_ERROR_PERMISSION ); /* If we timed out waiting for the object, return a timeout error */ if( waitCount >= MAX_WAITCOUNT ) { assert( NOTREACHED ); return( CRYPT_ERROR_TIMEOUT ); } /* Make sure that nothing happened to the object while we were waiting on it */ if( objectTable[ objectHandle ].uniqueID != uniqueID ) return( CRYPT_ERROR_SIGNALLED ); /* Update the object info pointer in case the object table was updated while we had yielded control */ *objectInfoPtrPtr = &objectTable[ objectHandle ]; /* Postconditions: The object is available for use */ POST( isValidObject( objectHandle ) ); POST( !isInUse( objectHandle ) ); return( CRYPT_OK ); } /**************************************************************************** * * * Message Routing * * * ****************************************************************************/ /* Find the ultimate target of an object attribute manipulation message by walking down the chain of controlling -> dependent objects. For example a message targeted at a device and sent to a certificate would be routed to the cert's dependent object (which would typically be a context). The device message targeted at the context would in turn be routed to the context's dependent device, which is its final destination */ int findTargetType( const int originalObjectHandle, const long targets ) { const OBJECT_TYPE target = targets & 0xFF; const OBJECT_TYPE altTarget1 = ( targets >> 8 ) & 0xFF; const OBJECT_TYPE altTarget2 = ( targets >> 16 ) & 0xFF; OBJECT_INFO *objectTable = krnlData->objectTable; OBJECT_TYPE type = objectTable[ originalObjectHandle ].type; int objectHandle = originalObjectHandle, iterations; /* Preconditions: Source is a valid object, destination(s) are valid target(s) */ PRE( isValidObject( objectHandle ) ); PRE( isValidType( target ) ); PRE( altTarget1 == OBJECT_TYPE_NONE || isValidType( altTarget1 ) ); PRE( altTarget2 == OBJECT_TYPE_NONE || isValidType( altTarget2 ) ); /* Route the request through any dependent objects as required until we reach the required target object type. "And thou shalt make loops..." -- Exodus 26:4 */ for( iterations = 0; \ iterations < 3 && isValidObject( objectHandle ) && \ !( target == type || \ ( altTarget1 != OBJECT_TYPE_NONE && altTarget1 == type ) || \ ( altTarget2 != OBJECT_TYPE_NONE && altTarget2 == type ) ); \ iterations++ ) { /* Loop invariants. "Fifty loops thou shalt make" -- Exodus 26:5 (some of the OT verses shouldn't be taken too literally, apparently the 50 used here merely means "many" as in "more than one or two" in the same way that "40 days and nights" is now generally taken as meaning "Lots, but that's as far as we're prepared to count") */ INV( isValidObject( objectHandle ) ); INV( iterations < 3 ); /* Find the next potential target object */ if( target == OBJECT_TYPE_DEVICE && \ objectTable[ objectHandle ].dependentDevice != CRYPT_ERROR ) objectHandle = objectTable[ objectHandle ].dependentDevice; else if( target == OBJECT_TYPE_USER ) objectHandle = objectTable[ objectHandle ].owner; else objectHandle = objectTable[ objectHandle ].dependentObject; if( isValidObject( objectHandle ) ) type = objectTable[ objectHandle ].type; /* If we've got a new object, it has the same owner as the original target candidate */ POST( !isValidObject( objectHandle ) || \ isSameOwningObject( originalObjectHandle, objectHandle ) || \ objectTable[ originalObjectHandle ].owner == objectHandle ); } if( iterations >= 3 ) retIntError(); /* Postcondition: We ran out of options or we reached the target object */ POST( iterations < 3 ); POST( objectHandle == CRYPT_ERROR || \ ( isValidObject( objectHandle ) && \ ( isSameOwningObject( originalObjectHandle, objectHandle ) || \ objectTable[ originalObjectHandle ].owner == objectHandle ) && \ ( target == type || \ ( altTarget1 != OBJECT_TYPE_NONE && altTarget1 == type ) || \ ( altTarget2 != OBJECT_TYPE_NONE && altTarget2 == type ) ) ) ); return( isValidObject( objectHandle ) ? \ objectHandle : CRYPT_ARGERROR_OBJECT ); } /* Find the ultimate target of a compare message by walking down the chain of controlling -> dependent objects. For example a message targeted at a device and sent to a certificate would be routed to the cert's dependent object (which would typically be a context). The device message targeted at the context would be routed to the context's dependent device, which is its final destination */ static int routeCompareMessageTarget( const int originalObjectHandle, const long messageValue ) { OBJECT_TYPE targetType = OBJECT_TYPE_NONE; int objectHandle = originalObjectHandle; /* Preconditions */ PRE( isValidObject( objectHandle ) ); PRE( messageValue == MESSAGE_COMPARE_HASH || \ messageValue == MESSAGE_COMPARE_KEYID || \ messageValue == MESSAGE_COMPARE_KEYID_PGP || \ messageValue == MESSAGE_COMPARE_KEYID_OPENPGP || \ messageValue == MESSAGE_COMPARE_SUBJECT || \ messageValue == MESSAGE_COMPARE_ISSUERANDSERIALNUMBER || \ messageValue == MESSAGE_COMPARE_FINGERPRINT || \ messageValue == MESSAGE_COMPARE_CERTOBJ ); /* Determine the ultimate target type for the message. We don't check for keysets, envelopes and sessions as dependent objects since this never occurs */ switch( messageValue ) { case MESSAGE_COMPARE_HASH: case MESSAGE_COMPARE_KEYID: case MESSAGE_COMPARE_KEYID_PGP: case MESSAGE_COMPARE_KEYID_OPENPGP: targetType = OBJECT_TYPE_CONTEXT; break; case MESSAGE_COMPARE_SUBJECT: case MESSAGE_COMPARE_ISSUERANDSERIALNUMBER: case MESSAGE_COMPARE_FINGERPRINT: case MESSAGE_COMPARE_CERTOBJ: targetType = OBJECT_TYPE_CERTIFICATE; break; default: assert( NOTREACHED ); return( CRYPT_ARGERROR_OBJECT ); } /* Route the message through to the appropriate object */ objectHandle = findTargetType( objectHandle, targetType ); /* Postcondition */ POST( objectHandle == CRYPT_ARGERROR_OBJECT || \ ( isValidObject( objectHandle ) && \ isSameOwningObject( originalObjectHandle, objectHandle ) ) ); return( objectHandle ); } /**************************************************************************** * * * Message Dispatch ACL * * * ****************************************************************************/ /* Each message type has certain properties such as whether it's routable, which object types it applies to, what checks are performed on it, whether it's processed by the kernel or dispatched to an object, etc etc. These are all defined in the following table. In addition to the usual checks, we also make various assertions about the parameters we're passed. Note that these don't check user data (that's checked programmatically and an error code returned) but values passed by cryptlib code */ typedef enum { PARAMTYPE_NONE_NONE, /* Data = 0, value = 0 */ PARAMTYPE_NONE_ANY, /* Data = 0, value = any */ PARAMTYPE_NONE_BOOLEAN, /* Data = 0, value = boolean */ PARAMTYPE_NONE_CHECKTYPE,/* Data = 0, value = check type */ PARAMTYPE_DATA_NONE, /* Data, value = 0 */ PARAMTYPE_DATA_ANY, /* Data, value = any */ PARAMTYPE_DATA_LENGTH, /* Data, value >= 0 */ PARAMTYPE_DATA_OBJTYPE, /* Data, value = object type */ PARAMTYPE_DATA_MECHTYPE,/* Data, value = mechanism type */ PARAMTYPE_DATA_ITEMTYPE,/* Data, value = keymgmt.item type */ PARAMTYPE_DATA_FORMATTYPE,/* Data, value = cert format type */ PARAMTYPE_DATA_COMPARETYPE,/* Data, value = compare type */ PARAMTYPE_DATA_SETDEPTYPE,/* Data, value = setdep.option type */ PARAMTYPE_LAST /* Last possible parameter check type */ } PARAMCHECK_TYPE; /* Symbolic defines for message handling types, used to make it clearer what's going on PRE_DISPATCH - Action before message is dispatched POST_DISPATCH - Action after message is dispatched HANDLE_INTERNAL - Message handled by the kernel */ #define PRE_DISPATCH( function ) preDispatch##function, NULL #define POST_DISPATCH( function ) NULL, postDispatch##function #define PRE_POST_DISPATCH( preFunction, postFunction ) \ preDispatch##preFunction, postDispatch##postFunction #define HANDLE_INTERNAL( function ) NULL, NULL, function /* The handling information, declared in the order in which it's applied */ typedef struct { /* The message type, used for consistency checking */ const MESSAGE_TYPE messageType; /* Message routing information if the message is routable. If the target is implicitly determined via the message value, the routing target is OBJECT_TYPE_NONE; if the target is explicitly determined, the routing target is identified in the target. If the routing function is null, the message isn't routed */ const long routingTarget; /* Target type if routable */ int ( *routingFunction )( const int objectHandle, const long arg ); /* Object type checking information: Object subtypes for which this message is valid (for object-type-specific message) */ const OBJECT_SUBTYPE subTypeA, subTypeB;/* Object subtype for which msg.valid */ /* Message type checking information used to assertion-check the function preconditions */ const PARAMCHECK_TYPE paramCheck; /* Parameter check assertion type */ /* Pre- and post-message-dispatch handlers. These perform any additional checking and processing that may be necessary before and after a message is dispatched to an object */ int ( *preDispatchFunction )( const int objectHandle, const MESSAGE_TYPE message, const void *messageDataPtr, const int messageValue, const void *auxInfo ); int ( *postDispatchFunction )( const int objectHandle, const MESSAGE_TYPE message, const void *messageDataPtr, const int messageValue, const void *auxInfo ); /* Message processing information. If the internal handler function is non-null, it's handled by the kernel */ int ( *internalHandlerFunction )( const int objectHandle, const int arg1, const void *arg2, const BOOLEAN isInternal ); } MESSAGE_HANDLING_INFO; static const MESSAGE_HANDLING_INFO FAR_BSS messageHandlingInfo[] = { { MESSAGE_NONE, ROUTE_NONE, 0, PARAMTYPE_NONE_NONE }, /* Control messages. These messages aren't routed, are valid for all object types and subtypes, take no (or minimal) parameters, and are handled by the kernel */ { MESSAGE_DESTROY, /* Destroy the object */ ROUTE_NONE, ST_ANY_A, ST_ANY_B, PARAMTYPE_NONE_NONE, PRE_DISPATCH( SignalDependentObjects ) }, { MESSAGE_INCREFCOUNT, /* Increment object ref.count */ ROUTE_NONE, ST_ANY_A, ST_ANY_B, PARAMTYPE_NONE_NONE, HANDLE_INTERNAL( incRefCount ) }, { MESSAGE_DECREFCOUNT, /* Decrement object ref.count */ ROUTE_NONE, ST_ANY_A, ST_ANY_B, PARAMTYPE_NONE_NONE, HANDLE_INTERNAL( decRefCount ) }, { MESSAGE_GETDEPENDENT, /* Get dependent object */ ROUTE_NONE, ST_ANY_A, ST_ANY_B, PARAMTYPE_DATA_OBJTYPE, HANDLE_INTERNAL( getDependentObject ) }, { MESSAGE_SETDEPENDENT, /* Set dependent object (e.g. ctx->dev) */ ROUTE_NONE, ST_ANY_A, ST_ANY_B, PARAMTYPE_DATA_SETDEPTYPE, HANDLE_INTERNAL( setDependentObject ) }, { MESSAGE_CLONE, /* Clone the object (only valid for ctxs) */ ROUTE_FIXED( OBJECT_TYPE_CONTEXT ), ST_CTX_CONV | ST_CTX_HASH, ST_NONE, PARAMTYPE_NONE_ANY, HANDLE_INTERNAL( cloneObject ) }, /* Attribute messages. These messages are implicitly routed by attribute type, more specific checking is performed using the attribute ACL's */ { MESSAGE_GETATTRIBUTE, /* Get numeric object attribute */ ROUTE_IMPLICIT, ST_ANY_A, ST_ANY_B, PARAMTYPE_DATA_ANY, PRE_POST_DISPATCH( CheckAttributeAccess, MakeObjectExternal ) }, { MESSAGE_GETATTRIBUTE_S, /* Get string object attribute */ ROUTE_IMPLICIT, ST_ANY_A, ST_ANY_B, PARAMTYPE_DATA_ANY, PRE_DISPATCH( CheckAttributeAccess ) }, { MESSAGE_SETATTRIBUTE, /* Set numeric object attribute */ ROUTE_IMPLICIT, ST_ANY_A, ST_ANY_B, PARAMTYPE_DATA_ANY, PRE_POST_DISPATCH( CheckAttributeAccess, ChangeStateOpt ) }, { MESSAGE_SETATTRIBUTE_S, /* Set string object attribute */ ROUTE_IMPLICIT, ST_ANY_A, ST_ANY_B, PARAMTYPE_DATA_ANY, PRE_POST_DISPATCH( CheckAttributeAccess, ChangeStateOpt ) }, { MESSAGE_DELETEATTRIBUTE, /* Delete object attribute */ ROUTE_IMPLICIT, ST_CTX_ANY | ST_CERT_ANY, ST_SESS_ANY | ST_USER_NORMAL | ST_USER_SO, PARAMTYPE_NONE_ANY, PRE_DISPATCH( CheckAttributeAccess ) }, /* General messages to objects */ { MESSAGE_COMPARE, /* Compare objs.or obj.properties */ ROUTE_SPECIAL( CompareMessageTarget ), ST_CTX_ANY | ST_CERT_ANY, ST_NONE, PARAMTYPE_DATA_COMPARETYPE, PRE_DISPATCH( CheckCompareParam ) }, { MESSAGE_CHECK, /* Check object info */ ROUTE_NONE, ST_ANY_A, ST_ANY_B, PARAMTYPE_NONE_CHECKTYPE, PRE_POST_DISPATCH( CheckCheckParam, ForwardToDependentObject ) }, /* Messages sent from the kernel to object message handlers. These messages are sent directly to the object from inside the kernel in response to a control message, so we set the checking to disallow everything to catch any that arrive from outside */ { MESSAGE_CHANGENOTIFY, /* Notification of obj.status chge.*/ ROUTE_NONE, ST_NONE, ST_NONE, PARAMTYPE_NONE_NONE }, /* Object-type-specific messages: Contexts */ { MESSAGE_CTX_ENCRYPT, /* Context: Action = encrypt */ ROUTE( OBJECT_TYPE_CONTEXT ), ST_CTX_CONV | ST_CTX_PKC, ST_NONE, PARAMTYPE_DATA_LENGTH, PRE_POST_DISPATCH( CheckActionAccess, UpdateUsageCount ) }, { MESSAGE_CTX_DECRYPT, /* Context: Action = decrypt */ ROUTE( OBJECT_TYPE_CONTEXT ), ST_CTX_CONV | ST_CTX_PKC, ST_NONE, PARAMTYPE_DATA_LENGTH, PRE_POST_DISPATCH( CheckActionAccess, UpdateUsageCount ) }, { MESSAGE_CTX_SIGN, /* Context: Action = sign */ ROUTE( OBJECT_TYPE_CONTEXT ), ST_CTX_PKC, ST_NONE, PARAMTYPE_DATA_LENGTH, PRE_POST_DISPATCH( CheckActionAccess, UpdateUsageCount ) }, { MESSAGE_CTX_SIGCHECK, /* Context: Action = sigcheck */ ROUTE( OBJECT_TYPE_CONTEXT ), ST_CTX_PKC, ST_NONE, PARAMTYPE_DATA_LENGTH, PRE_POST_DISPATCH( CheckActionAccess, UpdateUsageCount ) }, { MESSAGE_CTX_HASH, /* Context: Action = hash */ ROUTE( OBJECT_TYPE_CONTEXT ), ST_CTX_HASH | ST_CTX_MAC, ST_NONE, PARAMTYPE_DATA_LENGTH, PRE_POST_DISPATCH( CheckActionAccess, UpdateUsageCount ) }, { MESSAGE_CTX_GENKEY, /* Context: Generate a key */ ROUTE( OBJECT_TYPE_CONTEXT ), ST_CTX_CONV | ST_CTX_PKC | ST_CTX_MAC, ST_NONE, PARAMTYPE_NONE_BOOLEAN, PRE_POST_DISPATCH( CheckState, ChangeState ) }, { MESSAGE_CTX_GENIV, /* Context: Generate an IV */ ROUTE( OBJECT_TYPE_CONTEXT ), ST_CTX_CONV, ST_NONE, PARAMTYPE_NONE_NONE }, /* Object-type-specific messages: Certificates */ { MESSAGE_CRT_SIGN, /* Cert: Action = sign cert */ ROUTE( OBJECT_TYPE_CERTIFICATE ), ST_CERT_ANY_CERT | ST_CERT_ATTRCERT | ST_CERT_CRL | \ ST_CERT_OCSP_REQ | ST_CERT_OCSP_RESP, ST_NONE, PARAMTYPE_NONE_ANY, PRE_POST_DISPATCH( CheckStateParamHandle, ChangeState ) }, { MESSAGE_CRT_SIGCHECK, /* Cert: Action = check/verify cert */ ROUTE( OBJECT_TYPE_CERTIFICATE ), ST_CERT_ANY_CERT | ST_CERT_ATTRCERT | ST_CERT_CRL | \ ST_CERT_RTCS_RESP | ST_CERT_OCSP_RESP, ST_NONE, PARAMTYPE_NONE_ANY, PRE_DISPATCH( CheckParamHandleOpt ) }, { MESSAGE_CRT_EXPORT, /* Cert: Export encoded cert data */ ROUTE( OBJECT_TYPE_CERTIFICATE ), ST_CERT_ANY, ST_NONE, PARAMTYPE_DATA_FORMATTYPE, PRE_DISPATCH( CheckExportAccess ) }, /* Object-type-specific messages: Devices */ { MESSAGE_DEV_QUERYCAPABILITY, /* Device: Query capability */ ROUTE_FIXED( OBJECT_TYPE_DEVICE ), ST_DEV_ANY, ST_NONE, PARAMTYPE_DATA_ANY }, { MESSAGE_DEV_EXPORT, /* Device: Action = export key */ ROUTE( OBJECT_TYPE_DEVICE ), ST_DEV_ANY, ST_NONE, PARAMTYPE_DATA_MECHTYPE, PRE_DISPATCH( CheckMechanismWrapAccess ) }, { MESSAGE_DEV_IMPORT, /* Device: Action = import key */ ROUTE( OBJECT_TYPE_DEVICE ), ST_DEV_ANY, ST_NONE, PARAMTYPE_DATA_MECHTYPE, PRE_DISPATCH( CheckMechanismWrapAccess ) }, { MESSAGE_DEV_SIGN, /* Device: Action = sign */ ROUTE( OBJECT_TYPE_DEVICE ), ST_DEV_ANY, ST_NONE, PARAMTYPE_DATA_MECHTYPE, PRE_DISPATCH( CheckMechanismSignAccess ) }, { MESSAGE_DEV_SIGCHECK, /* Device: Action = sig.check */ ROUTE( OBJECT_TYPE_DEVICE ), ST_DEV_ANY, ST_NONE, PARAMTYPE_DATA_MECHTYPE, PRE_DISPATCH( CheckMechanismSignAccess ) }, { MESSAGE_DEV_DERIVE, /* Device: Action = derive key */ ROUTE( OBJECT_TYPE_DEVICE ), ST_DEV_ANY, ST_NONE, PARAMTYPE_DATA_MECHTYPE, PRE_DISPATCH( CheckMechanismDeriveAccess ) }, { MESSAGE_DEV_CREATEOBJECT, /* Device: Create object */ ROUTE_FIXED( OBJECT_TYPE_DEVICE ), ST_DEV_ANY, ST_NONE, PARAMTYPE_DATA_OBJTYPE, PRE_POST_DISPATCH( CheckCreate, MakeObjectExternal ) }, { MESSAGE_DEV_CREATEOBJECT_INDIRECT,/* Device: Create obj.from data */ ROUTE_FIXED( OBJECT_TYPE_DEVICE ), ST_DEV_ANY, ST_NONE, PARAMTYPE_DATA_OBJTYPE, PRE_POST_DISPATCH( CheckCreate, MakeObjectExternal ) }, /* Object-type-specific messages: Envelopes */ { MESSAGE_ENV_PUSHDATA, /* Envelope: Push data */ ROUTE_FIXED_ALT( OBJECT_TYPE_ENVELOPE, OBJECT_TYPE_SESSION ), ST_NONE, ST_ENV_ANY | ST_SESS_ANY_DATA, PARAMTYPE_DATA_NONE, PRE_DISPATCH( CheckData ) }, { MESSAGE_ENV_POPDATA, /* Envelope: Pop data */ ROUTE_FIXED_ALT( OBJECT_TYPE_ENVELOPE, OBJECT_TYPE_SESSION ), ST_NONE, ST_ENV_ANY | ST_SESS_ANY_DATA, PARAMTYPE_DATA_NONE, PRE_DISPATCH( CheckData ) }, /* Object-type-specific messages: Keysets */ { MESSAGE_KEY_GETKEY, /* Keyset: Instantiate ctx/cert */ ROUTE_FIXED_ALT( OBJECT_TYPE_KEYSET, OBJECT_TYPE_DEVICE ), ST_KEYSET_ANY | ST_DEV_ANY_STD, ST_NONE, PARAMTYPE_DATA_ITEMTYPE, PRE_POST_DISPATCH( CheckKeysetAccess, MakeObjectExternal ) }, { MESSAGE_KEY_SETKEY, /* Keyset: Add ctx/cert */ ROUTE_FIXED_ALT( OBJECT_TYPE_KEYSET, OBJECT_TYPE_DEVICE ), ST_KEYSET_ANY | ST_DEV_ANY_STD, ST_NONE, PARAMTYPE_DATA_ITEMTYPE, PRE_DISPATCH( CheckKeysetAccess ) }, { MESSAGE_KEY_DELETEKEY, /* Keyset: Delete key */ ROUTE_FIXED_ALT( OBJECT_TYPE_KEYSET, OBJECT_TYPE_DEVICE ), ST_KEYSET_ANY | ST_DEV_ANY_STD, ST_NONE, PARAMTYPE_DATA_ITEMTYPE, PRE_DISPATCH( CheckKeysetAccess ) }, { MESSAGE_KEY_GETFIRSTCERT, /* Keyset: Get first cert in sequence */ ROUTE_FIXED_ALT( OBJECT_TYPE_KEYSET, OBJECT_TYPE_DEVICE ), ST_KEYSET_ANY | ST_DEV_ANY_STD, ST_NONE, PARAMTYPE_DATA_ITEMTYPE, PRE_DISPATCH( CheckKeysetAccess ) }, { MESSAGE_KEY_GETNEXTCERT, /* Keyset: Get next cert in sequence */ ROUTE_FIXED_ALT( OBJECT_TYPE_KEYSET, OBJECT_TYPE_DEVICE ), ST_KEYSET_ANY | ST_DEV_ANY_STD, ST_NONE, PARAMTYPE_DATA_ITEMTYPE, PRE_POST_DISPATCH( CheckKeysetAccess, MakeObjectExternal ) }, { MESSAGE_KEY_CERTMGMT, /* Keyset: Cert management */ ROUTE_FIXED( OBJECT_TYPE_KEYSET ), ST_KEYSET_DBMS_STORE, ST_NONE, PARAMTYPE_DATA_ANY, PRE_POST_DISPATCH( CheckCertMgmtAccess, MakeObjectExternal ) } }; /**************************************************************************** * * * Init/Shutdown Functions * * * ****************************************************************************/ int initSendMessage( KERNEL_DATA *krnlDataPtr ) { int i; /* Perform a consistency check on various things that need to be set up in a certain way for things to work properly */ assert( MESSAGE_CTX_DECRYPT == MESSAGE_CTX_ENCRYPT + 1 ); assert( MESSAGE_CTX_SIGN == MESSAGE_CTX_DECRYPT + 1 ); assert( MESSAGE_CTX_SIGCHECK == MESSAGE_CTX_SIGN + 1 ); assert( MESSAGE_CTX_HASH == MESSAGE_CTX_SIGCHECK + 1 ); assert( MESSAGE_CTX_GENKEY == MESSAGE_CTX_HASH + 1 ); assert( MESSAGE_GETATTRIBUTE_S == MESSAGE_GETATTRIBUTE + 1 ); assert( MESSAGE_SETATTRIBUTE == MESSAGE_GETATTRIBUTE_S + 1 ); assert( MESSAGE_SETATTRIBUTE_S == MESSAGE_SETATTRIBUTE + 1 ); assert( MESSAGE_DELETEATTRIBUTE == MESSAGE_SETATTRIBUTE_S + 1 ); /* Perform a consistency check on various internal values and constants */ assert( ACTION_PERM_COUNT == 6 ); /* Perform a consistency check on the parameter ACL */ for( i = 0; messageParamACLTbl[ i ].type != MESSAGE_NONE && \ i < FAILSAFE_ARRAYSIZE( messageParamACLTbl, MESSAGE_ACL ); i++ ) { const MESSAGE_ACL *messageParamACL = &messageParamACLTbl[ i ]; if( !isParamMessage( messageParamACL->type ) || \ ( messageParamACL->objectACL.subTypeA & SUBTYPE_CLASS_B ) || \ ( messageParamACL->objectACL.subTypeB & SUBTYPE_CLASS_A ) ) retIntError(); } if( i >= FAILSAFE_ARRAYSIZE( messageParamACLTbl, MESSAGE_ACL ) ) retIntError(); /* Perform a consistency check on the message handling information */ for( i = 0; i < MESSAGE_LAST; i++ ) { const MESSAGE_HANDLING_INFO *messageInfo = &messageHandlingInfo[ i ]; if( messageInfo->messageType != i || \ messageInfo->paramCheck < PARAMTYPE_NONE_NONE || \ messageInfo->paramCheck >= PARAMTYPE_LAST || \ ( messageInfo->subTypeA & SUBTYPE_CLASS_B ) || \ ( messageInfo->subTypeB & SUBTYPE_CLASS_A ) ) retIntError(); } /* Set up the reference to the kernel data block */ krnlData = krnlDataPtr; return( CRYPT_OK ); } void endSendMessage( void ) { krnlData = NULL; } /**************************************************************************** * * * Message Queue * * * ****************************************************************************/ /* Enqueue a message */ static int enqueueMessage( const int objectHandle, const MESSAGE_HANDLING_INFO *handlingInfoPtr, const MESSAGE_TYPE message, const void *messageDataPtr, const int messageValue ) { MESSAGE_QUEUE_DATA *messageQueue = krnlData->messageQueue; int queuePos, i, iterationCount; /* Precondition: It's a valid message being sent to a valid object */ PRE( isValidObject( objectHandle ) ); PRE( isReadPtr( handlingInfoPtr, sizeof( MESSAGE_HANDLING_INFO ) ) ); PRE( isValidMessage( message & MESSAGE_MASK ) ); /* Make sure that we don't overflow the queue (this object is not responding to messages... now all we need is GPF's). We return a timeout error to indicate that there are too many messages queued for this (or other) objects */ if( krnlData->queueEnd < 0 || \ krnlData->queueEnd >= MESSAGE_QUEUE_SIZE - 1 ) { assert( NOTREACHED ); return( CRYPT_ERROR_TIMEOUT ); } /* Precondition: There's room to enqueue the message */ PRE( krnlData->queueEnd >= 0 && \ krnlData->queueEnd < MESSAGE_QUEUE_SIZE ); /* Check whether a message to this object is already present in the queue */ for( queuePos = krnlData->queueEnd - 1, iterationCount = 0; queuePos >= 0 && iterationCount++ < MESSAGE_QUEUE_SIZE; queuePos-- ) { if( messageQueue[ queuePos ].objectHandle == objectHandle ) break; } if( iterationCount >= MESSAGE_QUEUE_SIZE ) retIntError(); /* Postcondition: queuePos = -1 if not present, position in queue if present */ POST( queuePos == -1 || \ ( queuePos >= 0 && queuePos < krnlData->queueEnd ) ); /* Enqueue the message */ queuePos++; /* Insert after current position */ for( i = krnlData->queueEnd - 1, iterationCount = 0; i >= queuePos && iterationCount++ < MESSAGE_QUEUE_SIZE; i-- ) messageQueue[ i + 1 ] = messageQueue[ i ]; if( iterationCount >= MESSAGE_QUEUE_SIZE ) retIntError(); messageQueue[ queuePos ].objectHandle = objectHandle; messageQueue[ queuePos ].handlingInfoPtr = handlingInfoPtr; messageQueue[ queuePos ].message = message; messageQueue[ queuePos ].messageDataPtr = messageDataPtr; messageQueue[ queuePos ].messageValue = messageValue; krnlData->queueEnd++; if( queuePos > 0 ) /* A message for this object is already present, tell the caller to defer processing */ return( OK_SPECIAL ); return( CRYPT_OK ); } /* Dequeue a message */ static void dequeueMessage( const int messagePosition ) { MESSAGE_QUEUE_DATA *messageQueue = krnlData->messageQueue; int i, iterationCount; /* Precondition: We're deleting a valid queue position */ PRE( messagePosition >= 0 && messagePosition < krnlData->queueEnd ); /* Move the remaining messages down and clear the last entry */ for( i = messagePosition, iterationCount = 0; i < krnlData->queueEnd - 1 && \ iterationCount++ < MESSAGE_QUEUE_SIZE; i++ ) messageQueue[ i ] = messageQueue[ i + 1 ]; if( iterationCount >= MESSAGE_QUEUE_SIZE ) retIntError_Void(); zeroise( &messageQueue[ krnlData->queueEnd - 1 ], sizeof( MESSAGE_QUEUE_DATA ) ); krnlData->queueEnd--; /* Postcondition: all queue entries are valid, all non-queue entries are empty */ FORALL( i, 0, krnlData->queueEnd, messageQueue[ i ].handlingInfoPtr != NULL ); FORALL( i, krnlData->queueEnd, MESSAGE_QUEUE_SIZE, messageQueue[ i ].handlingInfoPtr == NULL ); } /* Get the next message in the queue */ static BOOLEAN getNextMessage( const int objectHandle, MESSAGE_QUEUE_DATA *messageQueueInfo ) { MESSAGE_QUEUE_DATA *messageQueue = krnlData->messageQueue; int i; PRE( messageQueueInfo == NULL || \ isWritePtr( messageQueueInfo, sizeof( MESSAGE_QUEUE_DATA ) ) ); /* Find the next message for this object. Since other messages can have come and gone in the meantime, we have to scan from the start each time */ for( i = 0; i < krnlData->queueEnd && i < MESSAGE_QUEUE_SIZE; i++ ) { if( messageQueue[ i ].objectHandle == objectHandle ) { if( messageQueueInfo != NULL ) *messageQueueInfo = messageQueue[ i ]; dequeueMessage( i ); return( TRUE ); } } if( i >= MESSAGE_QUEUE_SIZE ) retIntError_Boolean(); /* Postcondition: There are no more messages for this object present in the queue */ FORALL( i, 0, krnlData->queueEnd, messageQueue[ i ].objectHandle != objectHandle ); return( FALSE ); } /* Dequeue all messages for an object in the queue */ static void dequeueAllMessages( const int objectHandle ) { int iterationCount = 0; /* Dequeue all messages for a given object */ while( getNextMessage( objectHandle, NULL ) && \ iterationCount++ < MESSAGE_QUEUE_SIZE ); if( iterationCount >= MESSAGE_QUEUE_SIZE ) retIntError_Void(); /* Postcondition: There are no more messages for this object present in the queue */ FORALL( i, 0, krnlData->queueEnd, krnlData->messageQueue[ i ].objectHandle != objectHandle ); } /**************************************************************************** * * * Message Dispatcher * * * ****************************************************************************/ /* Dispatch a message to an object */ static int dispatchMessage( const int localObjectHandle, const MESSAGE_QUEUE_DATA *messageQueueData, OBJECT_INFO *objectInfoPtr, const void *aclPtr ) { const MESSAGE_HANDLING_INFO *handlingInfoPtr = \ messageQueueData->handlingInfoPtr; const MESSAGE_FUNCTION messageFunction = objectInfoPtr->messageFunction; const MESSAGE_TYPE localMessage = messageQueueData->message & MESSAGE_MASK; void *objectPtr = objectInfoPtr->objectPtr; const int lockCount = objectInfoPtr->lockCount + 1; int status; PRE( isValidHandle( localObjectHandle ) ); PRE( !isInUse( localObjectHandle ) || \ isObjectOwner( localObjectHandle ) ); PRE( isReadPtr( messageQueueData, sizeof( MESSAGE_QUEUE_DATA ) ) ); PRE( isWritePtr( objectInfoPtr, sizeof( OBJECT_INFO ) ) ); /* If there's a pre-dispatch handler present, apply it */ if( handlingInfoPtr->preDispatchFunction != NULL ) { status = handlingInfoPtr->preDispatchFunction( localObjectHandle, messageQueueData->message, messageQueueData->messageDataPtr, messageQueueData->messageValue, aclPtr ); if( cryptStatusError( status ) ) return( status ); } /* Mark the object as busy so that we have it available for our exclusive use and further messages to it will be enqueued, dispatch the message with the object table unlocked, and mark the object as non-busy again */ objectInfoPtr->lockCount++; #ifdef USE_THREADS objectInfoPtr->lockOwner = THREAD_SELF(); #endif /* USE_THREADS */ MUTEX_UNLOCK( objectTable ); status = messageFunction( objectPtr, localMessage, ( void * ) messageQueueData->messageDataPtr, messageQueueData->messageValue ); MUTEX_LOCK( objectTable ); objectInfoPtr = &krnlData->objectTable[ localObjectHandle ]; assert( localObjectHandle == SYSTEM_OBJECT_HANDLE || \ objectInfoPtr->lockCount == lockCount ); /* The system object may unlock itself while processing a message when it forwards the message elsewhere or performs non-object- specific processing, so we only decrement the lock count if it's unchanged and we still own the object. We have to perform the ownership check to avoid the situation where we unlock the object and another thread locks it, leading to an (apparently) unchanged lock count */ if( objectInfoPtr->lockCount == lockCount && \ isObjectOwner( localObjectHandle ) ) objectInfoPtr->lockCount--; /* Postcondition: The lock count is non-negative and, if it's not the system object, has been reset to its previous value */ POST( objectInfoPtr->lockCount >= 0 && \ ( localObjectHandle == SYSTEM_OBJECT_HANDLE || objectInfoPtr->lockCount == lockCount - 1 ) ); /* If there's a post-dispatch handler present, apply it. Since a destroy object message always succeeds but can return an error code (typically CRYPT_ERROR_INCOMPLETE), we don't treat an error return as a real error status for the purposes of further processing */ if( ( cryptStatusOK( status ) || localMessage == MESSAGE_DESTROY ) && \ handlingInfoPtr->postDispatchFunction != NULL ) status = handlingInfoPtr->postDispatchFunction( localObjectHandle, messageQueueData->message, messageQueueData->messageDataPtr, messageQueueData->messageValue, aclPtr ); return( status ); } /* Send a message to an object */ int krnlSendMessage( const int objectHandle, const MESSAGE_TYPE message, void *messageDataPtr, const int messageValue ) { const ATTRIBUTE_ACL *attributeACL = NULL; const MESSAGE_HANDLING_INFO *handlingInfoPtr; OBJECT_INFO *objectTable = krnlData->objectTable; OBJECT_INFO *objectInfoPtr; MESSAGE_QUEUE_DATA enqueuedMessageData; const BOOLEAN isInternalMessage = \ ( message & MESSAGE_FLAG_INTERNAL ) ? TRUE : FALSE; const void *aclPtr = NULL; MESSAGE_TYPE localMessage = message & MESSAGE_MASK; int localObjectHandle = objectHandle, iterationCount = 0; int status = CRYPT_OK; /* Preconditions. For external messages we don't provide any assertions at this point since they're coming straight from the user and could contain any values, and for internal messages we only trap on programming errors (thus for example isValidHandle() vs. isValidObject(), since this would trap if a message is sent to a destroyed object) */ PRE( isWritePtr( krnlData, sizeof( KERNEL_DATA ) ) ); PRE( isValidMessage( localMessage ) ); PRE( !isInternalMessage || isValidHandle( objectHandle ) ); /* Enforce the precondition at runtime as well */ if( !isValidMessage( localMessage ) ) { assert( NOTREACHED ); return( CRYPT_ERROR_NOTAVAIL ); } /* Get the information that we need to handle this message */ handlingInfoPtr = &messageHandlingInfo[ localMessage ]; /* Inner preconditions now that we have the handling information: Message parameters must be within the allowed range (again, this traps on programming errors only). This is done as a large number of individual assertions rather than a single huge check so that a failed assertion can provide more detailed information than just "it broke" */ PRE( handlingInfoPtr->paramCheck == PARAMTYPE_NONE_NONE || handlingInfoPtr->paramCheck == PARAMTYPE_NONE_ANY || \ handlingInfoPtr->paramCheck == PARAMTYPE_NONE_BOOLEAN || \ handlingInfoPtr->paramCheck == PARAMTYPE_NONE_CHECKTYPE || \ handlingInfoPtr->paramCheck == PARAMTYPE_DATA_NONE || \ handlingInfoPtr->paramCheck == PARAMTYPE_DATA_ANY || \ handlingInfoPtr->paramCheck == PARAMTYPE_DATA_LENGTH || \ handlingInfoPtr->paramCheck == PARAMTYPE_DATA_OBJTYPE || \ handlingInfoPtr->paramCheck == PARAMTYPE_DATA_MECHTYPE || \ handlingInfoPtr->paramCheck == PARAMTYPE_DATA_ITEMTYPE || \ handlingInfoPtr->paramCheck == PARAMTYPE_DATA_FORMATTYPE || \ handlingInfoPtr->paramCheck == PARAMTYPE_DATA_COMPARETYPE || \ handlingInfoPtr->paramCheck == PARAMTYPE_DATA_SETDEPTYPE ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_NONE_NONE || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_NONE_NONE && \ messageDataPtr == NULL && messageValue == 0 ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_NONE_ANY || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_NONE_ANY && \ messageDataPtr == NULL ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_NONE_BOOLEAN || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_NONE_BOOLEAN && \ messageDataPtr == NULL && \ ( messageValue == FALSE || messageValue == TRUE ) ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_NONE_CHECKTYPE || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_NONE_CHECKTYPE && \ messageDataPtr == NULL && \ ( messageValue > MESSAGE_CHECK_NONE && \ messageValue < MESSAGE_CHECK_LAST ) ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_DATA_NONE || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_DATA_NONE && \ messageDataPtr != NULL && messageValue == 0 ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_DATA_ANY || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_DATA_ANY && \ messageDataPtr != NULL ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_DATA_LENGTH || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_DATA_LENGTH && \ messageDataPtr != NULL && messageValue >= 0 ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_DATA_OBJTYPE || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_DATA_OBJTYPE && \ messageDataPtr != NULL && \ ( messageValue > OBJECT_TYPE_NONE && messageValue < OBJECT_TYPE_LAST ) ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_DATA_MECHTYPE || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_DATA_MECHTYPE && \ messageDataPtr != NULL && \ ( messageValue > MECHANISM_NONE && messageValue < MECHANISM_LAST ) ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_DATA_ITEMTYPE || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_DATA_ITEMTYPE && \ messageDataPtr != NULL && \ ( messageValue > KEYMGMT_ITEM_NONE && messageValue < KEYMGMT_ITEM_LAST ) ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_DATA_FORMATTYPE || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_DATA_FORMATTYPE && \ messageDataPtr != NULL && \ ( messageValue > CRYPT_CERTFORMAT_NONE && messageValue < CRYPT_CERTFORMAT_LAST ) ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_DATA_COMPARETYPE || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_DATA_COMPARETYPE && \ messageDataPtr != NULL && \ ( messageValue > MESSAGE_COMPARE_NONE && \ messageValue < MESSAGE_COMPARE_LAST ) ) ); PRE( handlingInfoPtr->paramCheck != PARAMTYPE_DATA_SETDEPTYPE || \ ( handlingInfoPtr->paramCheck == PARAMTYPE_DATA_SETDEPTYPE && \ messageDataPtr != NULL && \ messageValue > SETDEP_OPTION_NONE && \ messageValue < SETDEP_OPTION_LAST ) ); /* If it's an object-manipulation message get the attribute's mandatory ACL; if it's an object-parameter message get the parameter's mandatory ACL. Since these doesn't require access to any object information, we can do it before we lock the object table */ if( isAttributeMessage( localMessage ) ) { attributeACL = findAttributeACL( messageValue, isInternalMessage ); if( attributeACL == NULL ) return( CRYPT_ARGERROR_VALUE ); aclPtr = attributeACL; } if( isParamMessage( localMessage ) ) aclPtr = findParamACL( localMessage ); /* Inner precondition: If it's an attribute-manipulation message, we have a valid ACL for the attribute present */ PRE( !isAttributeMessage( localMessage ) || attributeACL != NULL ); /* If we're in the middle of a shutdown, don't allow any further messages except ones related to object destruction (the status read is needed for objects capable of performing async ops, since the shutdown code needs to determine whether they're currently busy). The check outside the object-table lock is done in order to have any remaining active objects exit quickly without tying up the object table, since we don't want them to block the shutdown. In addition if the thread is a leftover/long-running thread that's still active after the shutdown has occurred, we can't access the object table lock since it'll have been deleted */ if( krnlData->shutdownLevel >= SHUTDOWN_LEVEL_MESSAGES && \ !( localMessage == MESSAGE_DESTROY || \ localMessage == MESSAGE_DECREFCOUNT || \ ( localMessage == MESSAGE_GETATTRIBUTE && \ messageValue == CRYPT_IATTRIBUTE_STATUS ) ) ) return( CRYPT_ERROR_PERMISSION ); /* Lock the object table to ensure that other threads don't try to access it */ MUTEX_LOCK( objectTable ); /* The first line of defence: Make sure that the message is being sent to a valid object and that the object is externally visible and accessible to the caller if required by the message. The checks performed are: if( handle does not correspond to an object ) error; if( message is external ) { if( object is internal ) error; if( object isn't owned by calling thread ) error; } This is equivalent to the shorter form fullObjectCheck() that used elsewhere. The error condition reported in all of these cases is that the object handle isn't valid */ if( !isValidObject( objectHandle ) ) status = CRYPT_ARGERROR_OBJECT; else if( !isInternalMessage && \ ( isInternalObject( objectHandle ) || \ !checkObjectOwnership( objectTable[ objectHandle ] ) ) ) status = CRYPT_ARGERROR_OBJECT; if( cryptStatusError( status ) ) { MUTEX_UNLOCK( objectTable ); return( status ); } /* Inner precondition now that the outer check has been passed: It's a valid, accessible object and not a system object that can never be explicitly destroyed or have its refCount altered */ PRE( isValidObject( objectHandle ) ); PRE( isInternalMessage || ( !isInternalObject( objectHandle ) && \ checkObjectOwnership( objectTable[ objectHandle ] ) ) ); PRE( fullObjectCheck( objectHandle, message ) ); PRE( objectHandle >= NO_SYSTEM_OBJECTS || \ ( localMessage != MESSAGE_DESTROY && \ localMessage != MESSAGE_DECREFCOUNT && \ localMessage != MESSAGE_INCREFCOUNT ) ); /* If this message is routable, find its target object */ if( handlingInfoPtr->routingFunction != NULL ) { /* If it's implicitly routed, route it based on the attribute type */ if( isImplicitRouting( handlingInfoPtr->routingTarget ) ) { if( attributeACL->routingFunction != NULL ) localObjectHandle = attributeACL->routingFunction( objectHandle, attributeACL->routingTarget ); } else /* It's explicitly or directly routed, route it based on the message type or fixed-target type */ localObjectHandle = handlingInfoPtr->routingFunction( objectHandle, isExplicitRouting( handlingInfoPtr->routingTarget ) ? \ messageValue : handlingInfoPtr->routingTarget ); if( cryptStatusError( localObjectHandle ) ) { MUTEX_UNLOCK( objectTable ); return( CRYPT_ARGERROR_OBJECT ); } } /* Inner precodition: It's a valid destination object */ PRE( isValidObject( localObjectHandle ) ); /* It's a valid object, get its info */ objectInfoPtr = &objectTable[ localObjectHandle ]; /* Now that the message has been routed to its intended target, make sure that it's valid for the target object subtype */ if( !isValidSubtype( handlingInfoPtr->subTypeA, objectInfoPtr->subType ) && \ !isValidSubtype( handlingInfoPtr->subTypeB, objectInfoPtr->subType ) ) { MUTEX_UNLOCK( objectTable ); return( CRYPT_ARGERROR_OBJECT ); } /* Inner precondition: The message is valid for this object subtype */ PRE( isValidSubtype( handlingInfoPtr->subTypeA, objectInfoPtr->subType ) || \ isValidSubtype( handlingInfoPtr->subTypeB, objectInfoPtr->subType ) ); /* If this message is processed internally, handle it now. These messages aren't affected by the object's state so they're always processed */ if( handlingInfoPtr->internalHandlerFunction != NULL || \ ( attributeACL != NULL && \ attributeACL->flags & ATTRIBUTE_FLAG_PROPERTY ) ) { if( handlingInfoPtr->preDispatchFunction != NULL ) status = handlingInfoPtr->preDispatchFunction( localObjectHandle, message, messageDataPtr, messageValue, aclPtr ); if( cryptStatusOK( status ) ) { /* Precondition: Either the message as a whole is internally handled or it's a property attribute */ PRE( handlingInfoPtr->internalHandlerFunction == NULL || \ attributeACL == NULL ); /* If it's an object property attribute (which is handled by the kernel), get or set its value */ if( handlingInfoPtr->internalHandlerFunction == NULL ) { /* Precondition: Object properties are always numeric attributes */ PRE( handlingInfoPtr->messageType == MESSAGE_GETATTRIBUTE || \ handlingInfoPtr->messageType == MESSAGE_SETATTRIBUTE ); if( handlingInfoPtr->messageType == MESSAGE_GETATTRIBUTE ) status = getPropertyAttribute( localObjectHandle, messageValue, messageDataPtr ); else status = setPropertyAttribute( localObjectHandle, messageValue, messageDataPtr ); } else /* It's a kernel-handled message, process it */ status = handlingInfoPtr->internalHandlerFunction( \ localObjectHandle, messageValue, messageDataPtr, isInternalMessage ); if( cryptStatusOK( status ) && \ handlingInfoPtr->postDispatchFunction != NULL ) status = handlingInfoPtr->postDispatchFunction( localObjectHandle, message, messageDataPtr, messageValue, aclPtr ); } if( status != OK_SPECIAL ) { /* The message was processed normally, exit */ MUTEX_UNLOCK( objectTable ); return( status ); } /* The object has entered an invalid state (for example it was signalled while it was being initialised) and can't be used any more, destroy it, convert the (local copy of the) message into a destroy object message */ localMessage = MESSAGE_DESTROY; status = CRYPT_OK; } /* We shouldn't have aliased objects since we don't use copy-on-write any more */ assert( !isAliasedObject( localObjectHandle ) ); /* If the object isn't already processing a message and the message isn't a special type such as MESSAGE_DESTROY, dispatch it immediately rather than enqueueing it for later dispatch. This scoreboard mechanism greatly reduces the load on the queue */ if( !isInUse( localObjectHandle ) && localMessage != MESSAGE_DESTROY ) { CONST_INIT_STRUCT_5( MESSAGE_QUEUE_DATA messageQueueData, \ localObjectHandle, handlingInfoPtr, message, \ messageDataPtr, messageValue ); CONST_SET_STRUCT( messageQueueData.objectHandle = localObjectHandle; \ messageQueueData.handlingInfoPtr = handlingInfoPtr; \ messageQueueData.message = message; \ messageQueueData.messageDataPtr = messageDataPtr; \ messageQueueData.messageValue = messageValue ); /* If the object isn't in a valid state, we can't do anything with it. There are no messages that can be sent to it at this point, get/ set property messages have already been handled earlier and the destroy message isn't handled here */ if( isInvalidObjectState( localObjectHandle ) ) { status = getObjectStatusValue( objectInfoPtr->flags ); MUTEX_UNLOCK( objectTable ); return( status ); } /* In case a shutdown was signalled while we were performing other processing, exit now before we try and do anything with the object. It's safe to perform the check at this point since no message sent during shutdown will get here */ if( krnlData->shutdownLevel >= SHUTDOWN_LEVEL_MESSAGES ) { MUTEX_UNLOCK( objectTable ); return( CRYPT_ERROR_PERMISSION ); } /* Inner precondition: The object is in a valid state */ PRE( !isInvalidObjectState( localObjectHandle ) ); /* Dispatch the message to the object */ status = dispatchMessage( localObjectHandle, &messageQueueData, objectInfoPtr, aclPtr ); MUTEX_UNLOCK( objectTable ); /* Postcondition: The return status is valid */ POST( ( status >= CRYPT_ENVELOPE_RESOURCE && status <= CRYPT_OK ) || \ cryptArgError( status ) || status == OK_SPECIAL ); return( status ); } /* Inner precondition: The object is in use or it's a destroy object message, we have to enqueue it */ PRE( isInUse( localObjectHandle ) || localMessage == MESSAGE_DESTROY ); /* If we're stuck in a loop processing recursive messages, bail out. This would happen automatically anyway once we fill the message queue, but this early-out mechanism prevents a single object from filling the queue to the detriment of other objects */ if( objectInfoPtr->lockCount > MESSAGE_QUEUE_SIZE / 2 ) { MUTEX_UNLOCK( objectTable ); assert( NOTREACHED ); return( CRYPT_ERROR_TIMEOUT ); } /* If the object is in use by another thread, wait for it to become available */ if( isInUse( localObjectHandle ) && !isObjectOwner( localObjectHandle ) ) { status = waitForObject( localObjectHandle, &objectInfoPtr ); #if !defined( NDEBUG ) && defined( USE_THREADS ) if( cryptStatusOK( status ) && isInUse( localObjectHandle ) ) /* dispatchMessage() expects us to be the lock owner if the object is in use, however if the object has been */ objectInfoPtr->lockOwner = THREAD_SELF(); #endif /* !NDEBUG && USE_THREADS */ } if( cryptStatusError( status ) ) { MUTEX_UNLOCK( objectTable ); return( status ); } /******/ assert( !isInUse( localObjectHandle ) || isObjectOwner( localObjectHandle ) ); /******/ /* Enqueue the message */ if( ( message & MESSAGE_MASK ) != localMessage ) { /* The message was converted during processing, this can only happen when a message sent to an invalid-state object is converted into a destroy-object message. What we therefore enqueue is a destroy-object message, but with the messageValue parameter set to TRUE to indicate that it's a converted destroy message */ PRE( localMessage == MESSAGE_DESTROY ); status = enqueueMessage( localObjectHandle, &messageHandlingInfo[ MESSAGE_DESTROY ], MESSAGE_DESTROY, messageDataPtr, TRUE ); } else status = enqueueMessage( localObjectHandle, handlingInfoPtr, message, messageDataPtr, messageValue ); if( cryptStatusError( status ) ) { /* A message for this object is already present in the queue, defer processing until later */ MUTEX_UNLOCK( objectTable ); return( ( status == OK_SPECIAL ) ? CRYPT_OK : status ); } /******/ assert( !isInUse( localObjectHandle ) || isObjectOwner( localObjectHandle ) ); /******/ /* While there are more messages for this object present, dequeue them and dispatch them. Since messages will only be enqueued if krnlSendMessage() is called recursively, we only dequeue messages for the current object in this loop. Queued messages for other objects will be handled at a different level of recursion. Bounding this loop is a bit tricky, since new messages can arrive as the existing ones are dequeued, so that in theory the arrival rate could match the dispatch rate. However in practice a situation like this would be extremely unusual, so we bound the loop at FAILSAFE_ITERATIONS_LARGE */ while( getNextMessage( localObjectHandle, &enqueuedMessageData ) && \ iterationCount++ < FAILSAFE_ITERATIONS_LARGE ) { const BOOLEAN isDestroy = \ ( ( enqueuedMessageData.message & MESSAGE_MASK ) == MESSAGE_DESTROY ); /* If there's a problem with the object, initiate special processing. There are two exceptions to this, one is a destroy message sent to a busy object, the other is a destroy message that started out as a different type of message (that is, it was converted into a destroy object message due to the object being in an invalid state, indicated by the messageValue parameter being set to TRUE when it's normally zero for a destroy message). Both of these types are let through */ if( isInvalidObjectState( localObjectHandle ) && \ !( isDestroy && \ ( objectInfoPtr->flags & OBJECT_FLAG_BUSY ) || \ ( enqueuedMessageData.messageValue == TRUE ) ) ) { /* If it's a destroy object message being sent to an object in the process of being created, set the state to signalled and continue. The object will be destroyed when the caller notifies the kernel that the init is complete */ if( isDestroy && ( objectInfoPtr->flags & OBJECT_FLAG_NOTINITED ) ) { objectInfoPtr->flags |= OBJECT_FLAG_SIGNALLED; status = CRYPT_OK; } else { /* Remove all further messages for this object and return to the caller */ dequeueAllMessages( localObjectHandle ); status = getObjectStatusValue( objectInfoPtr->flags ); } continue; } /******/ assert( !isInUse( localObjectHandle ) || isObjectOwner( localObjectHandle ) ); /******/ /* Inner precondition: The object is in a valid state or it's a destroy message to a busy object or a destroy message that was converted from a different message type */ PRE( !isInvalidObjectState( localObjectHandle ) || \ ( isDestroy && \ ( objectInfoPtr->flags & OBJECT_FLAG_BUSY ) || \ ( enqueuedMessageData.messageValue == TRUE ) ) ); /* Dispatch the message to the object */ status = dispatchMessage( localObjectHandle, &enqueuedMessageData, objectInfoPtr, aclPtr ); /* If the message is a destroy object message, we have to explicitly remove it from the object table and dequeue all further messages for it since the object's message handler can't do this itself. Since a destroy object message always succeeds but can return an error code (typically CRYPT_ERROR_INCOMPLETE), we don't treat an error return as a real error status for the purposes of further processing */ if( isDestroy ) { destroyObjectData( localObjectHandle ); dequeueAllMessages( localObjectHandle ); } else /* If we ran into a problem, dequeue all further messages for this object. This causes getNextMessage() to fail and we drop out of the loop */ if( cryptStatusError( status ) ) dequeueAllMessages( localObjectHandle ); } if( iterationCount >= FAILSAFE_ITERATIONS_LARGE ) retIntError(); /* Unlock the object table to allow access by other threads */ MUTEX_UNLOCK( objectTable ); /* Postcondition: The return status is valid */ POST( ( status >= CRYPT_ENVELOPE_RESOURCE && status <= CRYPT_OK ) || \ cryptArgError( status ) || status == OK_SPECIAL ); return( status ); }