cryptkrn.c
来自「提供了很多种加密算法和CA认证及相关服务如CMP、OCSP等的开发」· C语言 代码 · 共 1,648 行 · 第 1/5 页
C
1,648 行
static void endObjectTable( void )
{
/* Hinc igitur effuge */
lockGlobalResource( objectTable );
zeroise( objectTable, objectTableSize * sizeof( OBJECT_INFO ) );
free( objectTable );
objectTable = NULL;
isClosingDown = FALSE;
unlockGlobalResource( objectTable );
deleteGlobalResourceLock( objectTable );
}
/****************************************************************************
* *
* Object Creation/Destruction *
* *
****************************************************************************/
/* Create a new object. This function has to be very careful about locking
to ensure that another thread can't manipulate the newly-created object
while it's in an indeterminate state. To accomplish this it locks the
object table and tries to create the new object. If this succeeds it sets
the object's status to CRYPT_ERROR_NOTINITED pending completion of the
object's initialisation by the caller, unlocks the object table, and
returns control to the caller. While the object is in this state, the
kernel will allow it to process only two message types, either a
notification from the caller that the init stage is complete (which sets
the object's state to CRYPT_OK), or a destroy object message, which sets
its state to CRYPT_ERROR_SIGNALLED pending arrival of the init complete
notification, whereupon the object is immediately destroyed. The state
diagram for this is:
State
Notinited Signalled
--------+-------------------+-----------------
-> OK | state -> OK, | Msg -> Destroy
| ret( OK ) |
Msg. Destroy | state -> Sig'd, | state -> Sig'd,
| ret( OK ) | ret( OK )
CtrlMsg | process as usual | process as usual
NonCtrl | ret( Notinited ) | ret( Sig'd )
The initialisation process for an object is therefore:
status = krnlCreateObject( ... );
if( cryptStatusError( status ) )
return( status );
initResourceLock();
lockResource();
// Complete object-specific initialisation
initStatus = ...;
unlockResource();
status = krnlSendMessage( ..., state -> CRYPT_OK );
return( ( cryptStatusError( initStatus ) ? initStatus : status );
If the object is destroyed during the object-specific initialisation
(either by the init code when an error is encountered or due to an
external signal), the destroy is deferred until the change state message
at the end occurs. If a destroy is pending, the change state is converted
to a destroy and the newly-created object is destroyed.
This mechanism ensures that the object table is only locked for a very
short time (typically for only a few lines of executed code in the create
object function) so that slow initialisation (for example of keyset
objects associated with network links) can't block other objects.
The locking is complicated by the fact that the object table and lock may
not have been initialised yet, so we also need to check the initialisation
lock before we try to lock or use the object table. Even this can create
problems since the initialisation lock may not have been set up yet, but
we can't really fix that. In any case under Win32 it's OK since the mutex
is set up by DllMain(), and under most Unixen the storage for the mutex is
set to all-zero which is equivalent to an initialised mutex.
In addition to the locking, we need to be careful with how we create new
objects because if we just allocate handles sequentially and reuse handles
as soon as possible, an existing object could be signalled and a new one
created in its place without the caller or owning object realizing that
they're now working with a different object. Unix systems handle this by
always incrementing pids and assuming there won't be any problems when
they wrap, we do the same thing but in addition allocate handles in a non-
sequential manner using an LFSR to step through the object table. There's
no strong reason for this, but it only costs a few extra clocks so we may
as well do it */
static int findFreeResource( int value )
{
int oldValue = value;
TEMP_VAR( iterations = 0 );
/* Preconditions: We're starting with a valid object handle, and it's not
a system object */
PRE( isValidHandle( value ) );
PRE( value >= NO_SYSTEM_OBJECTS );
/* Step through the entire table looking for a free entry */
do
{
/* Get the next value: Multiply by x and reduce by the polynomial */
value <<= 1;
if( value & objectStateInfo.lfsrMask )
value ^= objectStateInfo.lfsrPoly;
INV( iterations++ < objectTableSize );
}
while( objectTable[ value ].objectPtr != NULL && \
value != oldValue );
if( value == oldValue )
{
/* Postcondition: We tried all locations and there are no free slots
available */
POST( iterations == objectTableSize - 1 );
FORALL( i, 0, objectTableSize,
objectTable[ i ].objectPtr != NULL );
return( CRYPT_ERROR );
}
/* Postconditions: We found a handle to a free slot */
POST( isValidHandle( value ) );
POST( isFreeObject( value ) );
return( value );
}
int krnlCreateObject( void **objectDataPtr, const CRYPT_USER owner,
const OBJECT_TYPE type, const int subType,
const int objectSize, const int createObjectFlags,
const int actionFlags,
RESOURCE_MESSAGE_FUNCTION messageFunction )
{
OBJECT_INFO objectInfo;
int objectHandle = objectStateInfo.objectHandle;
TEMP_VAR( bitCount );
/* Preconditions (the subType check is just the standard hakmem bitcount
which ensures that we don't try and create multi-typed objects, the
sole exception to this rule is the default user object which acts as
both a user and SO object) */
PRE( objectDataPtr != NULL );
PRE( owner == CRYPT_UNUSED || isValidHandle( owner ) );
PRE( isValidType( type ) );
PRE( bitCount = ( subType & ~SUBTYPE_CLASS_MASK ) - \
( ( ( subType & ~SUBTYPE_CLASS_MASK ) >> 1 ) & 033333333333 ) - \
( ( ( subType & ~SUBTYPE_CLASS_MASK ) >> 2 ) & 011111111111 ) );
PRE( ( objectHandle == DEFAULTUSER_OBJECT_HANDLE - 1 ) || \
( ( ( bitCount + ( bitCount >> 3 ) ) & 030707070707 ) % 63 == 1 ) );
PRE( objectSize > 16 && objectSize < 16384 );
PRE( !( createObjectFlags & \
~( CREATEOBJECT_FLAG_SECUREMALLOC | CREATEOBJECT_FLAG_DUMMY ) ) );
PRE( actionFlags < ACTION_PERM_LAST );
PRE( messageFunction != NULL );
*objectDataPtr = NULL;
/* Allocate memory for the object and set up the object table entry. The
object is always created as an internal object, it's up to the caller
to make it externally visible. Since this step doesn't access the
object table, we do it outside the locked section */
if( createObjectFlags & CREATEOBJECT_FLAG_SECUREMALLOC )
{
int status = krnlMemalloc( objectDataPtr, objectSize );
if( cryptStatusError( status ) )
return( status );
}
else
if( ( *objectDataPtr = malloc( objectSize ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
memset( *objectDataPtr, 0, objectSize );
objectInfo = objectTemplate;
objectInfo.objectPtr = *objectDataPtr;
objectInfo.owner = owner;
objectInfo.type = type;
objectInfo.subType = subType;
objectInfo.actionFlags = actionFlags;
objectInfo.messageFunction = messageFunction;
setObjectOwnership( &objectInfo, CRYPT_UNUSED );
/* Make sure the kernel has been initialised, and if it has lock the
object table for exclusive access */
lockGlobalResource( initialisation );
if( !isInitialised )
{
unlockGlobalResource( initialisation );
return( CRYPT_ERROR_NOTINITED );
}
lockGlobalResource( objectTable );
unlockGlobalResource( initialisation );
/* If we're in the middle of a shutdown, we can't create any new
objects */
if( isClosingDown )
{
unlockGlobalResource( objectTable );
assert( NOTREACHED );
return( CRYPT_ERROR_PERMISSION );
}
/* The first objects created are internal objects with predefined
handles (spes lucis aeternae). As we create these objects we ratchet
up through the fixed handles until we reached the last fixed object,
whereupon we allocate handles normally */
if( objectHandle < NO_SYSTEM_OBJECTS - 1 )
{
PRE( ( objectHandle == SYSTEM_OBJECT_HANDLE - 1 && \
owner == CRYPT_UNUSED && \
type == OBJECT_TYPE_DEVICE && \
subType == SUBTYPE_DEV_SYSTEM ) || \
( objectHandle == DEFAULTUSER_OBJECT_HANDLE - 1 && \
owner == SYSTEM_OBJECT_HANDLE && \
type == OBJECT_TYPE_USER && \
subType == ( SUBTYPE_USER_NORMAL | SUBTYPE_USER_SO ) ) );
objectHandle++;
POST( isValidHandle( objectHandle ) && \
objectHandle < NO_SYSTEM_OBJECTS && \
objectHandle == objectStateInfo.objectHandle + 1 );
}
else
{
PRE( isValidHandle( owner ) );
/* Search the table for a free entry */
objectHandle = findFreeResource( objectHandle );
}
/* If the table is full, expand it */
if( objectHandle == CRYPT_ERROR )
{
static const int lfsrPolyTable[] = \
{ 0x83, 0x11D, 0x211, 0x409,
0x805, 0x1053, 0x201B, 0x402B,
0x8003, 0x1002D, 0x20009, 0x40027,
0x80027, 0x100009, 0x200005, 0x400003 };
OBJECT_INFO *newTable;
int i;
ORIGINAL_INT_VAR( oldLfsrPoly, objectStateInfo.lfsrPoly );
/* If we're already at the maximum number of allowed objects, don't
create any more. This prevents both accidental runaway code
which creates huge numbers of objects and DOS attacks */
if( objectTableSize >= MAX_OBJECTS )
{
unlockGlobalResource( objectTable );
return( CRYPT_ERROR_MEMORY );
}
/* Precondition: We haven't exceeded the maximum number of objects */
PRE( objectTableSize < MAX_OBJECTS );
/* Expand the table */
newTable = malloc( ( objectTableSize * 2 ) * sizeof( OBJECT_INFO ) );
if( newTable == NULL )
{
unlockGlobalResource( objectTable );
return( CRYPT_ERROR_MEMORY );
}
/* Copy the information across to the new table, set up the newly-
allocated entries, and clear the old table */
memcpy( newTable, objectTable,
objectTableSize * sizeof( OBJECT_INFO ) );
for( i = objectTableSize; i < objectTableSize * 2; i++ )
newTable[ i ] = objectTemplate;
zeroise( objectTable, objectTableSize * sizeof( OBJECT_INFO ) );
free( objectTable );
objectTable = newTable;
objectTableSize *= 2;
/* Add the new object at the end of the existing table */
objectStateInfo.lfsrMask <<= 1;
for( i = 0; i < 16; i++ )
if( lfsrPolyTable[ i ] > objectStateInfo.lfsrPoly )
break;
objectStateInfo.lfsrPoly = lfsrPolyTable[ i ];
objectHandle = findFreeResource( objectStateInfo.objectHandle );
/* Postcondition: We've moved on to the next LFSR polynomial value,
and the LFSR output covers the entire table */
POST( ( objectStateInfo.lfsrPoly & ~0x7F ) == \
( ORIGINAL_VALUE_VAR( oldLfsrPoly ) & ~0xFF ) << 1 );
POST( objectStateInfo.lfsrMask == \
( objectStateInfo.lfsrPoly & ~0x7F ) );
POST( objectTableSize == objectStateInfo.lfsrMask );
}
/* Set up the new object entry in the table and update the object table
state */
objectTable[ objectHandle ] = objectInfo;
if( objectHandle == NO_SYSTEM_OBJECTS - 1 )
{
/* If this is the last system object, we've been allocating handles
sequentially up to this point. From now on we start allocating
handles starting from a randomised location in the table */
objectStateInfo.objectHandle = \
( ( int ) time( NULL ) ) & ( objectStateInfo.lfsrMask - 1 );
if( objectStateInfo.objectHandle < NO_SYSTEM_OBJECTS )
/* Can occur with probability NO_SYSTEM_OBJECTS / 1024 */
objectStateInfo.objectHandle = NO_SYSTEM_OBJECTS + 42;
}
else
objectStateInfo.objectHandle = objectHandle;
/* Postconditions: It's a valid object set up as required */
POST( isValidObject( objectHandle ) );
POST( objectInfo.objectPtr == *objectDataPtr );
POST( objectInfo.owner == owner );
POST( objectInfo.type == type );
POST( objectInfo.subType == subType );
POST( objectInfo.actionFlags == actionFlags );
POST( objectInfo.messageFunction == messageFunction );
unlockGlobalResource( objectTable );
return( objectHandle );
}
/****************************************************************************
* *
* Internal Message Handlers *
* *
****************************************************************************/
/* Update an action permission. This implements a ratchet which only allows
permissions to be made more restrictive after they've initially been set,
so once a permission is set to a given level it can't be set to a less
restrictive level (ie it's a write-up policy) */
static int updateActionPerms( int currentPerm, const int newPerm )
{
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?