/****************************************************************************
*																			*
*							Object Alternative Access						*
*						Copyright Peter Gutmann 1997-2005					*
*																			*
****************************************************************************/

/* Sending a message to an object only makes the one object which is the
   target of the message available for use.  When we need simultaneous
   access to two objects (for example when copying a collection of cert
   extensions from one cert to another), we have to use the
   krnlAcquireObject()/krnlReleaseObject() functions to obtain access to
   the second object's internals.

   There is a second situation in which we need access to an object's
   internals, and that occurs when we need to export/import a key from/to
   a context.  This is handled via the key extract functions at the end
   of this module, see the comments there for further information */

#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;

/****************************************************************************
*																			*
*								Utility Functions							*
*																			*
****************************************************************************/

/* The type of checking that we perform for the object access.  The check 
   types are:

	CHECK_EXTACCESS
		Kernel-external call with a cert or crypto device to allow access to
		object-internal data.

	CHECK_KEYACCESS
		Kernel-internal call with a context for key export/import.

	CHECK_SUSPEND
		Kernel-external call with a user or system object to temporarily
		suspend object use and allow others access, providing a (somewhat 
		crude) mechanism for making kernel calls interruptible */

typedef enum {
	ACCESS_CHECK_NONE,		/* No access check type */
	ACCESS_CHECK_EXTACCESS,	/* Generic external call: Cert or crypt.dev.*/
	ACCESS_CHECK_KEYACCESS,	/* Internal call: Context for key export */
	ACCESS_CHECK_SUSPEND,	/* Suspend object use: User or sys.obj.*/
	ACCESS_CHECK_LAST		/* Last access check type */
	} ACCESS_CHECK_TYPE;

/* Check that this is an object for which direct access is valid.  We can 
   only access the following object types:

	Certificates: EXTACCESS, used when copying internal state such as cert 
		extensions or CRL info from one cert object to another.

	Contexts: KEYACCESS, used when importing/exporting keys to/from contexts 
		during key wrap/unwrap operations.

	Crypto hardware devices other than the system object: EXTACCESS, used 
		when a context tied to a device needs to perform an operation using 
		the device.

	System object: ACCESS_CHECK_SUSPEND, used when performing a randomness 
		data read/write, which can take some time to complete.

	User objects: ACCESS_CHECK_SUSPEND, used when committing config data to 
		persistent storage.  We don't actually use the object data but 
		merely unlock it to allow others access while performing the 
		potentially lengthy update.  Also used when performing the self-
		test */

