1 /****************************************************************************
2 *																			*
3 *					cryptlib Internal Memory Management API					*
4 *						Copyright Peter Gutmann 1992-2007					*
5 *																			*
6 ****************************************************************************/
7 
8 #include <stdarg.h>
9 #if defined( INC_ALL )
10   #include "crypt.h"
11 #else
12   #include "crypt.h"
13 #endif /* Compiler-specific includes */
14 
15 /****************************************************************************
16 *																			*
17 *						Dynamic Buffer Management Routines					*
18 *																			*
19 ****************************************************************************/
20 
21 /* Dynamic buffer management functions.  When reading variable-length
22    object data we can usually fit the data into a small fixed-length buffer
23    but occasionally we have to cope with larger data amounts that require a
24    dynamically-allocated buffer.  The following routines manage this
25    process, dynamically allocating and freeing a larger buffer if required */
26 
27 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
getDynData(OUT DYNBUF * dynBuf,IN_HANDLE const CRYPT_HANDLE cryptHandle,IN_MESSAGE const MESSAGE_TYPE message,IN_INT const int messageParam)28 static int getDynData( OUT DYNBUF *dynBuf,
29 					   IN_HANDLE const CRYPT_HANDLE cryptHandle,
30 					   IN_MESSAGE const MESSAGE_TYPE message,
31 					   IN_INT const int messageParam )
32 	{
33 	MESSAGE_DATA msgData;
34 	void *dataPtr = NULL;
35 	int status;
36 
37 	assert( isWritePtr( dynBuf, sizeof( DYNBUF ) ) );
38 
39 	REQUIRES( isHandleRangeValid( cryptHandle ) );
40 	REQUIRES( ( message == IMESSAGE_GETATTRIBUTE_S && \
41 				( isAttribute( messageParam ) || \
42 				  isInternalAttribute( messageParam ) ) ) || \
43 			  ( message == IMESSAGE_CRT_EXPORT && \
44 		 		( messageParam == CRYPT_CERTFORMAT_CERTIFICATE || \
45 				  messageParam == CRYPT_CERTFORMAT_CERTCHAIN ) ) );
46 
47 	/* Clear return values.  Note that we don't use the usual memset() to
48 	   clear the value since the structure contains the storage for the
49 	   fixed-size portion of the buffer appended to it, and using memset()
50 	   to clear that is just unnecessary overhead */
51 	dynBuf->data = dynBuf->dataBuffer;
52 	dynBuf->length = 0;
53 
54 	/* Get the data from the object */
55 	setMessageData( &msgData, NULL, 0 );
56 	status = krnlSendMessage( cryptHandle, message, &msgData, messageParam );
57 	if( cryptStatusError( status ) )
58 		return( status );
59 	if( msgData.length > DYNBUF_SIZE )
60 		{
61 		/* The data is larger than the built-in buffer size, dynamically
62 		   allocate a larger buffer */
63 		if( ( dataPtr = clDynAlloc( "getDynData", msgData.length ) ) == NULL )
64 			return( CRYPT_ERROR_MEMORY );
65 		msgData.data = dataPtr;
66 		status = krnlSendMessage( cryptHandle, message, &msgData,
67 								  messageParam );
68 		if( cryptStatusError( status ) )
69 			{
70 			clFree( "getDynData", dataPtr );
71 			return( status );
72 			}
73 		dynBuf->data = dataPtr;
74 		}
75 	else
76 		{
77 		/* The data will fit into the built-in buffer, read it directly into
78 		   the buffer */
79 		msgData.data = dynBuf->data;
80 		status = krnlSendMessage( cryptHandle, message, &msgData,
81 								  messageParam );
82 		if( cryptStatusError( status ) )
83 			return( status );
84 		}
85 	dynBuf->length = msgData.length;
86 
87 	return( CRYPT_OK );
88 	}
89 
90 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
dynCreate(OUT DYNBUF * dynBuf,IN_HANDLE const CRYPT_HANDLE cryptHandle,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeType)91 int dynCreate( OUT DYNBUF *dynBuf,
92 			   IN_HANDLE const CRYPT_HANDLE cryptHandle,
93 			   IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeType )
94 	{
95 	assert( isWritePtr( dynBuf, sizeof( DYNBUF ) ) );
96 
97 	REQUIRES( isHandleRangeValid( cryptHandle ) );
98 	REQUIRES( isAttribute( attributeType ) || \
99 			  isInternalAttribute( attributeType ) );
100 
101 	return( getDynData( dynBuf, cryptHandle, IMESSAGE_GETATTRIBUTE_S,
102 						attributeType ) );
103 	}
104 
105 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
dynCreateCert(OUT DYNBUF * dynBuf,IN_HANDLE const CRYPT_HANDLE cryptHandle,IN_ENUM (CRYPT_CERTFORMAT)const CRYPT_CERTFORMAT_TYPE formatType)106 int dynCreateCert( OUT DYNBUF *dynBuf,
107 				   IN_HANDLE const CRYPT_HANDLE cryptHandle,
108 				   IN_ENUM( CRYPT_CERTFORMAT ) \
109 				   const CRYPT_CERTFORMAT_TYPE formatType )
110 	{
111 	assert( isWritePtr( dynBuf, sizeof( DYNBUF ) ) );
112 
113 	REQUIRES( isHandleRangeValid( cryptHandle ) );
114 	REQUIRES( formatType == CRYPT_CERTFORMAT_CERTIFICATE || \
115 			  formatType == CRYPT_CERTFORMAT_CERTCHAIN );
116 
117 	return( getDynData( dynBuf, cryptHandle, IMESSAGE_CRT_EXPORT,
118 						formatType ) );
119 	}
120 
121 STDC_NONNULL_ARG( ( 1 ) ) \
dynDestroy(INOUT DYNBUF * dynBuf)122 void dynDestroy( INOUT DYNBUF *dynBuf )
123 	{
124 	assert( isWritePtr( dynBuf, sizeof( DYNBUF ) ) );
125 	assert( isWritePtr( dynBuf->data, dynBuf->length ) );
126 
127 	REQUIRES_V( dynBuf->data != NULL );
128 	REQUIRES_V( dynBuf->length > 0 && dynBuf->length < MAX_BUFFER_SIZE );
129 
130 	zeroise( dynBuf->data, dynBuf->length );
131 	if( dynBuf->data != dynBuf->dataBuffer )
132 		clFree( "dynDestroy", dynBuf->data );
133 	dynBuf->data = NULL;
134 	dynBuf->length = 0;
135 	}
136 
137 /****************************************************************************
138 *																			*
139 *						Memory Pool Management Routines						*
140 *																			*
141 ****************************************************************************/
142 
143 /* Memory pool management functions.  When allocating many small blocks of
144    memory, especially in resource-constrained systems, it's better if we pre-
145    allocate a small memory pool ourselves and grab chunks of it as required,
146    falling back to dynamically allocating memory later on if we exhaust the
147    pool.  The following functions implement the custom memory pool
148    management.  Usage is:
149 
150 	initMemPool( &memPoolState, storage, storageSize );
151 	newItem = getMemPool( &memPoolState, newItemSize ) */
152 
153 typedef struct {
154 	BUFFER( storageSize, storagePos )
155 	void *storage;					/* Memory pool */
156 	int storageSize, storagePos;	/* Current usage and total size of pool */
157 	} MEMPOOL_INFO;
158 
159 CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
sanityCheck(const MEMPOOL_INFO * state)160 static BOOLEAN sanityCheck( const MEMPOOL_INFO *state )
161 	{
162 	/* Make sure that the overall pool size information is in order */
163 	if( state->storageSize < 64 || \
164 		state->storageSize >= MAX_INTLENGTH_SHORT )
165 		return( FALSE );
166 
167 	/* Make sure that the pool allocation information is in order */
168 	if( state->storagePos < 0 || \
169 		state->storagePos >= MAX_INTLENGTH_SHORT || \
170 		state->storagePos > state->storageSize )
171 		return( FALSE );
172 
173 	return( TRUE );
174 	}
175 
176 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
177 int initMemPool( OUT void *statePtr,
178 				  IN_BUFFER( memPoolSize ) void *memPool,
179 				  IN_LENGTH_SHORT_MIN( 64 ) const int memPoolSize )
180 	{
181 	MEMPOOL_INFO *state = ( MEMPOOL_INFO * ) statePtr;
182 
183 	assert( isWritePtr( state, sizeof( MEMPOOL_INFO ) ) );
184 	assert( isWritePtr( memPool, memPoolSize ) );
185 
186 #if defined( __WIN32__ ) && defined( _MSC_VER )
187 	#pragma warning( disable: 4127 )	/* Needed for sizeof() in check */
188 #endif /* VC++ */
189 	REQUIRES( sizeof( MEMPOOL_STATE ) >= sizeof( MEMPOOL_INFO ) );
190 	REQUIRES( memPoolSize >= 64 && memPoolSize < MAX_INTLENGTH_SHORT );
191 #if defined( __WIN32__ ) && defined( _MSC_VER )
192 	#pragma warning( 4: 4127 )
193 #endif /* VC++ */
194 
195 	memset( state, 0, sizeof( MEMPOOL_INFO ) );
196 	state->storage = memPool;
197 	state->storageSize = memPoolSize;
198 
199 	return( CRYPT_OK );
200 	}
201 
202 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1 ) ) \
getMemPool(INOUT void * statePtr,IN_LENGTH_SHORT const int size)203 void *getMemPool( INOUT void *statePtr, IN_LENGTH_SHORT const int size )
204 	{
205 	MEMPOOL_INFO *state = ( MEMPOOL_INFO * ) statePtr;
206 	BYTE *allocPtr;
207 	const int allocSize = roundUp( size, sizeof( int ) );
208 
209 	assert( isWritePtr( state, sizeof( MEMPOOL_INFO ) ) );
210 	assert( isWritePtr( state->storage, state->storageSize ) );
211 
212 	REQUIRES_N( size > 0 && size < MAX_INTLENGTH_SHORT );
213 	REQUIRES_N( allocSize >= sizeof( int ) && \
214 				allocSize < MAX_INTLENGTH_SHORT );
215 	REQUIRES_N( sanityCheck( state ) );
216 
217 	/* If we can't satisfy the request from the memory pool we have to
218 	   allocate the memory block dynamically */
219 	if( state->storagePos + allocSize > state->storageSize )
220 		return( clDynAlloc( "getMemPool", size ) );
221 
222 	/* We can satisfy the request from the pool:
223 
224 	 memPool
225 		|
226 		v		 <- size -->
227 		+-------+-----------+-------+
228 		|		|			|		|
229 		+-------+-----------+-------+
230 				^			^
231 				|			|
232 			storagePos	storagePos' */
233 	allocPtr = ( BYTE * ) state->storage + state->storagePos;
234 	state->storagePos += allocSize;
235 	ENSURES_N( sanityCheck( state ) );
236 
237 	return( allocPtr );
238 	}
239 
240 STDC_NONNULL_ARG( ( 1, 2 ) ) \
freeMemPool(INOUT void * statePtr,IN void * memblock)241 void freeMemPool( INOUT void *statePtr, IN void *memblock )
242 	{
243 	MEMPOOL_INFO *state = ( MEMPOOL_INFO * ) statePtr;
244 
245 	assert( isWritePtr( state, sizeof( MEMPOOL_INFO ) ) );
246 	assert( isWritePtr( state->storage, state->storageSize ) );
247 
248 	REQUIRES_V( sanityCheck( state ) );
249 
250 	/* If the memory block to free lies within the pool, there's nothing to
251 	   do */
252 	if( memblock >= state->storage && \
253 		memblock < ( void * ) ( ( BYTE * ) state->storage + \
254 										   state->storageSize ) )
255 		return;
256 
257 	/* It's outside the pool and therefore dynamically allocated, free it */
258 	clFree( "freeMemPool", memblock );
259 	}
260 
261 /****************************************************************************
262 *																			*
263 *							Debugging Malloc Support						*
264 *																			*
265 ****************************************************************************/
266 
267 /* Debugging malloc() that dumps memory usage diagnostics to stdout.  Note
268    that these functions are only intended to be used during interactive
269    debugging sessions since they throw exceptions under error conditions
270    rather than returning an error status (the fact that they dump
271    diagnostics to stdout during operation should be a clue as to their
272    intended status and usage) */
273 
274 #ifdef CONFIG_DEBUG_MALLOC
275 
276 #ifdef __WIN32__
277   #include <direct.h>
278 #endif /* __WIN32__ */
279 
280 #ifdef __WINCE__
281 
282 CHECK_RETVAL_RANGE( 0, MAX_INTLENGTH_STRING ) STDC_NONNULL_ARG( ( 1 ) ) \
wcPrintf(FORMAT_STRING const char * format,...)283 static int wcPrintf( FORMAT_STRING const char *format, ... )
284 	{
285 	wchar_t wcBuffer[ 1024 + 8 ];
286 	char buffer[ 1024 + 8 ];
287 	va_list argPtr;
288 	int length;
289 
290 	va_start( argPtr, format );
291 	length = vsprintf_s( buffer, 1024, format, argPtr );
292 	va_end( argPtr );
293 	if( length < 1 )
294 		return( length );
295 	mbstowcs( wcBuffer, buffer, length + 1 );
296 	NKDbgPrintfW( wcBuffer );
297 
298 	return( length );
299 	}
300 
301 #define printf		wcPrintf
302 
303 #endif /* __WINCE__ */
304 
305 static int clAllocIndex = 0;
306 
clAllocFn(const char * fileName,const char * fnName,const int lineNo,size_t size)307 void *clAllocFn( const char *fileName, const char *fnName,
308 				 const int lineNo, size_t size )
309 	{
310 #ifdef CONFIG_MALLOCTEST
311 	static int mallocCount = 0, mallocFailCount = 0;
312 #endif /* CONFIG_MALLOCTEST */
313 #if defined( __WIN32__ ) || defined( __UNIX__ )
314 	char buffer[ 512 + 8 ];
315 #endif /* __WIN32__ || __UNIX__ */
316 	BYTE *memPtr;
317 	int length;
318 
319 	assert( fileName != NULL );
320 	assert( fnName != NULL );
321 	assert( lineNo > 0 );
322 	assert( size > 0 && size < MAX_INTLENGTH );
323 
324 	/* Strip off the leading path components if we can to reduce the amount
325 	   of noise in the output */
326 #if defined( __WIN32__ ) || defined( __UNIX__ )
327 	if( getcwd( buffer, 512 ) != NULL )
328 		{
329 		const int pathLen = strlen( buffer ) + 1;	/* Leading path + '/' */
330 
331 		assert( pathLen < strlen( fileName ) );
332 		fileName += pathLen;
333 		}
334 #endif /* __WIN32__ || __UNIX__ */
335 
336 	length = DEBUG_PRINT( "ALLOC: %s:%s:%d", fileName, fnName, lineNo );
337 	while( length < 46 )
338 		{
339 		putchar( ' ' );
340 		length++;
341 		}
342 	DEBUG_PRINT( " %4d - %d bytes.\n", clAllocIndex, size );
343 #ifdef CONFIG_MALLOCTEST
344 	/* If we've exceeded the allocation count, make the next attempt to
345 	   allocate memory fail */
346 	if( mallocCount >= mallocFailCount )
347 		{
348 		mallocCount = 0;
349 		mallocFailCount++;
350 
351 		return( NULL );
352 		}
353 	mallocCount++;
354 #endif /* CONFIG_MALLOCTEST */
355 	if( ( memPtr = malloc( size + sizeof( LONG ) ) ) == NULL )
356 		return( NULL );
357 	mputLong( memPtr, clAllocIndex );	/* Implicit memPtr += sizeof( LONG ) */
358 	clAllocIndex++;
359 	return( memPtr );
360 	}
361 
clFreeFn(const char * fileName,const char * fnName,const int lineNo,void * memblock)362 void clFreeFn( const char *fileName, const char *fnName,
363 			   const int lineNo, void *memblock )
364 	{
365 #if defined( __WIN32__ ) || defined( __UNIX__ )
366 	char buffer[ 512 + 8 ];
367 #endif /* __WIN32__ || __UNIX__ */
368 	BYTE *memPtr = ( BYTE * ) memblock - sizeof( LONG );
369 	int index, length;
370 
371 	assert( fileName != NULL );
372 	assert( fnName != NULL );
373 	assert( lineNo > 0 );
374 
375 	/* Strip off the leading path components if we can to reduce the amount
376 	   of noise in the output */
377 #if defined( __WIN32__ ) || defined( __UNIX__ )
378 	if( getcwd( buffer, 512 ) != NULL )
379 		{
380 		const int pathLen = strlen( buffer ) + 1;	/* Leading path + '/' */
381 
382 		assert( pathLen < strlen( fileName ) );
383 		fileName += pathLen;
384 		}
385 #endif /* __WIN32__ || __UNIX__ */
386 
387 	index = mgetLong( memPtr );
388 	memPtr -= sizeof( LONG );		/* mgetLong() changes memPtr */
389 	length = DEBUG_PRINT( "FREE : %s:%s:%d", fileName, fnName, lineNo );
390 	while( length < 46 )
391 		{
392 		putchar( ' ' );
393 		length++;
394 		}
395 	DEBUG_PRINT( " %4d.\n", index );
396 	free( memPtr );
397 	}
398 #endif /* CONFIG_DEBUG_MALLOC */
399 
400 /* Fault-testing malloc() that fails after a given number of allocations */
401 
402 #ifdef CONFIG_FAULT_MALLOC
403 
404 static int currentAllocCount = 0, failAllocCount = 0;
405 static BOOLEAN allocFailed = FALSE;
406 
clFaultAllocSetCount(const int number)407 void clFaultAllocSetCount( const int number )
408 	{
409 	currentAllocCount = 0;
410 	failAllocCount = number;
411 	allocFailed = FALSE;
412 	}
413 
clFaultAllocFn(const char * fileName,const char * fnName,const int lineNo,size_t size)414 void *clFaultAllocFn( const char *fileName, const char *fnName,
415 					  const int lineNo, size_t size )
416 	{
417 	/* If we've failed an allocation we probably shouldn't get here again,
418 	   however if we're running a multithreaded init then the second thread
419 	   could try and allocate memory after the first one has failed */
420 	if( allocFailed )
421 		{
422 #ifdef __WIN32__
423 		DEBUG_PRINT(( "\n<<< Further allocation call from thread %X after "
424 					  "previous call failed, called from %s line %d in "
425 					  "%s.>>>\n", GetCurrentThreadId(), fnName, lineNo,
426 					  fileName ));
427 #else
428 		DEBUG_PRINT(( "\n<<< Further allocation call after previous call "
429 					  "failed, called from %s line %d in %s.>>>\n", fnName,
430 					  lineNo, fileName ));
431 #endif /* __WIN32__  */
432 		if( failAllocCount < 15 )
433 			{
434 			DEBUG_PRINT(( "<<<  (This could be because of a multithreaded "
435 						  "init).>>>\n" ));
436 			DEBUG_PRINT(( "\n" ));
437 			}
438 		return( NULL );
439 		}
440 
441 	/* If we haven't reached the failure allocation count, return normally */
442 	if( currentAllocCount < failAllocCount )
443 		{
444 		currentAllocCount++;
445 		return( malloc( size ) );
446 		}
447 
448 	/* We've reached the failure count, fail the allocation */
449 #ifdef __WIN32__
450 	DEBUG_PRINT(( "\n<<< Failing allocation call #%d for thread %X, called "
451 				  "from %s line %d in %s.>>>\n\n", failAllocCount + 1,
452 				  GetCurrentThreadId(), fnName, lineNo, fileName ));
453 #else
454 	DEBUG_PRINT(( "\n<<< Failing at allocation call #%d, called from %s line "
455 				  "%d in %s.>>>\n\n", failAllocCount + 1, fnName, lineNo,
456 				  fileName ));
457 #endif /* __WIN32__  */
458 	allocFailed = TRUE;
459 	return( NULL );
460 	}
461 #endif /* CONFIG_FAULT_MALLOC */
462