cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
user_rw.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Configuration Read/Write Routines *
4 * Copyright Peter Gutmann 1994-2008 *
5 * *
6 ****************************************************************************/
7 
8 #include "crypt.h"
9 #ifdef INC_ALL
10  #include "trustmgr.h"
11  #include "asn1.h"
12  #include "user_int.h"
13  #include "user.h"
14 #else
15  #include "cert/trustmgr.h"
16  #include "enc_dec/asn1.h"
17  #include "misc/user_int.h"
18  #include "misc/user.h"
19 #endif /* Compiler-specific includes */
20 
21 /****************************************************************************
22 * *
23 * Utility Functions *
24 * *
25 ****************************************************************************/
26 
27 /* Read an individual configuration option */
28 
30 static int readConfigOption( INOUT STREAM *stream,
31  IN_HANDLE CRYPT_USER iCryptUser )
32  {
34  const BUILTIN_OPTION_INFO *builtinOptionInfoPtr;
36  void *dataPtr = DUMMY_INIT_PTR;
37  long optionCode;
38  int value, tag, length, status;
39 
40  /* Read the wrapper and option index and map it to the actual option.
41  If we find an unknown index or one that shouldn't be writeable to
42  persistent storage, we skip it and continue. This is done to handle
43  new options that may have been added after this version of cryptlib
44  was built (for unknown indices) and because the stored configuration
45  options are an untrusted source so we have to check for attempts to
46  feed in bogus values (for non-writeable options) */
47  readSequence( stream, NULL );
48  status = readShortInteger( stream, &optionCode );
49  if( cryptStatusError( status ) )
50  return( status );
51  if( optionCode < 0 || optionCode > LAST_OPTION_INDEX )
52  {
53  /* Unknown option, ignore it */
54  return( readUniversal( stream ) );
55  }
56  builtinOptionInfoPtr = getBuiltinOptionInfoByCode( optionCode );
57  if( builtinOptionInfoPtr == NULL || \
58  builtinOptionInfoPtr->index < 0 || \
59  builtinOptionInfoPtr->index > LAST_OPTION_INDEX || \
60  builtinOptionInfoPtr->index == CRYPT_UNUSED )
61  {
62  /* Unknown option, ignore it */
63  return( readUniversal( stream ) );
64  }
65  attributeType = builtinOptionInfoPtr->option;
66 
67  /* Read the option value and set the option. We don't treat a failure
68  to set the option as a problem since the user probably doesn't want
69  the entire system to fail because of a bad configuration option, and
70  in any case we'll fall back to a safe default value */
71  tag = peekTag( stream );
72  if( cryptStatusError( tag ) )
73  return( tag );
74  if( tag == BER_BOOLEAN || tag == BER_INTEGER )
75  {
76  /* It's a numeric value, read the appropriate type and try and set
77  the option */
78  if( tag == BER_BOOLEAN )
79  status = readBoolean( stream, &value );
80  else
81  {
82  long integer;
83 
84  status = readShortInteger( stream, &integer );
85  if( cryptStatusOK( status ) )
86  value = ( int ) integer;
87  }
88  if( cryptStatusError( status ) )
89  return( status );
90  ( void ) krnlSendMessage( iCryptUser, IMESSAGE_SETATTRIBUTE,
91  &value, attributeType );
92  return( CRYPT_OK );
93  }
94 
95  /* It's a string value, set the option straight from the encoded data */
96  status = readGenericHole( stream, &length, 1, BER_STRING_UTF8 );
97  if( cryptStatusOK( status ) )
98  status = sMemGetDataBlock( stream, &dataPtr, length );
99  if( cryptStatusOK( status ) )
100  status = sSkip( stream, length );
101  if( cryptStatusError( status ) )
102  return( status );
103  setMessageData( &msgData, dataPtr, length );
104  ( void ) krnlSendMessage( iCryptUser, IMESSAGE_SETATTRIBUTE_S,
105  &msgData, attributeType );
106 
107  return( CRYPT_OK );
108  }
109 
110 /* Rumble through the configuration options to determine the total encoded
111  length of the ones that don't match the default setting. We can't just
112  check the isDirty flag because if a value is reset to its default setting
113  the encoded size will be zero even though the isDirty flag is set */
114 
116 static int sizeofConfigData( IN_ARRAY( configOptionsCount ) \
117  const OPTION_INFO *optionList,
119  OUT_LENGTH_Z int *length )
120  {
121  int dataLength = 0, i;
122 
123  assert( isReadPtr( optionList,
124  sizeof( OPTION_INFO ) * configOptionsCount ) );
125  assert( isWritePtr( length, sizeof( int ) ) );
126 
127  REQUIRES( configOptionsCount > 0 && \
128  configOptionsCount < MAX_INTLENGTH_SHORT );
129 
130  /* Clear return value */
131  *length = 0;
132 
133  /* Check each option to see whether it needs to be written to disk. If
134  it does, determine its length */
135  for( i = 0;
136  i < configOptionsCount && \
137  optionList[ i ].builtinOptionInfo != NULL && \
138  optionList[ i ].builtinOptionInfo->option <= LAST_STORED_OPTION;
139  i++ )
140  {
141  const BUILTIN_OPTION_INFO *builtinOptionInfoPtr = \
142  optionList[ i ].builtinOptionInfo;
143  const OPTION_INFO *optionInfoPtr = &optionList[ i ];
144  int lengthValue;
145 
146  /* If it's an option that can't be written to disk, skip it */
147  if( builtinOptionInfoPtr->index == CRYPT_UNUSED )
148  continue;
149 
150  if( builtinOptionInfoPtr->type == OPTION_STRING )
151  {
152  /* If the string value is the same as the default, there's
153  nothing to do */
154  if( optionInfoPtr->strValue == NULL || \
155  optionInfoPtr->strValue == builtinOptionInfoPtr->strDefault )
156  continue;
157  lengthValue = ( int ) \
158  sizeofObject( \
159  sizeofShortInteger( builtinOptionInfoPtr->index ) + \
160  sizeofObject( optionInfoPtr->intValue ) );
161  }
162  else
163  {
164  /* If the integer/boolean value that's currently set isn't the
165  default setting, update it */
166  if( optionInfoPtr->intValue == builtinOptionInfoPtr->intDefault )
167  continue;
168  lengthValue = ( int ) \
169  sizeofObject( \
170  sizeofShortInteger( builtinOptionInfoPtr->index ) + \
171  ( builtinOptionInfoPtr->type == OPTION_NUMERIC ? \
172  sizeofShortInteger( optionInfoPtr->intValue ) : \
173  sizeofBoolean() ) );
174  }
175  ENSURES( lengthValue > 0 && lengthValue < MAX_INTLENGTH_SHORT );
176  dataLength += lengthValue;
177  }
178  ENSURES( i < configOptionsCount );
179  ENSURES( dataLength >= 0 && dataLength < MAX_INTLENGTH );
180 
181  *length = dataLength;
182  return( CRYPT_OK );
183  }
184 
185 /* Write the configuration data to a stream */
186 
188 static int writeConfigData( INOUT STREAM *stream,
190  const OPTION_INFO *optionList,
191  IN_INT_SHORT const int configOptionsCount )
192  {
193  int i, status;
194 
195  assert( isWritePtr( stream, sizeof( STREAM ) ) );
196  assert( isReadPtr( optionList,
197  sizeof( OPTION_INFO ) * configOptionsCount ) );
198 
199  REQUIRES( configOptionsCount > 0 && \
200  configOptionsCount < MAX_INTLENGTH_SHORT );
201 
202  /* Write each option that needs to be written to the stream */
203  for( i = 0;
204  i < configOptionsCount && \
205  optionList[ i ].builtinOptionInfo != NULL && \
206  optionList[ i ].builtinOptionInfo->option <= LAST_STORED_OPTION;
207  i++ )
208  {
209  const BUILTIN_OPTION_INFO *builtinOptionInfoPtr = \
210  optionList[ i ].builtinOptionInfo;
211  const OPTION_INFO *optionInfoPtr = &optionList[ i ];
212 
213  /* If it's an option that can't be written to disk, skip it */
214  if( builtinOptionInfoPtr->index == CRYPT_UNUSED )
215  continue;
216 
217  if( builtinOptionInfoPtr->type == OPTION_STRING )
218  {
219  if( optionInfoPtr->strValue == NULL || \
220  optionInfoPtr->strValue == builtinOptionInfoPtr->strDefault )
221  continue;
222  writeSequence( stream,
223  sizeofShortInteger( builtinOptionInfoPtr->index ) + \
224  sizeofObject( optionInfoPtr->intValue ) );
225  writeShortInteger( stream, builtinOptionInfoPtr->index,
226  DEFAULT_TAG );
227  status = writeCharacterString( stream, optionInfoPtr->strValue,
228  optionInfoPtr->intValue,
229  BER_STRING_UTF8 );
230  if( cryptStatusError( status ) )
231  return( status );
232  continue;
233  }
234 
235  if( optionInfoPtr->intValue == builtinOptionInfoPtr->intDefault )
236  continue;
237  if( builtinOptionInfoPtr->type == OPTION_NUMERIC )
238  {
239  writeSequence( stream,
240  sizeofShortInteger( builtinOptionInfoPtr->index ) + \
241  sizeofShortInteger( optionInfoPtr->intValue ) );
242  writeShortInteger( stream, builtinOptionInfoPtr->index,
243  DEFAULT_TAG );
244  status = writeShortInteger( stream, optionInfoPtr->intValue,
245  DEFAULT_TAG );
246  }
247  else
248  {
249  writeSequence( stream,
250  sizeofShortInteger( builtinOptionInfoPtr->index ) + \
251  sizeofBoolean() );
252  writeShortInteger( stream, builtinOptionInfoPtr->index,
253  DEFAULT_TAG );
254  status = writeBoolean( stream, optionInfoPtr->intValue,
255  DEFAULT_TAG );
256  }
257  if( cryptStatusError( status ) )
258  return( status );
259  }
260  ENSURES( i < configOptionsCount );
261 
262  return( CRYPT_OK );
263  }
264 
265 /****************************************************************************
266 * *
267 * Read Configuration Options *
268 * *
269 ****************************************************************************/
270 
271 /* Read any user-defined configuration options. Since the configuration
272  file is an untrusted source we set the values in it via external messages
273  rather than manipulating the configuration info directly, which means
274  that everything read is subject to the usual ACL checks */
275 
276 #ifdef USE_CERTIFICATES
277 
279 static int readTrustedCerts( IN_HANDLE const CRYPT_KEYSET iCryptKeyset,
280  INOUT void *trustInfoPtr )
281  {
283  BYTE buffer[ CRYPT_MAX_PKCSIZE + 1536 + 8 ];
284  int iterationCount, status;
285 
286  assert( trustInfoPtr != NULL );
287 
288  REQUIRES( isHandleRangeValid( iCryptKeyset ) );
289 
290  /* Read each trusted certificate from the keyset */
291  setMessageData( &msgData, buffer, CRYPT_MAX_PKCSIZE + 1536 );
292  status = krnlSendMessage( iCryptKeyset, IMESSAGE_GETATTRIBUTE_S,
293  &msgData, CRYPT_IATTRIBUTE_TRUSTEDCERT );
294  for( iterationCount = 0;
295  cryptStatusOK( status ) && \
296  iterationCount < FAILSAFE_ITERATIONS_LARGE;
297  iterationCount++ )
298  {
299  /* Add the certificate data as a trusted certificate item and look
300  for the next one */
301  status = addTrustEntry( trustInfoPtr, CRYPT_UNUSED, msgData.data,
302  msgData.length, TRUE );
303  if( cryptStatusOK( status ) )
304  {
305  setMessageData( &msgData, buffer, CRYPT_MAX_PKCSIZE + 1536 );
306  status = krnlSendMessage( iCryptKeyset, IMESSAGE_GETATTRIBUTE_S,
307  &msgData,
308  CRYPT_IATTRIBUTE_TRUSTEDCERT_NEXT );
309  }
310  }
311  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
312 
313  return( ( status == CRYPT_ERROR_NOTFOUND ) ? CRYPT_OK : status );
314  }
315 #endif /* USE_CERTIFICATES */
316 
318 int readConfig( IN_HANDLE const CRYPT_USER iCryptUser,
319  IN_STRING const char *fileName,
320  INOUT_OPT void *trustInfoPtr )
321  {
323  MESSAGE_CREATEOBJECT_INFO createInfo;
324  STREAM stream;
325  DYNBUF configDB;
326  char configFilePath[ MAX_PATH_LENGTH + 8 ];
327  int configFilePathLen, iterationCount, status;
328 
329  assert( isReadPtr( fileName, 2 ) );
330 
331  REQUIRES( iCryptUser == DEFAULTUSER_OBJECT_HANDLE || \
332  isHandleRangeValid( iCryptUser ) );
333 
334  /* Try and open the configuration file. If we can't open it it merely
335  means that the file doesn't exist, which isn't an error, we'll go
336  with the built-in defaults */
337  status = fileBuildCryptlibPath( configFilePath, MAX_PATH_LENGTH,
338  &configFilePathLen, fileName,
339  strlen( fileName ), BUILDPATH_GETPATH );
340  if( cryptStatusError( status ) )
341  return( CRYPT_OK ); /* Can't build configuration path */
343  createInfo.arg2 = CRYPT_KEYOPT_READONLY;
344  createInfo.strArg1 = configFilePath;
345  createInfo.strArgLen1 = configFilePathLen;
347  &createInfo, OBJECT_TYPE_KEYSET );
348  if( cryptStatusError( status ) )
349  return( CRYPT_OK ); /* No configuration data present */
350  iCryptKeyset = createInfo.cryptHandle;
351 
352  /* Get the configuration information from the keyset. We fetch the
353  overall configuration information into a dynbuf and then optionally
354  read the trust information (trusted certificates) if certificate use
355  is enabled, after which we close the keyset and read the
356  configuration data from the dynbuf */
357  status = dynCreate( &configDB, iCryptKeyset,
358  CRYPT_IATTRIBUTE_CONFIGDATA );
359  if( cryptStatusError( status ) )
360  {
361  /* If there were no configuration options present there may still be
362  trusted certificates so we try and read those before exiting */
363 #ifdef USE_CERTIFICATES
364  if( status == CRYPT_ERROR_NOTFOUND && trustInfoPtr != NULL )
365  status = readTrustedCerts( iCryptKeyset, trustInfoPtr );
366 #endif /* USE_CERTIFICATES */
367  krnlSendNotifier( iCryptKeyset, IMESSAGE_DECREFCOUNT );
368  return( status );
369  }
370 #ifdef USE_CERTIFICATES
371  if( trustInfoPtr != NULL )
372  status = readTrustedCerts( iCryptKeyset, trustInfoPtr );
373 #endif /* USE_CERTIFICATES */
374  krnlSendNotifier( iCryptKeyset, IMESSAGE_DECREFCOUNT );
375  if( cryptStatusError( status ) )
376  {
377  dynDestroy( &configDB );
378  return( status );
379  }
380 
381  /* Read each configuration option */
382  sMemConnect( &stream, dynData( configDB ), dynLength( configDB ) );
383  for( iterationCount = 0;
384  cryptStatusOK( status ) && \
385  stell( &stream ) < dynLength( configDB ) && \
386  iterationCount < FAILSAFE_ITERATIONS_LARGE;
387  iterationCount++ )
388  {
389  status = readConfigOption( &stream, iCryptUser );
390  }
391  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
392  sMemDisconnect( &stream );
393 
394  /* Clean up */
395  dynDestroy( &configDB );
396  return( status );
397  }
398 
399 /****************************************************************************
400 * *
401 * Write Configuration Options *
402 * *
403 ****************************************************************************/
404 
405 /* Write any user-defined configuration options. This is performed in two
406  phases, a first phase that encodes the configuration data and a second
407  phase that writes the data to disk. The reason for the split is that the
408  second phase doesn't require the use of the user object data any more
409  and can be a somewhat lengthy process due to disk accesses and other bits
410  and pieces, because of this the caller is expected to unlock the user
411  object between the two phases to ensure that the second phase doesn't
412  stall all other operations that require it.
413 
414  The prepare phase is further subdivided into two sub-phases, necessitated
415  by the fact that we can have configuration data, trusted certificates, or
416  both, present to write. First we determine what's present using
417  getConfigDisposition(), if there's configuration data present then we
418  prepare it using prepareConfigData(), and finally we write the
419  configuration data and/or trusted certificates using commitConfigData() */
420 
422 int getConfigDisposition( INOUT_ARRAY( configOptionsCount ) TYPECAST( OPTION_INFO * ) \
423  void *configOptions,
424  IN_INT_SHORT const int configOptionsCount,
425  const void *trustInfoPtr,
426  OUT_ENUM( CONFIG_DISPOSITION ) \
427  CONFIG_DISPOSITION_TYPE *disposition )
428  {
429 #ifdef USE_CERTIFICATES
430  const BOOLEAN hasTrustedCerts = trustedCertsPresent( trustInfoPtr );
431 #else
432  const BOOLEAN hasTrustedCerts = FALSE;
433 #endif /* USE_CERTIFICATES */
434  int length, status;
435 
436  assert( isReadPtr( configOptions,
437  sizeof( OPTION_INFO ) * configOptionsCount ) );
438  assert( trustInfoPtr != NULL );
439  assert( isWritePtr( disposition, sizeof( CONFIG_DISPOSITION_TYPE ) ) );
440 
441  REQUIRES( configOptionsCount > 0 && \
442  configOptionsCount < MAX_INTLENGTH_SHORT );
443 
444  /* Clear return value */
445  *disposition = CONFIG_DISPOSITION_NONE;
446 
447  /* If neither the configuration options nor any certificate trust
448  settings have changed, there's nothing to do */
449  if( !checkConfigChanged( configOptions, configOptionsCount ) && \
450  !hasTrustedCerts )
451  {
452  *disposition = CONFIG_DISPOSITION_NO_CHANGE;
453 
454  return( CRYPT_OK );
455  }
456 
457  /* Determine the total encoded length of the configuration options */
458  status = sizeofConfigData( configOptions, configOptionsCount, &length );
459  if( cryptStatusError( status ) )
460  return( status );
461  if( length <= 0 )
462  {
463  /* There's no configuration data present, if there are trusted
464  certificates present then we need to write those, otherwise the
465  configuration file will be empty and the caller can delete it */
466  *disposition = hasTrustedCerts ? \
467  CONFIG_DISPOSITION_TRUSTED_CERTS_ONLY : \
468  CONFIG_DISPOSITION_EMPTY_CONFIG_FILE;
469  return( CRYPT_OK );
470  }
471 
472  /* There's configuration data present, report whether there are trusted
473  certificates present as well */
474  *disposition = hasTrustedCerts ? CONFIG_DISPOSITION_BOTH : \
475  CONFIG_DISPOSITION_DATA_ONLY;
476  return( CRYPT_OK );
477  }
478 
479 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
480 int prepareConfigData( INOUT_ARRAY( configOptionsCount ) TYPECAST( OPTION_INFO * ) \
481  void *configOptions,
482  IN_INT_SHORT const int configOptionsCount,
484  OUT_LENGTH_Z int *dataLength )
485  {
486  STREAM stream;
487  void *dataPtr;
488  int length, status;
489 
490  assert( isReadPtr( configOptions,
491  sizeof( OPTION_INFO ) * configOptionsCount ) );
492  assert( isWritePtr( dataPtrPtr, sizeof( void * ) ) );
493  assert( isWritePtr( dataLength, sizeof( int ) ) );
494 
495  REQUIRES( configOptionsCount > 0 && \
496  configOptionsCount < MAX_INTLENGTH_SHORT );
497 
498  /* Clear return values */
499  *dataPtrPtr = NULL;
500  *dataLength = 0;
501 
502  /* Determine the total encoded length of the configuration options */
503  status = sizeofConfigData( configOptions, configOptionsCount, &length );
504  if( cryptStatusError( status ) )
505  return( status );
506  ENSURES( length > 0 && length < MAX_INTLENGTH );
507 
508  /* Allocate a buffer to hold the encoded values */
509  if( ( dataPtr = clAlloc( "prepareConfigData", length ) ) == NULL )
510  return( CRYPT_ERROR_MEMORY );
511 
512  /* Write the configuration options */
513  sMemOpen( &stream, dataPtr, length );
514  status = writeConfigData( &stream, configOptions, configOptionsCount );
515  if( cryptStatusOK( status ) )
516  length = stell( &stream );
517  sMemDisconnect( &stream );
518  if( cryptStatusError( status ) )
519  {
520  clFree( "prepareConfigData", dataPtr );
521  DEBUG_DIAG(( "Couldn't prepare config data for write" ));
522  assert( DEBUG_WARN );
523  return( status );
524  }
525  *dataPtrPtr = dataPtr;
526  *dataLength = length;
527 
528  return( CRYPT_OK );
529  }
530 
532 int commitConfigData( IN_STRING const char *fileName,
533  IN_BUFFER_OPT( dataLength ) const void *data,
534  IN_LENGTH_Z const int dataLength,
536  {
537  MESSAGE_CREATEOBJECT_INFO createInfo;
539  char configFilePath[ MAX_PATH_LENGTH + 8 ];
540  int configFilePathLen, status;
541 
542  assert( isReadPtr( fileName, 2 ) );
543  assert( ( data == NULL && dataLength == 0 ) || \
544  isReadPtr( data, dataLength ) );
545 
546  REQUIRES( iTrustedCertUserObject == CRYPT_UNUSED || \
547  ( iTrustedCertUserObject == DEFAULTUSER_OBJECT_HANDLE || \
548  isHandleRangeValid( iTrustedCertUserObject ) ) );
549  REQUIRES( ( data == NULL && dataLength == 0 ) || \
550  ( dataLength > 0 && dataLength < MAX_INTLENGTH ) );
551 
552  /* Build the path to the configuration file and try and create it */
553  status = fileBuildCryptlibPath( configFilePath, MAX_PATH_LENGTH,
554  &configFilePathLen, fileName,
555  strlen( fileName ),
557  if( cryptStatusError( status ) )
558  {
559  /* Map the lower-level filesystem-specific error into a more
560  meaningful generic error */
561  return( CRYPT_ERROR_OPEN );
562  }
564  createInfo.arg2 = CRYPT_KEYOPT_CREATE;
565  createInfo.strArg1 = configFilePath;
566  createInfo.strArgLen1 = configFilePathLen;
568  &createInfo, OBJECT_TYPE_KEYSET );
569  if( cryptStatusError( status ) )
570  {
571  /* Map the lower-level keyset-specific error into a more meaningful
572  generic error */
573  return( CRYPT_ERROR_OPEN );
574  }
575 
576  /* Send the configuration data (if there is any) and any trusted
577  certificates to the keyset. { data, dataLength } can be empty if
578  there are only trusted certificates to write */
579  if( dataLength > 0 )
580  {
581  setMessageData( &msgData, ( MESSAGE_CAST ) data, dataLength );
582  status = krnlSendMessage( createInfo.cryptHandle,
583  IMESSAGE_SETATTRIBUTE_S, &msgData,
584  CRYPT_IATTRIBUTE_CONFIGDATA );
585  }
586  if( cryptStatusOK( status ) && \
587  iTrustedCertUserObject != CRYPT_UNUSED )
588  {
589  status = krnlSendMessage( iTrustedCertUserObject,
591  &createInfo.cryptHandle,
592  CRYPT_IATTRUBUTE_CERTKEYSET );
593  }
595  if( cryptStatusError( status ) )
596  {
597  fileErase( configFilePath );
598  return( CRYPT_ERROR_WRITE );
599  }
600  return( CRYPT_OK );
601  }
602 
603 /****************************************************************************
604 * *
605 * Delete Configuration Options *
606 * *
607 ****************************************************************************/
608 
609 /* Delete the configuration file. This always returns an OK status even if
610  the delete fails since it's not certain what we should do in this case */
611 
613 int deleteConfig( IN_STRING const char *fileName )
614  {
615  char configFilePath[ MAX_PATH_LENGTH + 1 + 8 ];
616  int configFilePathLen, status;
617 
618  status = fileBuildCryptlibPath( configFilePath, MAX_PATH_LENGTH,
619  &configFilePathLen, fileName,
620  strlen( fileName ), BUILDPATH_GETPATH );
621  if( cryptStatusOK( status ) )
622  {
623  configFilePath[ configFilePathLen ] = '\0';
624  fileErase( configFilePath );
625  }
626  return( CRYPT_OK );
627  }