static int checkAccessValid( const int objectHandle,
							 const ACCESS_CHECK_TYPE checkType,
							 const int errorCode )
	{
	OBJECT_INFO *objectTable = krnlData->objectTable;
	OBJECT_INFO *objectInfoPtr;

	PRE( checkType > ACCESS_CHECK_NONE && checkType < ACCESS_CHECK_LAST );
	PRE( errorCode < 0 );

	/* Perform similar access checks to the ones performed in
	   krnlSendMessage(): It's a valid object owned by the calling
	   thread */
	if( !isValidObject( objectHandle ) || \
		!checkObjectOwnership( objectTable[ objectHandle ] ) )
		return( errorCode );

	/* It's a valid object, get its info */
	objectInfoPtr = &objectTable[ objectHandle ];

	/* Make sure that the object access is valid */
	switch( objectInfoPtr->type )
		{
		case OBJECT_TYPE_CONTEXT:
			/* Used when exporting/importing keying info, valid for contexts 
			   with keys when called from within the kernel */
			if( checkType != ACCESS_CHECK_KEYACCESS )
				return( errorCode );
			if( !isValidSubtype( objectInfoPtr->subType, SUBTYPE_CTX_CONV ) && \
				!isValidSubtype( objectInfoPtr->subType, SUBTYPE_CTX_MAC ) && \
				!isValidSubtype( objectInfoPtr->subType, SUBTYPE_CTX_PKC ) )
				return( errorCode );
			break;

		case OBJECT_TYPE_CERTIFICATE:
			/* Used when copying internal state such as cert extensions or
			   CRL info from one cert object to another.  This is valid for
			   all cert types */
			if( checkType != ACCESS_CHECK_EXTACCESS )
				return( errorCode );
			break;

		case OBJECT_TYPE_DEVICE:
			/* If it's an external access operation, it's used when a 
			   context tied to a crypto hardware device needs to perform an 
			   operation using the device.  This is valid for all devices 
			   other than the system object */
			if( checkType == ACCESS_CHECK_EXTACCESS )
				{
				if( !isValidSubtype( objectInfoPtr->subType, SUBTYPE_DEV_FORTEZZA ) && \
					!isValidSubtype( objectInfoPtr->subType, SUBTYPE_DEV_PKCS11 ) && \
					!isValidSubtype( objectInfoPtr->subType, SUBTYPE_DEV_CRYPTOAPI ) )
					return( errorCode );
				}
			else
				{
				/* If it's a suspend operation, it's used to temporarily 
				   allow access to the system object while other operations
				   are being performed */
				if( checkType != ACCESS_CHECK_SUSPEND )
					return( errorCode );
				if( !isValidSubtype( objectInfoPtr->subType, SUBTYPE_DEV_SYSTEM ) )
					return( errorCode );
				}
			break;

		case OBJECT_TYPE_USER:
			/* Used when updating config data, which can take awhile.  The 
			   default user is an SO user, which is why we check for this 
			   user type */
			if( checkType != ACCESS_CHECK_SUSPEND )
				return( errorCode );
			if( !isValidSubtype( objectInfoPtr->subType, SUBTYPE_USER_SO ) )
				return( errorCode );
			break;

		default:
			assert( NOTREACHED );
			return( errorCode );
		}

	/* Postcondition: The object is of the appropriate type for the access */
	POST( ( ( checkType == ACCESS_CHECK_EXTACCESS ) && \
			( objectInfoPtr->type == OBJECT_TYPE_CERTIFICATE || \
			  objectInfoPtr->type == OBJECT_TYPE_DEVICE ) ) || \
		  ( checkType == ACCESS_CHECK_KEYACCESS && \
		    objectInfoPtr->type == OBJECT_TYPE_CONTEXT ) || \
		  ( checkType == ACCESS_CHECK_SUSPEND && \
		    ( objectInfoPtr->type == OBJECT_TYPE_DEVICE || \
			  objectInfoPtr->type == OBJECT_TYPE_USER ) ) );


	return( CRYPT_OK );
	}

/* Get a pointer to an object's data from its handle */

int getObject( const int objectHandle, const OBJECT_TYPE type,
			   const ACCESS_CHECK_TYPE checkType, void **objectPtr, 
			   const int refCount, const int errorCode )
	{
	OBJECT_INFO *objectTable = krnlData->objectTable;
	OBJECT_INFO *objectInfoPtr;
	int status = CRYPT_OK;

	/* Preconditions: It's a valid object */
	PRE( isValidHandle( objectHandle ) );
	PRE( isValidType( type ) && \
		 ( type == OBJECT_TYPE_CONTEXT || type == OBJECT_TYPE_CERTIFICATE || \
		   type == OBJECT_TYPE_DEVICE || type == OBJECT_TYPE_USER ) );
	PRE( checkType > ACCESS_CHECK_NONE && \
		 checkType < ACCESS_CHECK_LAST );
	PRE( ( ( objectHandle == SYSTEM_OBJECT_HANDLE || \
			 objectHandle == DEFAULTUSER_OBJECT_HANDLE ) && \
		   objectPtr == NULL && refCount > 0 ) || \
		 ( !( objectHandle == SYSTEM_OBJECT_HANDLE || \
			  objectHandle == DEFAULTUSER_OBJECT_HANDLE ) && \
		   isWritePtr( objectPtr, sizeof( void * ) ) && \
		   refCount == CRYPT_UNUSED ) );

	/* Clear the return value */
	if( objectPtr != NULL )
		*objectPtr = NULL;

	MUTEX_LOCK( objectTable );

	/* Perform similar access checks to the ones performed in
	   krnlSendMessage(), as well as situation-specific additional checks 
	   for correct object types */
	status = checkAccessValid( objectHandle, checkType, errorCode );
	if( cryptStatusError( status ) )
		{
		MUTEX_UNLOCK( objectTable );
		assert( NOTREACHED );
		return( status );
		}

	/* Perform additional checks for correct object types */
	if( ( ( objectHandle == SYSTEM_OBJECT_HANDLE || \
			objectHandle == DEFAULTUSER_OBJECT_HANDLE ) && \
		  objectPtr != NULL ) || \
		objectTable[ objectHandle ].type != type )
		{
		MUTEX_UNLOCK( objectTable );
		assert( NOTREACHED );
		return( errorCode );
		}

	/* It's a valid object, get its info */
	objectInfoPtr = &objectTable[ objectHandle ];

	/* Inner precondition: The object is of the requested type */
	PRE( objectInfoPtr->type == type && \
		 ( objectInfoPtr->type == OBJECT_TYPE_CONTEXT || \
		   objectInfoPtr->type == OBJECT_TYPE_CERTIFICATE || \
		   objectInfoPtr->type == OBJECT_TYPE_DEVICE || \
		   objectInfoPtr->type == OBJECT_TYPE_USER ) );

	/* If the object is busy, wait for it to become available */
	if( isInUse( objectHandle ) && !isObjectOwner( objectHandle ) )
		status = waitForObject( objectHandle, &objectInfoPtr );
	if( cryptStatusError( status ) )
		{
		MUTEX_UNLOCK( objectTable );
		return( status );
		}

	/* If it's an external access to certificate/device info or an internal 
	   access to access the object's keying data, increment the object's 
	   reference count to reserve it for our exclusive use */
	if( checkType == ACCESS_CHECK_EXTACCESS || \
		checkType == ACCESS_CHECK_KEYACCESS )
		objectInfoPtr->lockCount++;
	else
		{
		/* If we're resuming use of an object that we suspended to allow 
		   others access, reset the reference count */
		PRE( checkType == ACCESS_CHECK_SUSPEND );
		PRE( objectInfoPtr->lockCount == 0 );
		PRE( refCount > 0 && refCount < 100 );

		objectInfoPtr->lockCount = refCount;
		}
#ifdef USE_THREADS
	objectInfoPtr->lockOwner = THREAD_SELF();
#endif /* USE_THREADS */
	if( objectPtr != NULL )
		*objectPtr = objectInfoPtr->objectPtr;

	MUTEX_UNLOCK( objectTable );
	return( status );
	}

