cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
cmp_rdmsg.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Read CMP Message Types *
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 "asn1_ext.h"
13  #include "session.h"
14  #include "cmp.h"
15 #else
16  #include "crypt.h"
17  #include "enc_dec/asn1.h"
18  #include "enc_dec/asn1_ext.h"
19  #include "session/session.h"
20  #include "session/cmp.h"
21 #endif /* Compiler-specific includes */
22 
23 #ifdef USE_CMP
24 
25 /****************************************************************************
26 * *
27 * Utility Routines *
28 * *
29 ****************************************************************************/
30 
31 #if 0 /* 12/6/09 Due to a bug in the buffer-positioning the following code
32  hasn't actually worked since 3.2.1 in 2005, since this
33  hasn't caused any complaints we disable it for attack-
34  surface reduction */
35 
36 /* Read a certificate encrypted with CMP's garbled reinvention of CMS
37  content:
38 
39  EncryptedCert ::= SEQUENCE {
40  dummy1 [0] ... OPTIONAL, -- Ignored
41  cekAlg [1] AlgorithmIdentifier,-- CEK algorithm
42  encCEK [2] BIT STRING, -- Encrypted CEK
43  dummy2 [3] ... OPTIONAL, -- Ignored
44  dummy3 [4] ... OPTIONAL, -- Ignored
45  encData BIT STRING -- Encrypted certificate
46  }
47 
48  This muddle is only applied for non-cryptlib sessions, if two cryptlib
49  implementations are communicating then the certificate is wrapped using
50  CMS */
51 
52 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 4, 5 ) ) \
53 static int readEncryptedDataInfo( INOUT STREAM *stream,
54  OUT_BUFFER_ALLOC( *encDataLength ) \
55  void **encDataPtrPtr,
56  OUT_LENGTH_SHORT_Z int *encDataLength,
57  IN_LENGTH_SHORT_MIN( 32 ) const int minLength,
58  IN_LENGTH_SHORT_MIN( 32 ) const int maxLength )
59  {
60  void *dataPtr;
61  int length, status;
62 
63  assert( isWritePtr( stream, sizeof( STREAM ) ) );
64  assert( isWritePtr( encDataPtrPtr, sizeof( void * ) ) );
65  assert( isWritePtr( encDataLength, sizeof( int ) ) );
66 
67  REQUIRES( minLength >= 32 && minLength < maxLength && \
68  minLength < MAX_INTLENGTH_SHORT );
69 
70  /* Clear return values */
71  *encDataPtrPtr = NULL;
72  *encDataLength = 0;
73 
74  /* Read and remember the encrypted data */
75  status = readBitStringHole( stream, &length, minLength,
77  if( cryptStatusError( status ) )
78  return( status );
79  if( length < MIN_PKCSIZE || length > CRYPT_MAX_PKCSIZE )
80  return( CRYPT_ERROR_BADDATA );
81  status = sMemGetDataBlock( stream, &dataPtr, length );
82  if( cryptStatusOK( status ) )
83  status = sSkip( stream, length );
84  if( cryptStatusError( status ) )
85  return( status );
86  *encDataPtrPtr = dataPtr;
87  *encDataLength = length;
88 
89  return( CRYPT_OK );
90  }
91 
92 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
93 static int readEncryptedCert( INOUT STREAM *stream,
95  OUT_BUFFER( outDataMaxLength, *outDataLength ) \
96  void *outData,
97  IN_LENGTH_MIN( 16 ) const int outDataMaxLength,
98  OUT_LENGTH_Z int *outDataLength,
100  {
101  CRYPT_CONTEXT iSessionKey;
104  void *encKeyPtr = DUMMY_INIT_PTR, *encCertPtr;
105  int encKeyLength = DUMMY_INIT, encCertLength, status;
106 
107  assert( isWritePtr( stream, sizeof( STREAM ) ) );
108  assert( isWritePtr( errorInfo, sizeof( ERROR_INFO ) ) );
109 
110  REQUIRES( isHandleRangeValid( iImportContext ) );
111 
112  /* Read the CEK algorithm identifier and encrypted CEK. All of the
113  values are optional although there's no indication of why or what
114  you're supposed to do if they're not present (OTOH for others there's
115  no indication of what you're supposed to do when they're present
116  either) so we treat an absent required value as an error and ignore
117  the others */
118  readSequence( stream, NULL );
119  if( peekTag( stream ) == MAKE_CTAG( CTAG_EV_DUMMY1 ) )
120  readUniversal( stream ); /* Junk */
121  status = readContextAlgoID( stream, &iSessionKey, &queryInfo,
122  CTAG_EV_CEKALGO );
123  if( cryptStatusOK( status ) ) /* CEK algo */
124  status = readEncryptedDataInfo( stream, &encKeyPtr, &encKeyLength,
125  MIN_PKCSIZE, CRYPT_MAX_PKCSIZE );
126  if( cryptStatusError( status ) ) /* Enc.CEK */
127  {
128  retExt( status,
129  ( status, errorInfo,
130  "Invalid encrypted certificate CEK information" ) );
131  }
132  if( peekTag( stream ) == MAKE_CTAG( CTAG_EV_DUMMY2 ) )
133  readUniversal( stream ); /* Junk */
134  if( peekTag( stream ) == MAKE_CTAG( CTAG_EV_DUMMY3 ) )
135  readUniversal( stream ); /* Junk */
136  status = readEncryptedDataInfo( stream, &encCertPtr, &encCertLength,
137  128, 8192 );
138  if( cryptStatusOK( status ) && /* Enc.certificate */
139  ( queryInfo.cryptMode == CRYPT_MODE_ECB || \
140  queryInfo.cryptMode == CRYPT_MODE_CBC ) )
141  {
142  int blockSize;
143 
144  /* Make sure that the data length is valid. Checking at this point
145  saves a lot of unnecessary processing and allows us to return a
146  more meaningful error code */
147  status = krnlSendMessage( iSessionKey, IMESSAGE_GETATTRIBUTE,
148  &blockSize, CRYPT_CTXINFO_BLOCKSIZE );
149  if( cryptStatusError( status ) || \
150  ( queryInfo.size % blockSize ) != 0 )
151  status = CRYPT_ERROR_BADDATA;
152  }
153  if( cryptStatusError( status ) )
154  {
155  krnlSendNotifier( iSessionKey, IMESSAGE_DECREFCOUNT );
156  retExt( status,
157  ( status, errorInfo,
158  "Invalid encrypted certificate data" ) );
159  }
160 
161  /* Import the wrapped session key into the session key context */
162  setMechanismWrapInfo( &mechanismInfo, encKeyPtr, encKeyLength,
163  NULL, 0, iSessionKey, iImportContext );
165  &mechanismInfo, MECHANISM_ENC_PKCS1 );
166  clearMechanismInfo( &mechanismInfo );
167  if( cryptStatusError( status ) )
168  {
169  krnlSendNotifier( iSessionKey, IMESSAGE_DECREFCOUNT );
170  retExt( status,
171  ( status, errorInfo,
172  "Couldn't decrypt encrypted certificate CEK" ) );
173  }
174 
175  /* Decrypt the returned certificate and copy the result back to the
176  caller. We don't worry about padding because the certificate-import
177  code knows when to stop based on the encoded certificate data */
178  status = krnlSendMessage( iSessionKey, IMESSAGE_CTX_DECRYPT,
179  encCertPtr, encCertLength );
180  krnlSendNotifier( iSessionKey, IMESSAGE_DECREFCOUNT );
181  if( cryptStatusError( status ) )
182  {
183  retExt( status,
184  ( status, errorInfo,
185  "Couldn't decrypt returned encrypted certificate using "
186  "CEK" ) );
187  }
188  return( attributeCopyParams( outData, outDataMaxLength, outDataLength,
189  encCertPtr, encCertLength ) );
190  }
191 #endif /* 0 */
192 
193 /* Try and obtain more detailed information on why a certificate request
194  wasn't compatible with stored PKI user information. Note that the
195  mapping table below is somewhat specific to the implementation of
196  copyPkiUserToCertReq() in certs/comp_cert.c, we hardcode a few common
197  cases here and use a generic error message for the rest */
198 
199 #ifdef USE_ERRMSGS
200 
201 typedef struct {
202  const CRYPT_ATTRIBUTE_TYPE errorLocus;
203  const CRYPT_ERRTYPE_TYPE errorType;
204  const char *errorString;
205  } EXT_ERROR_MAP_INFO;
206 
207 static const EXT_ERROR_MAP_INFO extErrorMapTbl[] = {
209  "Certificate request DN differs from PKI user DN" },
211  "Certificate request contains an empty DN" },
213  "Certificate request subjectAltName conflicts with PKI user "
214  "subjectAltName" },
216  "Certificate request DN is missing a CommonName" },
218  "Certificate request CommonName differs from PKI user CommonName" },
221  };
222 
224 static int reportExtendedCertErrorInfo( INOUT SESSION_INFO *sessionInfoPtr,
225  IN_ERROR const int errorStatus )
226  {
227  const EXT_ERROR_MAP_INFO *extErrorInfoPtr = NULL;
228  int errorType, errorLocus = DUMMY_INIT, i, status;
229 
230  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
231 
232  REQUIRES( cryptStatusError( errorStatus ) );
233 
234  /* Try and get further details on what went wrong */
235  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
236  IMESSAGE_GETATTRIBUTE, &errorType,
238  if( cryptStatusOK( status ) )
239  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
240  IMESSAGE_GETATTRIBUTE, &errorLocus,
242  if( cryptStatusError( status ) )
243  {
244  /* We couldn't get any further information, return a generic error
245  message */
246  retExt( errorStatus,
247  ( errorStatus, SESSION_ERRINFO,
248  "Information in certificate request can't be reconciled "
249  "with our information for the user (no further problem "
250  "details are available)" ) );
251  }
252 
253  /* Provide a more detailed report on what went wrong. We search the
254  table by locus as the primary ID since these are more unique than the
255  error type */
256  for( i = 0; extErrorMapTbl[ i ].errorType != CRYPT_ERRTYPE_NONE && \
257  i < FAILSAFE_ARRAYSIZE( extErrorMapTbl, EXT_ERROR_MAP_INFO );
258  i++ )
259  {
260  if( extErrorMapTbl[ i ].errorLocus == errorLocus && \
261  extErrorMapTbl[ i ].errorType == errorType )
262  {
263  extErrorInfoPtr = &extErrorMapTbl[ i ];
264  break;
265  }
266  }
267  ENSURES( i < FAILSAFE_ARRAYSIZE( extErrorMapTbl, EXT_ERROR_MAP_INFO ) );
268  if( extErrorInfoPtr == NULL )
269  {
270  /* It's no obviously recognisable problem, return a lower-level
271  error message */
272  retExt( errorStatus,
273  ( errorStatus, SESSION_ERRINFO,
274  "Information in certificate request can't be reconciled "
275  "with our information for the user, error type %d, error "
276  "locus %d", errorType, errorLocus ) );
277  }
278 
279  /* Return the actual error message. Note the somewhat unusual means
280  of passing the string argument, some tools will warn of a problem
281  due to passing in a non-literal string if we pass the errorString
282  in directly (even though it is a literal string) so we have to use
283  one level of indirection to bypass the warning */
284  retExt( errorStatus,
285  ( errorStatus, SESSION_ERRINFO,
286  "%s", extErrorInfoPtr->errorString ) );
287  }
288 #else
289 
290 #define reportExtendedCertErrorInfo( sessionInfoPtr, errorStatus ) \
291  return( errorStatus );
292 #endif /* USE_ERRMSGS */
293 
294 /****************************************************************************
295 * *
296 * PKI Body Functions *
297 * *
298 ****************************************************************************/
299 
300 /* Read request body:
301 
302  body [n] EXPLICIT SEQUENCE { -- Processed by caller
303  ... -- CRMF request
304  }
305 
306  The outer tag and SEQUENCE have already been processed by the caller */
307 
308 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
309 static int readRequestBody( INOUT STREAM *stream,
310  INOUT SESSION_INFO *sessionInfoPtr,
312  IN_ENUM_OPT( CTAG_PB ) \
313  const CMP_MESSAGE_TYPE messageType,
314  IN_LENGTH_SHORT const int messageLength )
315  {
316  CMP_INFO *cmpInfo = sessionInfoPtr->sessionCMP;
318  BYTE authCertID[ CRYPT_MAX_HASHSIZE + 8 ];
319  int value, status;
320 
321  assert( isWritePtr( stream, sizeof( STREAM ) ) );
322  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
323  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
324 
325  REQUIRES( messageType >= CTAG_PB_IR && messageType < CTAG_PB_LAST );
326  /* CTAG_PB_IR == 0 so this is the same as _NONE */
327  REQUIRES( messageLength > 0 && messageLength < MAX_INTLENGTH_SHORT );
328 
329  /* Import the CRMF request */
330  status = importCertFromStream( stream,
331  &sessionInfoPtr->iCertRequest,
333  ( messageType == CTAG_PB_P10CR ) ? \
335  ( messageType == CTAG_PB_RR ) ? \
338  messageLength );
339  if( cryptStatusError( status ) )
340  {
341  protocolInfo->pkiFailInfo = CMPFAILINFO_BADCERTTEMPLATE;
342  retExt( status,
343  ( status, SESSION_ERRINFO, "Invalid CRMF request" ) );
344  }
345 
346  /* If it's a request type that can be self-signed (revocation requests
347  are unsigned) and it's from an encryption-only key (that is, a key
348  that's not capable of signing, indicated by the request not being
349  self-signed) remember this so that we can peform special-case
350  processing later on */
351  if( messageType != CTAG_PB_RR )
352  {
353  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
354  IMESSAGE_GETATTRIBUTE, &value,
356  if( cryptStatusError( status ) )
357  return( status );
358  if( !value )
359  {
360  /* If the request is for a signing key then having an unsigned
361  request is an error */
362  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
363  IMESSAGE_GETATTRIBUTE, &value,
365  if( cryptStatusOK( status ) && \
366  ( value & ( KEYUSAGE_SIGN | KEYUSAGE_CA ) ) )
367  {
368  protocolInfo->pkiFailInfo = CMPFAILINFO_BADCERTTEMPLATE;
371  "CRMF request is for a signing key but the request "
372  "isn't signed" ) );
373  }
374  protocolInfo->cryptOnlyKey = TRUE;
375  }
376  }
377 
378  /* Record the identity of the PKI user (for a MAC'd request) or
379  certificate (for a signed request) that authorised this request */
380  setMessageData( &msgData, authCertID, CRYPT_MAX_HASHSIZE );
381  status = krnlSendMessage( protocolInfo->useMACreceive ? \
382  cmpInfo->userInfo : \
383  sessionInfoPtr->iAuthInContext,
384  IMESSAGE_GETATTRIBUTE_S, &msgData,
386  if( cryptStatusOK( status ) )
387  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
388  IMESSAGE_SETATTRIBUTE_S, &msgData,
389  CRYPT_IATTRIBUTE_AUTHCERTID );
390  if( cryptStatusError( status ) )
391  return( status );
392 
393 #if 0 /* 28/9/08 Should probably apply this filtering to all requests.
394  This is a bit tricky since it'll break RAs and in-house
395  CAs and other trusted-source certificate issue operations
396  where it's assumed that the data comes in pre-validated.
397  The following may need to be reset to its original
398  behaviour depending on what user reactions are... */
399  /* If it's not an ir which requires special-case processing because of a
400  potentially absent DN, we're done */
401  if( messageType != CTAG_PB_IR )
402  return( CRYPT_OK );
403 #else
404  /* Revocation requests don't contain any information so there's nothing
405  further to check */
406  if( messageType == CTAG_PB_RR )
407  return( CRYPT_OK );
408 #endif /* 0 */
409 
410  /* Make sure that the information in the request is consistent with the
411  user information template. If it's an ir then the subject may not
412  know their DN or may only know their CN, in which case they'll send
413  an empty/CN-only subject DN in the hope that we can fill it in for
414  them. If it's not an ir then the user information acts as a filter
415  to ensure that the request doesn't contain values that it shouldn't */
416  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
417  IMESSAGE_SETATTRIBUTE, &cmpInfo->userInfo,
418  CRYPT_IATTRIBUTE_PKIUSERINFO );
419  if( cryptStatusError( status ) )
420  {
421  protocolInfo->pkiFailInfo = CMPFAILINFO_BADCERTTEMPLATE;
422 
423  /* See if we can get any additional information on what the problem
424  was */
425  return( reportExtendedCertErrorInfo( sessionInfoPtr, status ) );
426  }
427  return( CRYPT_OK );
428  }
429 
430 /* Read response body:
431 
432  body [n] EXPLICIT SEQUENCE { -- Processed by caller
433  caPubs [1] EXPLICIT SEQUENCE {...} OPTIONAL,-- Ignored
434  response SEQUENCE {
435  SEQUENCE {
436  certReqID INTEGER (0),
437  status PKIStatusInfo,
438  certKeyPair SEQUENCE { -- If status == 0 or 1
439  cert[0] EXPLICIT Certificate,
440 or cmpEncCert -- For encr-only key
441  [1] EXPLICIT CMPEncryptedCert,
442 or cmsEncCert -- For encr-only key
443  [2] EXPLICIT EncryptedCert,
444  ... -- Ignored
445  }
446  }
447  }
448  }
449 
450  The outer tag and SEQUENCE have already been processed by the caller */
451 
452 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
453 static int readResponseBody( INOUT STREAM *stream,
454  INOUT SESSION_INFO *sessionInfoPtr,
455  INOUT CMP_PROTOCOL_INFO *protocolInfo,
456  STDC_UNUSED IN_ENUM_OPT( CTAG_PB ) \
457  const CMP_MESSAGE_TYPE messageType,
458  STDC_UNUSED IN_LENGTH_SHORT const int messageLength )
459  {
460  MESSAGE_CREATEOBJECT_INFO createInfo;
461  void *bodyInfoPtr = DUMMY_INIT_PTR;
462  int bodyLength, tag, value, status;
463 
464  assert( isWritePtr( stream, sizeof( STREAM ) ) );
465  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
466  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
467 
468  /* Skip any noise before the payload if necessary */
469  if( peekTag( stream ) == MAKE_CTAG( 1 ) )
470  {
471  status = readUniversal( stream ); /* caPubs */
472  if( cryptStatusError( status ) )
473  return( status );
474  }
475 
476  /* If it's a revocation response then the only returned data is the
477  status value */
478  if( protocolInfo->operation == CTAG_PB_RR )
479  {
480  readSequence( stream, NULL ); /* Inner wrapper */
481  return( readPkiStatusInfo( stream, isServer( sessionInfoPtr ),
482  &sessionInfoPtr->errorInfo ) );
483  }
484 
485  /* It's a certificate response, unwrap the body to find the certificate
486  payload */
487  readSequence( stream, NULL ); /* Inner wrapper */
488  readSequence( stream, NULL );
489  readUniversal( stream ); /* certReqId */
490  status = readPkiStatusInfo( stream, isServer( sessionInfoPtr ),
491  &sessionInfoPtr->errorInfo );
492  if( cryptStatusError( status ) )
493  return( status );
494  readSequence( stream, NULL ); /* certKeyPair wrapper */
495  tag = peekTag( stream );
496  if( cryptStatusError( tag ) )
497  return( tag );
498  tag = EXTRACT_CTAG( tag );
499  status = readConstructed( stream, &bodyLength, tag );
500  if( cryptStatusOK( status ) )
501  status = sMemGetDataBlock( stream, &bodyInfoPtr, bodyLength );
502  if( cryptStatusError( status ) )
503  return( status );
504  ANALYSER_HINT( bodyInfoPtr != NULL );
505 
506  /* Process the returned certificate as required */
507  switch( tag )
508  {
509  case CTAG_CK_CERT:
510  /* Plaintext certificate, we're done */
511  break;
512 
513 #if 0 /* 12/6/09 See earlier comment */
515  /* Certificate encrypted with CMP's garbled attempt at doing
516  CMS, try and decrypt it */
517  status = readEncryptedCert( stream, sessionInfoPtr->privateKey,
518  SESSION_ERRINFO );
519  break;
520 #endif /* 0 */
521 
523  /* Certificate encrypted with CMS, unwrap it */
524  status = envelopeUnwrap( bodyInfoPtr, bodyLength,
525  bodyInfoPtr, bodyLength, &bodyLength,
526  sessionInfoPtr->privateKey );
527  if( cryptStatusError( status ) )
528  {
529  retExt( cryptArgError( status ) ? \
530  CRYPT_ERROR_FAILED : status,
531  ( cryptArgError( status ) ? \
533  "Couldn't decrypt CMS enveloped certificate" ) );
534  }
535  break;
536 
537  default:
538  retExt( status,
539  ( status, SESSION_ERRINFO,
540  "Unknown returned certificate encapsulation type %d",
541  tag ) );
542  }
543  if( cryptStatusError( status ) )
544  return( status );
545 
546  /* Import the certificate as a cryptlib object */
547  setMessageCreateObjectIndirectInfo( &createInfo, bodyInfoPtr, bodyLength,
552  if( cryptStatusError( status ) )
553  {
554  retExt( status,
555  ( status, SESSION_ERRINFO,
556  "Invalid returned certificate" ) );
557  }
558  sessionInfoPtr->iCertResponse = createInfo.cryptHandle;
559 
560  /* In order to acknowledge receipt of this message we have to return at a
561  later point a hash of the certificate carried in this message created
562  using the hash algorithm used in the certificate signature. This
563  makes the CMP-level transport layer dependant on the certificate
564  format it's carrying (so the code will repeatedly break every time a
565  new certificate hash algorithm or certificate format is added), but
566  that's what the standard requires */
567  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
568  IMESSAGE_GETATTRIBUTE, &value,
569  CRYPT_IATTRIBUTE_CERTHASHALGO );
570  if( cryptStatusError( status ) )
571  {
572  retExt( status,
573  ( status, SESSION_ERRINFO,
574  "Couldn't extract confirmation hash type from returned "
575  "certificate" ) );
576  }
577  if( ( value != CRYPT_ALGO_MD5 && value != CRYPT_ALGO_SHA1 && \
578  value != CRYPT_ALGO_SHA2 && value != CRYPT_ALGO_SHAng ) || \
579  !algoAvailable( value ) )
580  {
581  /* Certificates can only provide fingerprints using a subset of
582  available hash algorithms */
585  "Can't confirm certificate issue using hash algorithm %d",
586  value ) );
587  }
588  protocolInfo->confHashAlgo = value;
589 
590  return( CRYPT_OK );
591  }
592 
593 /* Read conf body:
594 
595  body [19] EXPLICIT SEQUENCE { -- Processed by caller
596  SEQUENCE {
597  certHash OCTET STRING
598  certReqID INTEGER (0),
599  }
600  }
601 
602  The outer tag and SEQUENCE have already been processed by the caller */
603 
604 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
605 static int readConfBody( INOUT STREAM *stream,
606  INOUT SESSION_INFO *sessionInfoPtr,
607  INOUT CMP_PROTOCOL_INFO *protocolInfo,
608  IN_ENUM_OPT( CTAG_PB ) \
609  const CMP_MESSAGE_TYPE messageType,
610  IN_LENGTH_SHORT const int messageLength )
611  {
612  static const MAP_TABLE hashMapTable[] = {
613  /* We're the server so we control the hash algorithm that'll be
614  used, which means that it'll always be one of the following */
618  { CRYPT_ERROR, 0 }, { CRYPT_ERROR, 0 }
619  };
621  BYTE certHash[ CRYPT_MAX_HASHSIZE + 8 ];
622  int compareMessageValue, length, status;
623 
624  assert( isWritePtr( stream, sizeof( STREAM ) ) );
625  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
626  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
627 
628  REQUIRES( messageType >= CTAG_PB_IR && messageType < CTAG_PB_LAST );
629  /* CTAG_PB_IR == 0 so this is the same as _NONE */
630 
631  /* If there's no certStatus then the client has rejected the
632  certificate. This isn't an explicit error since it's a valid
633  protocol outcome so we return an OK status but set the overall
634  protocol status to a generic error value to indicate that we don't
635  want to continue normally */
636  if( messageLength <= 0 )
637  {
638  protocolInfo->status = CRYPT_ERROR;
639  return( CRYPT_OK );
640  }
641 
642  /* Read the client's returned confirmation information */
643  readSequence( stream, NULL );
644  status = readOctetString( stream, certHash, &length,
645  8, CRYPT_MAX_HASHSIZE );
646  if( cryptStatusError( status ) )
647  {
648  retExt( status,
649  ( status, SESSION_ERRINFO,
650  "Invalid certificate confirmation" ) );
651  }
652 
653  /* Compare the certificate hash to the one sent by the client. Since
654  we're the server this is a cryptlib-issued certificate so we know
655  that it'll always use one of the SHA family of hashes */
656  status = mapValue( protocolInfo->confHashAlgo, &compareMessageValue,
657  hashMapTable,
658  FAILSAFE_ARRAYSIZE( hashMapTable, MAP_TABLE ) );
659  ENSURES( cryptStatusOK( status ) );
660  setMessageData( &msgData, certHash, length );
661  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
662  IMESSAGE_COMPARE, &msgData,
663  compareMessageValue );
664  if( cryptStatusError( status ) )
665  {
666  /* The user is confirming an unknown certificate, the best that we
667  can do is return a generic certificate-mismatch error */
668  protocolInfo->pkiFailInfo = CMPFAILINFO_BADCERTID;
671  "Returned certificate hash doesn't match issued "
672  "certificate" ) );
673  }
674  return( CRYPT_OK );
675  }
676 
677 /* Read genMsg body:
678 
679  body [21] EXPLICIT SEQUENCE OF { -- Processed by caller
680  SEQUENCE {
681  infoType OBJECT IDENTIFIER,
682  intoValue ANY DEFINED BY infoType OPTIONAL
683  }
684  }
685 
686  The outer tag and SEQUENCE have already been processed by the caller */
687 
688 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
689 static int readGenMsgBody( INOUT STREAM *stream,
690  INOUT SESSION_INFO *sessionInfoPtr,
691  STDC_UNUSED CMP_PROTOCOL_INFO *protocolInfo,
692  IN_ENUM_OPT( CTAG_PB ) \
693  const CMP_MESSAGE_TYPE messageType,
694  IN_LENGTH_SHORT const int messageLength )
695  {
696  const BOOLEAN isRequest = ( messageType == CTAG_PB_GENM ) ? TRUE : FALSE;
697  int status;
698 
699  /* If it's a request GenMsg, check for a PKIBoot request. Note that
700  this assumes that the only GenMsg that we'll receive is a PKIBoot,
701  there are no known other users of this message type and even if
702  someone did decide to use it, it's not clear what we're supposed to
703  to with the contents */
704  if( isRequest )
705  {
706  readSequence( stream, NULL );
707  status = readFixedOID( stream, OID_PKIBOOT,
708  sizeofOID( OID_PKIBOOT ) );
709  if( cryptStatusError( status ) )
710  {
713  "Invalid genMsg type, expected PKIBoot request" ) );
714  }
715  return( CRYPT_OK );
716  }
717 
718  /* It's a PKIBoot response with the InfoTypeAndValue handled as CMS
719  content (see the comment for writeGenMsgResponseBody() in
720  cmp_wrmsg.c), import the certificate trust list. Since this isn't a
721  true certificate chain and isn't used as such, we use data-only
722  certificates (specified using the special-case CRYPT_ICERTTYPE_CTL
723  type specifier) */
724  status = importCertFromStream( stream, &sessionInfoPtr->iCertResponse,
726  CRYPT_ICERTTYPE_CTL, messageLength );
727  if( cryptStatusError( status ) )
728  retExt( status,
729  ( status, SESSION_ERRINFO, "Invalid PKIBoot response" ) );
730  return( CRYPT_OK );
731  }
732 
733 /* Read error body:
734 
735  body [23] EXPLICIT SEQUENCE { -- Processed by caller
736  status PKIFailureInfo,
737  errorCode INTEGER OPTIONAL,
738  errorMsg SEQUENCE ... OPTIONAL -- Ignored
739  }
740 
741  The outer tag and SEQUENCE have already been processed by the caller */
742 
743 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
744 static int readErrorBody( INOUT STREAM *stream,
745  INOUT SESSION_INFO *sessionInfoPtr,
746  STDC_UNUSED CMP_PROTOCOL_INFO *protocolInfo,
747  STDC_UNUSED const CMP_MESSAGE_TYPE messageType,
748  IN_LENGTH_SHORT const int messageLength )
749  {
750  ERROR_INFO *errorInfo = &sessionInfoPtr->errorInfo;
751  const char *peerTypeString = isServer( sessionInfoPtr ) ? \
752  "Client" : "Server";
753  const int endPos = stell( stream ) + messageLength;
754  int status;
755 
756  assert( isWritePtr( stream, sizeof( STREAM ) ) );
757  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
758 
759  /* Read the outer wrapper and PKI status information. In another one of
760  CMP's many wonderful features there are two places to communicate
761  error information with no clear indication in the spec as to which one
762  is meant to be used. To deal with this we stop at the first one that
763  contains an error status */
764  status = readPkiStatusInfo( stream, isServer( sessionInfoPtr ),
765  &sessionInfoPtr->errorInfo );
766  if( cryptStatusError( status ) )
767  return( status );
768 
769  /* In addition to the PKI status information there can be a second lot
770  of error information which is exactly the same only different, so if
771  we haven't got anything from the status information we check to see
772  whether this layer can give us anything */
773  if( stell( stream ) < endPos && peekTag( stream ) == BER_INTEGER )
774  {
775  long value;
776 
777  status = readShortInteger( stream, &value );
778  if( cryptStatusOK( status ) && \
780  status = CRYPT_ERROR_BADDATA;
781  if( cryptStatusOK( status ) )
782  {
783  const int errorCode = ( int ) value;
784 
786  ( CRYPT_ERROR_FAILED, errorInfo,
787  "%s returned nonspecific failure code %d",
788  peerTypeString, errorCode ) );
789  }
790  }
791  if( stell( stream ) < endPos && peekTag( stream ) == BER_SEQUENCE )
792  status = readUniversal( stream );
793 
794  /* Make sure that we always return an error code. That is, if there's no
795  error in reading the error information then we still have to return an
796  error because what we've successfully read is a report of an error */
797  retExt( cryptStatusError( status ) ? status : CRYPT_ERROR_FAILED,
798  ( cryptStatusError( status ) ? status : CRYPT_ERROR_FAILED,
799  errorInfo,
800  "%s returned nonspecific failure code", peerTypeString ) );
801  }
802 
803 /****************************************************************************
804 * *
805 * Read Function Access Information *
806 * *
807 ****************************************************************************/
808 
809 typedef struct {
810  const CMP_MESSAGE_TYPE type;
811  const READMESSAGE_FUNCTION function;
812  } MESSAGEREAD_INFO;
813 static const MESSAGEREAD_INFO FAR_BSS messageReadTable[] = {
814  { CTAG_PB_IR, readRequestBody },
815  { CTAG_PB_CR, readRequestBody },
816  { CTAG_PB_P10CR, readRequestBody },
817  { CTAG_PB_KUR, readRequestBody },
818  { CTAG_PB_RR, readRequestBody },
819  { CTAG_PB_IP, readResponseBody },
820  { CTAG_PB_CP, readResponseBody },
821  { CTAG_PB_KUP, readResponseBody },
822  { CTAG_PB_RP, readResponseBody },
823  { CTAG_PB_CERTCONF, readConfBody },
824  { CTAG_PB_PKICONF, readConfBody },
825  { CTAG_PB_GENM, readGenMsgBody },
826  { CTAG_PB_GENP, readGenMsgBody },
827  { CTAG_PB_ERROR, readErrorBody },
828  { CTAG_PB_LAST, NULL }, { CTAG_PB_LAST, NULL }
829  };
830 
831 CHECK_RETVAL_PTR \
832 READMESSAGE_FUNCTION getMessageReadFunction( IN_ENUM_OPT( CMP_MESSAGE ) \
833  const CMP_MESSAGE_TYPE messageType )
834  {
835  int i;
836 
837  REQUIRES_N( messageType >= CTAG_PB_IR && messageType < CTAG_PB_LAST );
838  /* CTAG_PB_IR == 0 so this is the same as _NONE */
839 
840  for( i = 0;
841  messageReadTable[ i ].type != CTAG_PB_LAST && \
842  i < FAILSAFE_ARRAYSIZE( messageReadTable, MESSAGEREAD_INFO );
843  i++ )
844  {
845  if( messageReadTable[ i ].type == messageType )
846  return( messageReadTable[ i ].function );
847  }
848  ENSURES_N( i < FAILSAFE_ARRAYSIZE( messageReadTable, MESSAGEREAD_INFO ) );
849 
850  return( NULL );
851  }
852 #endif /* USE_CMP */