cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
rtcs.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib RTCS Session Management *
4 * Copyright Peter Gutmann 1999-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "asn1.h"
11  #include "asn1_ext.h"
12  #include "session.h"
13 #else
14  #include "crypt.h"
15  #include "enc_dec/asn1.h"
16  #include "enc_dec/asn1_ext.h"
17  #include "session/session.h"
18 #endif /* Compiler-specific includes */
19 
20 #ifdef USE_RTCS
21 
22 /* RTCS HTTP content types */
23 
24 #define RTCS_CONTENT_TYPE_REQ "application/rtcs-request"
25 #define RTCS_CONTENT_TYPE_REQ_LEN 24
26 #define RTCS_CONTENT_TYPE_RESP "application/rtcs-response"
27 #define RTCS_CONTENT_TYPE_RESP_LEN 25
28 
29 /* The action to take to process an RTCS request/response */
30 
31 typedef enum {
32  ACTION_NONE, /* No processing */
33  ACTION_UNWRAP, /* Unwrap raw data */
34  ACTION_CRYPT, /* Decrypt data */
35  ACTION_SIGN, /* Sig.check data */
36  ACTION_LAST /* Last valid action type */
37  } ACTION_TYPE;
38 
39 /* RTCS protocol state information. This is passed around various
40  subfunctions that handle individual parts of the protocol */
41 
42 typedef struct {
43  /* State variable information. The nonce is copied from the request to
44  the response to prevent replay attacks */
45  BYTE nonce[ CRYPT_MAX_HASHSIZE + 8 ];
46  int nonceSize;
47  } RTCS_PROTOCOL_INFO;
48 
49 /****************************************************************************
50 * *
51 * Utility Functions *
52 * *
53 ****************************************************************************/
54 
55 /* Check for a valid-looking RTCS request/response header */
56 
57 static const CMS_CONTENT_INFO FAR_BSS oidInfoSignedData = { 0, 3 };
58 static const CMS_CONTENT_INFO FAR_BSS oidInfoEnvelopedData = { 0, 3 };
59 
60 static const OID_INFO FAR_BSS envelopeOIDinfo[] = {
61  { OID_CRYPTLIB_RTCSREQ, ACTION_UNWRAP },
62  { OID_CRYPTLIB_RTCSRESP, ACTION_UNWRAP },
63  { OID_CRYPTLIB_RTCSRESP_EXT, ACTION_UNWRAP },
64  { OID_CMS_SIGNEDDATA, ACTION_SIGN, &oidInfoSignedData },
65  { OID_CMS_ENVELOPEDDATA, ACTION_CRYPT, &oidInfoEnvelopedData },
66  { NULL, 0 }, { NULL, 0 }
67  };
68 
70 static int checkRtcsHeader( IN_BUFFER( rtcsDataLength ) const void *rtcsData,
71  IN_LENGTH_SHORT const int rtcsDataLength,
72  OUT_ENUM_OPT( ACTION ) ACTION_TYPE *actionType )
73  {
74  STREAM stream;
75  int status;
76 
77  assert( isReadPtr( rtcsData, rtcsDataLength ) );
78  assert( isWritePtr( actionType, sizeof( ACTION_TYPE ) ) );
79 
80  REQUIRES( rtcsDataLength > 0 && rtcsDataLength < MAX_INTLENGTH_SHORT );
81 
82  /* Clear return value */
83  *actionType = ACTION_NONE;
84 
85  /* We've got a valid response, check the CMS encapsulation */
86  sMemConnect( &stream, rtcsData, rtcsDataLength );
87  status = readCMSheader( &stream, envelopeOIDinfo,
88  FAILSAFE_ARRAYSIZE( envelopeOIDinfo, OID_INFO ),
89  NULL, READCMS_FLAG_NONE );
90  sMemDisconnect( &stream );
91  if( cryptStatusError( status ) )
92  return( status );
93  *actionType = status;
94  return( CRYPT_OK );
95  }
96 
97 /****************************************************************************
98 * *
99 * Client-side Functions *
100 * *
101 ****************************************************************************/
102 
103 /* Send a request to an RTCS server */
104 
106 static int sendClientRequest( INOUT SESSION_INFO *sessionInfoPtr )
107  {
109  int status;
110 
111  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
112 
113  /* Get the encoded request data and wrap it up for sending */
114  setMessageData( &msgData, sessionInfoPtr->receiveBuffer,
115  sessionInfoPtr->receiveBufSize );
116  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
117  IMESSAGE_CRT_EXPORT, &msgData,
118  CRYPT_ICERTFORMAT_DATA );
119  if( cryptStatusError( status ) )
120  {
121  retExt( status,
122  ( status, SESSION_ERRINFO,
123  "Couldn't get RTCS request data from RTCS request "
124  "object" ) );
125  }
126  status = envelopeWrap( sessionInfoPtr->receiveBuffer, msgData.length,
127  sessionInfoPtr->receiveBuffer,
128  sessionInfoPtr->receiveBufSize,
129  &sessionInfoPtr->receiveBufEnd,
131  CRYPT_UNUSED );
132  if( cryptStatusError( status ) )
133  {
134  retExt( status,
135  ( status, SESSION_ERRINFO,
136  "Couldn't CMS-envelope RTCS request data" ) );
137  }
138  DEBUG_DUMP_FILE( "rtcs_req", sessionInfoPtr->receiveBuffer,
139  sessionInfoPtr->receiveBufEnd );
140 
141  /* Send the request to the responder */
142  return( writePkiDatagram( sessionInfoPtr, RTCS_CONTENT_TYPE_REQ,
143  RTCS_CONTENT_TYPE_REQ_LEN ) );
144  }
145 
146 /* Read the response from the RTCS server */
147 
149 static int readServerResponse( INOUT SESSION_INFO *sessionInfoPtr )
150  {
151  CRYPT_CERTIFICATE iCmsAttributes;
152  MESSAGE_CREATEOBJECT_INFO createInfo;
154  ACTION_TYPE actionType;
155  BYTE nonceBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
156  int dataLength, sigResult, status;
157 
158  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
159 
160  /* Read the response from the responder */
161  status = readPkiDatagram( sessionInfoPtr );
162  if( cryptStatusError( status ) )
163  return( status );
164  DEBUG_DUMP_FILE( "rtcs_resp", sessionInfoPtr->receiveBuffer,
165  sessionInfoPtr->receiveBufEnd );
166  status = checkRtcsHeader( sessionInfoPtr->receiveBuffer,
167  sessionInfoPtr->receiveBufEnd, &actionType );
168  if( cryptStatusError( status ) )
169  {
170  retExt( status,
171  ( status, SESSION_ERRINFO,
172  "Invalid RTCS response header" ) );
173  }
174  if( actionType != ACTION_SIGN )
175  {
176  retExt( status,
177  ( status, SESSION_ERRINFO,
178  "Unexpected RTCS encapsulation type %d", actionType ) );
179  }
180 
181  /* Sig.check the data using the responder's key */
182  status = envelopeSigCheck( sessionInfoPtr->receiveBuffer,
183  sessionInfoPtr->receiveBufEnd,
184  sessionInfoPtr->receiveBuffer,
185  sessionInfoPtr->receiveBufSize, &dataLength,
186  CRYPT_UNUSED, &sigResult, NULL,
187  &iCmsAttributes );
188  if( cryptStatusError( status ) )
189  {
190  retExt( status,
191  ( status, SESSION_ERRINFO,
192  "Invalid CMS-enveloped RTCS response data" ) );
193  }
194 
195  /* Make sure that the nonce in the response matches the one in the
196  request */
197  setMessageData( &msgData, nonceBuffer, CRYPT_MAX_HASHSIZE );
198  status = krnlSendMessage( iCmsAttributes, IMESSAGE_GETATTRIBUTE_S,
199  &msgData, CRYPT_CERTINFO_CMS_NONCE );
200  krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
201  if( cryptStatusOK( status ) )
202  {
203  MESSAGE_DATA responseMsgData;
204  BYTE responseNonceBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
205 
206  setMessageData( &responseMsgData, responseNonceBuffer,
208  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
209  IMESSAGE_GETATTRIBUTE_S, &responseMsgData,
211  if( cryptStatusOK( status ) && \
212  ( msgData.length < 4 || \
213  msgData.length != responseMsgData.length || \
214  memcmp( msgData.data, responseMsgData.data, msgData.length ) ) )
215  status = CRYPT_ERROR_SIGNATURE;
216  }
217  krnlSendNotifier( sessionInfoPtr->iCertRequest, IMESSAGE_DECREFCOUNT );
218  sessionInfoPtr->iCertRequest = CRYPT_ERROR;
219  if( cryptStatusError( status ) )
220  {
221  /* The response doesn't contain a nonce or it doesn't match what
222  we sent, we can't trust it. The best error that we can return
223  here is a signature error to indicate that the integrity check
224  failed */
225  retExt( status,
226  ( status, SESSION_ERRINFO,
227  ( status != CRYPT_ERROR_SIGNATURE ) ? \
228  "RTCS response doesn't contain a nonce" : \
229  "RTCS response nonce doesn't match the one in the "
230  "request" ) );
231  }
232 
233  /* Everything is OK, import the response */
235  sessionInfoPtr->receiveBuffer, dataLength,
239  &createInfo, OBJECT_TYPE_CERTIFICATE );
240  if( cryptStatusError( status ) )
241  {
242  retExt( status,
243  ( status, SESSION_ERRINFO,
244  "Invalid RTCS response contents" ) );
245  }
246  sessionInfoPtr->iCertResponse = createInfo.cryptHandle;
247 
248  return( CRYPT_OK );
249  }
250 
251 /****************************************************************************
252 * *
253 * Server-side Functions *
254 * *
255 ****************************************************************************/
256 
257 /* Read a request from an RTCS client */
258 
260 static int readClientRequest( INOUT SESSION_INFO *sessionInfoPtr,
261  INOUT RTCS_PROTOCOL_INFO *protocolInfo )
262  {
263  MESSAGE_CREATEOBJECT_INFO createInfo;
265  ACTION_TYPE actionType;
266  int dataLength, status;
267 
268  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
269  assert( isWritePtr( protocolInfo, sizeof( RTCS_PROTOCOL_INFO ) ) );
270 
271  /* Read the request data from the client. We don't write an error
272  response at this initial stage to prevent scanning/DOS attacks
273  (vir sapit qui pauca loquitur) */
274  status = readPkiDatagram( sessionInfoPtr );
275  if( cryptStatusError( status ) )
276  return( status );
277  DEBUG_DUMP_FILE( "rtcs_sreq", sessionInfoPtr->receiveBuffer,
278  sessionInfoPtr->receiveBufEnd );
279  status = checkRtcsHeader( sessionInfoPtr->receiveBuffer,
280  sessionInfoPtr->receiveBufEnd, &actionType );
281  if( cryptStatusError( status ) )
282  {
283  retExt( status,
284  ( status, SESSION_ERRINFO, "Invalid RTCS request header" ) );
285  }
286  if( actionType != ACTION_UNWRAP )
287  {
288  retExt( status,
289  ( status, SESSION_ERRINFO,
290  "Unexpected RTCS encapsulation type %d", actionType ) );
291  }
292  status = envelopeUnwrap( sessionInfoPtr->receiveBuffer,
293  sessionInfoPtr->receiveBufEnd,
294  sessionInfoPtr->receiveBuffer,
295  sessionInfoPtr->receiveBufSize, &dataLength,
296  CRYPT_UNUSED );
297  if( cryptStatusError( status ) )
298  {
299  retExt( status,
300  ( status, SESSION_ERRINFO,
301  "Invalid CMS-enveloped RTCS request data" ) );
302  }
303 
304  /* Create an RTCS response. We always create this since an empty
305  response is sent to indicate an error condition */
308  &createInfo, OBJECT_TYPE_CERTIFICATE );
309  if( cryptStatusError( status ) )
310  return( status );
311  sessionInfoPtr->iCertResponse = createInfo.cryptHandle;
312 
313  /* Import the request as a cryptlib object and read the nonce from it */
315  sessionInfoPtr->receiveBuffer, dataLength,
319  &createInfo, OBJECT_TYPE_CERTIFICATE );
320  if( cryptStatusOK( status ) )
321  {
322  setMessageData( &msgData, protocolInfo->nonce, CRYPT_MAX_HASHSIZE );
323  status = krnlSendMessage( createInfo.cryptHandle,
324  IMESSAGE_GETATTRIBUTE_S, &msgData,
326  if( cryptStatusOK( status ) )
327  protocolInfo->nonceSize = msgData.length;
328  else
329  {
330  /* We couldn't read the nonce, delete the request object prior
331  to exiting */
333  }
334  }
335  if( cryptStatusError( status ) )
336  {
337  retExt( status,
338  ( status, SESSION_ERRINFO,
339  "Invalid RTCS request contents" ) );
340  }
341 
342  /* Create an RTCS response and add the request information to it */
343  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
345  &createInfo.cryptHandle,
346  CRYPT_IATTRIBUTE_RTCSREQUEST );
348  if( cryptStatusError( status ) )
349  {
350  retExt( status,
351  ( status, SESSION_ERRINFO,
352  "Couldn't create RTCS response from request" ) );
353  }
354  return( CRYPT_OK );
355  }
356 
357 /* Return a response to an RTCS client */
358 
360 static int sendServerResponse( INOUT SESSION_INFO *sessionInfoPtr,
361  INOUT RTCS_PROTOCOL_INFO *protocolInfo )
362  {
363  CRYPT_CERTIFICATE iCmsAttributes = CRYPT_UNUSED;
365  int status;
366 
367  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
368  assert( isWritePtr( protocolInfo, sizeof( RTCS_PROTOCOL_INFO ) ) );
369 
370  /* Check the entries from the request against the certificate store and
371  sign the resulting status information ("Love, ken"). Note that
372  CRYPT_ERROR_INVALID is a valid return status for the sigcheck call
373  since it indicates that one (or more) of the certificates are
374  invalid */
375  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
376  IMESSAGE_CRT_SIGCHECK, NULL,
377  sessionInfoPtr->cryptKeyset );
378  if( cryptStatusError( status ) && status != CRYPT_ERROR_INVALID )
379  {
380  retExt( status,
381  ( status, SESSION_ERRINFO,
382  "Couldn't check RTCS request against certificate "
383  "store" ) );
384  }
385 
386  /* If there's a nonce present, create CMS attributes to contain it */
387  if( protocolInfo->nonceSize > 0 )
388  {
389  MESSAGE_CREATEOBJECT_INFO createInfo;
390 
391  setMessageCreateObjectInfo( &createInfo,
395  &createInfo, OBJECT_TYPE_CERTIFICATE );
396  if( cryptStatusError( status ) )
397  return( status );
398  iCmsAttributes = createInfo.cryptHandle;
399  setMessageData( &msgData, protocolInfo->nonce,
400  protocolInfo->nonceSize );
401  status = krnlSendMessage( iCmsAttributes, IMESSAGE_SETATTRIBUTE_S,
402  &msgData, CRYPT_CERTINFO_CMS_NONCE );
403  if( cryptStatusError( status ) )
404  {
405  krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
406  return( status );
407  }
408  }
409 
410  /* Sign the response data using the responder's key and send it to the
411  client */
412  setMessageData( &msgData, sessionInfoPtr->receiveBuffer,
413  sessionInfoPtr->receiveBufSize );
414  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
415  IMESSAGE_CRT_EXPORT, &msgData,
416  CRYPT_ICERTFORMAT_DATA );
417  if( cryptStatusOK( status ) )
418  {
419  status = envelopeSign( sessionInfoPtr->receiveBuffer, msgData.length,
420  sessionInfoPtr->receiveBuffer,
421  sessionInfoPtr->receiveBufSize,
422  &sessionInfoPtr->receiveBufEnd,
424  sessionInfoPtr->privateKey, iCmsAttributes );
425  }
426  if( iCmsAttributes != CRYPT_UNUSED )
427  krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
428  if( cryptStatusError( status ) )
429  {
430  retExt( status,
431  ( status, SESSION_ERRINFO,
432  "Couldn't CMS-envelope RTCS response" ) );
433  }
434  DEBUG_DUMP_FILE( "rtcs_sresp", sessionInfoPtr->receiveBuffer,
435  sessionInfoPtr->receiveBufEnd );
436  return( writePkiDatagram( sessionInfoPtr, RTCS_CONTENT_TYPE_RESP,
437  RTCS_CONTENT_TYPE_RESP_LEN ) );
438  }
439 
440 /****************************************************************************
441 * *
442 * Init/Shutdown Functions *
443 * *
444 ****************************************************************************/
445 
446 /* Exchange data with an RTCS client/server */
447 
449 static int clientTransact( INOUT SESSION_INFO *sessionInfoPtr )
450  {
451  int status;
452 
453  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
454 
455  /* Get certificate status information from the server */
456  status = sendClientRequest( sessionInfoPtr );
457  if( cryptStatusOK( status ) )
458  status = readServerResponse( sessionInfoPtr );
459  return( status );
460  }
461 
463 static int serverTransact( INOUT SESSION_INFO *sessionInfoPtr )
464  {
465  RTCS_PROTOCOL_INFO protocolInfo;
466  int status;
467 
468  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
469 
470  /* Send certificate status information to the client */
471  memset( &protocolInfo, 0, sizeof( RTCS_PROTOCOL_INFO ) );
472  status = readClientRequest( sessionInfoPtr, &protocolInfo );
473  if( cryptStatusOK( status ) )
474  status = sendServerResponse( sessionInfoPtr, &protocolInfo );
475  return( status );
476  }
477 
478 /****************************************************************************
479 * *
480 * Control Information Management Functions *
481 * *
482 ****************************************************************************/
483 
484 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
485 static int setAttributeFunction( INOUT SESSION_INFO *sessionInfoPtr,
486  IN const void *data,
488  {
489  const CRYPT_CERTIFICATE rtcsRequest = *( ( CRYPT_CERTIFICATE * ) data );
490  MESSAGE_DATA msgData = { NULL, 0 };
491  int status;
492 
493  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
494  assert( isReadPtr( data, sizeof( int ) ) );
495 
496  REQUIRES( type == CRYPT_SESSINFO_REQUEST );
497 
498  /* Make sure that everything is set up ready to go. Since RTCS requests
499  aren't signed like normal certificate objects we can't just check the
500  immutable attribute but have to perform a dummy export for which the
501  certificate export code will return an error status if there's a
502  problem with the request. If not, it pseudo-signs the request (if it
503  hasn't already done so) and prepares it for use */
504  status = krnlSendMessage( rtcsRequest, IMESSAGE_CRT_EXPORT, &msgData,
505  CRYPT_ICERTFORMAT_DATA );
506  if( cryptStatusError( status ) )
507  return( CRYPT_ARGERROR_NUM1 );
508 
509  /* If we haven't already got a server name explicitly set, try and get
510  it from the request. This is an opportunistic action so we ignore
511  any potential error, the caller can still set the value explicitly */
512  if( findSessionInfo( sessionInfoPtr->attributeList,
513  CRYPT_SESSINFO_SERVER_NAME ) == NULL )
514  {
515  char buffer[ MAX_URL_SIZE + 8 ];
516 
517  setMessageData( &msgData, buffer, MAX_URL_SIZE );
518  status = krnlSendMessage( rtcsRequest, IMESSAGE_GETATTRIBUTE_S,
519  &msgData, CRYPT_IATTRIBUTE_RESPONDERURL );
520  if( cryptStatusOK( status ) )
521  ( void ) krnlSendMessage( sessionInfoPtr->objectHandle,
522  IMESSAGE_SETATTRIBUTE_S, &msgData,
524  }
525 
526  /* Add the request and increment its usage count */
527  krnlSendNotifier( rtcsRequest, IMESSAGE_INCREFCOUNT );
528  sessionInfoPtr->iCertRequest = rtcsRequest;
529 
530  return( CRYPT_OK );
531  }
532 
533 /****************************************************************************
534 * *
535 * Session Access Routines *
536 * *
537 ****************************************************************************/
538 
539 /* Open/close an RTCS session */
540 
542 int setAccessMethodRTCS( INOUT SESSION_INFO *sessionInfoPtr )
543  {
544  static const PROTOCOL_INFO protocolInfo = {
545  /* General session information */
546  TRUE, /* Request-response protocol */
547  SESSION_ISHTTPTRANSPORT, /* Flags */
548  80, /* HTTP port */
549  SESSION_NEEDS_REQUEST, /* Client flags */
550  SESSION_NEEDS_PRIVATEKEY | /* Server flags */
552  SESSION_NEEDS_PRIVKEYCERT | \
553  SESSION_NEEDS_KEYSET,
554  1, 1, 1 /* Version 1 */
555 
556  /* Protocol-specific information */
557  };
558 
559  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
560 
561  /* Set the access method pointers */
562  sessionInfoPtr->protocolInfo = &protocolInfo;
563  if( isServer( sessionInfoPtr ) )
564  sessionInfoPtr->transactFunction = serverTransact;
565  else
566  sessionInfoPtr->transactFunction = clientTransact;
567  sessionInfoPtr->setAttributeFunction = setAttributeFunction;
568 
569  return( CRYPT_OK );
570  }
571 #endif /* USE_RTCS */