/* Release an object that we previously acquired directly */

static int releaseObject( const int objectHandle,
						  const ACCESS_CHECK_TYPE checkType,
						  int *refCount )
	{
	OBJECT_INFO *objectTable = krnlData->objectTable;
	OBJECT_INFO *objectInfoPtr;
	int status;
	DECLARE_ORIGINAL_INT( lockCount );

	MUTEX_LOCK( objectTable );

	/* Preconditions: It's a valid object in use by the caller */
	PRE( isValidObject( objectHandle ) );
	PRE( isInUse( objectHandle ) && isObjectOwner( objectHandle ) );
	PRE( checkType > ACCESS_CHECK_NONE && \
		 checkType < ACCESS_CHECK_LAST );

	/* Perform similar access checks to the ones performed in
	   krnlSendMessage(), as well as situation-specific additional checks 
	   for correct object types */
	status = checkAccessValid( objectHandle, checkType, 
							   CRYPT_ERROR_PERMISSION );
	if( cryptStatusError( status ) )
		{
		MUTEX_UNLOCK( objectTable );
		assert( NOTREACHED );
		return( status );
		}

	/* Perform additional checks for correct object types.  The ownership 
	   check in checkAccessValid() simply checks whether the current thread 
	   is the overall object owner, isObjectOwner() checks whether the 
	   current thread owns the lock on the object */
	if( !isInUse( objectHandle ) || !isObjectOwner( objectHandle ) )
		{
		MUTEX_UNLOCK( objectTable );
		assert( NOTREACHED );
		return( CRYPT_ERROR_PERMISSION );
		}

	/* It's a valid object, get its info */
	objectInfoPtr = &objectTable[ objectHandle ];

	/* If it was an external access to certificate/device info or an 
	   internal access to the object's keying data, decrement the object's 
	   reference count to allow others access again */
	if( checkType == ACCESS_CHECK_EXTACCESS || \
		checkType == ACCESS_CHECK_KEYACCESS )
		{
		STORE_ORIGINAL_INT( lockCount, objectInfoPtr->lockCount );

		objectInfoPtr->lockCount--;

		/* Postcondition: The object's lock count has been decremented and 
		   is non-negative */
		POST( objectInfoPtr->lockCount == \
								ORIGINAL_VALUE( lockCount ) - 1 );
		POST( objectInfoPtr->lockCount >= 0 );
		}
	else
		{
		/* It's an external access to free the object for access by others, 
		   clear the reference count */
		PRE( checkType == ACCESS_CHECK_SUSPEND );

		*refCount = objectInfoPtr->lockCount;
		objectInfoPtr->lockCount = 0;

		/* Postcondition: The object has been completely released */
		POST( !isInUse( objectHandle ) );
		}

	MUTEX_UNLOCK( objectTable );
	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Init/Shutdown Functions							*
*																			*
****************************************************************************/

int initObjectAltAccess( KERNEL_DATA *krnlDataPtr )
	{
	/* Set up the reference to the kernel data block */
	krnlData = krnlDataPtr;

	return( CRYPT_OK );
	}

void endObjectAltAccess( void )
	{
	krnlData = NULL;
	}

/****************************************************************************
*																			*
*						Direct Object Access Functions						*
*																			*
****************************************************************************/

/* Acquire/release an object */

int krnlAcquireObject( const int objectHandle, const OBJECT_TYPE type,
					   void **objectPtr, const int errorCode )
	{
	return( getObject( objectHandle, type, ACCESS_CHECK_EXTACCESS, 
					   objectPtr, CRYPT_UNUSED, errorCode ) );
	}

int krnlReleaseObject( const int objectHandle )
	{
	return( releaseObject( objectHandle, ACCESS_CHECK_EXTACCESS, NULL ) );
	}

/* Temporarily suspend use of an object to allow other threads access, and
   resume object use afterwards */

int krnlSuspendObject( const int objectHandle, int *refCount )
	{
	return( releaseObject( objectHandle, ACCESS_CHECK_SUSPEND, refCount ) );
	}

int krnlResumeObject( const int objectHandle, const int refCount )
	{
	return( getObject( objectHandle, 
					   ( objectHandle == SYSTEM_OBJECT_HANDLE ) ? \
						 OBJECT_TYPE_DEVICE : OBJECT_TYPE_USER, 
					   ACCESS_CHECK_SUSPEND, NULL, refCount, 
					   CRYPT_ERROR_FAILED ) );
	}

/****************************************************************************
*																			*
*							Key Extract Functions							*
*																			*
****************************************************************************/

/* The cryptlib equivalent of trusted downgraders in other security models:
   Functions that extract a key from a context.  These functions need to
   bypass the kernel's security checking in order to allow key export and
   are the only ones that can do this.  This is an unavoidable requirement
   in the complete-isolation model - some bypass mechanism needs to be
   present in order to allow a key to be exported from an encryption action
   object.  The three functions that perform the necessary operations are:

	extractKeyData: Extract a session key from a conventional/MAC context
					prior to encryption with a KEK.
	exportPrivateKey: Write private key data to a stream prior to encryption
					  with a KEK.
	importPrivateKey: Read private key data from a stream after decryption
					  with a KEK.  We use this rather than a generic 
					  external private key load to avoid having the key 
					  marked as an untrusted user-set key, and also because
					  it's easier to read the key data directly into the
					  context's bignum storage rather than adding indirection
					  via a CRYPT_PKCINFO_xxx structure */

#define PKC_CONTEXT		/* Indicate that we're working with PKC context */
#if defined( INC_ALL )
  #include "context.h"
#else
  #include "context/context.h"
#endif /* Compiler-specific includes */

int extractKeyData( const CRYPT_CONTEXT iCryptContext, void *keyData, 
					const int keyDataMaxLen )
	{
	CONTEXT_INFO *contextInfoPtr;
	int status;

	assert( isHandleRangeValid( iCryptContext ) );
	assert( isWritePtr( keyData, keyDataMaxLen ) );

	/* Clear return value */
	memset( keyData, 0, keyDataMaxLen );

	/* Make sure that we've been given a conventional encryption or MAC 
	   context with a key loaded.  This has already been checked at a higher 
	   level, but we perform a sanity check here to be safe */
	status = getObject( iCryptContext, OBJECT_TYPE_CONTEXT,
						ACCESS_CHECK_KEYACCESS,
						( void ** ) &contextInfoPtr, CRYPT_UNUSED, 
						CRYPT_ARGERROR_OBJECT );
	if( cryptStatusError( status ) )
		return( status );
	if( ( contextInfoPtr->type != CONTEXT_CONV && \
		  contextInfoPtr->type != CONTEXT_MAC ) || \
		!( contextInfoPtr->flags & CONTEXT_KEY_SET ) )
		{
		releaseObject( iCryptContext, ACCESS_CHECK_KEYACCESS, NULL );
		return( CRYPT_ARGERROR_OBJECT );
		}

	/* Export the key data from the context */
	switch( contextInfoPtr->type )
		{
		case CONTEXT_CONV:
			if( contextInfoPtr->ctxConv->userKeyLength > keyDataMaxLen )
				status = CRYPT_ERROR_OVERFLOW;
			else
				memcpy( keyData, contextInfoPtr->ctxConv->userKey,
						contextInfoPtr->ctxConv->userKeyLength );
			break;

		case CONTEXT_MAC:
			if( contextInfoPtr->ctxMAC->userKeyLength > keyDataMaxLen )
				status = CRYPT_ERROR_OVERFLOW;
			else
				memcpy( keyData, contextInfoPtr->ctxMAC->userKey,
						contextInfoPtr->ctxMAC->userKeyLength );
			break;

		default:
			assert( NOTREACHED );
			status = CRYPT_ARGERROR_OBJECT;
		}
	releaseObject( iCryptContext, ACCESS_CHECK_KEYACCESS, NULL );
	return( status );
	}

int exportPrivateKeyData( STREAM *stream, const CRYPT_CONTEXT iCryptContext,
						  const KEYFORMAT_TYPE formatType )
	{
	CONTEXT_INFO *contextInfoPtr;
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isHandleRangeValid( iCryptContext ) );
	assert( formatType > KEYFORMAT_NONE && formatType < KEYFORMAT_LAST );

	/* Make sure that we've been given a PKC context with a private key
	   loaded.  This has already been checked at a higher level, but we
	   perform a sanity check here to be safe */
	status = getObject( iCryptContext, OBJECT_TYPE_CONTEXT, 
						ACCESS_CHECK_KEYACCESS,
						( void ** ) &contextInfoPtr, CRYPT_UNUSED, 
						CRYPT_ARGERROR_OBJECT );
	if( cryptStatusError( status ) )
		return( status );
	if( contextInfoPtr->type != CONTEXT_PKC || \
		!( contextInfoPtr->flags & CONTEXT_KEY_SET ) || \
		( contextInfoPtr->flags & CONTEXT_ISPUBLICKEY ) )
		{
		releaseObject( iCryptContext, ACCESS_CHECK_KEYACCESS, NULL );
		return( CRYPT_ARGERROR_OBJECT );
		}

	/* Export the key data from the context */
	status = contextInfoPtr->ctxPKC->writePrivateKeyFunction( stream, 
										contextInfoPtr, formatType, "private" );
	releaseObject( iCryptContext, ACCESS_CHECK_KEYACCESS, NULL );
	return( status );
	}

