cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
int_debug.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Internal Debugging API *
4 * Copyright Peter Gutmann 1992-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "stream.h"
11 #else
12  #include "crypt.h"
13  #include "io/stream.h"
14 #endif /* Compiler-specific includes */
15 
16 /* The following functions are intended purely for diagnostic purposes
17  during development. They perform minimal checking (for example using
18  assertions rather than returning error codes, since the calling code
19  can't hardwire in tests for their return status), and should only
20  be used with a debugger */
21 
22 #ifndef NDEBUG
23 
24 /* Older versions of the WinCE runtime don't provide complete stdio
25  support so we have to emulate it using wrappers for native
26  functions */
27 
28 #if defined( __WINCE__ ) && _WIN32_WCE < 500
29 
30 int remove( const char *pathname )
31  {
32  wchar_t wcBuffer[ _MAX_PATH + 1 ];
33 
34  mbstowcs( wcBuffer, pathname, strlen( pathname ) + 1 );
35  DeleteFile( wcBuffer );
36 
37  return( 0 );
38  }
39 #endif /* WinCE < 5.x doesn't have remove() */
40 
41 /* Output text to the debug console or whatever the OS'es nearest equivalent
42  is. If possible this is normally done via a macro in a header file that
43  remaps the debug-output macros to the appropriate function but Windows
44  only provides a puts()-equivalent and not a printf()-equivalent and under
45  Unix we need to send the output to stderr which can't easily be done in a
46  macro. In addition OutputDebugString() under Windows isn't very reliable,
47  it's implemented using a 4kB chunk of shared memory DBWIN_BUFFER
48  controlled by a mutex DBWinMutex and two events DBWIN_BUFFER_READY and
49  DBWIN_DATA_READY. OutputDebugString() waits for exclusive access to
50  DBWinMutex, maps in DBWIN_BUFFER, waits for DBWIN_BUFFER_READY to be
51  signalled, copies in up to 4K of data, fires DBWIN_DATA_READY, and
52  releases the mutex. If any of these operations fail, the call to
53  OutputDebugString() is treated as a no-op and we never see the output.
54  On the receiving side the debugger performs the obvious actions to
55  receive the data. Beyond this Rube-Goldberg message-passing mechanism
56  there are also problems with permissions on the mutex and the way the
57  DACL for it has been tweaked in almost every OS release. The end result
58  of this is that data sent to OutputDebugString() may be randomly lost
59  if other threads are also sending data to it (e.g. Visual Studio as part
60  of its normal chattiness about modules and threads being loaded/started/
61  unloaded/stopped/etc). The only way to avoid this is to step through
62  the code using OutputDebugString() so that enough delay is inserted to
63  allow other callers and the code being debugged to get out of each
64  others' hair */
65 
66 #if defined( __WIN32__ )
67 
68 int debugPrintf( const char *format, ... )
69  {
70  va_list argPtr;
71  char buffer[ 1024 ];
72  int length;
73 
74  va_start( argPtr, format );
75 
76 #if VC_GE_2005( _MSC_VER )
77  length = vsnprintf_s( buffer, 1024, _TRUNCATE, format, argPtr );
78 #else
79  length = vsprintf( buffer, format, argPtr );
80 #endif /* VC++ 2005 or newer */
81  va_end( argPtr );
82  OutputDebugString( buffer );
83 
84  return( length );
85  }
86 
87 #elif defined( __UNIX__ )
88 
89 #include <stdarg.h> /* Needed for va_list */
90 
91 int debugPrintf( const char *format, ... )
92  {
93  va_list argPtr;
94  int length;
95 
96  va_start( argPtr, format );
97  length = vfprintf( stderr, format, argPtr );
98  va_end( argPtr );
99 
100  return( length );
101  }
102 #endif /* OS-specific debug output functions */
103 
104 /* Dump a PDU to disk */
105 
106 STDC_NONNULL_ARG( ( 1, 2 ) ) \
107 static void buildFilePath( IN_STRING const char *fileName,
108  OUT_BUFFER_FIXED( 1024 ) char *filenameBuffer )
109  {
110  int i;
111 
112  /* Check whether the filename appears to be an absolute path */
113  for( i = 0; fileName[ i ] != '\0'; i++ )
114  {
115  if( fileName[ i ] == ':' || fileName[ i ] == '/' )
116  break;
117  }
118  if( fileName[ i ] == '\0' )
119  {
120  /* It's a relative path, put the file in the temp directory */
121 #if defined( __WIN32__ )
122  GetTempPath( 512, filenameBuffer );
123 #else
124  strlcpy_s( filenameBuffer, 1024, "/tmp/" );
125 #endif /* __WIN32__ */
126  }
127  strlcat_s( filenameBuffer, 1024, fileName );
128  strlcat_s( filenameBuffer, 1024, ".der" );
129  }
130 
131 STDC_NONNULL_ARG( ( 1, 2 ) ) \
132 void debugDumpFile( IN_STRING const char *fileName,
133  IN_BUFFER( dataLength ) const void *data,
134  IN_LENGTH_SHORT const int dataLength )
135  {
136  FILE *filePtr;
137  char filenameBuffer[ 1024 ];
138  int count = DUMMY_INIT;
139 
140  assert( isReadPtr( fileName, 2 ) );
141  assert( isReadPtr( data, dataLength ) );
142 
143  buildFilePath( fileName, filenameBuffer );
144 #ifdef __STDC_LIB_EXT1__
145  if( fopen_s( &filePtr, filenameBuffer, "wb" ) != 0 )
146  filePtr = NULL;
147 #else
148  filePtr = fopen( filenameBuffer, "wb" );
149 #endif /* __STDC_LIB_EXT1__ */
150  assert( filePtr != NULL );
151  if( filePtr == NULL )
152  return;
153  if( dataLength > 0 )
154  {
155  count = fwrite( data, 1, dataLength, filePtr );
156  assert( count == dataLength );
157  }
158  fclose( filePtr );
159  if( dataLength > 0 && count < dataLength )
160  ( void ) remove( filenameBuffer );
161  }
162 
164 void debugDumpFileCert( IN_STRING const char *fileName,
166  {
168  FILE *filePtr;
169  BYTE certData[ 2048 ];
170  char filenameBuffer[ 1024 ];
171  int count = DUMMY_INIT, status;
172 
173  assert( isReadPtr( fileName, 2 ) );
174  assert( isHandleRangeValid( iCryptCert ) );
175 
176  buildFilePath( fileName, filenameBuffer );
177 #ifdef __STDC_LIB_EXT1__
178  if( fopen_s( &filePtr, filenameBuffer, "wb" ) != 0 )
179  filePtr = NULL;
180 #else
181  filePtr = fopen( filenameBuffer, "wb" );
182 #endif /* __STDC_LIB_EXT1__ */
183  assert( filePtr != NULL );
184  if( filePtr == NULL )
185  return;
186  setMessageData( &msgData, certData, 2048 );
187  status = krnlSendMessage( iCryptCert, IMESSAGE_CRT_EXPORT, &msgData,
189  if( cryptStatusOK( status ) )
190  {
191  count = fwrite( msgData.data, 1, msgData.length, filePtr );
192  assert( count == msgData.length );
193  }
194  fclose( filePtr );
195  if( cryptStatusError( status ) || count < msgData.length )
196  ( void ) remove( filenameBuffer );
197  }
198 
199 /* Create a hex dump of the first n bytes of a buffer along with the length
200  and a checksum of the entire buffer, used to output a block of hex data
201  along with checksums for debugging things like client/server sessions
202  where it can be used to detect data corruption. The use of a memory
203  buffer is to allow the hex dump to be performed from multiple threads
204  without them fighting over stdout */
205 
206 STDC_NONNULL_ARG( ( 1, 2 ) ) \
207 void debugDumpHex( IN_STRING const char *prefixString,
208  IN_BUFFER( dataLength ) const void *data,
209  IN_LENGTH_SHORT const int dataLength )
210  {
211  char dumpBuffer[ 128 ];
212  int offset, i, j;
213 
214  offset = sprintf_s( dumpBuffer, 128, "%3s %4d %04X ", prefixString,
215  dataLength, checksumData( data, dataLength ) );
216  for( i = 0; i < dataLength; i += 16 )
217  {
218  const int innerLen = min( dataLength - i, 16 );
219 
220  if( i > 0 )
221  {
222  offset = sprintf_s( dumpBuffer, 128, "%3s ",
223  prefixString );
224  }
225  for( j = 0; j < innerLen; j++ )
226  {
227  offset += sprintf_s( dumpBuffer + offset, 128 - offset, "%02X ",
228  ( ( BYTE * ) data )[ i + j ] );
229  }
230  for( ; j < 16; j++ )
231  offset += sprintf_s( dumpBuffer + offset, 128 - offset, " " );
232  for( j = 0; j < innerLen; j++ )
233  {
234  const BYTE ch = ( ( BYTE * ) data )[ i + j ];
235 
236  offset += sprintf_s( dumpBuffer + offset, 128 - offset, "%c",
237  isprint( ch ) ? ch : '.' );
238  }
239  strcpy_s( dumpBuffer + offset, 128 - offset, "\n" );
240  DEBUG_OUT( dumpBuffer );
241  }
242 
243 #if !defined( __WIN32__ ) || defined( __WINCE__ ) || defined( __ECOS__ )
244  fflush( stdout );
245 #endif /* Systems where output doesn't to go stdout */
246  }
247 
248 /* Variants of debugDumpHex() that only output the raw hex data, for use in
249  conjunction with DEBUG_PRINT() to output other information about the
250  data */
251 
252 STDC_NONNULL_ARG( ( 1 ) ) \
253 void debugDumpData( IN_BUFFER( dataLength ) const void *data,
254  IN_LENGTH_SHORT const int dataLength )
255  {
256  char dumpBuffer[ 128 ];
257  int offset, i, j;
258 
259  for( i = 0; i < dataLength; i += 16 )
260  {
261  const int innerLen = min( dataLength - i, 16 );
262 
263  offset = sprintf_s( dumpBuffer, 128, "%04d: ", i );
264  for( j = 0; j < innerLen; j++ )
265  {
266  offset += sprintf_s( dumpBuffer + offset, 128 - offset, "%02X ",
267  ( ( BYTE * ) data )[ i + j ] );
268  }
269  for( ; j < 16; j++ )
270  offset += sprintf_s( dumpBuffer + offset, 128 - offset, " " );
271  for( j = 0; j < innerLen; j++ )
272  {
273  const BYTE ch = ( ( BYTE * ) data )[ i + j ];
274 
275  offset += sprintf_s( dumpBuffer + offset, 128 - offset, "%c",
276  isprint( ch ) ? ch : '.' );
277  }
278  strcpy_s( dumpBuffer + offset, 128 - offset, "\n" );
279  DEBUG_OUT( dumpBuffer );
280  }
281 
282 #if !defined( __WIN32__ ) || defined( __WINCE__ ) || defined( __ECOS__ )
283  fflush( stdout );
284 #endif /* Systems where output doesn't to go stdout */
285  }
286 
287 STDC_NONNULL_ARG( ( 1 ) ) \
288 void debugDumpStream( INOUT /*STREAM*/ void *streamPtr,
289  IN_LENGTH const int position,
290  IN_LENGTH const int length )
291  {
293  BYTE *dataPtr;
294  int status;
295 
296  /* In some cases we may be asked to dump zero-length packets, since
297  we're being called unconditionally (the caller can't put conditional
298  code into a debug macro) we just turn the call into a no-op */
299  if( length <= 0 )
300  return;
301 
302  status = sMemGetDataBlockAbs( stream, position, ( void ** ) &dataPtr,
303  length );
304  if( cryptStatusError( status ) )
305  return;
306  ANALYSER_HINT( dataPtr != NULL );
307  debugDumpData( dataPtr, length );
308  }
309 
310 /* Support function used with streams to pull data bytes out of the stream,
311  allowing type and content data to be dumped with DEBUG_PRINT() */
312 
314 int debugGetStreamByte( INOUT /*STREAM*/ void *streamPtr,
315  IN_LENGTH const int position )
316  {
317  STREAM *stream = streamPtr;
318  BYTE *dataPtr;
319  int status;
320 
321  status = sMemGetDataBlockAbs( stream, position, ( void ** ) &dataPtr, 1 );
322  if( cryptStatusError( status ) )
323  return( 0 );
324  return( byteToInt( *dataPtr ) );
325  }
326 #endif /* !NDEBUG */