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