cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
cmp_cli.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib CMP Client Management *
4 * Copyright Peter Gutmann 1999-2009 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "session.h"
11  #include "cmp.h"
12 #else
13  #include "crypt.h"
14  #include "session/session.h"
15  #include "session/cmp.h"
16 #endif /* Compiler-specific includes */
17 
18 #ifdef USE_CMP
19 
20 /****************************************************************************
21 * *
22 * Utility Routines *
23 * *
24 ****************************************************************************/
25 
26 /* Map request to response types */
27 
28 static const MAP_TABLE clibReqReqMapTbl[] = {
36  };
37 
39 static int clibReqToReq( IN_ENUM( CRYPT_REQUESTTYPE ) const int reqType )
40  {
41  int value, status;
42 
43  REQUIRES( reqType > CRYPT_REQUESTTYPE_NONE && \
44  reqType < CRYPT_REQUESTTYPE_LAST );
45 
46  status = mapValue( reqType, &value, clibReqReqMapTbl,
47  FAILSAFE_ARRAYSIZE( clibReqReqMapTbl, MAP_TABLE ) );
48  return( cryptStatusError( status ) ? status : value );
49  }
50 
51 /* Set up information needed to perform a client-side transaction */
52 
53 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
54 static int initClientInfo( INOUT SESSION_INFO *sessionInfoPtr,
56  {
57  CMP_INFO *cmpInfo = sessionInfoPtr->sessionCMP;
58  const ATTRIBUTE_LIST *userNamePtr = \
59  findSessionInfo( sessionInfoPtr->attributeList,
61  const ATTRIBUTE_LIST *passwordPtr = \
62  findSessionInfo( sessionInfoPtr->attributeList,
64  int status;
65 
66  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
67  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
68 
69  REQUIRES( !isServer( sessionInfoPtr ) );
70 
71  /* Determine what we need to do based on the request type */
72  status = protocolInfo->operation = clibReqToReq( cmpInfo->requestType );
73  if( cryptStatusError( status ) )
74  return( status );
75 
76  /* If we're using public key-based authentication, set up the key and
77  user ID information */
78  if( cmpInfo->requestType != CRYPT_REQUESTTYPE_PKIBOOT && \
80  !( cmpInfo->requestType == CRYPT_REQUESTTYPE_REVOCATION && \
81  passwordPtr != NULL ) )
82  {
83  /* If it's an encryption-only key, remember this for later when we
84  need to authenticate our request messages */
85  status = krnlSendMessage( sessionInfoPtr->privateKey, IMESSAGE_CHECK,
86  NULL, MESSAGE_CHECK_PKC_SIGN );
87  if( cryptStatusError( status ) )
88  {
89  /* The private key can't be used for signature creation, use
90  the alternate authentication key instead */
91  protocolInfo->authContext = sessionInfoPtr->iAuthOutContext;
92  protocolInfo->cryptOnlyKey = TRUE;
93  }
94  else
95  {
96  /* The private key that we're using is capable of authenticating
97  requests */
98  protocolInfo->authContext = sessionInfoPtr->privateKey;
99  }
100 
101  /* If we're not talking to a cryptlib server, get the user ID. If
102  it's a standard signed request the authenticating object will be
103  the private key, however if the private key is an encryption-only
104  key the message authentication key is a separate object. To
105  handle this we get the user ID from the signing key rather than
106  automatically using the private key */
107  if( !protocolInfo->isCryptlib )
108  {
110  BYTE userID[ CRYPT_MAX_HASHSIZE + 8 ];
111 
112  setMessageData( &msgData, userID, CRYPT_MAX_HASHSIZE );
113  status = krnlSendMessage( protocolInfo->authContext,
114  IMESSAGE_GETATTRIBUTE_S, &msgData,
116  if( cryptStatusOK( status ) )
117  {
118  status = setCMPprotocolInfo( protocolInfo, userID,
119  msgData.length,
122  }
123  return( status );
124  }
125 
126  /* It's a cryptlib peer, the certificate is identified by an
127  unambiguous certificate ID and so we don't have to try and make
128  do with an arbitrary value derived from the associated public
129  key */
130  return( setCMPprotocolInfo( protocolInfo, NULL, 0,
132  }
133 
134  /* If there's a MAC context present from a previous transaction, reuse
135  it for the current one. See the discussion in cmp.h for details */
136  if( cmpInfo->savedMacContext != CRYPT_ERROR )
137  {
138  status = setCMPprotocolInfo( protocolInfo, NULL, 0,
140  if( cryptStatusError( status ) )
141  return( status );
142  protocolInfo->useMACsend = protocolInfo->useMACreceive = TRUE;
143  protocolInfo->iMacContext = cmpInfo->savedMacContext;
144  cmpInfo->savedMacContext = CRYPT_ERROR;
145  return( CRYPT_OK );
146  }
147 
148  /* We're using MAC authentication, initialise the protocol information */
149  REQUIRES( userNamePtr != NULL );
150  if( userNamePtr->flags & ATTR_FLAG_ENCODEDVALUE )
151  {
152  BYTE decodedValue[ 64 + 8 ];
153  int decodedValueLength;
154 
155  /* It's a cryptlib-style encoded user ID, decode it into its binary
156  value */
157  status = decodePKIUserValue( decodedValue, 64, &decodedValueLength,
158  userNamePtr->value,
159  userNamePtr->valueLength );
160  ENSURES( cryptStatusOK( status ) );
161  status = setCMPprotocolInfo( protocolInfo, decodedValue,
162  decodedValueLength,
164  zeroise( decodedValue, CRYPT_MAX_TEXTSIZE );
165  }
166  else
167  {
168  /* It's an arbitrary non-cryptlib user ID, use it as is */
169  status = setCMPprotocolInfo( protocolInfo, userNamePtr->value,
170  min( userNamePtr->valueLength, \
173  }
174  if( cryptStatusError( status ) )
175  return( status );
176 
177  REQUIRES( passwordPtr != NULL );
178 
179  /* Set up the MAC context used to authenticate messages */
180  if( passwordPtr->flags & ATTR_FLAG_ENCODEDVALUE )
181  {
182  BYTE decodedValue[ 64 + 8 ];
183  int decodedValueLength;
184 
185  /* It's a cryptlib-style encoded password, decode it into its binary
186  value */
187  status = decodePKIUserValue( decodedValue, 64, &decodedValueLength,
188  passwordPtr->value,
189  passwordPtr->valueLength );
190  ENSURES( cryptStatusOK( status ) );
191  status = initMacInfo( protocolInfo->iMacContext, decodedValue,
192  decodedValueLength, protocolInfo->salt,
193  protocolInfo->saltSize,
194  protocolInfo->iterations );
195  zeroise( decodedValue, CRYPT_MAX_TEXTSIZE );
196  }
197  else
198  {
199  /* It's an arbitrary non-cryptlib password, use it as is */
200  status = initMacInfo( protocolInfo->iMacContext,
201  passwordPtr->value, passwordPtr->valueLength,
202  protocolInfo->salt, protocolInfo->saltSize,
203  protocolInfo->iterations );
204  }
205  return( status );
206  }
207 
208 /****************************************************************************
209 * *
210 * Init/Shutdown Functions *
211 * *
212 ****************************************************************************/
213 
214 /* Prepare a CMP session */
215 
217 static int clientStartup( INOUT SESSION_INFO *sessionInfoPtr )
218  {
219 #ifdef USE_CMP_TRANSPORT
220  const PROTOCOL_INFO *protocolInfoPtr = sessionInfoPtr->protocolInfo;
221 #endif /* USE_CMP_TRANSPORT */
222  CMP_INFO *cmpInfo = sessionInfoPtr->sessionCMP;
224  int status;
225 
226  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
227 
228  /* Make sure that we have all the needed information. Plug-and-play PKI
229  uses PKIBoot to get the CA certificate and generates the requests
230  internally so we only need to check for these values if we're doing
231  standard CMP. The check for user ID and authentication information
232  has already been done at the general session level */
233  if( !( cmpInfo->flags & CMP_PFLAG_PNPPKI ) )
234  {
235  if( cmpInfo->requestType == CRYPT_REQUESTTYPE_NONE )
236  {
239  return( CRYPT_ERROR_NOTINITED );
240  }
241  if( cmpInfo->requestType != CRYPT_REQUESTTYPE_PKIBOOT && \
242  sessionInfoPtr->iAuthInContext == CRYPT_ERROR )
243  {
246  return( CRYPT_ERROR_NOTINITED );
247  }
248  if( cmpInfo->requestType != CRYPT_REQUESTTYPE_PKIBOOT && \
249  sessionInfoPtr->iCertRequest == CRYPT_ERROR )
250  {
251  setErrorInfo( sessionInfoPtr, CRYPT_SESSINFO_REQUEST,
253  return( CRYPT_ERROR_NOTINITED );
254  }
255  }
256 
257  /* Connect to the remote server */
258  status = initSessionNetConnectInfo( sessionInfoPtr, &connectInfo );
259  if( cryptStatusError( status ) )
260  return( status );
261 #ifdef USE_CMP_TRANSPORT
262  if( sessionInfoPtr->flags & SESSION_ISHTTPTRANSPORT )
263  status = sNetConnect( &sessionInfoPtr->stream, STREAM_PROTOCOL_HTTP,
264  &connectInfo, &sessionInfoPtr->errorInfo );
265  else
266  {
267  const ALTPROTOCOL_INFO *altProtocolInfoPtr = \
268  protocolInfoPtr->altProtocolInfo;
269 
270  REQUIRES( sessionInfoPtr->flags & SESSION_USEALTTRANSPORT );
271 
272  /* If we're using the HTTP port for a session-specific protocol,
273  change it to the default port for the session-specific protocol
274  instead */
275  if( connectInfo.port == 80 )
276  connectInfo.port = altProtocolInfoPtr->port;
277  status = sNetConnect( &sessionInfoPtr->stream,
278  altProtocolInfoPtr->type,
279  &connectInfo, &sessionInfoPtr->errorInfo );
280  }
281  return( status );
282 #else
283  return( sNetConnect( &sessionInfoPtr->stream, STREAM_PROTOCOL_HTTP,
284  &connectInfo, &sessionInfoPtr->errorInfo ) );
285 #endif /* USE_CMP_TRANSPORT */
286  }
287 
288 /* Exchange data with a CMP server. Since the plug-and-play PKI client
289  performs multiple transactions, we wrap the basic clientTransact() in an
290  external function that either calls it indirectly when required from the
291  PnP code or just passes the call through to the transaction function */
292 
294 static int clientTransact( INOUT SESSION_INFO *sessionInfoPtr )
295  {
296  CMP_INFO *cmpInfo = sessionInfoPtr->sessionCMP;
298  int status;
299 
300  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
301 
302  /* Check that everything we need is present. If it's a general CMP
303  session this will already have been checked in clientStartup(), but
304  if it's coming from the PnPPKI wrapper it doesn't go through the
305  startup checks each time so we double-check here */
308  sessionInfoPtr->iCertRequest != CRYPT_ERROR );
310  sessionInfoPtr->iAuthInContext != CRYPT_ERROR );
311 
312  /* Initialise the client-side protocol state information */
313  initCMPprotocolInfo( &protocolInfo,
314  ( sessionInfoPtr->flags & SESSION_ISCRYPTLIB ) ? \
315  TRUE : FALSE, FALSE );
316  status = initClientInfo( sessionInfoPtr, &protocolInfo );
317  if( cryptStatusError( status ) )
318  {
319  destroyCMPprotocolInfo( &protocolInfo );
320  return( status );
321  }
322 
323  /* Write the message into the session buffer and send it to the server */
324  status = writePkiMessage( sessionInfoPtr, &protocolInfo,
325  ( cmpInfo->requestType == \
328  if( cryptStatusOK( status ) )
329  {
330  DEBUG_DUMP_CMP( protocolInfo.operation, 1, sessionInfoPtr );
331  if( ( protocolInfo.operation == CTAG_PB_GENM || \
332  protocolInfo.operation == CTAG_PB_RR ) && \
333  !( sessionInfoPtr->protocolFlags & CMP_PFLAG_RETAINCONNECTION ) )
334  {
335  /* There's no confirmation handshake for PKIBoot or a revocation
336  request so we mark this as the last message if required */
337  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_LASTMESSAGE,
338  TRUE );
339  }
340  status = writePkiDatagram( sessionInfoPtr, CMP_CONTENT_TYPE,
342  }
343  if( cryptStatusError( status ) )
344  {
345  destroyCMPprotocolInfo( &protocolInfo );
346  return( status );
347  }
348 
349  /* Read the server response */
350  status = readPkiDatagram( sessionInfoPtr );
351  if( cryptStatusOK( status ) )
352  {
353  const int responseType = reqToResp( protocolInfo.operation );
354 
355  DEBUG_DUMP_CMP( protocolInfo.operation, 2, sessionInfoPtr );
356  if( cryptStatusError( responseType ) )
357  status = responseType;
358  else
359  {
360  status = readPkiMessage( sessionInfoPtr, &protocolInfo,
361  responseType );
362  }
363  }
364  if( cryptStatusOK( status ) && protocolInfo.operation == CTAG_PB_GENM )
365  {
366  /* It's a PKIBoot, add the trusted certificates. If the user wants
367  the setting made permanent then they need to flush the
368  configuration to disk after the session has completed */
369  status = krnlSendMessage( sessionInfoPtr->ownerHandle,
371  &sessionInfoPtr->iCertResponse,
372  CRYPT_IATTRIBUTE_CTL );
373  if( status == CRYPT_ERROR_INITED )
374  {
375  /* If the certificates are already present, trying to add them
376  again isn't an error */
377  status = CRYPT_OK;
378  }
379  }
380  if( cryptStatusError( status ) )
381  {
382  destroyCMPprotocolInfo( &protocolInfo );
383  return( status );
384  }
385 
386  /* If it's a transaction type that doesn't need a confirmation, we're
387  done */
388  if( protocolInfo.operation == CTAG_PB_GENM || \
389  protocolInfo.operation == CTAG_PB_RR )
390  {
391  /* Remember the authentication context in case we can reuse it for
392  another transaction */
393  if( protocolInfo.iMacContext != CRYPT_ERROR )
394  {
395  cmpInfo->savedMacContext = protocolInfo.iMacContext;
396  protocolInfo.iMacContext = CRYPT_ERROR;
397  }
398 
399  destroyCMPprotocolInfo( &protocolInfo );
400  return( CRYPT_OK );
401  }
402 
403  /* Exchange confirmation data with the server */
404  if( !( sessionInfoPtr->protocolFlags & CMP_PFLAG_RETAINCONNECTION ) )
405  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_LASTMESSAGE, TRUE );
406  status = writePkiMessage( sessionInfoPtr, &protocolInfo,
408  if( cryptStatusOK( status ) )
409  {
410  DEBUG_DUMP_CMP( protocolInfo.operation, 3, sessionInfoPtr );
411  status = writePkiDatagram( sessionInfoPtr, CMP_CONTENT_TYPE,
413  }
414  if( cryptStatusOK( status ) )
415  status = readPkiDatagram( sessionInfoPtr );
416  if( cryptStatusOK( status ) )
417  {
418  DEBUG_DUMP_CMP( protocolInfo.operation, 4, sessionInfoPtr );
419  status = readPkiMessage( sessionInfoPtr, &protocolInfo, CTAG_PB_PKICONF );
420  }
421  if( cryptStatusOK( status ) && protocolInfo.iMacContext != CRYPT_ERROR )
422  {
423  /* Remember the authentication context in case we can reuse it for
424  another transaction */
425  cmpInfo->savedMacContext = protocolInfo.iMacContext;
426  protocolInfo.iMacContext = CRYPT_ERROR;
427  }
428  destroyCMPprotocolInfo( &protocolInfo );
429  return( status );
430  }
431 
433 static int clientTransactWrapper( INOUT SESSION_INFO *sessionInfoPtr )
434  {
435  int status;
436 
437  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
438 
439  /* If it's not a plug-and-play PKI session, just pass the call on down
440  to the client transaction function */
441  if( !( sessionInfoPtr->sessionCMP->flags & CMP_PFLAG_PNPPKI ) )
442  return( clientTransact( sessionInfoPtr ) );
443 
444  /* We're doing plug-and-play PKI, point the transaction function at the
445  client-transact function while we execute the PnP steps, then reset
446  it back to the PnP wrapper after we're done */
447  sessionInfoPtr->transactFunction = clientTransact;
448  status = pnpPkiSession( sessionInfoPtr );
449  sessionInfoPtr->transactFunction = clientTransactWrapper;
450  return( status );
451  }
452 
453 /****************************************************************************
454 * *
455 * Session Access Routines *
456 * *
457 ****************************************************************************/
458 
459 STDC_NONNULL_ARG( ( 1 ) ) \
460 void initCMPclientProcessing( SESSION_INFO *sessionInfoPtr )
461  {
462  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
463 
464  sessionInfoPtr->connectFunction = clientStartup;
465  sessionInfoPtr->transactFunction = clientTransactWrapper;
466  }
467 #endif /* USE_CMP */