cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
scep_svr.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib SCEP Server 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 "session.h"
12  #include "certstore.h"
13  #include "scep.h"
14 #else
15  #include "crypt.h"
16  #include "enc_dec/asn1.h"
17  #include "session/session.h"
18  #include "session/certstore.h"
19  #include "session/scep.h"
20 #endif /* Compiler-specific includes */
21 
22 #ifdef USE_SCEP
23 
24 /* Table mapping a query submitted as an HTTP GET to a supplementary SCEP
25  operation. Note that the first letter must be lowercase for the
26  case-insensitive quick match */
27 
28 enum { SCEP_OPERATION_GETCACAPS, SCEP_OPERATION_GETCACERT,
29  SCEP_OPERATION_GETCACERTCHAIN };
30 
31 static const CERTSTORE_READ_INFO certstoreReadInfo[] = {
32  { "getCACaps", 9, SCEP_OPERATION_GETCACAPS, CERTSTORE_FLAG_NONE },
33  { "getCACert", 9, SCEP_OPERATION_GETCACERT, CERTSTORE_FLAG_NONE },
34  { "getCACertChain", 14, SCEP_OPERATION_GETCACERTCHAIN, CERTSTORE_FLAG_NONE },
35  { NULL, 0, CRYPT_ERROR, CERTSTORE_FLAG_NONE },
36  { NULL, 0, CRYPT_ERROR, CERTSTORE_FLAG_NONE }
37  };
38 
39 /****************************************************************************
40 * *
41 * Utility Functions *
42 * *
43 ****************************************************************************/
44 
45 /* Deliver an Einladung betreff Kehrseite to the client. We don't bother
46  checking the return value since there's nothing that we can do in the case
47  of an error except close the connection, which we do anyway since this is
48  the last message. We don't return extended error information since this
49  would overwrite the information for the error that caused us to return an
50  error response */
51 
52 STDC_NONNULL_ARG( ( 1, 2 ) ) \
53 static void sendErrorResponse( INOUT SESSION_INFO *sessionInfoPtr,
55  IN_ERROR const int scepStatus )
56  {
57  CRYPT_CERTIFICATE iCmsAttributes;
58  int status;
59 
60  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
61  assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) );
62 
63  REQUIRES_V( cryptStatusError( scepStatus ) );
64 
65  /* Sign the error response using the CA key and SCEP attributes */
66  status = createScepAttributes( sessionInfoPtr, protocolInfo,
67  &iCmsAttributes, FALSE, scepStatus );
68  if( cryptStatusOK( status ) )
69  {
70  status = envelopeSign( sessionInfoPtr->receiveBuffer, 0,
71  sessionInfoPtr->receiveBuffer,
72  sessionInfoPtr->receiveBufSize,
73  &sessionInfoPtr->receiveBufEnd,
74  CRYPT_CONTENT_NONE, sessionInfoPtr->privateKey,
75  iCmsAttributes );
76  krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
77  }
78  if( cryptStatusError( status ) )
79  {
80  HTTP_DATA_INFO httpDataInfo;
81 
82  /* If we encounter an error processing the initial request then
83  there won't be enough information available to create an error
84  response. Similarly if we run into problems generating the
85  response then there won't be anything available to send. At this
86  point the best that we can do is send an error at the HTTP
87  level */
88  initHttpDataInfo( &httpDataInfo, sessionInfoPtr->receiveBuffer,
89  sessionInfoPtr->receiveBufSize );
90  httpDataInfo.reqStatus = scepStatus;
91  swrite( &sessionInfoPtr->stream, &httpDataInfo,
92  sizeof( HTTP_DATA_INFO ) );
93  return;
94  }
95  DEBUG_DUMP_FILE( "scep_srespx", sessionInfoPtr->receiveBuffer,
96  sessionInfoPtr->receiveBufEnd );
97 
98  /* Return the response to the client, discarding any error indication
99  from the write. Since we're already in an error state there's not
100  much that we can do in terms of alerting the user if a further error
101  occurs when writing the error response, so we ignore any potential
102  write errors that occur at this point */
103  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_LASTMESSAGE, TRUE );
104  ( void ) writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE,
106  }
107 
108 /* Check that the information supplied in a request matches what's stored for
109  a PKI user */
110 
111 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
112 static int checkPkiUserInfo( INOUT SESSION_INFO *sessionInfoPtr,
113  INOUT SCEP_PROTOCOL_INFO *protocolInfo )
114  {
115  const ATTRIBUTE_LIST *userNamePtr = \
116  findSessionInfo( sessionInfoPtr->attributeList,
118  MESSAGE_KEYMGMT_INFO getkeyInfo;
120  BYTE keyID[ 64 + 8 ];
121  BYTE requestPassword[ CRYPT_MAX_TEXTSIZE + 8 ];
122  BYTE userPassword[ CRYPT_MAX_TEXTSIZE + 8 ];
123  int requestPasswordSize, userPasswordSize = DUMMY_INIT;
124  int keyIDsize, status;
125 
126  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
127  assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) );
128 
129  REQUIRES( userNamePtr != NULL );
130 
131  /* Get the password from the PKCS #10 request */
132  setMessageData( &msgData, requestPassword, CRYPT_MAX_TEXTSIZE );
133  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
134  IMESSAGE_GETATTRIBUTE_S, &msgData,
136  if( cryptStatusError( status ) )
137  {
138  retExt( status,
139  ( status, SESSION_ERRINFO,
140  "Couldn't get challenge password from PKCS #10 request" ) );
141  }
142  requestPasswordSize = msgData.length;
143 
144  /* Since it's a cryptlib encoded user ID we need to decode it before we
145  can look up a PKI user with it */
146  REQUIRES( userNamePtr->flags & ATTR_FLAG_ENCODEDVALUE );
147  status = decodePKIUserValue( keyID, 64, &keyIDsize,
148  userNamePtr->value,
149  userNamePtr->valueLength );
150  ENSURES( cryptStatusOK( status ) );
151 
152  /* Get the user information for the request from the certificate
153  store */
154  setMessageKeymgmtInfo( &getkeyInfo, CRYPT_IKEYID_KEYID, keyID,
155  keyIDsize, NULL, 0, KEYMGMT_FLAG_NONE );
156  status = krnlSendMessage( sessionInfoPtr->cryptKeyset,
157  IMESSAGE_KEY_GETKEY, &getkeyInfo,
159  if( cryptStatusError( status ) )
160  {
161  char userID[ CRYPT_MAX_TEXTSIZE + 8 ];
162  int userIDlen;
163 
164  zeroise( requestPassword, CRYPT_MAX_TEXTSIZE );
165  userIDlen = min( userNamePtr->valueLength, CRYPT_MAX_TEXTSIZE );
166  memcpy( userID, userNamePtr->value, userIDlen );
167  retExtObj( status,
168  ( status, SESSION_ERRINFO, sessionInfoPtr->cryptKeyset,
169  "Couldn't find PKI user information for %s",
170  sanitiseString( userID, CRYPT_MAX_TEXTSIZE,
171  userIDlen ) ) );
172  }
173 
174  /* Get the password from the PKI user object */
175  setMessageData( &msgData, userPassword, CRYPT_MAX_TEXTSIZE );
176  status = krnlSendMessage( getkeyInfo.cryptHandle,
177  IMESSAGE_GETATTRIBUTE_S, &msgData,
179  if( cryptStatusOK( status ) )
180  {
181  userPasswordSize = msgData.length;
182  status = updateSessionInfo( &sessionInfoPtr->attributeList,
184  userPassword, userPasswordSize,
187  }
188  if( cryptStatusError( status ) )
189  {
190  zeroise( requestPassword, CRYPT_MAX_TEXTSIZE );
191  zeroise( userPassword, CRYPT_MAX_TEXTSIZE );
193  retExt( status,
194  ( status, SESSION_ERRINFO,
195  "Couldn't copy read PKI user data from PKI user object "
196  "into session object" ) );
197  }
198 
199  /* Make sure that the password matches the one in the request */
200  if( userPasswordSize != requestPasswordSize || \
201  !compareDataConstTime( userPassword, requestPassword,
202  userPasswordSize ) )
203  {
204  zeroise( requestPassword, CRYPT_MAX_TEXTSIZE );
205  zeroise( userPassword, CRYPT_MAX_TEXTSIZE );
207  retExt( status,
208  ( status, SESSION_ERRINFO,
209  "Password in PKCS #10 request doesn't match PKI user "
210  "password" ) );
211  }
212  zeroise( userPassword, CRYPT_MAX_TEXTSIZE );
213 
214  /* If the subject only knows their CN, they may send a CN-only subject DN
215  in the hope that we can fill it in for them. In addition there may be
216  other constraints that the CA wants to apply, these are handled by
217  applying the PKI user information to the request */
218  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
219  IMESSAGE_SETATTRIBUTE, &getkeyInfo.cryptHandle,
220  CRYPT_IATTRIBUTE_PKIUSERINFO );
222  if( cryptStatusError( status ) )
223  {
226  "User information in PKCS #10 request can't be "
227  "reconciled with stored information for the user" ) );
228  }
229 
230  return( CRYPT_OK );
231  }
232 
233 /****************************************************************************
234 * *
235 * Additional Request Management Functions *
236 * *
237 ****************************************************************************/
238 
239 /* Process one of the bolted-on additions to the basic SCEP protocol */
240 
241 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
242 static int processAdditionalScepRequest( INOUT SESSION_INFO *sessionInfoPtr,
243  const HTTP_URI_INFO *httpReqInfo )
244  {
245  HTTP_URI_INFO rewrittenHttpReqInfo;
247  int operationType, status;
248 
249  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
250  assert( isReadPtr( httpReqInfo, sizeof( HTTP_URI_INFO ) ) );
251 
252  /* If the client has fed us an HTTP GET request, find out what they
253  want. SCEP's handling of HTTP requests is a bit different from the
254  "attribute '=' value" lookup that's normally used for HTTP data
255  retrieval. Instead, it uses the format
256  "'operation =' value '&' extraData", with the search key buried in
257  the 'extraData' value. In addition the content of the 'extraData'
258  value isn't defined outside of "any string which is understood by the
259  CA". However since 'value' defines what we want, we can determine
260  what to return based on this and ignore the 'extraData' portion.
261 
262  In order to fix up the query information into a format that works
263  with standard HTTP queries, we rewrite the query data from the
264  "'operation =' value '&' extraData" form into "attribute '=' value"
265  before we process the query */
266  memset( &rewrittenHttpReqInfo, 0, sizeof( HTTP_URI_INFO ) );
267  memcpy( rewrittenHttpReqInfo.attribute, httpReqInfo->value,
268  httpReqInfo->valueLen );
269  rewrittenHttpReqInfo.attributeLen = httpReqInfo->valueLen;
270  if( httpReqInfo->extraDataLen > 0 )
271  {
272  memcpy( rewrittenHttpReqInfo.value, httpReqInfo->extraData,
273  httpReqInfo->extraDataLen );
274  rewrittenHttpReqInfo.valueLen = httpReqInfo->extraDataLen;
275  }
276  status = processCertQuery( sessionInfoPtr, &rewrittenHttpReqInfo,
277  certstoreReadInfo,
278  FAILSAFE_ARRAYSIZE( certstoreReadInfo, \
280  &operationType, NULL, 0, NULL );
281  if( cryptStatusError( status ) )
282  {
283  sendCertErrorResponse( sessionInfoPtr, status );
284  return( status );
285  }
286  ENSURES( operationType == SCEP_OPERATION_GETCACAPS || \
287  operationType == SCEP_OPERATION_GETCACERT || \
288  operationType == SCEP_OPERATION_GETCACERTCHAIN );
289 
290  /* If it's a CA capabilities query, return the information as raw text
291  over HTTP */
292  if( operationType == SCEP_OPERATION_GETCACAPS )
293  {
294  STREAM stream;
295 
296  sMemOpen( &stream, sessionInfoPtr->receiveBuffer, 1024 );
297  status = swrite( &stream, "POSTPKIOperation\n", 17 );
299  status = swrite( &stream, "SHA-1\n", 6 );
301  status = swrite( &stream, "SHA-256\n", 8 );
303  status = swrite( &stream, "SHAng\n", 6 );
305  status = swrite( &stream, "DES3\n", 5 );
307  status = swrite( &stream, "AES\n", 4 );
308  if( cryptStatusOK( status ) )
309  sessionInfoPtr->receiveBufEnd = stell( &stream );
310  sMemDisconnect( &stream );
311  ENSURES( cryptStatusOK( status ) );
312  return( writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE,
314  }
315 
316  /* Export the CA certificate and send it to the client */
317  setMessageData( &msgData, sessionInfoPtr->receiveBuffer,
318  sessionInfoPtr->receiveBufSize );
319  status = krnlSendMessage( sessionInfoPtr->privateKey,
320  IMESSAGE_CRT_EXPORT, &msgData,
321  ( operationType == SCEP_OPERATION_GETCACERT ) ? \
324  if( cryptStatusError( status ) )
325  {
326  retExt( status,
327  ( status, SESSION_ERRINFO,
328  "Couldn't export CA certificate%s for '%s' request",
329  ( operationType == SCEP_OPERATION_GETCACERT ) ? \
330  "" : " chain",
331  ( operationType == SCEP_OPERATION_GETCACERT ) ? \
332  "GetCACert" : "GetCACertChain" ) );
333  }
334  sessionInfoPtr->receiveBufEnd = msgData.length;
335  if( operationType == SCEP_OPERATION_GETCACERT )
336  {
337  return( writePkiDatagram( sessionInfoPtr,
340  }
341  return( writePkiDatagram( sessionInfoPtr,
344  }
345 
346 /****************************************************************************
347 * *
348 * Request Management Functions *
349 * *
350 ****************************************************************************/
351 
352 /* Check an SCEP request message */
353 
354 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
355 static int checkScepRequest( INOUT SESSION_INFO *sessionInfoPtr,
356  INOUT SCEP_PROTOCOL_INFO *protocolInfo,
357  OUT BOOLEAN *requestDataAvailable )
358  {
359  CRYPT_CERTIFICATE iCmsAttributes;
360  MESSAGE_CREATEOBJECT_INFO createInfo;
362  int dataLength, sigResult, value, status;
363 
364  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
365  assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) );
366  assert( isWritePtr( requestDataAvailable, sizeof( BOOLEAN ) ) );
367 
368  /* Clear return value */
369  *requestDataAvailable = FALSE;
370 
371  /* Phase 1: Sig-check the self-signed data */
372  DEBUG_DUMP_FILE( "scep_sreq2", sessionInfoPtr->receiveBuffer,
373  sessionInfoPtr->receiveBufEnd );
374  status = envelopeSigCheck( sessionInfoPtr->receiveBuffer,
375  sessionInfoPtr->receiveBufEnd,
376  sessionInfoPtr->receiveBuffer,
377  sessionInfoPtr->receiveBufSize, &dataLength,
378  CRYPT_UNUSED, &sigResult,
379  &protocolInfo->iScepCert, &iCmsAttributes );
380  if( cryptStatusError( status ) )
381  {
382  retExt( status,
383  ( status, SESSION_ERRINFO,
384  "Invalid CMS signed data in client request" ) );
385  }
386  DEBUG_DUMP_FILE( "scep_sreq1", sessionInfoPtr->receiveBuffer,
387  dataLength );
388  if( cryptStatusError( sigResult ) )
389  {
390  /* The signed data was valid but the signature on it wasn't, this is
391  a different style of error than the previous one */
392  krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
393  retExt( sigResult,
394  ( sigResult, SESSION_ERRINFO,
395  "Bad signature on client request data" ) );
396  }
397 
398  /* Make sure that the client certificate is valid for signing and
399  decryption. In effect the signing capability has already been
400  checked by the fact that the certificate signed the request but we do
401  an explicit check here just to be thorough */
402  status = krnlSendMessage( protocolInfo->iScepCert, IMESSAGE_CHECK,
404  if( cryptStatusOK( status ) )
405  status = krnlSendMessage( protocolInfo->iScepCert, IMESSAGE_CHECK,
407  if( cryptStatusError( status ) )
408  {
409  krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
412  "Ephemeral SCEP client certificate isn't valid for "
413  "signing/encryption" ) );
414  }
415 
416  /* Get the nonce and transaction ID and save it for the reply */
417  setMessageData( &msgData, protocolInfo->nonce, CRYPT_MAX_HASHSIZE );
418  status = krnlSendMessage( iCmsAttributes, IMESSAGE_GETATTRIBUTE_S,
420  if( cryptStatusOK( status ) )
421  {
422  protocolInfo->nonceSize = msgData.length;
423  setMessageData( &msgData, protocolInfo->transID, CRYPT_MAX_HASHSIZE );
424  status = krnlSendMessage( iCmsAttributes, IMESSAGE_GETATTRIBUTE_S,
425  &msgData,
427  }
428  if( cryptStatusError( status ) )
429  {
430  krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
433  "Request is missing a nonce/transaction ID" ) );
434  }
435  protocolInfo->transIDsize = msgData.length;
436 
437  /* We've now got enough request data available to construct a SCEP-level
438  response to an error instead of an HTTP-level one, let the caller
439  know. Note that this lets an attacker know that they've made it this
440  far in the checking, but it's not obvious that this is a security
441  problem, especially since they can get a good idea of how far they
442  got from the different error conditions that will be returned */
443  *requestDataAvailable = TRUE;
444 
445  /* Since the request is for a cryptlib server it'll have a transaction
446  ID that's a cryptlib-encoded PKI user ID. If we don't get this then
447  we reject the request */
448  if( protocolInfo->transIDsize != 17 || \
449  !isPKIUserValue( protocolInfo->transID, protocolInfo->transIDsize ) )
450  {
451  krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
454  "Request has an invalid transaction ID '%s'",
455  sanitiseString( protocolInfo->transID,
456  protocolInfo->transIDsize,
457  protocolInfo->transIDsize ) ) );
458  }
459  status = updateSessionInfo( &sessionInfoPtr->attributeList,
461  protocolInfo->transID,
462  protocolInfo->transIDsize,
464  if( cryptStatusError( status ) )
465  {
466  krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
467  return( status );
468  }
469 
470  /* Check that we've been sent the correct type of message */
471  status = getScepStatusValue( iCmsAttributes,
473  if( cryptStatusOK( status ) && value != MESSAGETYPE_PKCSREQ_VALUE )
474  status = CRYPT_ERROR_BADDATA;
475  krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
476  if( cryptStatusError( status ) )
477  {
478  retExt( status,
479  ( status, SESSION_ERRINFO,
480  "Incorrect SCEP message type %d", value ) );
481  }
482 
483  /* Phase 2: Decrypt the data using our CA key */
484  status = envelopeUnwrap( sessionInfoPtr->receiveBuffer, dataLength,
485  sessionInfoPtr->receiveBuffer,
486  sessionInfoPtr->receiveBufSize, &dataLength,
487  sessionInfoPtr->privateKey );
488  if( cryptStatusError( status ) )
489  {
490  retExt( status,
491  ( status, SESSION_ERRINFO,
492  "Couldn't decrypt CMS enveloped data in client request" ) );
493  }
494 
495  /* Finally, import the request as a PKCS #10 request */
497  sessionInfoPtr->receiveBuffer, dataLength,
501  &createInfo, OBJECT_TYPE_CERTIFICATE );
502  if( cryptStatusError( status ) )
503  {
504  retExt( status,
505  ( status, SESSION_ERRINFO,
506  "Invalid PKCS #10 request in client request" ) );
507  }
508  sessionInfoPtr->iCertRequest = createInfo.cryptHandle;
509 
510  return( CRYPT_OK );
511  }
512 
513 /* Issue a certificate from a SCEP request */
514 
515 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
516 static int issueCertFromRequest( INOUT SESSION_INFO *sessionInfoPtr,
517  INOUT SCEP_PROTOCOL_INFO *protocolInfo )
518  {
519  MESSAGE_KEYMGMT_INFO setkeyInfo;
520  MESSAGE_CERTMGMT_INFO certMgmtInfo;
521  int status;
522 
523  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
524  assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) );
525 
526  /* Check that the request is permitted and add it to the certificate
527  store */
528  status = checkPkiUserInfo( sessionInfoPtr, protocolInfo );
529  if( cryptStatusError( status ) )
530  return( status );
531  setMessageKeymgmtInfo( &setkeyInfo, CRYPT_KEYID_NONE, NULL, 0,
532  NULL, 0, KEYMGMT_FLAG_NONE );
533  setkeyInfo.cryptHandle = sessionInfoPtr->iCertRequest;
534  status = krnlSendMessage( sessionInfoPtr->cryptKeyset,
535  IMESSAGE_KEY_SETKEY, &setkeyInfo,
537  if( cryptStatusError( status ) )
538  {
539  retExt( status,
540  ( status, SESSION_ERRINFO,
541  "Request couldn't be added to certificate store" ) );
542  }
543 
544  /* Convert the request into a certificate */
545  setMessageCertMgmtInfo( &certMgmtInfo, sessionInfoPtr->privateKey,
546  sessionInfoPtr->iCertRequest );
547  status = krnlSendMessage( sessionInfoPtr->cryptKeyset,
548  IMESSAGE_KEY_CERTMGMT, &certMgmtInfo,
550  if( cryptStatusError( status ) )
551  {
552  retExt( status,
553  ( status, SESSION_ERRINFO,
554  "Couldn't issue certificate for user" ) );
555  }
556  sessionInfoPtr->iCertResponse = certMgmtInfo.cryptCert;
557 
558  return( CRYPT_OK );
559  }
560 
561 /* Create an SCEP response message */
562 
563 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
564 static int createScepResponse( INOUT SESSION_INFO *sessionInfoPtr,
565  INOUT SCEP_PROTOCOL_INFO *protocolInfo )
566  {
567  CRYPT_CERTIFICATE iCmsAttributes;
569  int dataLength, status;
570 
571  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
572  assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) );
573 
574  /* Extract the response data into the session buffer */
575  setMessageData( &msgData, sessionInfoPtr->receiveBuffer,
576  sessionInfoPtr->receiveBufSize );
577  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
578  IMESSAGE_CRT_EXPORT, &msgData,
580  if( cryptStatusError( status ) )
581  {
582  retExt( status,
583  ( status, SESSION_ERRINFO,
584  "Couldn't get PKCS #7 certificate chain from SCEP "
585  "response object" ) );
586  }
587  DEBUG_DUMP_FILE( "scep_sresp0", sessionInfoPtr->receiveBuffer,
588  msgData.length );
589 
590  /* Phase 1: Encrypt the data using the client's key */
591  status = envelopeWrap( sessionInfoPtr->receiveBuffer, msgData.length,
592  sessionInfoPtr->receiveBuffer,
593  sessionInfoPtr->receiveBufSize, &dataLength,
595  protocolInfo->iScepCert );
596  if( cryptStatusError( status ) )
597  {
598  retExt( status,
599  ( status, SESSION_ERRINFO,
600  "Couldn't encrypt response data with client key" ) );
601  }
602  DEBUG_DUMP_FILE( "scep_sresp1", sessionInfoPtr->receiveBuffer,
603  dataLength );
604 
605  /* Create the SCEP signing attributes */
606  status = createScepAttributes( sessionInfoPtr, protocolInfo,
607  &iCmsAttributes, FALSE, CRYPT_OK );
608  if( cryptStatusError( status ) )
609  {
610  retExt( status,
611  ( status, SESSION_ERRINFO,
612  "Couldn't create SCEP response signing attributes" ) );
613  }
614 
615  /* Phase 2: Sign the data using the CA key and SCEP attributes */
616  status = envelopeSign( sessionInfoPtr->receiveBuffer, dataLength,
617  sessionInfoPtr->receiveBuffer,
618  sessionInfoPtr->receiveBufSize,
619  &sessionInfoPtr->receiveBufEnd,
620  CRYPT_CONTENT_NONE, sessionInfoPtr->privateKey,
621  iCmsAttributes );
622  krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT );
623  if( cryptStatusError( status ) )
624  {
625  retExt( status,
626  ( status, SESSION_ERRINFO,
627  "Couldn't sign response data with CA key" ) );
628  }
629  DEBUG_DUMP_FILE( "scep_sresp2", sessionInfoPtr->receiveBuffer,
630  sessionInfoPtr->receiveBufEnd );
631 
632  return( CRYPT_OK );
633  }
634 
635 /****************************************************************************
636 * *
637 * SCEP Server Functions *
638 * *
639 ****************************************************************************/
640 
641 /* Exchange data with an SCEP client */
642 
644 static int serverTransact( INOUT SESSION_INFO *sessionInfoPtr )
645  {
647  HTTP_DATA_INFO httpDataInfo;
649  BOOLEAN requestDataOK;
650  int requestCount, length = DUMMY_INIT, status;
651 
652  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
653 
654  /* SCEP is a weird protocol that started out as a basic IPsec
655  certificate-provisioning mechanism for routers but then had a pile of
656  additional functionality bolted onto it via HTTP mechanisms rather
657  than having the protocol itself handle the extra functionality.
658  Because of this we have to handle not only the standard HTTP-as-a-
659  substrate mechanism used by the other protocols but also HTTP GET
660  requests for additional information that the original protocol didn't
661  accomodate */
662  sessionInfoPtr->receiveBufEnd = 0;
663  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HTTPREQTYPES,
665  for( requestCount = 0; requestCount < 5; requestCount++ )
666  {
667  initHttpDataInfoEx( &httpDataInfo, sessionInfoPtr->receiveBuffer,
668  sessionInfoPtr->receiveBufSize, &httpReqInfo );
669  status = sread( &sessionInfoPtr->stream, &httpDataInfo,
670  sizeof( HTTP_DATA_INFO ) );
671  if( cryptStatusError( status ) )
672  {
673  sNetGetErrorInfo( &sessionInfoPtr->stream,
674  &sessionInfoPtr->errorInfo );
675  return( status );
676  }
677 
678  /* If it's a proper SCEP protocol message, switch back to handling
679  the main protocol */
680  if( httpDataInfo.reqType != STREAM_HTTPREQTYPE_GET )
681  {
682  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HTTPREQTYPES,
684  length = httpDataInfo.bytesAvail;
685  break;
686  }
687 
688  /* It's one of the bolted-on additions to the basic SCEP protocol,
689  handle it specially */
690  status = processAdditionalScepRequest( sessionInfoPtr,
691  &httpReqInfo );
692  if( cryptStatusError( status ) )
693  return( status );
694  }
695  if( requestCount >= 5 )
696  {
697  /* The exact type of error response to send at this point is a bit
698  tricky, the least inappropriate one is probably
699  CRYPT_ERROR_DUPLICATE to indicate that too many duplicate
700  requests were sent, since to get here the client would have had
701  to send repeated identical bolt-on requests */
702  sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_DUPLICATE );
703  return( CRYPT_ERROR_OVERFLOW );
704  }
705 
706  /* Unfortunately we can't use readPkiDatagram() because of the weird
707  dual-purpose HTTP transport used in SCEP so we have to duplicate
708  portions of readPkiDatagram() here. See the readPkiDatagram()
709  function for code comments explaining the following operations */
710  if( length < 4 || length >= MAX_INTLENGTH )
711  {
712  sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_BADDATA );
715  "Invalid PKI message length %d", length ) );
716  }
717  status = length = \
718  checkObjectEncoding( sessionInfoPtr->receiveBuffer, length );
719  if( cryptStatusError( status ) )
720  {
721  sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_BADDATA );
722  retExt( status,
723  ( status, SESSION_ERRINFO, "Invalid PKI message encoding" ) );
724  }
725  sessionInfoPtr->receiveBufEnd = length;
726 
727  /* Process the initial message from the client */
728  initSCEPprotocolInfo( &protocolInfo );
729  status = checkScepRequest( sessionInfoPtr, &protocolInfo,
730  &requestDataOK );
731  if( cryptStatusError( status ) )
732  {
733  /* If we got far enough into the request data to be able to send a
734  SCEP-level response, send that, otherwise just send an HTTP-level
735  response */
736  if( requestDataOK )
737  sendErrorResponse( sessionInfoPtr, &protocolInfo, status );
738  else
739  sendCertErrorResponse( sessionInfoPtr, status );
740  return( status );
741  }
742 
743  /* Issue a certificate from the request */
744  status = issueCertFromRequest( sessionInfoPtr, &protocolInfo );
745  if( cryptStatusError( status ) )
746  {
747  sendErrorResponse( sessionInfoPtr, &protocolInfo, status );
748  destroySCEPprotocolInfo( &protocolInfo );
749  return( status );
750  }
751 
752  /* Return the certificate to the client */
753  status = createScepResponse( sessionInfoPtr, &protocolInfo );
754  if( cryptStatusOK( status ) )
755  status = writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE,
757  destroySCEPprotocolInfo( &protocolInfo );
758  return( status );
759  }
760 
761 /****************************************************************************
762 * *
763 * Session Access Routines *
764 * *
765 ****************************************************************************/
766 
767 STDC_NONNULL_ARG( ( 1 ) ) \
768 void initSCEPserverProcessing( SESSION_INFO *sessionInfoPtr )
769  {
770  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
771 
772  sessionInfoPtr->transactFunction = serverTransact;
773  }
774 #endif /* USE_SCEP */