555 lines
10 KiB
C
555 lines
10 KiB
C
/*++
|
|
|
|
Copyright (c) 1998-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
rc4safe.c
|
|
|
|
Abstract:
|
|
|
|
access rc4 key material in thread safe manner, without adversly affecting
|
|
performance of multi-thread users.
|
|
|
|
Author:
|
|
|
|
Scott Field (sfield) 02-Jul-99
|
|
|
|
--*/
|
|
|
|
#ifndef KMODE_RNG
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <zwapi.h>
|
|
|
|
#else
|
|
|
|
#include <ntosp.h>
|
|
#include <windef.h>
|
|
|
|
#endif // KMODE_RNG
|
|
|
|
|
|
#include <rc4.h>
|
|
#include <randlib.h>
|
|
|
|
#include "umkm.h"
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
unsigned int BytesUsed; // bytes processed for key associated with this entry
|
|
__LOCK_TYPE lock; // lock for serializing key entry
|
|
RC4_KEYSTRUCT rc4key; // key material associated with this entry
|
|
} RC4_SAFE, *PRC4_SAFE, *LPRC4_SAFE;
|
|
|
|
|
|
typedef enum _MemoryMode {
|
|
Paged = 1,
|
|
NonPaged
|
|
} MemoryMode;
|
|
|
|
|
|
typedef struct {
|
|
unsigned int Entries; // count of array entries.
|
|
MemoryMode Mode; // allocation & behavior mode of SAFE_ARRAY
|
|
RC4_SAFE *Array[]; // array of pointers to RC4_SAFE entries.
|
|
} RC4_SAFE_ARRAY, *PRC4_SAFE_ARRAY, *LPRC4_SAFE_ARRAY;
|
|
|
|
|
|
|
|
|
|
//
|
|
// !!! RC4_SAFE_ENTRIES must be even multiple of 4 !!!
|
|
//
|
|
|
|
#ifndef KMODE_RNG
|
|
#define RC4_SAFE_ENTRIES (8)
|
|
#else
|
|
//
|
|
// we don't expect as much traffic in kernel mode, so use less resources.
|
|
//
|
|
#define RC4_SAFE_ENTRIES (4)
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef KMODE_RNG
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE, rc4_safe_select)
|
|
#pragma alloc_text(PAGE, rc4_safe)
|
|
#pragma alloc_text(PAGE, rc4_safe_key)
|
|
#pragma alloc_text(PAGE, rc4_safe_select)
|
|
#pragma alloc_text(PAGE, rc4_safe_startup)
|
|
#pragma alloc_text(PAGE, rc4_safe_shutdown)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
#endif // KMODE_RNG
|
|
|
|
|
|
|
|
void
|
|
rc4_safe_select(
|
|
IN void *pContext,
|
|
OUT unsigned int *pEntry,
|
|
OUT unsigned int *pBytesUsed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
key selector: choose a thread safe key.
|
|
outputs key identifier and bytes used with key.
|
|
|
|
--*/
|
|
{
|
|
RC4_SAFE_ARRAY *pRC4SafeArray;
|
|
RC4_SAFE *pRC4Safe;
|
|
|
|
static unsigned int circular;
|
|
unsigned int local;
|
|
|
|
|
|
#ifdef KMODE_RNG
|
|
PAGED_CODE();
|
|
#endif // KMODE_RNG
|
|
|
|
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
|
|
|
|
|
|
//
|
|
// there are 2 ways to increment the array selector:
|
|
// 1. Just increment the pointer. On a multi-processor system,
|
|
// with multiple threads calling rc4_safe_select simultaneously,
|
|
// this can lead to several threads selecting the same array element.
|
|
// This will lead to lock contention on the array element lock.
|
|
// Currently, we've decided this scenario will be fairly rare, so the
|
|
// penalty associated with option 2 is avoided.
|
|
// 2. Use InterlockedIncrement to determine the array element. This would
|
|
// yield no collision on an array element, hence no lock contention
|
|
// on the embedded array lock. This introduces additional bus traffic
|
|
// on SMP machines due to the lock primitive.
|
|
//
|
|
//
|
|
|
|
#ifdef KMODE_RNG
|
|
|
|
local = (unsigned int)InterlockedIncrement( (PLONG)&circular );
|
|
|
|
#else
|
|
|
|
circular++;
|
|
local = circular;
|
|
|
|
#endif // KMODE_RNG
|
|
|
|
|
|
//
|
|
// array index will not wrap.
|
|
//
|
|
|
|
local &= ( pRC4SafeArray->Entries-1 );
|
|
pRC4Safe = pRC4SafeArray->Array[local];
|
|
|
|
*pEntry = local;
|
|
*pBytesUsed = pRC4Safe->BytesUsed;
|
|
}
|
|
|
|
|
|
#ifdef KMODE_RNG
|
|
|
|
void
|
|
rc4_safe_select_np(
|
|
IN void *pContext,
|
|
OUT unsigned int *pEntry,
|
|
OUT unsigned int *pBytesUsed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Non-paged, high-IRQL version of rc4_safe_select()
|
|
|
|
--*/
|
|
{
|
|
RC4_SAFE_ARRAY *pRC4SafeArray;
|
|
RC4_SAFE *pRC4Safe;
|
|
unsigned int local;
|
|
|
|
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
|
|
|
|
if( *pEntry == 0 ) {
|
|
|
|
if( pRC4SafeArray->Entries == 1 ) {
|
|
local = 0;
|
|
} else {
|
|
static unsigned int circular;
|
|
|
|
local = (unsigned int)InterlockedIncrement( (PLONG)&circular );
|
|
|
|
//
|
|
// array index will not wrap.
|
|
//
|
|
|
|
local = (local % pRC4SafeArray->Entries);
|
|
}
|
|
|
|
} else {
|
|
|
|
local = KeGetCurrentProcessorNumber();
|
|
}
|
|
|
|
pRC4Safe = pRC4SafeArray->Array[local];
|
|
*pEntry = local;
|
|
*pBytesUsed = pRC4Safe->BytesUsed;
|
|
}
|
|
|
|
|
|
#endif // KMODE_RNG
|
|
|
|
|
|
|
|
void
|
|
rc4_safe(
|
|
IN void *pContext,
|
|
IN unsigned int Entry,
|
|
IN unsigned int cb,
|
|
IN void *pv
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the rc4 key identified by the Entry index.
|
|
|
|
Input key material is pv, of size cb.
|
|
|
|
--*/
|
|
{
|
|
RC4_SAFE_ARRAY *pRC4SafeArray;
|
|
RC4_SAFE *pRC4Safe;
|
|
|
|
#ifdef KMODE_RNG
|
|
KIRQL OldIrql;
|
|
|
|
PAGED_CODE();
|
|
#endif // KMODE_RNG
|
|
|
|
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
|
|
|
|
Entry &= ( pRC4SafeArray->Entries - 1 );
|
|
pRC4Safe = pRC4SafeArray->Array[ Entry ];
|
|
|
|
ENTER_LOCK( &(pRC4Safe->lock) );
|
|
|
|
rc4( &(pRC4Safe->rc4key), cb, (unsigned char*) pv );
|
|
pRC4Safe->BytesUsed += cb;
|
|
|
|
LEAVE_LOCK( &(pRC4Safe->lock) );
|
|
|
|
}
|
|
|
|
#ifdef KMODE_RNG
|
|
|
|
void
|
|
rc4_safe_np(
|
|
IN void *pContext,
|
|
IN unsigned int Entry,
|
|
IN unsigned int cb,
|
|
IN void *pv
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Non-paged, high IRQL version of rc4_safe()
|
|
|
|
--*/
|
|
{
|
|
RC4_SAFE_ARRAY *pRC4SafeArray;
|
|
RC4_SAFE *pRC4Safe;
|
|
|
|
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
|
|
|
|
// NOTE:
|
|
// we ignore the Entry parameter.
|
|
// future consideration may be ignore only when Entry == 0xffffffff
|
|
// but, this would only be perf penalty currently.
|
|
//
|
|
|
|
Entry = KeGetCurrentProcessorNumber();
|
|
|
|
pRC4Safe = pRC4SafeArray->Array[ Entry ];
|
|
|
|
rc4( &(pRC4Safe->rc4key), cb, (unsigned char*) pv );
|
|
pRC4Safe->BytesUsed += cb;
|
|
|
|
}
|
|
|
|
#endif // KMODE_RNG
|
|
|
|
void
|
|
rc4_safe_key(
|
|
IN void *pContext,
|
|
IN unsigned int Entry,
|
|
IN unsigned int cb,
|
|
IN const void *pv
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the rc4 key identified by the Entry index.
|
|
|
|
Input key material is pv, of size cb.
|
|
|
|
--*/
|
|
{
|
|
RC4_SAFE_ARRAY *pRC4SafeArray;
|
|
RC4_SAFE *pRC4Safe;
|
|
|
|
#ifdef KMODE_RNG
|
|
KIRQL OldIrql;
|
|
|
|
PAGED_CODE();
|
|
#endif // KMODE_RNG
|
|
|
|
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
|
|
|
|
|
|
Entry &= ( pRC4SafeArray->Entries - 1 );
|
|
pRC4Safe = pRC4SafeArray->Array[ Entry ];
|
|
|
|
ENTER_LOCK( &(pRC4Safe->lock) );
|
|
|
|
rc4_key( &(pRC4Safe->rc4key), cb, (unsigned char*) pv );
|
|
pRC4Safe->BytesUsed = 0;
|
|
|
|
LEAVE_LOCK( &(pRC4Safe->lock) );
|
|
|
|
}
|
|
|
|
#ifdef KMODE_RNG
|
|
|
|
void
|
|
rc4_safe_key_np(
|
|
IN void *pContext,
|
|
IN unsigned int Entry,
|
|
IN unsigned int cb,
|
|
IN const void *pv
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Non-paged, high-IRQL version of rc4_safe_key()
|
|
|
|
--*/
|
|
{
|
|
RC4_SAFE_ARRAY *pRC4SafeArray;
|
|
RC4_SAFE *pRC4Safe;
|
|
|
|
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
|
|
|
|
if( Entry == 0xffffffff ) {
|
|
Entry = KeGetCurrentProcessorNumber();
|
|
}
|
|
|
|
pRC4Safe = pRC4SafeArray->Array[ Entry ];
|
|
|
|
rc4_key( &pRC4Safe->rc4key, cb, (unsigned char*) pv );
|
|
pRC4Safe->BytesUsed = 0;
|
|
|
|
}
|
|
|
|
#endif // KMODE_RNG
|
|
|
|
unsigned int
|
|
rc4_safe_startup(
|
|
IN OUT void **ppContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
key selector: choose a thread safe key.
|
|
outputs key identifier and bytes used with key.
|
|
|
|
--*/
|
|
{
|
|
RC4_SAFE_ARRAY *pRC4SafeArray;
|
|
RC4_SAFE *pRC4Safe;
|
|
unsigned int Entries;
|
|
unsigned int i;
|
|
|
|
#ifdef KMODE_RNG
|
|
PAGED_CODE();
|
|
#endif // KMODE_RNG
|
|
|
|
Entries = RC4_SAFE_ENTRIES;
|
|
|
|
|
|
pRC4SafeArray = (RC4_SAFE_ARRAY *)ALLOC_NP(
|
|
sizeof(RC4_SAFE_ARRAY) +
|
|
(Entries * sizeof(RC4_SAFE *)) +
|
|
(Entries * sizeof(RC4_SAFE))
|
|
);
|
|
|
|
if( pRC4SafeArray == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
pRC4SafeArray->Entries = Entries;
|
|
pRC4SafeArray->Mode = Paged;
|
|
|
|
pRC4Safe = (RC4_SAFE*) ((unsigned char*)pRC4SafeArray +
|
|
sizeof(RC4_SAFE_ARRAY) +
|
|
(Entries * sizeof(RC4_SAFE*))
|
|
);
|
|
|
|
for ( i = 0 ; i < Entries ; i++, pRC4Safe++ ) {
|
|
|
|
pRC4SafeArray->Array[i] = pRC4Safe;
|
|
|
|
INIT_LOCK( &(pRC4Safe->lock) );
|
|
|
|
//
|
|
// cause client to re-key for each initialized array entry.
|
|
//
|
|
|
|
pRC4Safe->BytesUsed = 0xffffffff;
|
|
}
|
|
|
|
*ppContext = pRC4SafeArray;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef KMODE_RNG
|
|
|
|
unsigned int
|
|
rc4_safe_startup_np(
|
|
IN OUT void **ppContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Non-Paged, high IRQL version of rc4_safe_startup()
|
|
|
|
--*/
|
|
{
|
|
RC4_SAFE_ARRAY *pRC4SafeArray;
|
|
RC4_SAFE *pRC4Safe;
|
|
unsigned int Entries;
|
|
unsigned int i;
|
|
|
|
//
|
|
// get installed processor count.
|
|
//
|
|
|
|
Entries = KeNumberProcessors;
|
|
|
|
pRC4SafeArray = (RC4_SAFE_ARRAY *)ALLOC_NP(
|
|
sizeof(RC4_SAFE_ARRAY) +
|
|
(Entries * sizeof(RC4_SAFE *)) +
|
|
(Entries * sizeof(RC4_SAFE))
|
|
);
|
|
|
|
if( pRC4SafeArray == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
pRC4SafeArray->Entries = Entries;
|
|
pRC4SafeArray->Mode = NonPaged;
|
|
|
|
pRC4Safe = (RC4_SAFE*) ((unsigned char*)pRC4SafeArray +
|
|
sizeof(RC4_SAFE_ARRAY) +
|
|
(Entries * sizeof(RC4_SAFE*))
|
|
);
|
|
|
|
for ( i = 0 ; i < Entries ; i++, pRC4Safe++ ) {
|
|
|
|
pRC4SafeArray->Array[i] = pRC4Safe;
|
|
|
|
//
|
|
// cause client to re-key for each initialized array entry.
|
|
//
|
|
|
|
pRC4Safe->BytesUsed = 0xffffffff;
|
|
}
|
|
|
|
*ppContext = pRC4SafeArray;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
void
|
|
rc4_safe_shutdown(
|
|
IN void *pContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
rc4_safe_shutdown called to free resources associated with internal structures.
|
|
typically called during DLL_PROCESS_DETACH type shutdown code.
|
|
|
|
--*/
|
|
{
|
|
|
|
RC4_SAFE_ARRAY *SafeArray;
|
|
unsigned int SafeEntries;
|
|
unsigned int i;
|
|
|
|
#ifdef KMODE_RNG
|
|
PAGED_CODE();
|
|
#endif // KMODE_RNG
|
|
|
|
|
|
SafeArray = (RC4_SAFE_ARRAY*)pContext;
|
|
SafeEntries = SafeArray->Entries;
|
|
|
|
for ( i = 0 ; i < SafeEntries ; i++ ) {
|
|
RC4_SAFE *pRC4Safe = SafeArray->Array[i];
|
|
|
|
DELETE_LOCK( &(pRC4Safe->lock) );
|
|
}
|
|
|
|
FREE( pContext );
|
|
}
|
|
|
|
|
|
|
|
#ifdef KMODE_RNG
|
|
|
|
void
|
|
rc4_safe_shutdown_np(
|
|
IN void *pContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Non-paged, high-IRQL version of rc4_safe_shutdown()
|
|
|
|
--*/
|
|
{
|
|
FREE( pContext );
|
|
}
|
|
|
|
#endif
|
|
|