cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ocsp.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib OCSP Session Management *
4 * Copyright Peter Gutmann 1999-2008 *
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 #else
15  #include "crypt.h"
16  #include "enc_dec/asn1.h"
17  #include "enc_dec/asn1_ext.h"
18  #include "session/session.h"
19 #endif /* Compiler-specific includes */
20 
21 #ifdef USE_OCSP
22 
23 /* OCSP HTTP content types */
24 
25 #define OCSP_CONTENT_TYPE_REQ "application/ocsp-request"
26 #define OCSP_CONTENT_TYPE_REQ_LEN 24
27 #define OCSP_CONTENT_TYPE_RESP "application/ocsp-response"
28 #define OCSP_CONTENT_TYPE_RESP_LEN 25
29 
30 /* OCSP query/response types */
31 
32 typedef enum {
33  OCSPRESPONSE_TYPE_NONE, /* No response type */
34  OCSPRESPONSE_TYPE_OCSP, /* OCSP standard response */
35  OCSPRESPONSE_TYPE_LAST /* Last valid response type */
36  } OCSPRESPONSE_TYPE;
37 
38 /* OCSP response status values */
39 
40 enum { OCSP_RESP_SUCCESSFUL, OCSP_RESP_MALFORMEDREQUEST,
41  OCSP_RESP_INTERNALERROR, OCSP_RESP_TRYLATER, OCSP_RESP_DUMMY,
42  OCSP_RESP_SIGREQUIRED, OCSP_RESP_UNAUTHORISED };
43 
44 /* OCSP protocol state information. This is passed around various
45  subfunctions that handle individual parts of the protocol */
46 
47 typedef struct {
48  OCSPRESPONSE_TYPE responseType; /* Response type to return */
49  } OCSP_PROTOCOL_INFO;
50 
51 /****************************************************************************
52 * *
53 * Utility Functions *
54 * *
55 ****************************************************************************/
56 
57 /* Deliver an Einladung betreff Kehrseite to the client. We don't bother
58  checking the return value since there's nothing that we can do in the
59  case of an error except close the connection, which we do anyway since
60  this is the last message */
61 
62 STDC_NONNULL_ARG( ( 1, 2 ) ) \
63 static void sendErrorResponse( INOUT SESSION_INFO *sessionInfoPtr,
64  IN_BUFFER( responseDataLength ) \
65  const void *responseData,
66  IN_LENGTH_SHORT const int responseDataLength )
67  {
68  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
69  assert( isReadPtr( responseData, responseDataLength ) );
70 
71  REQUIRES_V( responseDataLength > 0 && \
72  responseDataLength < MAX_INTLENGTH_SHORT );
73 
74  /* Since we're already in an error state there's not much that we can do
75  in terms of alerting the user if a further error occurs when writing
76  the error response, so we ignore any potential write errors that occur
77  at this point */
78  memcpy( sessionInfoPtr->receiveBuffer, responseData,
79  responseDataLength );
80  sessionInfoPtr->receiveBufEnd = responseDataLength;
81  ( void ) writePkiDatagram( sessionInfoPtr, OCSP_CONTENT_TYPE_RESP,
82  OCSP_CONTENT_TYPE_RESP_LEN );
83  }
84 
85 /* Compare the nonce in a request with the returned nonce in the response */
86 
88 static int checkNonce( IN_HANDLE const CRYPT_CERTIFICATE iCertResponse,
89  IN_BUFFER( requestNonceLength ) const void *requestNonce,
90  IN_LENGTH_SHORT const int requestNonceLength )
91  {
92  MESSAGE_DATA responseMsgData;
93  BYTE responseNonceBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
94 
95  assert( isReadPtr( requestNonce, requestNonceLength ) );
96 
97  REQUIRES( isHandleRangeValid( iCertResponse ) );
98  REQUIRES( requestNonceLength > 0 && \
99  requestNonceLength < MAX_INTLENGTH_SHORT );
100 
101  /* Make sure that the nonce has a plausible length */
102  if( requestNonceLength < 4 || requestNonceLength > CRYPT_MAX_HASHSIZE )
103  return( CRYPT_ERROR_BADDATA );
104 
105  /* Try and read the nonce from the response */
106  setMessageData( &responseMsgData, responseNonceBuffer,
107  CRYPT_MAX_HASHSIZE );
108  if( cryptStatusError( \
110  &responseMsgData, CRYPT_CERTINFO_OCSP_NONCE ) ) )
111  return( CRYPT_ERROR_NOTFOUND );
112 
113  /* Make sure that the two nonces match. The comparison is in theory
114  somewhat complex because OCSP never specifies how the nonce is meant
115  to be encoded so it's possible that some implementations will use
116  things like TSP's bizarre INTEGER rather than the obvious and logical
117  OCTET STRING. In theory this means we might need to check for the
118  INTEGER-encoding alternatives that arise due to sign bits, but this
119  doesn't seem to be required in practice since everyone use a de facto
120  encoding of OCTET STRING */
121  if( requestNonceLength == responseMsgData.length && \
122  !memcmp( requestNonce, responseMsgData.data, requestNonceLength ) )
123  return( TRUE );
124 
125  return( CRYPT_ERROR_SIGNATURE );
126  }
127 
128 /****************************************************************************
129 * *
130 * Client-side Functions *
131 * *
132 ****************************************************************************/
133 
134 /* OID information used to read responses */
135 
136 static const OID_INFO FAR_BSS ocspOIDinfo[] = {
137  { OID_OCSP_RESPONSE_OCSP, OCSPRESPONSE_TYPE_OCSP },
138  { NULL, 0 }, { NULL, 0 }
139  };
140 
141 /* Send a request to an OCSP server */
142 
144 static int sendClientRequest( INOUT SESSION_INFO *sessionInfoPtr )
145  {
147  int status;
148 
149  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
150 
151  /* Get the encoded request data. We store this in the session buffer,
152  which at its minimum size is roughly two orders of magnitude larger
153  than the request */
154  setMessageData( &msgData, sessionInfoPtr->receiveBuffer,
155  sessionInfoPtr->receiveBufSize );
156  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
157  IMESSAGE_CRT_EXPORT, &msgData,
158  CRYPT_ICERTFORMAT_DATA );
159  if( cryptStatusError( status ) )
160  {
161  retExt( status,
162  ( status, SESSION_ERRINFO,
163  "Couldn't get OCSP request data from OCSP request "
164  "object" ) );
165  }
166  sessionInfoPtr->receiveBufEnd = msgData.length;
167  DEBUG_DUMP_FILE( "ocsp_req", sessionInfoPtr->receiveBuffer,
168  sessionInfoPtr->receiveBufEnd );
169 
170  /* Send the request to the responder */
171  return( writePkiDatagram( sessionInfoPtr, OCSP_CONTENT_TYPE_REQ,
172  OCSP_CONTENT_TYPE_REQ_LEN ) );
173  }
174 
175 /* Read the response from the OCSP server */
176 
178 static int readServerResponse( INOUT SESSION_INFO *sessionInfoPtr )
179  {
180  CRYPT_CERTIFICATE iCertResponse;
182  STREAM stream;
183  BYTE nonceBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
184  const char *errorString = NULL;
185  int errorCode, responseType, length, status;
186 
187  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
188 
189  /* Read the response from the responder */
190  status = readPkiDatagram( sessionInfoPtr );
191  if( cryptStatusError( status ) )
192  return( status );
193  DEBUG_DUMP_FILE( "ocsp_resp", sessionInfoPtr->receiveBuffer,
194  sessionInfoPtr->receiveBufEnd );
195 
196  /* Try and extract an OCSP status code from the returned object:
197 
198  SEQUENCE {
199  respStatus ENUMERATED, -- 0 = OK
200  respBytes [0] EXPLICIT SEQUENCE {
201  ... */
202  sMemConnect( &stream, sessionInfoPtr->receiveBuffer,
203  sessionInfoPtr->receiveBufEnd );
204  readSequence( &stream, NULL );
205  status = readEnumerated( &stream, &errorCode );
206  if( cryptStatusError( status ) )
207  {
208  sMemDisconnect( &stream );
209  retExt( status,
210  ( status, SESSION_ERRINFO,
211  "Invalid OCSP response status data" ) );
212  }
213 
214  /* If it's an error status, try and translate it into something a bit
215  more meaningful. Some of the translations are a bit questionable,
216  but it's better than the generic no va response (which should
217  actually be "no marcha" in any case) */
218  switch( errorCode )
219  {
220  case OCSP_RESP_SUCCESSFUL:
221  status = CRYPT_OK;
222  break;
223 
224  case OCSP_RESP_TRYLATER:
225  errorString = "Try again later";
226  status = CRYPT_ERROR_NOTAVAIL;
227  break;
228 
229  case OCSP_RESP_SIGREQUIRED:
230  errorString = "Signed OCSP request required";
231  status = CRYPT_ERROR_SIGNATURE;
232  break;
233 
234  case OCSP_RESP_UNAUTHORISED:
235  errorString = "Client isn't authorised to perform query";
236  status = CRYPT_ERROR_PERMISSION;
237  break;
238 
239  default:
240  errorString = "Unknown error";
241  status = CRYPT_ERROR_INVALID;
242  break;
243  }
244  if( cryptStatusError( status ) )
245  {
246  sMemDisconnect( &stream );
247  retExt( status,
248  ( status, SESSION_ERRINFO,
249  "OCSP server returned status %d: %s",
250  errorCode, errorString ) );
251  }
252 
253  /* We've got a valid response, read the [0] EXPLICIT SEQUENCE { OID,
254  OCTET STRING { encapsulation and import the response into an OCSP
255  certificate object */
256  readConstructed( &stream, NULL, 0 ); /* responseBytes */
257  readSequence( &stream, NULL );
258  readOID( &stream, ocspOIDinfo, /* responseType */
259  FAILSAFE_ARRAYSIZE( ocspOIDinfo, OID_INFO ), &responseType );
260  status = readGenericHole( &stream, &length, 16, DEFAULT_TAG );
261  if( cryptStatusError( status ) )
262  {
263  sMemDisconnect( &stream );
264  retExt( status,
265  ( status, SESSION_ERRINFO,
266  "Invalid OCSP response data header" ) );
267  }
268  status = importCertFromStream( &stream, &iCertResponse,
271  sMemDisconnect( &stream );
272  if( cryptStatusError( status ) )
273  retExt( status,
274  ( status, SESSION_ERRINFO, "Invalid OCSP response data" ) );
275 
276  /* If the request went out with a nonce included (which it does by
277  default), make sure that it matches the nonce in the response */
278  setMessageData( &msgData, nonceBuffer, CRYPT_MAX_HASHSIZE );
279  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
280  IMESSAGE_GETATTRIBUTE_S, &msgData,
282  if( cryptStatusOK( status ) )
283  {
284  /* There's a nonce in the request, check that it matches the one in
285  the response */
286  status = checkNonce( iCertResponse, msgData.data, msgData.length );
287  if( cryptStatusError( status ) )
288  {
289  /* The response doesn't contain a nonce or it doesn't match what
290  we sent, we can't trust it. The best error that we can return
291  here is a signature error to indicate that the integrity
292  check failed (note that a later modification to OCSP, in an
293  attempt to make it scale, removed the nonce, thus breaking
294  the security of the protocol against replay attack. Since
295  the protocol is now broken against attack we treat a nonce-
296  less response from one of these responders as a failure,
297  since it's indistinguishable from an actual attack */
298  krnlSendNotifier( iCertResponse, IMESSAGE_DECREFCOUNT );
301  ( status == CRYPT_ERROR_NOTFOUND ) ? \
302  "OCSP response doesn't contain a nonce" : \
303  "OCSP response nonce doesn't match the one in the "
304  "request" ) );
305  }
306  }
307  krnlSendNotifier( sessionInfoPtr->iCertRequest, IMESSAGE_DECREFCOUNT );
308  sessionInfoPtr->iCertRequest = CRYPT_ERROR;
309  sessionInfoPtr->iCertResponse = iCertResponse;
310 
311  return( CRYPT_OK );
312  }
313 
314 /****************************************************************************
315 * *
316 * Server-side Functions *
317 * *
318 ****************************************************************************/
319 
320 /* Send an error response back to the client. Since there are only a small
321  number of these, we write back a fixed blob rather than encoding each
322  one */
323 
324 #define RESPONSE_SIZE 5
325 
326 static const BYTE FAR_BSS respBadRequest[] = {
327  0x30, 0x03, 0x0A, 0x01, 0x01 /* Rejection, malformed request */
328  };
329 static const BYTE FAR_BSS respIntError[] = {
330  0x30, 0x03, 0x0A, 0x01, 0x02 /* Rejection, internal error */
331  };
332 
333 /* Read a request from an OCSP client */
334 
336 static int readClientRequest( INOUT SESSION_INFO *sessionInfoPtr )
337  {
338  CRYPT_CERTIFICATE iOcspRequest;
339  MESSAGE_CREATEOBJECT_INFO createInfo;
340  STREAM stream;
341  int status;
342 
343  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
344 
345  /* Read the request data from the client. We don't write an error
346  response at this initial stage to prevent scanning/DOS attacks
347  (vir sapit qui pauca loquitur) */
348  status = readPkiDatagram( sessionInfoPtr );
349  if( cryptStatusError( status ) )
350  return( status );
351  DEBUG_DUMP_FILE( "ocsp_sreq", sessionInfoPtr->receiveBuffer,
352  sessionInfoPtr->receiveBufEnd );
353 
354  /* Basic lint filter to check for approximately-OK requests before we
355  try creating a certificate object from the data:
356 
357  SEQUENCE {
358  SEQUENCE { -- tbsRequest
359  version [0] ...
360  reqName [1] ...
361  SEQUENCE { -- requestList
362  SEQUENCE { -- request
363  ... */
364  sMemConnect( &stream, sessionInfoPtr->receiveBuffer,
365  sessionInfoPtr->receiveBufEnd );
366  readSequence( &stream, NULL );
367  readSequence( &stream, NULL );
368  if( peekTag( &stream ) == MAKE_CTAG( 0 ) )
369  readUniversal( &stream );
370  if( peekTag( &stream ) == MAKE_CTAG( 1 ) )
371  readUniversal( &stream );
372  readSequence( &stream, NULL );
373  status = readSequence( &stream, NULL );
374  sMemDisconnect( &stream );
375  if( cryptStatusError( status ) )
376  retExt( status,
377  ( status, SESSION_ERRINFO, "Invalid OCSP request header" ) );
378 
379  /* Import the request as a cryptlib object */
381  sessionInfoPtr->receiveBuffer,
382  sessionInfoPtr->receiveBufEnd,
386  &createInfo, OBJECT_TYPE_CERTIFICATE );
387  if( cryptStatusError( status ) )
388  {
389  sendErrorResponse( sessionInfoPtr, respBadRequest, RESPONSE_SIZE );
390  retExt( status,
391  ( status, SESSION_ERRINFO, "Invalid OCSP request data" ) );
392  }
393  iOcspRequest = createInfo.cryptHandle;
394 
395  /* Create an OCSP response and add the request information to it */
398  &createInfo, OBJECT_TYPE_CERTIFICATE );
399  if( cryptStatusError( status ) )
400  {
401  krnlSendNotifier( iOcspRequest, IMESSAGE_DECREFCOUNT );
402  sendErrorResponse( sessionInfoPtr, respIntError, RESPONSE_SIZE );
403  return( status );
404  }
405  status = krnlSendMessage( createInfo.cryptHandle,
406  IMESSAGE_SETATTRIBUTE, &iOcspRequest,
407  CRYPT_IATTRIBUTE_OCSPREQUEST );
408  krnlSendNotifier( iOcspRequest, IMESSAGE_DECREFCOUNT );
409  if( cryptStatusError( status ) )
410  {
412  sendErrorResponse( sessionInfoPtr, respIntError, RESPONSE_SIZE );
413  retExt( status,
414  ( status, SESSION_ERRINFO,
415  "Couldn't create OCSP response from request" ) );
416  }
417  sessionInfoPtr->iCertResponse = createInfo.cryptHandle;
418  return( CRYPT_OK );
419  }
420 
421 /* Return a response to an OCSP client */
422 
424 static int sendServerResponse( INOUT SESSION_INFO *sessionInfoPtr )
425  {
427  STREAM stream;
428  int responseLength, responseDataLength, status;
429 
430  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
431 
432  /* Check the entries from the request against the certificate store and
433  sign the resulting status information ("Love, ken"). Note that
434  CRYPT_ERROR_INVALID is a valid return status for the sigcheck call
435  since it indicates that one (or more) of the certificates was
436  revoked */
437  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
438  IMESSAGE_CRT_SIGCHECK, NULL,
439  sessionInfoPtr->cryptKeyset );
440  if( cryptStatusError( status ) && status != CRYPT_ERROR_INVALID )
441  {
442  sendErrorResponse( sessionInfoPtr, respIntError, RESPONSE_SIZE );
443  retExt( status,
444  ( status, SESSION_ERRINFO,
445  "Couldn't check OCSP request against certificate "
446  "store" ) );
447  }
448  setMessageData( &msgData, NULL, 0 );
449  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
450  IMESSAGE_CRT_SIGN, NULL,
451  sessionInfoPtr->privateKey );
452  if( cryptStatusOK( status ) )
453  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
454  IMESSAGE_CRT_EXPORT, &msgData,
456  if( cryptStatusError( status ) )
457  {
458  sendErrorResponse( sessionInfoPtr, respIntError, RESPONSE_SIZE );
459  retExt( status,
460  ( status, SESSION_ERRINFO,
461  "Couldn't create signed OCSP response" ) );
462  }
463  responseDataLength = msgData.length;
464 
465  /* Write the wrapper for the response */
466  sMemOpen( &stream, sessionInfoPtr->receiveBuffer,
467  sessionInfoPtr->receiveBufSize );
468  responseLength = sizeofOID( OID_OCSP_RESPONSE_OCSP ) + \
469  sizeofObject( responseDataLength );
470  writeSequence( &stream, sizeofEnumerated( 0 ) + \
471  sizeofObject( sizeofObject( responseLength ) ) );
472  writeEnumerated( &stream, 0, DEFAULT_TAG ); /* respStatus */
473  writeConstructed( &stream, sizeofObject( responseLength ), 0 );
474  writeSequence( &stream, responseLength ); /* respBytes */
475  writeOID( &stream, OID_OCSP_RESPONSE_OCSP ); /* respType */
476  writeOctetStringHole( &stream, responseDataLength, DEFAULT_TAG );
477  /* response */
478  /* Get the encoded response data */
479  status = exportCertToStream( &stream, sessionInfoPtr->iCertResponse,
481  if( cryptStatusOK( status ) )
482  sessionInfoPtr->receiveBufEnd = stell( &stream );
483  sMemDisconnect( &stream );
484  if( cryptStatusError( status ) )
485  {
486  sendErrorResponse( sessionInfoPtr, respIntError, RESPONSE_SIZE );
487  return( status );
488  }
489  DEBUG_DUMP_FILE( "ocsp_sresp", sessionInfoPtr->receiveBuffer,
490  sessionInfoPtr->receiveBufEnd );
491 
492  /* Send the response to the client */
493  return( writePkiDatagram( sessionInfoPtr, OCSP_CONTENT_TYPE_RESP,
494  OCSP_CONTENT_TYPE_RESP_LEN ) );
495  }
496 
497 /****************************************************************************
498 * *
499 * Init/Shutdown Functions *
500 * *
501 ****************************************************************************/
502 
503 /* Exchange data with an OCSP client/server */
504 
506 static int clientTransact( INOUT SESSION_INFO *sessionInfoPtr )
507  {
508  int status;
509 
510  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
511 
512  /* Get certificate revocation information from the server */
513  status = sendClientRequest( sessionInfoPtr );
514  if( cryptStatusOK( status ) )
515  status = readServerResponse( sessionInfoPtr );
516  return( status );
517  }
518 
520 static int serverTransact( INOUT SESSION_INFO *sessionInfoPtr )
521  {
522  int status;
523 
524  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
525 
526  /* Send certificate revocation information to the client */
527  status = readClientRequest( sessionInfoPtr );
528  if( cryptStatusOK( status ) )
529  status = sendServerResponse( sessionInfoPtr );
530  return( status );
531  }
532 
533 /****************************************************************************
534 * *
535 * Control Information Management Functions *
536 * *
537 ****************************************************************************/
538 
539 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
540 static int setAttributeFunction( INOUT SESSION_INFO *sessionInfoPtr,
541  IN const void *data,
543  {
544  const CRYPT_CERTIFICATE ocspRequest = *( ( CRYPT_CERTIFICATE * ) data );
545  MESSAGE_DATA msgData = { NULL, 0 };
546  int status;
547 
548  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
549  assert( isReadPtr( data, sizeof( int ) ) );
550 
551  REQUIRES( type == CRYPT_SESSINFO_REQUEST );
552 
553  /* Make sure that everything is set up ready to go. Since OCSP requests
554  aren't (usually) signed like normal certificate objects we can't just
555  check the immutable attribute but have to perform a dummy export for
556  which the certificate export code will return an error status if
557  there's a problem with the request. If not, it pseudo-signs the
558  request (if it hasn't already done so) and prepares it for use */
559  status = krnlSendMessage( ocspRequest, IMESSAGE_CRT_EXPORT, &msgData,
560  CRYPT_ICERTFORMAT_DATA );
561  if( cryptStatusError( status ) )
562  return( CRYPT_ARGERROR_NUM1 );
563 
564  /* If we haven't already got a server name explicitly set, try and get
565  it from the request. This is an opportunistic action so we ignore
566  any potential error, the caller can still set the value explicitly */
567  if( findSessionInfo( sessionInfoPtr->attributeList,
568  CRYPT_SESSINFO_SERVER_NAME ) == NULL )
569  {
570  char buffer[ MAX_URL_SIZE + 8 ];
571 
572  setMessageData( &msgData, buffer, MAX_URL_SIZE );
573  status = krnlSendMessage( ocspRequest, IMESSAGE_GETATTRIBUTE_S,
574  &msgData, CRYPT_IATTRIBUTE_RESPONDERURL );
575  if( cryptStatusOK( status ) )
576  ( void ) krnlSendMessage( sessionInfoPtr->objectHandle,
577  IMESSAGE_SETATTRIBUTE_S, &msgData,
579  }
580 
581  /* Add the request and increment its usage count */
582  krnlSendNotifier( ocspRequest, IMESSAGE_INCREFCOUNT );
583  sessionInfoPtr->iCertRequest = ocspRequest;
584 
585  return( CRYPT_OK );
586  }
587 
588 /****************************************************************************
589 * *
590 * Session Access Routines *
591 * *
592 ****************************************************************************/
593 
595 int setAccessMethodOCSP( INOUT SESSION_INFO *sessionInfoPtr )
596  {
597  static const PROTOCOL_INFO protocolInfo = {
598  /* General session information */
599  TRUE, /* Request-response protocol */
600  SESSION_ISHTTPTRANSPORT, /* Flags */
601  80, /* HTTP port */
602  SESSION_NEEDS_REQUEST, /* Client attributes */
603  SESSION_NEEDS_PRIVATEKEY | /* Server attributes */
605  SESSION_NEEDS_PRIVKEYCERT | \
606  SESSION_NEEDS_KEYSET,
607  1, 1, 2 /* Version 1 */
608 
609  /* Protocol-specific information */
610  };
611 
612  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
613 
614  /* Set the access method pointers */
615  sessionInfoPtr->protocolInfo = &protocolInfo;
616  if( isServer( sessionInfoPtr ) )
617  sessionInfoPtr->transactFunction = serverTransact;
618  else
619  sessionInfoPtr->transactFunction = clientTransact;
620  sessionInfoPtr->setAttributeFunction = setAttributeFunction;
621 
622  return( CRYPT_OK );
623  }
624 #endif /* USE_OCSP */