cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
cmp_err.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Read CMP (and TSP) Status Information *
4 * Copyright Peter Gutmann 1999-2009 *
5 * *
6 ****************************************************************************/
7 
8 #include <stdio.h>
9 #if defined( INC_ALL )
10  #include "crypt.h"
11  #include "asn1.h"
12  #include "session.h"
13  #include "cmp.h"
14 #else
15  #include "crypt.h"
16  #include "enc_dec/asn1.h"
17  #include "session/session.h"
18  #include "session/cmp.h"
19 #endif /* Compiler-specific includes */
20 
21 /* The following code is shared between CMP and TSP due to TSP's use of
22  random elements cut & pasted from CMP without any real understanding of
23  their function or semantics */
24 
25 #if defined( USE_CMP ) || defined( USE_TSP )
26 
27 /* CMP includes a comical facility for the server to tell the client "You
28  asked for food, I've given you a flaming telephone directory on a silver
29  platter" (actually what it says is "You asked for food, I've given you
30  something that isn't food but I won't tell you what"). Like much of the
31  rest of CMP it's unclear what we're supposed to do in this situation, the
32  only implementation that's known to use this facility will return
33  something totally different from what was requested so for now we treat
34  PKISTATUS_OK_WITHINFO as an error */
35 
36 #define cmpStatusOK( value ) ( value == PKISTATUS_OK )
37 
38 /* CMP error messages */
39 
40 typedef struct {
41  const int failureCode; /* CMP failure code */
42  const int status; /* cryptlib error status */
43  const char FAR_BSS *string; /* Descriptive error message */
44  const int stringLength;
45  } FAILURE_INFO;
46 
47 static const FAILURE_INFO FAR_BSS failureInfo[] = {
49  "Unrecognized or unsupported Algorithm Identifier", 48 },
51  "The integrity check failed (e.g. signature did not verify)", 58 },
53  "This transaction is not permitted or supported", 46 },
55  "The messageTime was not sufficiently close to the system time as "
56  "defined by local policy", 88 },
58  "No certificate could be found matching the provided criteria", 60 },
60  "The data submitted has the wrong format", 39 },
62  "The authority indicated in the request is different from the one "
63  "creating the response token", 92 },
65  "The requester's data is incorrect (used for notary services)", 60 },
67  "Timestamp is missing but should be there (by policy)", 52 },
69  "The proof-of-possession failed", 30 },
71  "The certificate has already been revoked", 40 },
73  "The certificate has already been confirmed", 42 },
75  "Invalid integrity, password based instead of signature or vice "
76  "versa", 68 },
78  "Invalid recipient nonce, either missing or wrong value", 54 },
80  "The TSA's time source is not available", 38 },
82  "The requested TSA policy is not supported by the TSA", 52 },
84  "The requested extension is not supported by the TSA", 51 },
86  "The additional information requested could not be understood or "
87  "is not available", 80 },
89  "Invalid sender nonce, either missing or wrong size", 50 },
91  "Invalid certificate template or missing mandatory information", 61 },
93  "Signer of the message unknown or not trusted", 44 },
95  "The transaction identifier is already in use", 44 },
97  "The version of the message is not supported", 43 },
99  "The sender was not authorized to make the preceding request or "
100  "perform the preceding action", 91 },
102  "The request cannot be handled due to system unavailability", 58 },
104  "The request cannot be handled due to system failure", 51 },
106  "Certificate cannot be issued because a duplicate certificate "
107  "already exists", 75 },
108  { CRYPT_ERROR, CRYPT_ERROR, "Unknown PKI failure code", 24 },
109  { CRYPT_ERROR, CRYPT_ERROR, "Unknown PKI failure code", 24 }
110  };
111 
112 /****************************************************************************
113 * *
114 * Utility Functions *
115 * *
116 ****************************************************************************/
117 
118 /* Map a PKI failure information value to an error string */
119 
120 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 4 ) ) \
121 static int getFailureInfo( OUT_BUFFER_ALLOC_OPT( *stringLength ) \
122  const char **stringPtrPtr,
124  OUT_ERROR int *failureStatus,
125  OUT_INT_SHORT_Z int *failureBitPos,
126  IN_INT_Z const int value )
127  {
128  const FAILURE_INFO *failureInfoPtr = NULL;
129  int bitPos = 0, i;
130 
131  assert( isWritePtr( ( char ** ) stringPtrPtr, sizeof( char * ) ) );
132  assert( isWritePtr( stringLength, sizeof( int ) ) );
133  assert( isWritePtr( failureStatus, sizeof( int ) ) );
134  assert( isWritePtr( failureBitPos, sizeof( int ) ) );
135 
136  REQUIRES( value >= 0 && value < MAX_INTLENGTH );
137 
138  /* Clear return values */
139  *stringPtrPtr = NULL;
140  *stringLength = *failureBitPos = 0;
141  *failureStatus = CRYPT_ERROR_FAILED;
142 
143  /* For no known reason the status is encoded as a BIT STRING instead of
144  an ENUMERATED so to find the appropriate failure string we have to
145  walk down the bit flags to find the first failure string
146  corresponding to a bit set in the failure information */
147  if( value <= 0 )
148  {
149  *stringPtrPtr = "Missing PKI failure code";
150  *stringLength = 24;
151 
152  return( CRYPT_OK );
153  }
154  for( i = 0; failureInfo[ i ].failureCode != CRYPT_ERROR && \
155  i < FAILSAFE_ARRAYSIZE( failureInfo, FAILURE_INFO ); i++ )
156  {
157  const int failureCode = failureInfo[ i ].failureCode;
158 
159  if( ( failureCode & value ) == failureCode )
160  {
161  failureInfoPtr = &failureInfo[ i ];
162  bitPos = i;
163  break;
164  }
165  }
166  ENSURES( i < FAILSAFE_ARRAYSIZE( failureInfo, FAILURE_INFO ) );
167  if( failureInfoPtr == NULL )
168  {
169  *stringPtrPtr = "Unknown PKI failure code";
170  *stringLength = 24;
171 
172  return( CRYPT_OK );
173  }
174 
175  /* We've got information for this failure code, return it to the
176  caller */
177  *stringPtrPtr = failureInfoPtr->string;
178  *stringLength = failureInfoPtr->stringLength;
179  *failureStatus = failureInfoPtr->status;
180  *failureBitPos = bitPos;
181 
182  return( CRYPT_OK );
183  }
184 
185 /* Map a cryptlib status value to PKI failure information. Note that we use
186  a distinct mapping table rather than the general failureInfo table because
187  the mappings are multivalued, so that a single cryptlib status may
188  correspond to multiple CMP failure codes. The mappings below are the most
189  generic ones */
190 
191 static const MAP_TABLE pkiStatusMapTbl[] = {
200  };
201 
202 static long getFailureBitString( IN_STATUS const int pkiStatus )
203  {
204  int i;
205 
206  REQUIRES_EXT( cryptStatusError( pkiStatus ), 0 );
207 
208  /* Try and map the cryptlib status value to a CMP failure information
209  code. We can't use mapValue() for this because we're mapping from a
210  negative value, which is used by mapValue() as the end-of-data
211  marker */
212  for( i = 0; pkiStatusMapTbl[ i ].source != CRYPT_OK && \
213  i < FAILSAFE_ARRAYSIZE( pkiStatusMapTbl, MAP_TABLE ); i++ )
214  {
215  if( pkiStatusMapTbl[ i ].source == pkiStatus )
216  return( pkiStatusMapTbl[ i ].destination );
217  }
218  ENSURES_EXT( i < FAILSAFE_ARRAYSIZE( pkiStatusMapTbl, MAP_TABLE ), 0 );
219 
220  /* We couldn't find any appropriate failure information code, don't use
221  one at all */
222  return( 0 );
223  }
224 
225 /****************************************************************************
226 * *
227 * Read Status Information *
228 * *
229 ****************************************************************************/
230 
231 #if 0 /* 28/9/08 In the usual CMP weirdness the failure information is
232  encoded as a BIT STRING instead of an ENUMERATED value,
233  and comes with a side-order of an arbitrary number of
234  free-format text strings of unknown type or function.
235  Although we could in theory jump through all sorts of
236  hoops to try and handle the resulting multivalued
237  status code and multivalued string data it doesn't make
238  any sense to do so and just increases our attack surface
239  significantly, so all we do is look for the first (and in
240  all known implementations only) bit set and use that as
241  the error value */
242 
243 /* Read PKIStatus information:
244 
245  PKIStatusInfo ::= SEQUENCE {
246  status INTEGER,
247  statusString SEQUENCE OF UTF8String OPTIONAL,
248  failInfo BIT STRING OPTIONAL
249  } */
250 
251 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
252 static int readFreeText( INOUT STREAM *stream,
253  OUT_BUFFER( stringMaxLen, *stringLength ) char *string,
254  IN_LENGTH_SHORT_MIN( 16 ) const int stringMaxLen,
255  OUT_LENGTH_SHORT_Z int *stringLength )
256  {
257  int endPos, length, status;
258 
259  assert( isWritePtr( stream, sizeof( STREAM ) ) );
260  assert( isWritePtr( string, stringMaxLen ) );
261  assert( isWritePtr( stringLength, sizeof( int ) ) );
262 
263  REQUIRES( stringMaxLen >= 16 && stringMaxLen < MAX_INTLENGTH_SHORT );
264 
265  /* Read the status string(s). There can be more than one of these,
266  there's no indication of what the subsequent ones are used for and
267  not much that we can do with them in any case, so we skip them */
268  status = readSequence( stream, &endPos );
269  if( cryptStatusOK( status ) )
270  {
271  endPos += stell( stream );
272  status = readCharacterString( stream, string, stringMaxLen,
273  &length, BER_STRING_UTF8 );
274  }
275  if( cryptStatusError( status ) )
276  {
277  strlcpy_s( string, stringMaxLen, "Invalid PKI free text" );
278  return( status );
279  }
280  *stringLength = length;
281  length = endPos - stell( stream );
282  if( length > 0 )
283  {
284  /* There's extra junk present, skip it */
285  status = sSkip( stream, length );
286  }
287  return( status );
288  }
289 
290 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
291 int readPkiStatusInfo( INOUT STREAM *stream, INOUT ERROR_INFO *errorInfo )
292  {
293  char errorMessage[ MAX_ERRMSG_SIZE + 8 ], textBitString[ 128 + 8 ];
294  long value, endPos;
295  int errorMessageLength = 0, textBitStringLength;
296  int bitString, noBits, bitMask, bitNo = -1, i, length, status;
297 
298  assert( isWritePtr( stream, sizeof( STREAM ) ) );
299  assert( isWritePtr( errorInfo, sizeof( ERROR_INFO ) ) );
300 
301  /* Clear the return values */
302  memset( errorInfo, 0, sizeof( ERROR_INFO ) );
303 
304  /* Read the outer wrapper and status value */
305  status = readSequence( stream, &length );
306  if( cryptStatusOK( status ) )
307  {
308  endPos = stell( stream ) + length;
309  status = readShortInteger( stream, &value );
310  if( cryptStatusOK( status ) && \
312  status = CRYPT_ERROR_BADDATA;
313  }
314  if( cryptStatusError( status ) )
315  {
316  setErrorString( errorInfo, "Invalid PKI status value", 24 );
317  return( status );
318  }
319  errorInfo->errorCode = ( int ) value;
320  if( stell( stream ) < endPos && peekTag( stream ) == BER_SEQUENCE )
321  {
322  memcpy( errorMessage, "Server returned error: ", 23 );
323 
324  /* Read the free-form text message data */
325  status = readFreeText( stream, errorMessage + 23,
326  MAX_ERRMSG_SIZE - 32, &errorMessageLength );
327  if( cryptStatusError( status ) )
328  return( status );
329  errorMessageLength += 23;
330  }
331  if( stell( stream ) >= endPos )
332  {
333  /* If there was a problem but there's no extra error information
334  present, return a "This page deliberately left blank" error */
335  if( errorMessageLength <= 0 && errorInfo->errorCode != PKISTATUS_OK )
336  {
337  setErrorString( errorInfo,
338  "Server returned nonspecific error information",
339  45 );
340  }
341 
342  /* See the comment at end of this function for the translation of
343  error codes */
344  return( ( errorInfo->errorCode == PKISTATUS_OK || \
345  errorInfo->errorCode == PKISTATUS_OK_WITHINFO ) ? \
347  }
348 
349  /* Read the failure information and slot it into the error string */
350  status = readBitString( stream, &bitString );
351  if( cryptStatusError( status ) )
352  {
353  setErrorString( errorInfo, "Invalid PKI failure information", 24 );
354  return( status );
355  }
356  memcpy( textBitString, "Server returned status value ", 29 );
357  textBitStringLength = 29;
358  i = bitString;
359  for( noBits = 0; i > 0 && noBits < 32; noBits++ )
360  i >>= 1;
361  bitMask = 1 << ( noBits - 1 );
362  for( i = 0; i < noBits; i++ )
363  {
364  if( bitString & bitMask )
365  {
366  /* If there's no bit set yet, set it. If there's already a bit
367  set, set it to a non-value that indicates that more than one
368  bit is set */
369  bitNo = ( bitNo == -1 ) ? ( noBits - 1 ) - i : -2;
370  textBitString[ textBitStringLength++ ] = '1';
371  }
372  else
373  textBitString[ textBitStringLength++ ] = '0';
374  bitMask >>= 1;
375  }
376  if( bitNo >= 0 )
377  {
378  textBitStringLength = \
379  sprintf_s( textBitString, 64,
380  "Server returned status bit %d: ", bitNo );
381  }
382  else
383  {
384  memcpy( textBitString + textBitStringLength, "'B: ", 4 );
385  textBitStringLength += 4;
386  }
387  if( errorMessageLength > 0 )
388  {
389  /* There's error message text present, move it up to make room for
390  the bit string text */
391  if( errorMessageLength > MAX_ERRMSG_SIZE - textBitStringLength )
392  errorMessageLength = MAX_ERRMSG_SIZE - textBitStringLength;
393  memmove( errorMessage + textBitStringLength,
394  errorMessage, errorMessageLength );
395  memcpy( errorMessage, textBitString, textBitStringLength );
396  errorMessageLength += textBitStringLength;
397  }
398  else
399  {
400  /* If there's a failure code present, turn it into an error string */
401  if( bitString > 0 )
402  {
403  const char *failureString;
404  int failureStringLength;
405 
406  memcpy( errorMessage, textBitString, textBitStringLength );
407  status = getFailureString( &failureString, &failureStringLength,
408  bitString );
409  if( cryptStatusError( status ) || \
410  textBitStringLength + failureStringLength >= MAX_ERRMSG_SIZE )
411  {
412  failureString = "[...]";
413  failureStringLength = 5;
414  }
415  memcpy( errorMessage + textBitStringLength, failureString,
416  failureStringLength );
417  errorMessageLength = textBitStringLength + failureStringLength;
418  }
419  }
420  if( errorMessageLength > 0 )
421  setErrorString( errorInfo, errorMessage, errorMessageLength );
422 
423  /* If we can return something more useful than the generic "failed"
424  error code, try and do so */
425  if( bitString & CMPFAILINFO_BADALG )
426  return( CRYPT_ERROR_NOTAVAIL );
427  if( ( bitString & CMPFAILINFO_BADMESSAGECHECK ) || \
428  ( bitString & CMPFAILINFO_BADPOP ) || \
429  ( bitString & CMPFAILINFO_WRONGINTEGRITY ) )
430  return( CRYPT_ERROR_WRONGKEY );
431  if( ( bitString & CMPFAILINFO_BADREQUEST ) || \
432  ( bitString & CMPFAILINFO_SIGNERNOTTRUSTED ) || \
433  ( bitString & CMPFAILINFO_NOTAUTHORIZED ) )
434  return( CRYPT_ERROR_PERMISSION );
435  if( bitString & CMPFAILINFO_BADDATAFORMAT )
436  return( CRYPT_ERROR_BADDATA );
437  if( ( bitString & CMPFAILINFO_UNACCEPTEDPOLICY ) || \
438  ( bitString & CMPFAILINFO_UNACCEPTEDEXTENSION ) || \
439  ( bitString & CMPFAILINFO_BADCERTTEMPLATE ) )
440  return( CRYPT_ERROR_INVALID );
441  if( ( bitString & CMPFAILINFO_TRANSACTIONIDINUSE ) || \
442  ( bitString & CMPFAILINFO_DUPLICATECERTREQ ) )
443  return( CRYPT_ERROR_DUPLICATE );
444 
445  /* A PKI status code is a bit difficult to turn into anything useful,
446  the best we can do is to report that the operation failed and let
447  the user get the exact details from the PKI status information */
448  return( ( errorInfo->errorCode == PKISTATUS_OK || \
449  errorInfo->errorCode == PKISTATUS_OK_WITHINFO ) ? \
451  }
452 #else
453 
454 /* Read PKIStatus information:
455 
456  PKIStatusInfo ::= SEQUENCE {
457  status INTEGER,
458  dummy SEQUENCE ... OPTIONAL,
459  failInfo BIT STRING OPTIONAL
460  } */
461 
462 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
463 int readPkiStatusInfo( INOUT STREAM *stream,
464  const BOOLEAN isServer,
465  INOUT ERROR_INFO *errorInfo )
466  {
467  const char *failureString;
468  long endPos, value;
469  int bitString = 0, bitPos, failureStringLength, failureStatus;
470  int errorCode, length, status;
471 
472  assert( isWritePtr( stream, sizeof( STREAM ) ) );
473  assert( isWritePtr( errorInfo, sizeof( ERROR_INFO ) ) );
474 
475  /* Clear the return values */
476  memset( errorInfo, 0, sizeof( ERROR_INFO ) );
477 
478  /* Read the outer wrapper and status value */
479  status = readSequence( stream, &length );
480  if( cryptStatusError( status ) )
481  return( status );
482  endPos = stell( stream ) + length;
483  status = readShortInteger( stream, &value );
484  if( cryptStatusOK( status ) && \
486  status = CRYPT_ERROR_BADDATA;
487  if( cryptStatusError( status ) )
488  {
489  retExt( status,
490  ( status, errorInfo,
491  "Invalid PKI status value" ) );
492  }
493  errorCode = ( int ) value;
494 
495  /* Read the failure information, skipping any intervening junk that may
496  precede it */
497  if( stell( stream ) < endPos && peekTag( stream ) == BER_SEQUENCE )
498  status = readUniversal( stream );
499  if( cryptStatusOK( status ) && stell( stream ) < endPos )
500  status = readBitString( stream, &bitString );
501  if( cryptStatusError( status ) )
502  {
503  retExt( status,
504  ( status, errorInfo,
505  "Invalid PKI failure information" ) );
506  }
507 
508  /* If everything's OK, we're done */
509  if( cmpStatusOK( errorCode ) )
510  return( CRYPT_OK );
511 
512  /* Convert the failure code into a message string and report the result
513  to the caller */
514  status = getFailureInfo( &failureString, &failureStringLength,
515  &failureStatus, &bitPos, bitString );
516  if( cryptStatusError( status ) )
517  return( status );
518  ENSURES( cryptStatusError( failureStatus ) );
519  if( bitString == 0 )
520  {
521  /* If we haven't been given any specific details for the problem,
522  there's not much more that we can report. Note that we need to
523  peform this operation after calling getFailureInfo() because
524  even though there's no returned detailed error information we're
525  still using the failure status value that's returned */
526  retExt( failureStatus,
527  ( failureStatus, errorInfo,
528  "%s returned nonspecific failure code",
529  isServer ? "Client" : "Server" ) );
530  }
531  retExt( failureStatus,
532  ( failureStatus, errorInfo,
533  "%s returned error code %X (bit %d): %s",
534  isServer ? "Client" : "Server", bitString, bitPos,
535  failureString ) );
536  }
537 #endif /* 0 */
538 
539 /****************************************************************************
540 * *
541 * Write Status Information *
542 * *
543 ****************************************************************************/
544 
545 /* Write PKIStatus information:
546 
547  PKIStatusInfo ::= SEQUENCE {
548  status INTEGER,
549  failInfo BIT STRING OPTIONAL
550  } */
551 
552 CHECK_RETVAL \
553 int sizeofPkiStatusInfo( IN_STATUS const int pkiStatus,
554  IN_ENUM_OPT( CMPFAILINFO ) const long pkiFailureInfo )
555  {
556  long localPKIFailureInfo;
557 
558  /* If it's an OK status then there's just a single integer value */
559  if( cryptStatusOK( pkiStatus ) )
560  return( objSize( sizeofShortInteger( PKISTATUS_OK ) ) );
561 
562  /* Return the size of the error status and optional extended error
563  code */
564  localPKIFailureInfo = ( pkiFailureInfo != CMPFAILINFO_OK ) ? \
565  pkiFailureInfo : getFailureBitString( pkiStatus );
567  ( ( localPKIFailureInfo != CMPFAILINFO_OK ) ? \
568  sizeofBitString( localPKIFailureInfo ) : 0 ) ) );
569  }
570 
572 int writePkiStatusInfo( INOUT STREAM *stream,
573  IN_STATUS const int pkiStatus,
574  IN_ENUM_OPT( CMPFAILINFO ) const long pkiFailureInfo )
575  {
576  long localPKIFailureInfo;
577 
578  assert( isWritePtr( stream, sizeof( STREAM ) ) );
579 
580  REQUIRES( cryptStatusOK( pkiStatus ) || cryptStatusError( pkiStatus ) );
581  REQUIRES( pkiFailureInfo >= CMPFAILINFO_OK && \
582  pkiFailureInfo < CMPFAILINFO_LAST );
583  /* The failure code is another piece of CMP stupidity, it
584  looks like an enum but it's actually a bit flag, however we
585  only ever set one bit in it so we treat it as an enum for
586  checking purposes. In addition there's a no-error status
587  CMPFAILINFO_OK that has the same value as CMPFAILINFO_NONE
588  so we use _OPT and >= 0 for the low range check */
589 
590  /* If it's an OK status then there's just a single integer value */
591  if( cryptStatusOK( pkiStatus ) )
592  {
593  writeSequence( stream, sizeofShortInteger( PKISTATUS_OK ) );
594  return( writeShortInteger( stream, PKISTATUS_OK, DEFAULT_TAG ) );
595  }
596 
597  /* Write the error status and optional extended error code */
598  localPKIFailureInfo = ( pkiFailureInfo != CMPFAILINFO_OK ) ? \
599  pkiFailureInfo : getFailureBitString( pkiStatus );
600  if( localPKIFailureInfo == CMPFAILINFO_OK )
601  {
602  /* There's no extended error code, just write a basic failure
603  status */
604  writeSequence( stream, sizeofShortInteger( PKISTATUS_REJECTED ) );
605  return( writeShortInteger( stream, PKISTATUS_REJECTED, DEFAULT_TAG ) );
606  }
607  writeSequence( stream, sizeofShortInteger( PKISTATUS_REJECTED ) + \
608  sizeofBitString( localPKIFailureInfo ) );
609  writeShortInteger( stream, PKISTATUS_REJECTED, DEFAULT_TAG );
610  return( writeBitString( stream, localPKIFailureInfo, DEFAULT_TAG ) );
611  }
612 #endif /* USE_CMP || USE_TSP */