cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
int_err.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Internal Error Reporting API *
4 * Copyright Peter Gutmann 1992-2007 *
5 * *
6 ****************************************************************************/
7 
8 #include <stdarg.h>
9 #include <stdio.h> /* Needed on some systems for macro-mapped *printf()'s */
10 #if defined( INC_ALL )
11  #include "crypt.h"
12 #else
13  #include "crypt.h"
14 #endif /* Compiler-specific includes */
15 
16 #ifdef USE_ERRMSGS
17 
18 /****************************************************************************
19 * *
20 * Utility Functions *
21 * *
22 ****************************************************************************/
23 
24 /* Check the error status and, if it's a leaked status code from a lower-
25  level call, convert it to a generic CRYPT_ERROR_FAILED. These status
26  codes should only ever occur in 'can't-occur' error situations so we only
27  warn in debug mode */
28 
29 CHECK_RETVAL_ERROR \
30 static int convertErrorStatus( IN_ERROR const int status )
31  {
32  REQUIRES( cryptStatusError( status ) );
33 
34  if( cryptArgError( status ) )
35  {
36  DEBUG_DIAG(( "Error exit was passed argError status" ));
37  assert( DEBUG_WARN );
38  return( CRYPT_ERROR_FAILED );
39  }
40 
41  return( status );
42  }
43 
44 /* Format a printf-style error string.
45 
46  In the following we can't make the third arg a NONNULL_ARG because in the
47  Arm ABI it's a scalar value */
48 
49 RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2 ) ) \
50 static BOOLEAN formatErrorString( OUT ERROR_INFO *errorInfoPtr,
51  IN_STRING const char *format,
52  IN va_list argPtr )
53  {
54  assert( isWritePtr( errorInfoPtr, sizeof( ERROR_INFO ) ) );
55  assert( isReadPtr( format, 4 ) );
56 
57  REQUIRES_B( verifyVAList( argPtr ) );
58 
59  /* Clear return value */
60  memset( errorInfoPtr, 0, sizeof( ERROR_INFO ) );
61 
62  /* This function is a bit tricky to deal with because of the
63  braindamaged behaviour of some of the underlying functions that it
64  may be mapped to. Specifically, (v)snprintf() returns the number of
65  bytes it *could* have written had it felt like it rather than how
66  many it actually wrote on non-Windows systems and an error indicator
67  with no guarantee of null-termination on Windows systems. The latter
68  isn't a problem because we both catch the error and don't require
69  null termination, the former is more problematic because it can lead
70  to a length indication that's larger than the actual buffer. To
71  handle this we explicitly check for an overflow as well as an
72  error/underflow */
73  errorInfoPtr->errorStringLength = \
74  vsprintf_s( errorInfoPtr->errorString, MAX_ERRMSG_SIZE,
75  format, argPtr );
76  if( errorInfoPtr->errorStringLength <= 0 || \
77  errorInfoPtr->errorStringLength > MAX_ERRMSG_SIZE )
78  {
79  DEBUG_DIAG(( "Invalid error string data" ));
80  assert( DEBUG_WARN );
81  setErrorString( errorInfoPtr,
82  "(Couldn't record error information)", 35 );
83 
84  return( FALSE );
85  }
86 
87  return( TRUE );
88  }
89 
90 /* Append a second error string containing further explanatory information
91  to an existing one. There's no failure/success return value for this
92  function since there's not much that we can do in the case of a failure,
93  we rely on the existing primary error string to convey as much
94  information as possible */
95 
96 STDC_NONNULL_ARG( ( 1, 2 ) ) \
97 static void appendErrorString( INOUT ERROR_INFO *errorInfoPtr,
98  IN_BUFFER( extErrorStringLength ) \
99  const char *extErrorString,
101  const int extErrorStringLength )
102  {
103  assert( isWritePtr( errorInfoPtr, sizeof( ERROR_INFO ) ) );
104  assert( isReadPtr( extErrorString, extErrorStringLength ) );
105 
106  REQUIRES_V( errorInfoPtr->errorStringLength > 0 && \
107  errorInfoPtr->errorStringLength <= MAX_ERRMSG_SIZE );
108  REQUIRES_V( extErrorStringLength > 0 && \
109  extErrorStringLength <= MAX_ERRMSG_SIZE );
110 
111  if( errorInfoPtr->errorStringLength + \
112  extErrorStringLength < MAX_ERRMSG_SIZE - 8 )
113  {
114  ENSURES_V( rangeCheck( errorInfoPtr->errorStringLength,
115  extErrorStringLength, MAX_ERRMSG_SIZE ) );
116  memcpy( errorInfoPtr->errorString + errorInfoPtr->errorStringLength,
117  extErrorString, extErrorStringLength );
118  errorInfoPtr->errorStringLength += extErrorStringLength;
119  }
120  }
121 
122 /****************************************************************************
123 * *
124 * Clear/Set/Copy Error Strings *
125 * *
126 ****************************************************************************/
127 
128 /* Set a fixed string as the error message. This is used to set a
129  predefined error string from something like a table of error messages */
130 
131 STDC_NONNULL_ARG( ( 1 ) ) \
132 void clearErrorString( OUT ERROR_INFO *errorInfoPtr )
133  {
134  assert( isWritePtr( errorInfoPtr, sizeof( ERROR_INFO ) ) );
135 
136  memset( errorInfoPtr, 0, sizeof( ERROR_INFO ) );
137  }
138 
139 STDC_NONNULL_ARG( ( 1, 2 ) ) \
140 void setErrorString( OUT ERROR_INFO *errorInfoPtr,
141  IN_BUFFER( stringLength ) const char *string,
143  {
144  int length = stringLength;
145 
146  assert( isWritePtr( errorInfoPtr, sizeof( ERROR_INFO ) ) );
147 
148  /* Clear return value */
149  memset( errorInfoPtr, 0, sizeof( ERROR_INFO ) );
150 
151  /* Since we're already in an error-handling function we don't use the
152  REQUIRES() predicate (which would result in an infinite page fault)
153  but make the sanity-checking of parameters explicit */
154  if( stringLength <= 0 || stringLength > MAX_ERRMSG_SIZE )
155  {
156  DEBUG_DIAG(( "Invalid error string data" ));
157  assert( DEBUG_WARN );
158  string = "(Couldn't record error information)";
159  length = 35;
160  }
161 
162  memcpy( errorInfoPtr->errorString, string, length );
163  errorInfoPtr->errorStringLength = length;
164  }
165 
166 /* Copy error information from a low-level state structure (for example a
167  stream) to a high-level one (for example a session or envelope) */
168 
169 STDC_NONNULL_ARG( ( 1, 2 ) ) \
170 void copyErrorInfo( OUT ERROR_INFO *destErrorInfoPtr,
171  const ERROR_INFO *srcErrorInfoPtr )
172  {
173  assert( isWritePtr( destErrorInfoPtr, sizeof( ERROR_INFO ) ) );
174  assert( isReadPtr( srcErrorInfoPtr, sizeof( ERROR_INFO ) ) );
175 
176  memset( destErrorInfoPtr, 0, sizeof( ERROR_INFO ) );
177  if( srcErrorInfoPtr->errorStringLength > 0 )
178  {
179  setErrorString( destErrorInfoPtr, srcErrorInfoPtr->errorString,
180  srcErrorInfoPtr->errorStringLength );
181  }
182  }
183 
184 /****************************************************************************
185 * *
186 * Return Extended Error Information *
187 * *
188 ****************************************************************************/
189 
190 /* Exit after recording a detailed error message. This is used by lower-
191  level code to provide more information to the caller than a basic error
192  code. Felix qui potuit rerum cognoscere causas.
193 
194  Since we're already in an error-handling function when we call these
195  functions we don't use the REQUIRES() predicate (which would result in an
196  infinite page fault) but make the sanity-checking of parameters
197  explicit */
198 
199 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 3 ) ) STDC_PRINTF_FN( 3, 4 ) \
200 int retExtFn( IN_ERROR const int status,
201  OUT ERROR_INFO *errorInfoPtr,
202  FORMAT_STRING const char *format, ... )
203  {
204  va_list argPtr;
205  const int localStatus = convertErrorStatus( status );
206 
207  assert( isWritePtr( errorInfoPtr, sizeof( ERROR_INFO ) ) );
208  assert( isReadPtr( format, 4 ) );
209 
210  REQUIRES( cryptStatusError( status ) );
211 
212  /* Clear return value */
213  memset( errorInfoPtr, 0, sizeof( ERROR_INFO ) );
214 
215  va_start( argPtr, format );
216  formatErrorString( errorInfoPtr, format, argPtr );
217  va_end( argPtr );
218 
219  return( localStatus );
220  }
221 
222 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 3 ) ) STDC_PRINTF_FN( 3, 4 ) \
223 int retExtArgFn( IN_ERROR const int status,
224  OUT ERROR_INFO *errorInfoPtr,
225  FORMAT_STRING const char *format, ... )
226  {
227  va_list argPtr;
228 
229  /* This function is identical to retExtFn() except that it doesn't trap
230  CRYPT_ARGERROR_xxx values, since they're valid return values in some
231  cases */
232 
233  assert( isWritePtr( errorInfoPtr, sizeof( ERROR_INFO ) ) );
234  assert( isReadPtr( format, 4 ) );
235 
236  REQUIRES( cryptStatusError( status ) );
237 
238  /* Clear return value */
239  memset( errorInfoPtr, 0, sizeof( ERROR_INFO ) );
240 
241  va_start( argPtr, format );
242  formatErrorString( errorInfoPtr, format, argPtr );
243  va_end( argPtr );
244 
245  return( status );
246  }
247 
248 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 4 ) ) STDC_PRINTF_FN( 4, 5 ) \
249 int retExtObjFn( IN_ERROR const int status,
250  OUT ERROR_INFO *errorInfoPtr,
251  IN_HANDLE const CRYPT_HANDLE extErrorObject,
252  FORMAT_STRING const char *format, ... )
253  {
255  va_list argPtr;
256  char extraErrorString[ MAX_ERRMSG_SIZE + 8 ];
257  const int localStatus = convertErrorStatus( status );
258  BOOLEAN errorStringOK;
259  int errorStringLength, extErrorStatus, extErrorStringLength;
260 
261  assert( isWritePtr( errorInfoPtr, sizeof( ERROR_INFO ) ) );
262  assert( isReadPtr( format, 4 ) );
263 
264  REQUIRES( cryptStatusError( status ) );
265  REQUIRES( extErrorObject == DEFAULTUSER_OBJECT_HANDLE || \
266  isHandleRangeValid( extErrorObject ) );
267 
268  /* Clear return value */
269  memset( errorInfoPtr, 0, sizeof( ERROR_INFO ) );
270 
271  /* Format the basic error string */
272  va_start( argPtr, format );
273  errorStringOK = formatErrorString( errorInfoPtr, format, argPtr );
274  va_end( argPtr );
275  if( !errorStringOK )
276  {
277  /* If we couldn't format the basic error string there's no point in
278  continuing */
279  return( localStatus );
280  }
281  errorStringLength = errorInfoPtr->errorStringLength;
282  ENSURES( errorStringLength > 0 && errorStringLength < MAX_ERRMSG_SIZE );
283 
284  /* Check whether there's any additional error information available */
285  setMessageData( &msgData, extraErrorString, MAX_ERRMSG_SIZE );
286  extErrorStatus = krnlSendMessage( extErrorObject, IMESSAGE_GETATTRIBUTE_S,
287  &msgData,
289  if( cryptStatusError( extErrorStatus ) )
290  {
291  /* Nothing further to report, exit */
292  return( localStatus );
293  }
294  extErrorStringLength = msgData.length;
295  ENSURES( extErrorStringLength > 0 && \
296  extErrorStringLength < MAX_ERRMSG_SIZE );
297 
298  /* There's additional information present via the additional object,
299  fetch it and append it to the session-level error message */
300  if( errorStringLength + extErrorStringLength < MAX_ERRMSG_SIZE - 32 )
301  {
302  ENSURES( rangeCheck( errorStringLength + 26, extErrorStringLength,
303  MAX_ERRMSG_SIZE ) );
304  memcpy( errorInfoPtr->errorString + errorStringLength,
305  ". Additional information: ", 26 );
306  memcpy( errorInfoPtr->errorString + errorStringLength + 26,
307  extraErrorString, extErrorStringLength );
308  errorInfoPtr->errorStringLength += 26 + extErrorStringLength;
309  }
310 
311  return( localStatus );
312  }
313 
314 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 3, 5 ) ) STDC_PRINTF_FN( 5, 6 ) \
315 int retExtStrFn( IN_ERROR const int status,
316  OUT ERROR_INFO *errorInfoPtr,
317  IN_BUFFER( extErrorStringLength ) const char *extErrorString,
318  IN_LENGTH_ERRORMESSAGE const int extErrorStringLength,
319  FORMAT_STRING const char *format, ... )
320  {
321  va_list argPtr;
322  const int localStatus = convertErrorStatus( status );
323  BOOLEAN errorStringOK;
324 
325  assert( isWritePtr( errorInfoPtr, sizeof( ERROR_INFO ) ) );
326  assert( isReadPtr( extErrorString, extErrorStringLength ) );
327  assert( isReadPtr( format, 4 ) );
328 
329  REQUIRES( cryptStatusError( status ) );
330  REQUIRES( extErrorStringLength > 0 && \
331  extErrorStringLength < MAX_ERRMSG_SIZE );
332 
333  /* Clear return value */
334  memset( errorInfoPtr, 0, sizeof( ERROR_INFO ) );
335 
336  /* Format the basic error string */
337  va_start( argPtr, format );
338  errorStringOK = formatErrorString( errorInfoPtr, format, argPtr );
339  va_end( argPtr );
340  if( !errorStringOK )
341  {
342  /* If we couldn't format the basic error string then there's no
343  point in continuing */
344  return( localStatus );
345  }
346 
347  /* Append the additional status string */
348  appendErrorString( errorInfoPtr, extErrorString, extErrorStringLength );
349  return( localStatus );
350  }
351 
352 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 3, 4 ) ) STDC_PRINTF_FN( 4, 5 ) \
353 int retExtErrFn( IN_ERROR const int status,
354  OUT ERROR_INFO *errorInfoPtr,
355  const ERROR_INFO *existingErrorInfoPtr,
356  FORMAT_STRING const char *format, ... )
357  {
358  va_list argPtr;
359  const int localStatus = convertErrorStatus( status );
360  char extErrorString[ MAX_ERRMSG_SIZE + 8 ];
361  BOOLEAN errorStringOK;
362  int extErrorStringLength;
363 
364  /* Clear return value */
365  memset( errorInfoPtr, 0, sizeof( ERROR_INFO ) );
366 
367  /* This function is typically used when the caller wants to convert
368  something like "Low-level error string" into "High-level error
369  string: Low-level error string". Since the low-level error string
370  may already be held in the errorInfo buffer where the high-level
371  error string needs to go, we copy the string into a temporary buffer
372  from where it can be appended back onto the string in the errorInfo
373  buffer */
374  if( existingErrorInfoPtr->errorStringLength > 0 && \
375  existingErrorInfoPtr->errorStringLength < MAX_ERRMSG_SIZE )
376  {
377  memcpy( extErrorString, existingErrorInfoPtr->errorString,
378  existingErrorInfoPtr->errorStringLength );
379  extErrorStringLength = existingErrorInfoPtr->errorStringLength;
380  }
381  else
382  {
383  memcpy( extErrorString, "(No additional information)", 27 );
384  extErrorStringLength = 27;
385  }
386  ENSURES( extErrorStringLength > 0 && \
387  extErrorStringLength < MAX_ERRMSG_SIZE );
388 
389  /* Format the basic error string */
390  va_start( argPtr, format );
391  errorStringOK = formatErrorString( errorInfoPtr, format, argPtr );
392  va_end( argPtr );
393  if( !errorStringOK )
394  {
395  /* If we couldn't format the basic error string then there's no
396  point in continuing */
397  return( localStatus );
398  }
399 
400  /* Append the additional status string */
401  appendErrorString( errorInfoPtr, extErrorString, extErrorStringLength );
402  return( localStatus );
403  }
404 
405 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 3 ) ) STDC_PRINTF_FN( 3, 4 ) \
406 int retExtErrAltFn( IN_ERROR const int status,
407  OUT ERROR_INFO *errorInfoPtr,
408  FORMAT_STRING const char *format, ... )
409  {
410  va_list argPtr;
411  const int localStatus = convertErrorStatus( status );
412  char extErrorString[ MAX_ERRMSG_SIZE + 8 ];
413  int extErrorStringLength;
414 
415  /* This function is typically used when the caller wants to convert
416  something like "Low-level error string" into "Low-level error string,
417  additional comments". First we format the additional-comments
418  string */
419  va_start( argPtr, format );
420  extErrorStringLength = vsprintf_s( extErrorString, MAX_ERRMSG_SIZE,
421  format, argPtr );
422  va_end( argPtr );
423  if( extErrorStringLength <= 0 || extErrorStringLength > MAX_ERRMSG_SIZE )
424  {
425  DEBUG_DIAG(( "Invalid error string data" ));
426  assert( DEBUG_WARN );
427  setErrorString( errorInfoPtr,
428  "(Couldn't record error information)", 35 );
429  return( localStatus );
430  }
431 
432  /* Append the additional status string */
433  appendErrorString( errorInfoPtr, extErrorString, extErrorStringLength );
434  return( localStatus );
435  }
436 #else
437 
438 /****************************************************************************
439 * *
440 * Minimal Error Reporting Functions *
441 * *
442 ****************************************************************************/
443 
444 /* If we're not using extended error reporting there is one minimal facility
445  that we still need to support, which is the copying of an integer error
446  code from source to destination */
447 
448 STDC_NONNULL_ARG( ( 1, 2 ) ) \
449 void copyErrorInfo( OUT ERROR_INFO *destErrorInfoPtr,
450  const ERROR_INFO *srcErrorInfoPtr )
451  {
452  assert( isWritePtr( destErrorInfoPtr, sizeof( ERROR_INFO ) ) );
453  assert( isReadPtr( srcErrorInfoPtr, sizeof( ERROR_INFO ) ) );
454 
455  memset( destErrorInfoPtr, 0, sizeof( ERROR_INFO ) );
456  destErrorInfoPtr->errorCode = srcErrorInfoPtr->errorCode;
457  }
458 #endif /* USE_ERRMSGS */