cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
certstore.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib HTTP Certstore Session Management *
4 * Copyright Peter Gutmann 1998-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "misc_rw.h"
11  #include "session.h"
12  #include "certstore.h"
13 #else
14  #include "crypt.h"
15  #include "enc_dec/misc_rw.h"
16  #include "session/session.h"
17  #include "session/certstore.h"
18 #endif /* Compiler-specific includes */
19 
20 /* SCEP's bolted-on HTTP query mechanism requires that we use the HTTP
21  certstore access routines to return responses to certificate queries,
22  so we enable the use of this code if either certstores or SCEP are
23  used */
24 
25 #if defined( USE_CERTSTORE ) || defined( USE_SCEP )
26 
27 /* Table mapping a query submitted as an HTTP GET to a cryptlib-internal
28  keyset query. Note that the first letter must be lowercase for the
29  case-insensitive quick match */
30 
31 static const CERTSTORE_READ_INFO certstoreReadInfo[] = {
32  { "certHash", 8, CRYPT_IKEYID_CERTID, CERTSTORE_FLAG_BASE64 },
33  { "name", 4, CRYPT_KEYID_NAME, CERTSTORE_FLAG_NONE },
34  { "uri", 3, CRYPT_KEYID_URI, CERTSTORE_FLAG_NONE },
35  { "email", 5, CRYPT_KEYID_URI, CERTSTORE_FLAG_NONE },
36  { "sHash", 5, CRYPT_IKEYID_ISSUERID, CERTSTORE_FLAG_BASE64 },
37  { "iHash", 5, CRYPT_IKEYID_ISSUERID, CERTSTORE_FLAG_BASE64 },
38  { "iAndSHash", 9, CRYPT_IKEYID_ISSUERANDSERIALNUMBER, CERTSTORE_FLAG_BASE64 },
39  { "sKIDHash", 8, CRYPT_IKEYID_KEYID, CERTSTORE_FLAG_BASE64 },
42  };
43 
44 /****************************************************************************
45 * *
46 * Utility Functions *
47 * *
48 ****************************************************************************/
49 
50 /* Convert a query attribute into a text string suitable for use with
51  retExt() */
52 
53 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
54 static int queryAttributeToString( OUT_BUFFER_FIXED( textBufMaxLen ) char *textBuffer,
55  IN_LENGTH_SHORT_MIN( 16 ) const int textBufMaxLen,
57  IN_LENGTH_SHORT const int attributeLen )
58  {
59  assert( isWritePtr( textBuffer, textBufMaxLen ) );
60  assert( isReadPtr( attribute, attributeLen ) );
61 
62  REQUIRES( textBufMaxLen >= 16 && textBufMaxLen < MAX_INTLENGTH_SHORT );
63  REQUIRES( attributeLen > 0 && attributeLen < MAX_INTLENGTH_SHORT );
64 
65  /* Copy as much of the attribute as will fit across and clean it up so
66  that it can be returned to the user */
67  memcpy( textBuffer, attribute, min( attributeLen, textBufMaxLen ) );
68  sanitiseString( textBuffer, textBufMaxLen, attributeLen );
69 
70  return( CRYPT_OK );
71  }
72 
73 /* Process a certificate query and return the requested certificate. See
74  the comment above for why this is declared non-static */
75 
76 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 5 ) ) \
77 int processCertQuery( INOUT SESSION_INFO *sessionInfoPtr,
79  IN_ARRAY( queryReqInfoSize ) \
80  const CERTSTORE_READ_INFO *queryReqInfo,
81  IN_RANGE( 1, 64 ) const int queryReqInfoSize,
83  OUT_BUFFER_OPT( attributeMaxLen, *attributeLen ) \
84  void *attribute,
86  OUT_OPT_LENGTH_SHORT_Z int *attributeLen )
87  {
88  const CERTSTORE_READ_INFO *queryInfoPtr = NULL;
89  const int firstChar = toLower( httpReqInfo->attribute[ 0 ] );
90  int i, status;
91 
92  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
93  assert( isReadPtr( httpReqInfo, sizeof( HTTP_URI_INFO ) ) );
94  assert( isReadPtr( queryReqInfo,
95  sizeof( CERTSTORE_READ_INFO ) * queryReqInfoSize ) );
96  assert( isWritePtr( attributeID, sizeof( int ) ) );
97  assert( ( attribute == NULL && attributeMaxLen == 0 && \
98  attributeLen == NULL ) || \
99  ( isWritePtr( attribute, attributeMaxLen ) && \
100  isWritePtr( attributeLen, sizeof( int ) ) ) );
101 
102  REQUIRES( queryReqInfoSize > 0 && queryReqInfoSize <= 64 );
103  REQUIRES( ( attribute == NULL && attributeMaxLen == 0 && \
104  attributeLen == NULL ) || \
105  ( attribute != NULL && \
106  attributeMaxLen > 0 && \
107  attributeMaxLen < MAX_INTLENGTH_SHORT && \
108  attributeLen != NULL ) );
109 
110  /* Clear return values */
111  *attributeID = CRYPT_ATTRIBUTE_NONE;
112  if( attribute != NULL )
113  {
114  memset( attribute, 0, min( 16, attributeMaxLen ) );
115  *attributeLen = 0;
116  }
117 
118  /* Convert the search attribute type into a cryptlib key ID */
119  for( i = 0; i < queryReqInfoSize && \
120  queryReqInfo[ i ].attrName != NULL; i++ )
121  {
122  if( httpReqInfo->attributeLen == queryReqInfo[ i ].attrNameLen && \
123  queryReqInfo[ i ].attrName[ 0 ] == firstChar && \
124  !strCompare( httpReqInfo->attribute, \
125  queryReqInfo[ i ].attrName, \
126  queryReqInfo[ i ].attrNameLen ) )
127  {
128  queryInfoPtr = &queryReqInfo[ i ];
129  break;
130  }
131  }
132  ENSURES( i < queryReqInfoSize );
133  if( queryInfoPtr == NULL )
134  {
135  char queryText[ CRYPT_MAX_TEXTSIZE + 8 ];
136 
137  status = queryAttributeToString( queryText, CRYPT_MAX_TEXTSIZE,
138  httpReqInfo->attribute,
139  httpReqInfo->attributeLen );
140  ENSURES( cryptStatusOK( status ) );
143  "Invalid certificate query attribute '%s'", queryText ) );
144  }
145 
146  /* We've got a valid attribute, let the caller know which one it is. If
147  that's all the information that they're after, we're done */
148  *attributeID = queryInfoPtr->attrID;
149  if( attribute == NULL )
150  return( CRYPT_OK );
151 
152  /* If the query data wasn't encoded in any way, we're done */
153  if( !( queryInfoPtr->flags & CERTSTORE_FLAG_BASE64 ) )
154  {
155  return( attributeCopyParams( attribute, attributeMaxLen,
156  attributeLen, httpReqInfo->value,
157  httpReqInfo->valueLen ) );
158  }
159 
160  /* The value was base64-encoded in transit, decode it to get the actual
161  query data */
162  status = base64decode( attribute, attributeMaxLen, attributeLen,
163  httpReqInfo->value, httpReqInfo->valueLen,
165  if( cryptStatusError( status ) )
166  {
167  char queryText[ CRYPT_MAX_TEXTSIZE + 8 ];
168 
169  status = queryAttributeToString( queryText, CRYPT_MAX_TEXTSIZE,
170  httpReqInfo->value,
171  httpReqInfo->valueLen );
172  ENSURES( cryptStatusOK( status ) );
175  "Invalid base64-encoded query value '%s'", queryText ) );
176  }
177 
178  return( CRYPT_OK );
179  }
180 
181 /* Send an HTTP error response to the client (the error status value is
182  mapped at the HTTP layer to an appropriate HTTP response). We don't
183  return a status from this since the caller already has an error status
184  available */
185 
186 STDC_NONNULL_ARG( ( 1 ) ) \
187 void sendCertErrorResponse( INOUT SESSION_INFO *sessionInfoPtr,
188  IN_ERROR const int errorStatus )
189  {
190  HTTP_DATA_INFO httpDataInfo;
191 
192  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
193 
194  REQUIRES_V( cryptStatusError( errorStatus ) );
195 
196  initHttpDataInfo( &httpDataInfo, sessionInfoPtr->receiveBuffer,
197  sessionInfoPtr->receiveBufSize );
198  httpDataInfo.reqStatus = errorStatus;
199  ( void ) swrite( &sessionInfoPtr->stream, &httpDataInfo,
200  sizeof( HTTP_DATA_INFO ) );
201  }
202 
203 /****************************************************************************
204 * *
205 * Init/Shutdown Functions *
206 * *
207 ****************************************************************************/
208 
209 /* Exchange data with an HTTP client */
210 
212 static int serverTransact( INOUT SESSION_INFO *sessionInfoPtr )
213  {
214  HTTP_DATA_INFO httpDataInfo;
216  MESSAGE_KEYMGMT_INFO getkeyInfo;
219  int keyIDtype, keyIDLen, status;
220 
221  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
222 
223  sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HTTPREQTYPES,
225 
226  /* Read the request data from the client. We do a direct read rather
227  than using readPkiDatagram() since we're reading an idempotent HTTP
228  GET request and not a PKI datagram submitted via an HTTP POST */
229  initHttpDataInfoEx( &httpDataInfo, sessionInfoPtr->receiveBuffer,
230  sessionInfoPtr->receiveBufSize, &httpReqInfo );
231  status = sread( &sessionInfoPtr->stream, &httpDataInfo,
232  sizeof( HTTP_DATA_INFO ) );
233  if( cryptStatusError( status ) )
234  {
235  sNetGetErrorInfo( &sessionInfoPtr->stream,
236  &sessionInfoPtr->errorInfo );
237  return( status );
238  }
239 
240  /* Convert the certificate query into a certstore search key */
241  status = processCertQuery( sessionInfoPtr, &httpReqInfo,
242  certstoreReadInfo,
243  FAILSAFE_ARRAYSIZE( certstoreReadInfo, \
245  &keyIDtype, keyID, CRYPT_MAX_TEXTSIZE,
246  &keyIDLen );
247  if( cryptStatusError( status ) )
248  {
249  sendCertErrorResponse( sessionInfoPtr, status );
250  return( status );
251  }
252 
253  /* Try and fetch the requested certificate. Note that this is somewhat
254  suboptimal since we have to instantiate the certificate only to
255  destroy it again as soon as we've exported the certificate data, for
256  a proper high-performance implementation the server would query the
257  certificate database directly and send the stored encoded value to
258  the client */
259  setMessageKeymgmtInfo( &getkeyInfo, keyIDtype, keyID, keyIDLen,
260  NULL, 0, KEYMGMT_FLAG_NONE );
261  status = krnlSendMessage( sessionInfoPtr->cryptKeyset,
262  IMESSAGE_KEY_GETKEY, &getkeyInfo,
264  if( cryptStatusError( status ) )
265  {
266  char queryText[ CRYPT_MAX_TEXTSIZE + 8 ];
267  char textBuffer[ 64 + CRYPT_MAX_TEXTSIZE + 8 ];
268  int textLength;
269 
270  /* Not finding a certificate in response to a request isn't a real
271  error so all we do is return a warning to the caller.
272  Unfortunately since we're not using retExt() we have to assemble
273  the message string ourselves */
274  sendCertErrorResponse( sessionInfoPtr, status );
275  status = queryAttributeToString( queryText, CRYPT_MAX_TEXTSIZE,
276  httpReqInfo.value,
277  httpReqInfo.valueLen );
278  ENSURES( cryptStatusOK( status ) );
279  textLength = sprintf_s( textBuffer, 64 + CRYPT_MAX_TEXTSIZE,
280  "Warning: Couldn't find certificate for '%s'",
281  queryText );
282  setErrorString( SESSION_ERRINFO, textBuffer, textLength );
283 
284  return( CRYPT_OK );
285  }
286 
287  /* Write the certificate to the session buffer */
288  setMessageData( &msgData, sessionInfoPtr->receiveBuffer,
289  sessionInfoPtr->receiveBufSize );
290  status = krnlSendMessage( getkeyInfo.cryptHandle, IMESSAGE_CRT_EXPORT,
291  &msgData, CRYPT_CERTFORMAT_CERTIFICATE );
293  if( cryptStatusError( status ) )
294  {
295  char queryText[ CRYPT_MAX_TEXTSIZE + 8 ];
296  int altStatus;
297 
298  sendCertErrorResponse( sessionInfoPtr, status );
299  altStatus = queryAttributeToString( queryText, CRYPT_MAX_TEXTSIZE,
300  httpReqInfo.value,
301  httpReqInfo.valueLen );
302  ENSURES( cryptStatusOK( altStatus ) );
303  retExt( status,
304  ( status, SESSION_ERRINFO,
305  "Couldn't export requested certificate for '%s'",
306  queryText ) );
307  }
308  sessionInfoPtr->receiveBufEnd = msgData.length;
309 
310  /* Send the result to the client */
311  return( writePkiDatagram( sessionInfoPtr, CERTSTORE_CONTENT_TYPE,
313  }
314 
315 /****************************************************************************
316 * *
317 * Session Access Routines *
318 * *
319 ****************************************************************************/
320 
322 int setAccessMethodCertstore( INOUT SESSION_INFO *sessionInfoPtr )
323  {
324  static const PROTOCOL_INFO protocolInfo = {
325  /* General session information */
326  TRUE, /* Request-response protocol */
327  SESSION_ISHTTPTRANSPORT, /* Flags */
328  80, /* HTTP port */
329  0, /* Client flags */
330  SESSION_NEEDS_KEYSET, /* Server flags */
331  1, 1, 1 /* Version 1 */
332 
333  /* Protocol-specific information */
334  };
335 
336  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
337 
338  /* Set the access method pointers. The client-side implementation is
339  just a standard HTTP fetch so there's no explicit certstore client
340  implementation */
341  sessionInfoPtr->protocolInfo = &protocolInfo;
342  if( isServer( sessionInfoPtr ) )
343  sessionInfoPtr->transactFunction = serverTransact;
344  else
345  return( CRYPT_ERROR_NOTAVAIL );
346 
347  return( CRYPT_OK );
348  }
349 #endif /* USE_CERTSTORE || USE_SCEP */