int importPrivateKeyData( STREAM *stream, const CRYPT_CONTEXT iCryptContext,
						  const KEYFORMAT_TYPE formatType )
	{
	CONTEXT_INFO *contextInfoPtr;
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isHandleRangeValid( iCryptContext ) );
	assert( formatType > KEYFORMAT_NONE && formatType < KEYFORMAT_LAST );

	/* Make sure that we've been given a PKC context with no private key
	   loaded.  This has already been checked at a higher level, but we
	   perform a sanity check here to be safe */
	status = getObject( iCryptContext, OBJECT_TYPE_CONTEXT, 
						ACCESS_CHECK_KEYACCESS,
						( void ** ) &contextInfoPtr, CRYPT_UNUSED, 
						CRYPT_ARGERROR_OBJECT );
	if( cryptStatusError( status ) )
		return( status );
	if( contextInfoPtr->type != CONTEXT_PKC || \
		( contextInfoPtr->flags & CONTEXT_KEY_SET ) || \
		( contextInfoPtr->flags & CONTEXT_ISPUBLICKEY ) )
		{
		releaseObject( iCryptContext, ACCESS_CHECK_KEYACCESS, NULL );
		return( CRYPT_ARGERROR_OBJECT );
		}

	/* Import the key data into the context */
	status = contextInfoPtr->ctxPKC->readPrivateKeyFunction( stream, 
										contextInfoPtr, formatType );
	if( cryptStatusOK( status ) )
		{
		/* If everything went OK, perform an internal load that uses the
		   values already present in the context */
		status = contextInfoPtr->loadKeyFunction( contextInfoPtr, NULL, 0 );
		if( cryptStatusOK( status ) )
			{
			krnlSendMessage( iCryptContext, IMESSAGE_SETATTRIBUTE, 
							 MESSAGE_VALUE_UNUSED, 
							 CRYPT_IATTRIBUTE_INITIALISED );
			contextInfoPtr->flags |= CONTEXT_KEY_SET;
			}
		else
			/* If the problem was indicated as a function argument error, 
			   map it to a more appropriate code */
			if( cryptArgError( status ) )
				status = CRYPT_ERROR_BADDATA;
		}
	releaseObject( iCryptContext, ACCESS_CHECK_KEYACCESS, NULL );
	return( status );
	}
