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