cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
file.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * File Stream I/O Functions *
4 * Copyright Peter Gutmann 1993-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( __UNIX__ ) && defined( __linux__ )
9  /* In order for the fileReadonly() check to work we need to be able to
10  check errno, however for this to work the headers that specify that
11  threading is being used must be the first headers included
12  (specifically, the include order has to be pthread.h, unistd.h,
13  everything else) or errno.h, which is pulled in by stdlib.h, gets
14  set up as an extern int rather than a function */
15  #include "crypt.h"
16 #endif /* Older Linux broken include-file dependencies */
17 
18 #include <stdarg.h>
19 #if defined( INC_ALL )
20  #include "stream_int.h"
21  #include "file.h"
22 #else
23  #include "io/stream_int.h"
24  #include "io/file.h"
25 #endif /* Compiler-specific includes */
26 
27 /* In order to get enhanced control over things like file security and
28  buffering we can't use stdio but have to rely on using OS-level file
29  routines, which is essential for working with things like ACL's for
30  sensitive files and forcing disk writes for files we want to erase.
31  Without the forced disk write the data in the cache doesn't get flushed
32  before the file delete request arrives, after which it's discarded rather
33  than being written, so the file never gets overwritten. In addition some
34  embedded environments don't support stdio so we have to supply our own
35  alternatives.
36 
37  When implementing the following for new systems there are certain things
38  that you need to ensure to guarantee error-free operation:
39 
40  - File permissions should be set as indicated by the file open flags.
41 
42  - File sharing controls (shared vs. exclusive access locks) should be
43  implemented.
44 
45  - If the file is locked for exclusive access, the open call should either
46  block until the lock is released (they're never held for more than a
47  fraction of a second) or return CRYPT_ERROR_TIMEOUT depending on how
48  the OS handles locks.
49 
50  When erasing data, we may run into problems on embedded systems using
51  solid-state storage that implements wear-levelling by using a log-
52  structured filesystem (LFS) type arrangement. These work by never
53  writing a sector twice but always appending newly-written data at the
54  next free location until the volume is full, at which point a garbage
55  collector runs to reclaim. A main goal of LFS's is speed (data is
56  written in large sequential writes rather than lots of small random
57  writes) and error-recovery by taking advantage of the characteristics
58  of the log structure, however a side-effect of the write mechanism is
59  that it makes wear-levelling management quite simple. However, the use
60  of a LFS also makes it impossible to reliably overwrite data, since
61  new writes never touch the existing data. There's no easy way to cope
62  with this since we have no way of telling what the underlying media is
63  doing with our data. A mediating factor though is that embedded systems
64  are usually sealed, single-use systems where the chances of a second user
65  accessing the data is low. The only possible threat then is post system-
66  retirement recovery of the data, presumably if it contains valuable data
67  it'll be disposed of appropriately */
68 
69 /* If we're using DDNAME I/O under MVS we can't use the Posix I/O APIs but
70  have to use stdio stream I/O functions, enabled via CONFIG_NO_STDIO since
71  we have to use RECFM=x specifiers and other oddities */
72 
73 #if defined( __MVS__ ) && defined( DDNAME_IO )
74  #define CONFIG_NO_STDIO
75 #endif /* __MVS__ && DDNAME_IO */
76 
77 /* Symbolic defines for stdio-style file access modes */
78 
79 #if defined( __MVS__ ) && defined( DDNAME_IO )
80  #pragma convlit( suspend )
81  #define MODE_READ "rb,byteseek"
82  #define MODE_WRITE "wb,byteseek,recfm=*"
83  #define MODE_READWRITE "rb+,byteseek,recfm=*"
84  #pragma convlit( resume )
85 #elif defined( EBCDIC_CHARS )
86  #pragma convlit( suspend )
87  #define MODE_READ "rb"
88  #define MODE_WRITE "wb"
89  #define MODE_READWRITE "rb+"
90  #pragma convlit( resume )
91 #else
92  #define MODE_READ "rb"
93  #define MODE_WRITE "wb"
94  #define MODE_READWRITE "rb+"
95 #endif /* Different types of I/O and character sets */
96 
97 /****************************************************************************
98 * *
99 * Utility Functions *
100 * *
101 ****************************************************************************/
102 
103 /* Append a filename to a path and add the suffix. If we're on an EBCDIC
104  system we need two versions of this function, a standard ASCII one for
105  internal-use paths and an EBCDIC one for use with path components coming
106  from the OS like the location of $HOME */
107 
109 static int appendFilename( INOUT_BUFFER( pathMaxLen, *pathLen ) char *path,
110  IN_LENGTH_SHORT const int pathMaxLen,
112  IN_BUFFER( fileNameLen ) const char *fileName,
113  IN_LENGTH_SHORT const int fileNameLen,
114  IN_ENUM( BUILDPATH_OPTION ) \
116  {
117  const int partialPathLen = strlen( path );
118 
119  assert( isWritePtr( path, pathMaxLen ) );
120  assert( isWritePtr( pathLen, sizeof( int ) ) );
121  assert( ( option == BUILDPATH_RNDSEEDFILE ) || \
122  isReadPtr( fileName, fileNameLen ) );
123 
124  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH_SHORT );
125  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
126  option == BUILDPATH_GETPATH ) && fileName != NULL && \
127  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
128  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
129  fileNameLen == 0 ) );
130  REQUIRES( option > BUILDPATH_NONE && option < BUILDPATH_LAST );
131 
132  /* Clear return value */
133  *pathLen = 0;
134 
135  /* If we're using a fixed filename it's quite simple, just append it
136  and we're done */
137  if( option == BUILDPATH_RNDSEEDFILE )
138  {
139  if( partialPathLen + 12 > pathMaxLen )
140  return( CRYPT_ERROR_OVERFLOW );
141  memcpy( path + partialPathLen, "randseed.dat", 12 );
142  *pathLen = partialPathLen + 12;
143 
144  return( CRYPT_OK );
145  }
146 
147  /* User-defined filenames are a bit more complex because we have to
148  safely append a variable-length quantity to the path */
149  if( partialPathLen + fileNameLen + 4 > pathMaxLen )
150  return( CRYPT_ERROR_OVERFLOW );
151  memcpy( path + partialPathLen, fileName, fileNameLen );
152  memcpy( path + partialPathLen + fileNameLen, ".p15", 4 );
153  *pathLen = partialPathLen + fileNameLen + 4;
154 
155  return( CRYPT_OK );
156  }
157 
158 #ifdef EBCDIC_CHARS
159 
160 #pragma convlit( suspend )
161 
162 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
163 static int appendFilenameEBCDIC( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
164  IN_LENGTH_SHORT const int pathMaxLen,
165  OUT_LENGTH_SHORT_Z int *pathLen,
166  IN_BUFFER( fileNameLen ) const char *fileName,
167  IN_LENGTH_SHORT const int fileNameLen,
168  IN_ENUM( BUILDPATH_OPTION ) \
170  {
171  const int partialPathLen = strlen( path );
172 
173  assert( isWritePtr( path, pathMaxLen ) );
174  assert( isWritePtr( pathLen, sizeof( int ) ) );
175  assert( ( option == BUILDPATH_RNDSEEDFILE ) || \
176  isReadPtr( fileName, fileNameLen ) );
177 
178  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH_SHORT );
179  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
180  option == BUILDPATH_GETPATH ) && fileName != NULL && \
181  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
182  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
183  fileNameLen == 0 ) );
184  REQUIRES( option > BUILDPATH_NONE && option < BUILDPATH_LAST );
185 
186  /* Clear return value */
187  *pathLen = 0;
188 
189  /* If we're using a fixed filename it's quite simple, just append it
190  and we're done */
191  if( option == BUILDPATH_RNDSEEDFILE )
192  {
193  if( partialPathLen + 12 > pathMaxLen )
194  return( CRYPT_ERROR_OVERFLOW );
195  memcpy( path + partialPathLen, "randseed.dat", 12 );
196  *pathLen = partialPathLen + 12;
197 
198  return( CRYPT_OK );
199  }
200 
201  /* User-defined filenames are a bit more complex because we have to
202  safely append a variable-length quantity to the path */
203  if( partialPathLen + fileNameLen + 4 > pathMaxLen )
204  return( CRYPT_ERROR_OVERFLOW );
205  memcpy( path + partialPathLen, fileName, fileNameLen );
206  memcpy( path + partialPathLen + fileNameLen, ".p15", 4 );
207  *pathLen = partialPathLen + fileNameLen + 4;
208 
209  return( CRYPT_OK );
210  }
211 
212 #pragma convlit( resume )
213 
214 #endif /* EBCDIC_CHARS */
215 
216 /****************************************************************************
217 * *
218 * AMX File Stream Functions *
219 * *
220 ****************************************************************************/
221 
222 #if defined( __AMX__ )
223 
224 /* Open/close a file stream */
225 
226 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
227 int sFileOpen( OUT STREAM *stream, IN_STRING const char *fileName,
228  IN_FLAGS( FILE ) const int mode )
229  {
230  static const int modes[] = {
231  FJ_O_RDONLY, FJ_O_RDONLY,
232  FJ_O_WRONLY | FJ_O_CREAT | FJ_O_NOSHAREANY,
233  FJ_O_RDWR | FJ_O_NOSHAREWR
234  };
235 
236  assert( isWritePtr( stream, sizeof( STREAM ) ) );
237  assert( isReadPtr( fileName, 2 ) );
238 
239  REQUIRES( mode != 0 );
240 
241  /* Initialise the stream structure */
242  memset( stream, 0, sizeof( STREAM ) );
243  stream->type = STREAM_TYPE_FILE;
244  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
245  stream->flags = STREAM_FLAG_READONLY;
246  openMode = modes[ mode & FILE_FLAG_RW_MASK ];
247 
248  /* If we're trying to write to the file, check whether we've got
249  permission to do so */
250  if( ( mode & FILE_FLAG_WRITE ) && fileReadonly( fileName ) )
251  return( CRYPT_ERROR_PERMISSION );
252 
253  /* Try and open the file */
254  stream->fd = fjopen( fileName, openMode, ( openMode & FJ_O_CREAT ) ? \
255  FJ_S_IREAD | FJ_S_IWRITE : 0 );
256  if( stream->fd < 0 )
257  {
258  const int errNo = fjfserrno();
259 
260  return( ( errNo == FJ_EACCES || errNo == FJ_ESHARE ) ? \
262  ( errNo == FJ_ENOENT ) ? \
264  }
265 
266  return( CRYPT_OK );
267  }
268 
269 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
270 int sFileClose( INOUT STREAM *stream )
271  {
272  assert( isWritePtr( stream, sizeof( STREAM ) ) );
273 
274  REQUIRES( stream->type == STREAM_TYPE_FILE );
275 
276  /* Close the file and clear the stream structure */
277  fjclose( stream->fd );
278  zeroise( stream, sizeof( STREAM ) );
279 
280  return( CRYPT_OK );
281  }
282 
283 /* Read/write a block of data from/to a file stream */
284 
285 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
286 int fileRead( INOUT STREAM *stream,
287  OUT_BUFFER( length, *bytesRead ) void *buffer,
288  IN_LENGTH const int length,
289  OUT_LENGTH_Z int *bytesRead )
290  {
291  int byteCount;
292 
293  assert( isWritePtr( stream, sizeof( STREAM ) ) );
294  assert( isWritePtr( buffer, length ) );
295  assert( isWritePtr( bytesRead, sizeof( int ) ) );
296 
297  REQUIRES( stream->type == STREAM_TYPE_FILE );
298  REQUIRES( length > 0 && length < MAX_INTLENGTH );
299 
300  /* Clear return value */
301  *bytesRead = 0;
302 
303  if( ( byteCount = fjread( stream->fd, buffer, length ) ) < 0 )
304  return( sSetError( stream, CRYPT_ERROR_READ ) );
305  *bytesRead = byteCount;
306 
307  return( CRYPT_OK );
308  }
309 
310 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
311 int fileWrite( INOUT STREAM *stream,
312  IN_BUFFER( length ) const void *buffer,
313  IN_LENGTH const int length )
314  {
315  int bytesWritten;
316 
317  assert( isWritePtr( stream, sizeof( STREAM ) ) );
318  assert( isReadPtr( buffer, length ) );
319 
320  REQUIRES( stream->type == STREAM_TYPE_FILE );
321  REQUIRES( length > 0 && length < MAX_INTLENGTH );
322 
323  if( ( bytesWritten = fjwrite( stream->fd, buffer, length ) ) < 0 || \
324  bytesWritten != length )
325  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
326  return( CRYPT_OK );
327  }
328 
329 /* Commit data in a file stream to backing storage */
330 
332 int fileFlush( INOUT STREAM *stream )
333  {
334  assert( isWritePtr( stream, sizeof( STREAM ) ) );
335 
336  REQUIRES( stream->type == STREAM_TYPE_FILE );
337 
338  fjflush( stream->fd );
339  }
340 
341 /* Change the read/write position in a file */
342 
344 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
345  {
346  assert( isWritePtr( stream, sizeof( STREAM ) ) );
347 
348  REQUIRES( stream->type == STREAM_TYPE_FILE );
349  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
350 
351  if( fjlseek( stream->fd, position, FJ_SEEK_SET ) < 0 )
352  return( sSetError( stream, CRYPT_ERROR_READ ) );
353  return( CRYPT_OK );
354  }
355 
356 /* Check whether a file is writeable */
357 
359 BOOLEAN fileReadonly( IN_STRING const char *fileName )
360  {
361  struct fjxstat fileInfo;
362 
363  assert( isReadPtr( fileName, 2 ) );
364 
365  if( fjstat( fileName, &fileInfo ) < 0 )
366  return( TRUE );
367 
368  return( ( fileInfo->_xxx ) ? TRUE : FALSE );
369  }
370 
371 /* File deletion functions: Wipe a file from the current position to EOF,
372  and wipe and delete a file (although it's not terribly rigorous).
373  Vestigia nulla retrorsum */
374 
375 static void eraseFile( const STREAM *stream, long position, long length )
376  {
377  assert( isReadPtr( stream, sizeof( STREAM ) ) );
378 
379  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
380  REQUIRES_V( position >= 0 && position < MAX_INTLENGTH );
381  REQUIRES_V( length >= 0 && length < MAX_INTLENGTH );
382  /* May be zero if a file-open failed leaving a zero-length
383  file */
384 
385  /* Wipe everything past the current position in the file */
386  while( length > 0 )
387  {
389  BYTE buffer[ ( BUFSIZ * 2 ) + 8 ];
390  int bytesToWrite = min( length, BUFSIZ * 2 );
391 
392  /* We need to make sure that we fill the buffer with random data for
393  each write, otherwise compressing filesystems will just compress
394  it to nothing */
395  setMessageData( &msgData, buffer, bytesToWrite );
397  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
398 
399  if( fjwrite( stream->fd, buffer, bytesToWrite ) < 0 )
400  break; /* An error occurred while writing, exit */
401  length -= bytesToWrite;
402  }
403 
404  fjchsize( stream->fd, position );
405  }
406 
407 STDC_NONNULL_ARG( ( 1 ) ) \
408 void fileClearToEOF( const STREAM *stream )
409  {
410  struct fjxstat fileInfo;
411  int length, position;
412 
413  assert( isReadPtr( stream, sizeof( STREAM ) ) );
414 
415  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
416 
417  /* Wipe everything past the current position in the file */
418  if( fjstat( fileName, &fileInfo ) < 0 )
419  return;
420  length = fileInfo._xxx;
421  if( ( position = fjtell( stream->fd ) ) < 0 )
422  return;
423  length -= position;
424  if( length <= 0 )
425  return; /* Nothing to do, exit */
426  eraseFile( stream, position, length );
427  }
428 
429 STDC_NONNULL_ARG( ( 1 ) ) \
430 void fileErase( IN_STRING const char *fileName )
431  {
432  STREAM stream;
433  struct fjxstat fileInfo;
434  int status;
435 
436  assert( isReadPtr( fileName, 2 ) );
437 
438  /* Try and open the file so that we can erase it. If this fails, the
439  best that we can do is a straight unlink */
440  status = sFileOpen( &stream, fileName,
441  FILE_FLAG_READ | FILE_FLAG_WRITE | \
443  if( cryptStatusError( status ) )
444  {
445  if( status != CRYPT_ERROR_NOTFOUND )
446  remove( fileName );
447  return;
448  }
449 
450  /* Determine the size of the file and erase it */
451  fjstat( fileName, &fileInfo );
452  eraseFile( &stream, 0, fileInfo._xxx );
453 
454  /* Reset the file's attributes */
455  fjfattr( stream.fd, FJ_DA_NORMAL );
456 
457  /* Delete the file */
458  sFileClose( &stream );
459  fjunlink( fileName );
460  }
461 
462 /* Build the path to a file in the cryptlib directory */
463 
464 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
465 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
466  IN_LENGTH_SHORT const int pathMaxLen,
467  OUT_LENGTH_SHORT_Z int *pathLen,
468  IN_BUFFER( fileNameLen ) const char *fileName,
469  IN_LENGTH_SHORT const int fileNameLen,
470  IN_ENUM( BUILDPATH_OPTION ) \
471  const BUILDPATH_OPTION_TYPE option )
472  {
473  assert( isWritePtr( path, pathMaxLen ) );
474  assert( isWritePtr( pathLen, sizeof( int ) ) );
475  assert( isReadPtr( fileName, fileNameLen ) );
476 
477  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
478  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
479  option == BUILDPATH_GETPATH ) && fileName != NULL && \
480  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
481  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
482  fileNameLen == 0 ) );
483 
484  /* Make sure that the open fails if we can't build the path */
485  *path = '\0';
486 
487  /* Build the path to the configuration file if necessary. We assume that
488  we're on the correct drive */
489  strlcpy_s( path, pathMaxLen, "\\cryptlib\\" );
490 
491  /* If we're being asked to create the cryptlib directory and it doesn't
492  already exist, create it now */
493  if( option == BUILDPATH_CREATEPATH && fjisdir( path ) == 0 )
494  {
495  /* The directory doesn't exist, try and create it */
496  if( fjmkdir( path ) < 0 )
497  return( CRYPT_ERROR_OPEN );
498  }
499 
500  /* Add the filename to the path */
501  return( appendFilename( path, pathMaxLen, pathLen, fileName,
502  fileNameLen, option ) );
503  }
504 
505 /****************************************************************************
506 * *
507 * uC/OS-II File Stream Functions *
508 * *
509 ****************************************************************************/
510 
511 #elif defined( __UCOSII__ )
512 
513 /* Note that the following code requires at least uC/FS 2.x for functions
514  like FS_GetFileAttributes()/FS_SetFileAttributes(), FS_GetFileSize(),
515  and FS_SetFileTime() */
516 
517 /* Open/close a file stream */
518 
519 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
520 int sFileOpen( OUT STREAM *stream, IN_STRING const char *fileName,
521  IN_FLAGS( FILE ) const int mode )
522  {
523  static const char *modes[] = { MODE_READ, MODE_READ,
525  const char *openMode;
526 
527  assert( isWritePtr( stream, sizeof( STREAM ) ) );
528  assert( isReadPtr( fileName, 2 ) );
529 
530  REQUIRES( mode != 0 );
531 
532  /* Initialise the stream structure */
533  memset( stream, 0, sizeof( STREAM ) );
534  stream->type = STREAM_TYPE_FILE;
535  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
536  stream->flags = STREAM_FLAG_READONLY;
537  openMode = modes[ mode & FILE_FLAG_RW_MASK ];
538 
539  /* If we're trying to write to the file, check whether we've got
540  permission to do so */
541  if( ( mode & FILE_FLAG_WRITE ) && fileReadonly( fileName ) )
542  return( CRYPT_ERROR_PERMISSION );
543 
544  /* Try and open the file */
545  stream->pFile = FS_FOpen( fileName, openMode );
546  if( stream->pFile == NULL )
547  {
548  const FS_i16 errNo = FS_FError();
549 
550  /* Return what we can in the way of an error message. Curiously
551  uC/FS doesn't provide an indicator for common errors like file
552  not found, although it does provide strange indicators like
553  FS_ERR_CLOSE, an error occurred while calling FS_FClose() */
554  return( ( errNo == FS_ERR_DISKFULL ) ? \
555  CRYPT_ERROR_OVEWFLOW : \
556  ( errNo == FS_ERR_READONLY ) ? \
558  }
559 
560  return( CRYPT_OK );
561  }
562 
563 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
564 int sFileClose( INOUT STREAM *stream )
565  {
566  assert( isWritePtr( stream, sizeof( STREAM ) ) );
567 
568  REQUIRES( stream->type == STREAM_TYPE_FILE );
569 
570  /* Close the file and clear the stream structure */
571  FS_FClose( stream->pFile );
572  zeroise( stream, sizeof( STREAM ) );
573 
574  return( CRYPT_OK );
575  }
576 
577 /* Read/write a block of data from/to a file stream */
578 
579 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
580 int fileRead( INOUT STREAM *stream,
581  OUT_BUFFER( length, *bytesRead ) void *buffer,
582  IN_LENGTH const int length,
583  OUT_LENGTH_Z int *bytesRead )
584  {
585  int byteCount;
586 
587  assert( isWritePtr( stream, sizeof( STREAM ) ) );
588  assert( isWritePtr( buffer, length ) );
589  assert( isWritePtr( bytesRead, sizeof( int ) ) );
590 
591  REQUIRES( stream->type == STREAM_TYPE_FILE );
592  REQUIRES( length > 0 && length < MAX_INTLENGTH );
593 
594  /* Clear return value */
595  *bytesRead = 0;
596 
597  if( ( byteCount = FS_Read( stream->pFile, buffer, length ) ) < 0 )
598  return( sSetError( stream, CRYPT_ERROR_READ ) );
599  *bytesRead = byteCount;
600 
601  return( CRYPT_OK );
602  }
603 
604 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
605 int fileWrite( INOUT STREAM *stream,
606  IN_BUFFER( length ) const void *buffer,
607  IN_LENGTH const int length )
608  {
609  int bytesWritten;
610 
611  assert( isWritePtr( stream, sizeof( STREAM ) ) );
612  assert( isReadPtr( buffer, length ) );
613 
614  REQUIRES( stream->type == STREAM_TYPE_FILE );
615  REQUIRES( length > 0 && length < MAX_INTLENGTH );
616 
617  if( ( bytesWritten = FS_Write( stream->pFile, buffer, length ) ) < 0 || \
618  bytesWritten != length )
619  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
620  return( CRYPT_OK );
621  }
622 
623 /* Commit data in a file stream to backing storage */
624 
626 int fileFlush( INOUT STREAM *stream )
627  {
628  assert( isWritePtr( stream, sizeof( STREAM ) ) );
629 
630  REQUIRES( stream->type == STREAM_TYPE_FILE );
631 
632  /* There is an IOCTL to flush all buffers (for all files) to the backing
633  store, but it's no supported in all drivers and seems a bit excessive
634  for this case */
635  return( CRYPT_OK );
636  }
637 
638 /* Change the read/write position in a file */
639 
641 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
642  {
643  assert( isWritePtr( stream, sizeof( STREAM ) ) );
644 
645  REQUIRES( stream->type == STREAM_TYPE_FILE );
646  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
647 
648  if( FS_FSeek( stream->pFile, position, FS_SEEK_SET ) < 0 )
649  return( sSetError( stream, CRYPT_ERROR_READ ) );
650  return( CRYPT_OK );
651  }
652 
653 /* Check whether a file is writeable */
654 
656 BOOLEAN fileReadonly( IN_STRING const char *fileName )
657  {
658  FS_U8 fileAttr;
659 
660  assert( isReadPtr( fileName, 2 ) );
661 
662  if( ( fileAttr = FS_GetFileAttributes( fileName ) ) == 0xFF )
663  return( TRUE );
664 
665  return( ( fileAttr & FS_ATTR_READONLY ) ? TRUE : FALSE );
666  }
667 
668 /* File deletion functions: Wipe a file from the current position to EOF,
669  and wipe and delete a file (although it's not terribly rigorous).
670  Vestigia nulla retrorsum */
671 
672 static void eraseFile( const STREAM *stream, long position, long length )
673  {
674  assert( isReadPtr( stream, sizeof( STREAM ) ) );
675 
676  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
677  REQUIRES_V( position >= 0 && position < MAX_INTLENGTH );
678  REQUIRES_V( length >= 0 && length < MAX_INTLENGTH );
679  /* May be zero if a file-open failed leaving a zero-length
680  file */
681 
682  /* Wipe everything past the current position in the file */
683  while( length > 0 )
684  {
686  BYTE buffer[ ( BUFSIZ * 2 ) + 8 ];
687  int bytesToWrite = min( length, BUFSIZ * 2 );
688 
689  /* We need to make sure that we fill the buffer with random data for
690  each write, otherwise compressing filesystems will just compress
691  it to nothing */
692  setMessageData( &msgData, buffer, bytesToWrite );
694  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
695 
696  if( FS_Write( stream->pFile, buffer, bytesToWrite ) < 0 )
697  break; /* An error occurred while writing, exit */
698  length -= bytesToWrite;
699  }
700 
701  fjchsize( stream->pFile, position );
702  }
703 
704 STDC_NONNULL_ARG( ( 1 ) ) \
705 void fileClearToEOF( const STREAM *stream )
706  {
707  int length, position;
708 
709  assert( isReadPtr( stream, sizeof( STREAM ) ) );
710 
711  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
712 
713  /* Wipe everything past the current position in the file */
714  if( ( length = FS_GetFileSize( fileName ) ) < 0 )
715  return;
716  if( ( position = FS_FTell( stream->pFile ) ) < 0 )
717  return;
718  length -= position;
719  if( length <= 0 )
720  return; /* Nothing to do, exit */
721  eraseFile( stream, position, length );
722  }
723 
724 STDC_NONNULL_ARG( ( 1 ) ) \
725 void fileErase( IN_STRING const char *fileName )
726  {
727  STREAM stream;
728  int length, status;
729 
730  assert( isReadPtr( fileName, 2 ) );
731 
732  if( ( length = FS_GetFileSize( fileName ) ) < 0 )
733  return;
734 
735  /* Try and open the file so that we can erase it. If this fails, the
736  best that we can do is a straight unlink */
737  status = sFileOpen( &stream, fileName,
738  FILE_FLAG_READ | FILE_FLAG_WRITE | \
740  if( cryptStatusError( status ) )
741  {
742  if( status != CRYPT_ERROR_NOTFOUND )
743  remove( fileName );
744  return;
745  }
746 
747  /* Determine the size of the file and erase it */
748  eraseFile( &stream, 0, length );
749 
750  /* Reset the file's attributes and delete it */
751  sFileClose( &stream );
752  FS_SetFileAttributes( stream.pFile, FS_ATTR_ARCHIVE );
753  FS_SetFileTime( stream.pFile, 0 );
754  FS_Remove( fileName );
755  }
756 
757 /* Build the path to a file in the cryptlib directory */
758 
759 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
760 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
761  IN_LENGTH_SHORT const int pathMaxLen,
762  OUT_LENGTH_SHORT_Z int *pathLen,
763  IN_BUFFER( fileNameLen ) const char *fileName,
764  IN_LENGTH_SHORT const int fileNameLen,
765  IN_ENUM( BUILDPATH_OPTION ) \
766  const BUILDPATH_OPTION_TYPE option )
767  {
768  assert( isWritePtr( path, pathMaxLen ) );
769  assert( isWritePtr( pathLen, sizeof( int ) ) );
770  assert( isReadPtr( fileName, fileNameLen ) );
771 
772  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
773  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
774  option == BUILDPATH_GETPATH ) && fileName != NULL && \
775  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
776  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
777  fileNameLen == 0 ) );
778 
779  /* Make sure that the open fails if we can't build the path */
780  *path = '\0';
781 
782  /* Build the path to the configuration file if necessary. We assume that
783  we're on the correct drive */
784  strlcpy_s( path, pathMaxLen, "\\cryptlib\\" );
785 
786  /* If we're being asked to create the cryptlib directory and it doesn't
787  already exist, create it now */
788  if( option == BUILDPATH_CREATEPATH )
789  {
790  FS_DIR dirInfo;
791 
792  /* Note that the following two functions are uc/FS 2.x functions,
793  uc/FS 3.x uses the rather odd FS_FindFirstFile() in place of
794  these */
795  if( ( dirInfo = FS_OpenDir( path ) ) != NULL )
796  FSCloseDir( dirInfo );
797  else
798  {
799  /* The directory doesn't exist, try and create it */
800  if( FS_MkDir( path ) < 0 )
801  return( CRYPT_ERROR_OPEN );
802  }
803  }
804 
805  /* Add the filename to the path */
806  return( appendFilename( path, pathMaxLen, pathLen, fileName,
807  fileNameLen, option ) );
808  }
809 
810 /****************************************************************************
811 * *
812 * uITRON File Stream Functions *
813 * *
814 ****************************************************************************/
815 
816 /* See the comment in str_file.h for uITRON file handling */
817 
818 #elif defined( __ITRON__ )
819 
820 /* Open/close a file stream */
821 
822 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
823 int sFileOpen( OUT STREAM *stream, IN_STRING const char *fileName,
824  IN_FLAGS( FILE ) const int mode )
825  {
826  assert( isWritePtr( stream, sizeof( STREAM ) ) );
827  assert( isReadPtr( fileName, 2 ) );
828 
829  REQUIRES( mode != 0 );
830 
831  /* Initialise the stream structure */
832  memset( stream, 0, sizeof( STREAM ) );
833  stream->type = STREAM_TYPE_FILE;
834  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
835  stream->flags = STREAM_FLAG_READONLY;
836 
837  /* If we're trying to write to the file, check whether we've got
838  permission to do so */
839  if( ( mode & FILE_FLAG_WRITE ) && fileReadonly( fileName ) )
840  return( CRYPT_ERROR_PERMISSION );
841 
842  /* Try and open the file */
843  return( CRYPT_ERROR_OPEN );
844  }
845 
846 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
847 int sFileClose( INOUT STREAM *stream )
848  {
849  assert( isWritePtr( stream, sizeof( STREAM ) ) );
850 
851  REQUIRES( stream->type == STREAM_TYPE_FILE );
852 
853  /* Close the file and clear the stream structure */
854  zeroise( stream, sizeof( STREAM ) );
855 
856  return( CRYPT_OK );
857  }
858 
859 /* Read/write a block of data from/to a file stream */
860 
861 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
862 int fileRead( INOUT STREAM *stream,
863  OUT_BUFFER( length, *bytesRead ) void *buffer,
864  IN_LENGTH const int length,
865  OUT_LENGTH_Z int *bytesRead )
866  {
867  assert( isWritePtr( stream, sizeof( STREAM ) ) );
868  assert( isWritePtr( buffer, length ) );
869  assert( isWritePtr( bytesRead, sizeof( int ) ) );
870 
871  REQUIRES( stream->type == STREAM_TYPE_FILE );
872  REQUIRES( length > 0 && length < MAX_INTLENGTH );
873 
874  /* Clear return value */
875  *bytesRead = 0;
876 
877  return( sSetError( stream, CRYPT_ERROR_READ ) );
878  }
879 
880 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
881 int fileWrite( INOUT STREAM *stream,
882  IN_BUFFER( length ) const void *buffer,
883  IN_LENGTH const int length )
884  {
885  assert( isWritePtr( stream, sizeof( STREAM ) ) );
886  assert( isReadPtr( buffer, length ) );
887 
888  REQUIRES( stream->type == STREAM_TYPE_FILE );
889  REQUIRES( length > 0 && length < MAX_INTLENGTH );
890 
891  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
892  }
893 
894 /* Commit data in a file stream to backing storage */
895 
897 int fileFlush( INOUT STREAM *stream )
898  {
899  assert( isWritePtr( stream, sizeof( STREAM ) ) );
900 
901  REQUIRES( stream->type == STREAM_TYPE_FILE );
902 
903  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
904  }
905 
906 /* Change the read/write position in a file */
907 
909 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
910  {
911  assert( isWritePtr( stream, sizeof( STREAM ) ) );
912 
913  REQUIRES( stream->type == STREAM_TYPE_FILE );
914  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
915 
916  return( sSetError( stream, CRYPT_ERROR_READ ) );
917  }
918 
919 /* Check whether a file is writeable */
920 
922 BOOLEAN fileReadonly( IN_STRING const char *fileName )
923  {
924  return( TRUE );
925  }
926 
927 /* File deletion functions: Wipe a file from the current position to EOF,
928  and wipe and delete a file (although it's not terribly rigorous).
929  Vestigia nulla retrorsum */
930 
931 static void eraseFile( const STREAM *stream, long position, long length )
932  {
933  assert( isReadPtr( stream, sizeof( STREAM ) ) );
934 
935  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
936  REQUIRES_V( position >= 0 && position < MAX_INTLENGTH );
937  REQUIRES_V( length >= 0 && length < MAX_INTLENGTH );
938  /* May be zero if a file-open failed leaving a zero-length
939  file */
940 
941  /* Wipe everything past the current position in the file */
942  while( length > 0 )
943  {
945  BYTE buffer[ ( BUFSIZ * 2 ) + 8 ];
946  int bytesToWrite = min( length, BUFSIZ * 2 );
947 
948  /* We need to make sure that we fill the buffer with random data for
949  each write, otherwise compressing filesystems will just compress
950  it to nothing */
951  setMessageData( &msgData, buffer, bytesToWrite );
953  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
954  if( fwrite( buffer, 1, bytesToWrite, stream->filePtr ) == 0 )
955  break; /* An error occurred while writing, exit */
956  length -= bytesToWrite;
957  }
958  fflush( stream->filePtr );
959 
960  /* Truncate the file and if we're erasing the entire file, reset the
961  timestamps. This is only possible through a file handle on some
962  systems, on others the caller has to do it via the filename */
963  chsize( fileno( stream->filePtr ), position );
964  if( position <= 0 )
965  {
966  struct ftime fileTime;
967 
968  memset( &fileTime, 0, sizeof( struct ftime ) );
969  setftime( fileno( stream->filePtr ), &fileTime );
970  }
971  }
972 
973 STDC_NONNULL_ARG( ( 1 ) ) \
974 void fileClearToEOF( const STREAM *stream )
975  {
976  long position, length;
977 
978  assert( isReadPtr( stream, sizeof( STREAM ) ) );
979 
980  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
981 
982  /* Wipe everything past the current position in the file */
983  position = ftell( stream->filePtr );
984  fseek( stream->filePtr, 0, SEEK_END );
985  length = ftell( stream->filePtr ) - position;
986  fseek( stream->filePtr, position, SEEK_SET );
987  eraseFile( stream, position, length );
988  }
989 
990 STDC_NONNULL_ARG( ( 1 ) ) \
991 void fileErase( IN_STRING const char *fileName )
992  {
993  STREAM stream;
994  int length, status;
995 
996  assert( isReadPtr( fileName, 2 ) );
997 
998  /* Try and open the file so that we can erase it. If this fails, the
999  best that we can do is a straight unlink */
1000  status = sFileOpen( &stream, fileName,
1001  FILE_FLAG_READ | FILE_FLAG_WRITE | \
1003  if( cryptStatusError( status ) )
1004  {
1005  if( status != CRYPT_ERROR_NOTFOUND )
1006  remove( fileName );
1007  return;
1008  }
1009 
1010  /* Determine the size of the file and erase it */
1011  fseek( stream.filePtr, 0, SEEK_END );
1012  length = ( int ) ftell( stream.filePtr );
1013  fseek( stream.filePtr, 0, SEEK_SET );
1014  eraseFile( stream, 0, length );
1015 
1016  /* Truncate the file to 0 bytes if we couldn't do it in eraseFile, reset
1017  the time stamps, and delete it */
1018  sFileClose( &stream );
1019 
1020  /* Finally, delete the file */
1021  remove( fileName );
1022  }
1023 
1024 /* Build the path to a file in the cryptlib directory */
1025 
1026 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
1027 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
1028  IN_LENGTH_SHORT const int pathMaxLen,
1029  OUT_LENGTH_SHORT_Z int *pathLen,
1030  IN_BUFFER( fileNameLen ) const char *fileName,
1031  IN_LENGTH_SHORT const int fileNameLen,
1032  IN_ENUM( BUILDPATH_OPTION ) \
1033  const BUILDPATH_OPTION_TYPE option )
1034  {
1035  assert( isWritePtr( path, pathMaxLen ) );
1036  assert( isWritePtr( pathLen, sizeof( int ) ) );
1037  assert( isReadPtr( fileName, fileNameLen ) );
1038 
1039  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
1040  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
1041  option == BUILDPATH_GETPATH ) && fileName != NULL && \
1042  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
1043  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
1044  fileNameLen == 0 ) );
1045 
1046  /* Make sure that the open fails if we can't build the path */
1047  *path = '\0';
1048 
1049  /* Add the filename to the path */
1050  return( appendFilename( path, pathMaxLen, pathLen, fileName,
1051  fileNameLen, option ) );
1052  }
1053 
1054 /****************************************************************************
1055 * *
1056 * Macintosh File Stream Functions *
1057 * *
1058 ****************************************************************************/
1059 
1060 #elif defined( __MAC__ )
1061 
1062 /* Convert a C to a Pascal string */
1063 
1064 static void CStringToPString( const char *cstring, StringPtr pstring )
1065  {
1066  short len = min( strlen( cstring ), 255 );
1067 
1068  memmove( pstring + 1, cstring, len );
1069  *pstring = len;
1070  }
1071 
1072 /* Open/close a file stream */
1073 
1074 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
1075 int sFileOpen( OUT STREAM *stream, IN_STRING const char *fileName,
1076  IN_FLAGS( FILE ) const int mode )
1077  {
1078  Str255 pFileName;
1079  OSErr err;
1080 
1081  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1082  assert( isReadPtr( fileName, 2 ) );
1083 
1084  REQUIRES( mode != 0 );
1085 
1086  /* Initialise the stream structure */
1087  memset( stream, 0, sizeof( STREAM ) );
1088  stream->type = STREAM_TYPE_FILE;
1089  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
1090  stream->flags = STREAM_FLAG_READONLY;
1091 
1092  CStringToPString( fileName, pFileName );
1093  err = FSMakeFSSpec( 0, 0, pFileName, &stream->fsspec );
1094  if( err == dirNFErr || err == nsvErr )
1095  {
1096  /* Volume or parent directory not found */
1097  return( CRYPT_ERROR_NOTFOUND );
1098  }
1099  if( err != noErr && err != fnfErr )
1100  {
1101  /* fnfErr is OK since the fsspec is still valid */
1102  return( CRYPT_ERROR_OPEN );
1103  }
1104 
1105  if( mode & FILE_FLAG_WRITE )
1106  {
1107  /* Try and create the file, specifying its type and creator. The
1108  wierd string-looking constants are Mac compiler-specific and
1109  evaluate to 32-bit unsigned type and creator IDs. Unfortunately
1110  the type value, which should be '????', triggers warnings about
1111  trigraphs in unnecessarily pedantic compilers so we have to use
1112  the hex equivalent instead */
1113  err = FSpCreate( &stream->fsspec, 0x3F3F3F3F /* '????' */, 'CLib',
1114  smSystemScript );
1115  if( err == wPrErr || err == vLckdErr || err == afpAccessDenied )
1116  return( CRYPT_ERROR_PERMISSION );
1117  if( err != noErr && err != dupFNErr && err != afpObjectTypeErr )
1118  return( CRYPT_ERROR_OPEN );
1119  }
1120 
1121  err = FSpOpenDF( &stream->fsspec, mode & FILE_FLAG_RW_MASK,
1122  &stream->refNum );
1123  if( err == nsvErr || err == dirNFErr || err == fnfErr )
1124  return( CRYPT_ERROR_NOTFOUND );
1125  if( err == opWrErr || err == permErr || err == afpAccessDenied )
1126  return( CRYPT_ERROR_PERMISSION );
1127  if( err != noErr )
1128  return( CRYPT_ERROR_OPEN );
1129 
1130  return( CRYPT_OK );
1131  }
1132 
1133 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1134 int sFileClose( INOUT STREAM *stream )
1135  {
1136  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1137 
1138  REQUIRES( stream->type == STREAM_TYPE_FILE );
1139 
1140  /* Close the file and clear the stream structure */
1141  FSClose( stream->refNum );
1142  zeroise( stream, sizeof( STREAM ) );
1143 
1144  return( CRYPT_OK );
1145  }
1146 
1147 /* Read/write a block of data from/to a file stream */
1148 
1149 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
1150 int fileRead( INOUT STREAM *stream,
1151  OUT_BUFFER( length, *bytesRead ) void *buffer,
1152  IN_LENGTH const int length,
1153  OUT_LENGTH_Z int *bytesRead )
1154  {
1155  long byteCount = length;
1156 
1157  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1158  assert( isWritePtr( buffer, length ) );
1159  assert( isWritePtr( bytesRead, sizeof( int ) ) );
1160 
1161  REQUIRES( stream->type == STREAM_TYPE_FILE );
1162  REQUIRES( length > 0 && length < MAX_INTLENGTH );
1163 
1164  /* Clear return value */
1165  *bytesRead = 0;
1166 
1167  if( FSRead( stream->refNum, &bytesRead, buffer ) != noErr )
1168  return( sSetError( stream, CRYPT_ERROR_READ ) );
1169  *bytesRead = byteCount;
1170 
1171  return( CRYPT_OK );
1172  }
1173 
1174 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
1175 int fileWrite( INOUT STREAM *stream,
1176  IN_BUFFER( length ) const void *buffer,
1177  IN_LENGTH const int length )
1178  {
1179  long bytesWritten = length;
1180 
1181  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1182  assert( isReadPtr( buffer, length ) );
1183 
1184  REQUIRES( stream->type == STREAM_TYPE_FILE );
1185  REQUIRES( length > 0 && length < MAX_INTLENGTH );
1186 
1187  if( FSWrite( stream->refNum, &bytesWritten, buffer ) != noErr || \
1188  ( int ) bytesWritten != length )
1189  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
1190  return( CRYPT_OK );
1191  }
1192 
1193 /* Commit data in a file stream to backing storage */
1194 
1195 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1196 int fileFlush( INOUT STREAM *stream )
1197  {
1198  FileParam paramBlock;
1199 
1200  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1201 
1202  REQUIRES( stream->type == STREAM_TYPE_FILE );
1203 
1204  paramBlock.ioCompletion = NULL;
1205  paramBlock.ioFRefNum = stream->refNum;
1206  PBFlushFileSync( ( union ParamBlockRec * ) &paramBlock );
1207  return( CRYPT_OK );
1208  }
1209 
1210 /* Change the read/write position in a file */
1211 
1212 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1213 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
1214  {
1215  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1216 
1217  REQUIRES( stream->type == STREAM_TYPE_FILE );
1218  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
1219 
1220  if( SetFPos( stream->refNum, fsFromStart, position ) != noErr )
1221  return( sSetError( stream, CRYPT_ERROR_READ ) );
1222  return( CRYPT_OK );
1223  }
1224 
1225 /* Check whether a file is writeable */
1226 
1228 BOOLEAN fileReadonly( IN_STRING const char *fileName )
1229  {
1230  Str255 pFileName;
1231  FSSpec fsspec;
1232  OSErr err;
1233  short refnum;
1234 
1235  assert( isReadPtr( fileName, 2 ) );
1236 
1237  CStringToPString( fileName, pFileName );
1238 
1239  err = FSMakeFSSpec( 0, 0, pFileName, &fsspec );
1240  if ( err == noErr )
1241  err = FSpOpenDF( &fsspec, fsRdWrPerm, &refnum );
1242  if ( err == noErr )
1243  FSClose( refnum );
1244 
1245  if ( err == opWrErr || err == permErr || err == afpAccessDenied )
1246  return( TRUE );
1247 
1248  return( FALSE );
1249  }
1250 
1251 /* File deletion functions: Wipe a file from the current position to EOF,
1252  and wipe and delete a file (although it's not terribly rigorous).
1253  Vestigia nulla retrorsum */
1254 
1255 static void eraseFile( const STREAM *stream, long position, long length )
1256  {
1257  assert( isReadPtr( stream, sizeof( STREAM ) ) );
1258 
1259  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
1260  REQUIRES_V( position >= 0 && position < MAX_INTLENGTH );
1261  REQUIRES_V( length >= 0 && length < MAX_INTLENGTH );
1262  /* May be zero if a file-open failed leaving a zero-length
1263  file */
1264 
1265  /* Wipe everything past the current position in the file */
1266  while( length > 0 )
1267  {
1269  BYTE buffer[ ( BUFSIZ * 2 ) + 8 ];
1270  int bytesToWrite = min( length, BUFSIZ * 2 );
1271 
1272  /* We need to make sure that we fill the buffer with random data for
1273  each write, otherwise compressing filesystems will just compress
1274  it to nothing */
1275  setMessageData( &msgData, buffer, bytesToWrite );
1277  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
1278  if( FSWrite( stream->refNum, &bytesWritten, buffer ) != noErr )
1279  break; /* An error occurred while writing, exit */
1280  length -= bytesToWrite;
1281  }
1282 
1283  SetFPos( stream->refNum, fsFromStart, position );
1284  SetEOF( stream->refNum, position );
1285  }
1286 
1287 STDC_NONNULL_ARG( ( 1 ) ) \
1288 void fileClearToEOF( const STREAM *stream )
1289  {
1290  long eof, position, length;
1291 
1292  assert( isReadPtr( stream, sizeof( STREAM ) ) );
1293 
1294  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
1295 
1296  /* Wipe everything past the current position in the file */
1297  if( GetFPos( stream->refNum, &position ) != noErr || \
1298  GetEOF( stream->refNum, &eof ) != noErr )
1299  return;
1300  length = eof - position;
1301  if( length <= 0 )
1302  return; /* Nothing to do, exit */
1303  eraseFile( stream, position, length );
1304  }
1305 
1306 STDC_NONNULL_ARG( ( 1 ) ) \
1307 void fileErase( IN_STRING const char *fileName )
1308  {
1309  STREAM stream;
1310  int length, status;
1311 
1312  assert( isReadPtr( fileName, 2 ) );
1313 
1314  /* Try and open the file so that we can erase it. If this fails, the
1315  best that we can do is a straight unlink */
1316  status = sFileOpen( &stream, fileName,
1317  FILE_FLAG_READ | FILE_FLAG_WRITE | \
1319  if( cryptStatusError( status ) )
1320  {
1321  if( status != CRYPT_ERROR_NOTFOUND )
1322  remove( fileName );
1323  return;
1324  }
1325 
1326  /* Determine the size of the file and erase it */
1327  SetFPos( stream.refNum, fsFromStart, 0 );
1328  GetEOF( stream.refNum, &length );
1329  eraseFile( stream, position, length );
1330 
1331  /* Delete the file */
1332  sFileClose( &stream );
1333  FSpDelete( stream.fsspec );
1334  }
1335 
1336 /* Build the path to a file in the cryptlib directory */
1337 
1338 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
1339 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
1340  IN_LENGTH_SHORT const int pathMaxLen,
1341  OUT_LENGTH_SHORT_Z int *pathLen,
1342  IN_BUFFER( fileNameLen ) const char *fileName,
1343  IN_LENGTH_SHORT const int fileNameLen,
1344  IN_ENUM( BUILDPATH_OPTION ) \
1345  const BUILDPATH_OPTION_TYPE option )
1346  {
1347  assert( isWritePtr( path, pathMaxLen ) );
1348  assert( isWritePtr( pathLen, sizeof( int ) ) );
1349  assert( isReadPtr( fileName, fileNameLen ) );
1350 
1351  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
1352  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
1353  option == BUILDPATH_GETPATH ) && fileName != NULL && \
1354  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
1355  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
1356  fileNameLen == 0 ) );
1357 
1358  /* Make sure that the open fails if we can't build the path */
1359  *path = '\0';
1360 
1361  strlcpy_s( path, pathMaxLen, ":" );
1362 
1363  /* Add the filename to the path */
1364  return( appendFilename( path, pathMaxLen, pathLen, fileName,
1365  fileNameLen, option ) );
1366  }
1367 
1368 /****************************************************************************
1369 * *
1370 * Non-STDIO File Stream Functions *
1371 * *
1372 ****************************************************************************/
1373 
1374 #elif defined( CONFIG_NO_STDIO )
1375 
1376 #if defined( __MVS__ ) || defined( __VMCMS__ ) || \
1377  defined( __IBM4758__ ) || defined( __TESTIO__ )
1378 
1379 /* Some environments place severe restrictions on what can be done with file
1380  I/O, either having no filesystem at all or having one with characteristics
1381  that don't fit the stdio model. For these systems we used our own in-
1382  memory buffers and make them look like virtual file streams until they're
1383  flushed, at which point they're written to backing store (flash RAM/
1384  EEPROM/DASD/whatever non-FS storage is being used) in one go.
1385 
1386  For streams with the sensitive bit set we don't expand the buffer size
1387  because the original was probably in protected memory, for non-sensitive
1388  streams we expand the size if necessary. This means that we have to
1389  choose a suitably large buffer for sensitive streams (private keys), but
1390  one that isn't too big. 16K is about right, since typical private key
1391  files with cert chains are 2K */
1392 
1393 #endif /* __MVS__ || __VMCMS__ || __IBM4758__ || __TESTIO__ */
1394 
1395 /* Open/close a file stream */
1396 
1397 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
1398 int sFileOpen( OUT STREAM *stream, IN_STRING const char *fileName,
1399  IN_FLAGS( FILE ) const int mode )
1400  {
1401 #ifdef __IBM4758__
1402  const BOOLEAN useBBRAM = ( mode & FILE_FLAG_SENSITIVE ) ? TRUE : FALSE;
1403 #elif defined( EBCDIC_CHARS )
1404  #pragma convlit( suspend )
1405  static const char *modes[] = { MODE_READ, MODE_READ,
1407  #pragma convlit( resume )
1408  char fileNameBuffer[ MAX_PATH_LENGTH + 8 ];
1409 #else
1410  static const char *modes[] = { MODE_READ, MODE_READ,
1412 #endif /* __IBM4758__ */
1413 #if defined( __MVS__ ) || defined( __VMCMS__ ) || defined( __TESTIO__ )
1414  const char *openMode;
1415 #endif /* __MVS__ || __VMCMS__ || __TESTIO__ */
1416  long length;
1417  int status;
1418 
1419  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1420  assert( isReadPtr( fileName, 2 ) );
1421 
1422  REQUIRES( mode != 0 );
1423 
1424  /* Initialise the stream structure as a virtual file stream */
1425  memset( stream, 0, sizeof( STREAM ) );
1426  stream->type = STREAM_TYPE_MEMORY;
1427  stream->flags = STREAM_MFLAG_VFILE;
1428  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
1429  stream->flags |= STREAM_FLAG_READONLY;
1430 
1431 #if defined( __IBM4758__ )
1432  /* Make sure that the filename matches the 4758's data item naming
1433  conventions and remember the filename. The best error code to return
1434  if there's a problem is a file open error, since this is buried so
1435  many levels down that a parameter error won't be meaningful to the
1436  caller */
1437  if( strlen( fileName ) > 8 )
1438  return( CRYPT_ERROR_OPEN );
1439  strlcpy_s( stream->name, 8, fileName );
1440 
1441  /* If we're doing a read, fetch the data into memory */
1442  if( mode & FILE_FLAG_READ )
1443  {
1444  /* Find out how big the data item is and allocate a buffer for
1445  it */
1446  status = sccGetPPDLen( ( char * ) fileName, &length );
1447  if( status != PPDGood )
1448  {
1449  return( ( status == PPD_NOT_FOUND ) ? CRYPT_ERROR_NOTFOUND : \
1450  ( status == PPD_NOT_AUTHORIZED ) ? CRYPT_ERROR_PERMISSION : \
1451  CRYPT_ERROR_OPEN );
1452  }
1453  if( ( stream->buffer = clAlloc( "sFileOpen", length ) ) == NULL )
1454  return( CRYPT_ERROR_MEMORY );
1455  stream->bufSize = stream->bufEnd = length;
1456  stream->isIOStream = TRUE;
1457 
1458  /* Fetch the data into the buffer so it can be read as a memory
1459  stream */
1460  status = sccGetPPD( ( char * ) fileName, stream->buffer, length );
1461  return( ( status != PPDGood ) ? CRYPT_ERROR_READ : CRYPT_OK );
1462  }
1463 
1464  /* We're doing a write, make sure that there's enough room available.
1465  This doesn't guarantee that there'll be enough when the data is
1466  committed, but it makes sense to at least check when the "file" is
1467  opened */
1468  status = sccQueryPPDSpace( &length, useBBRAM ? PPD_BBRAM : PPD_FLASH );
1469  if( status != PPDGood || length < STREAM_VFILE_BUFSIZE )
1470  return( CRYPT_ERROR_OPEN );
1471 
1472  /* Allocate the initial I/O buffer for the data */
1473  if( ( stream->buffer = clAlloc( "sFileOpen",
1474  STREAM_VFILE_BUFSIZE ) ) == NULL )
1475  return( CRYPT_ERROR_MEMORY );
1476  stream->bufSize = STREAM_VFILE_BUFSIZE;
1477  stream->isSensitive = useBBRAM;
1478 
1479  return( CRYPT_OK );
1480 #elif defined( __MVS__ ) || defined( __VMCMS__ ) || defined( __TESTIO__ )
1481  /* If we're going to be doing a write either now or later, we can't open
1482  the file until we have all of the data that we want to write to it
1483  available since the open arg has to include the file format
1484  information, so all we can do at this point is remember the name for
1485  later use */
1486  openMode = modes[ mode & FILE_FLAG_RW_MASK ];
1487  strlcpy_s( stream->name, MAX_PATH_LENGTH, fileName );
1488  #ifdef EBCDIC_CHARS
1489  fileName = bufferToEbcdic( fileNameBuffer, fileName );
1490  #endif /* EBCDIC_CHARS */
1491 
1492  /* If we're doing a read, fetch the data into memory */
1493  if( mode & FILE_FLAG_READ )
1494  {
1495  FILE *filePtr;
1496  #if defined( __MVS__ ) || defined( __VMCMS__ )
1497  fldata_t fileData;
1498  char fileBuffer[ MAX_PATH_LENGTH + 8 ];
1499  #endif /* __MVS__ || __VMCMS__ */
1500  int allocSize = STREAM_VFILE_BUFSIZE;
1501 
1502  /* Open the file and determine how large it is */
1503  filePtr = fopen( fileName, openMode );
1504  if( filePtr == NULL )
1505  {
1506  /* The open failed, determine whether it was because the file
1507  doesn't exist or because we can't use that access mode. We
1508  need to distinguish between not-found and failed-to-open
1509  status values because not-found is an allowable condition
1510  but (presumably found but) failed to open isn't */
1511  #if defined( __MVS__ ) || defined( __VMCMS__ )
1512  /* An errno value of ENOENT results from a DDNAME not found, 67
1513  (no mnemonic name defined by IBM for DYNALLOC return codes)
1514  is member not found and 49 is data set not found */
1515  return( ( errno == ENOENT || errno == 67 || errno == 49 ) ? \
1517  #elif defined( __WIN32__ )
1518  return( ( GetLastError() == ERROR_FILE_NOT_FOUND ) ? \
1519  CRYPT_ERROR_NOTFOUND : CRYPT_ERROR_OPEN );
1520  #else
1521  return( errno == ENOENT ) ? \
1522  CRYPT_ERROR_NOTFOUND : CRYPT_ERROR_OPEN );
1523  #endif /* Nonstandard I/O environments */
1524  }
1525  #if defined( __MVS__ ) || defined( __VMCMS__ )
1526  status = fldata( filePtr, fileBuffer, &fileData );
1527  if( status )
1528  {
1529  fclose( filePtr );
1530  return( CRYPT_ERROR_OPEN );
1531  }
1532  length = fileData.__maxreclen;
1533  #else
1534  fseek( filePtr, 0L, SEEK_END );
1535  length = ftell( filePtr );
1536  fseek( filePtr, 0L, SEEK_SET );
1537  #endif /* Nonstandard I/O environments */
1538  if( length <= 0 )
1539  {
1540  fclose( filePtr );
1541  return( CRYPT_ERROR_OPEN );
1542  }
1543  if( stream->flags & STREAM_FLAG_READONLY )
1544  {
1545  /* If it's a read-only file we only need to allocate a buffer
1546  large enough to hold the existing data */
1547  allocSize = length;
1548  }
1549 
1550  /* Fetch the data into a buffer large enough to contain the entire
1551  stream */
1552  if( ( stream->buffer = clAlloc( "sFileOpen", allocSize ) ) == NULL )
1553  {
1554  fclose( filePtr );
1555  return( CRYPT_ERROR_MEMORY );
1556  }
1557  stream->bufSize = allocSize;
1558  stream->bufEnd = length;
1559  status = fread( stream->buffer, length, 1, filePtr );
1560  fclose( filePtr );
1561  if( status != 1 )
1562  {
1563  clFree( "sFileOpen", stream->buffer );
1564  return( CRYPT_ERROR_READ );
1565  }
1566  return( CRYPT_OK );
1567  }
1568 
1569  /* Allocate the initial I/O buffer for the data */
1570  if( ( stream->buffer = clAlloc( "sFileOpen",
1571  STREAM_VFILE_BUFSIZE ) ) == NULL )
1572  return( CRYPT_ERROR_MEMORY );
1573  stream->bufSize = STREAM_VFILE_BUFSIZE;
1574 
1575  return( CRYPT_OK );
1576 #else
1577  #error Need to add mechanism to connect stream to backing store
1578  return( CRYPT_ERROR_OPEN );
1579 #endif /* Nonstandard I/O environments */
1580  }
1581 
1582 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1583 int sFileClose( INOUT STREAM *stream )
1584  {
1585  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1586 
1587  REQUIRES( sIsVirtualFileStream( stream ) );
1588 
1589 #if defined( __IBM4758__ ) || defined( __MVS__ ) || \
1590  defined( __VMCMS__ ) || defined( __TESTIO__ )
1591  /* Close the file and clear the stream structure */
1592  zeroise( stream->buffer, stream->bufSize );
1593  clFree( "sFileClose", stream->buffer );
1594  zeroise( stream, sizeof( STREAM ) );
1595 
1596  return( CRYPT_OK );
1597 #else
1598  #error Need to add mechanism to disconnect stream from backing store
1599  zeroise( stream, sizeof( STREAM ) );
1600 
1601  return( CRYPT_OK );
1602 #endif /* Nonstandard I/O environments */
1603  }
1604 
1605 /* Read/write a block of data from/to a file stream */
1606 
1607 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
1608 int fileRead( INOUT STREAM *stream,
1609  OUT_BUFFER( length, *bytesRead ) void *buffer,
1610  IN_LENGTH const int length,
1611  OUT_LENGTH_Z int *bytesRead )
1612  {
1613  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1614  assert( isWritePtr( buffer, length ) );
1615  assert( isWritePtr( bytesRead, sizeof( int ) ) );
1616 
1617  REQUIRES( sIsVirtualFileStream( stream ) );
1618  REQUIRES( length > 0 && length < MAX_INTLENGTH );
1619 
1620  /* Clear return value */
1621  *bytesRead = 0;
1622 
1623  /* These environments move all data into an in-memory buffer when the
1624  file is opened so there's never any need to read more data from the
1625  stream */
1626  retIntError();
1627  }
1628 
1629 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
1630 int fileWrite( INOUT STREAM *stream,
1631  IN_BUFFER( length ) const void *buffer,
1632  IN_LENGTH const int length )
1633  {
1634  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1635  assert( isReadPtr( buffer, length ) );
1636 
1637  REQUIRES( sIsVirtualFileStream( stream ) );
1638  REQUIRES( length > 0 && length < MAX_INTLENGTH );
1639 
1640  /* These environments keep all data in an in-memory buffer that's
1641  committed to backing store when the file is closed so there's never
1642  any need to write data to the stream */
1643  retIntError();
1644  }
1645 
1646 /* Commit data in a file stream to backing storage */
1647 
1648 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1649 int fileFlush( INOUT STREAM *stream )
1650  {
1651 #if defined( __MVS__ ) || defined( __VMCMS__ ) || defined( __TESTIO__ )
1652  FILE *filePtr;
1653  int count;
1654 #endif /* __MVS__ || __VMCMS__ || __TESTIO__ */
1655 
1656  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1657 
1658  REQUIRES( sIsVirtualFileStream( stream ) );
1659 
1660 #if defined( __IBM4758__ )
1661  /* Write the data to flash or BB memory as appropriate */
1662  if( sccSavePPD( stream->name, stream->buffer, stream->bufEnd,
1663  ( stream->isSensitive ? PPD_BBRAM : PPD_FLASH ) | PPD_TRIPLE ) != PPDGood )
1664  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
1665  return( CRYPT_OK );
1666 #elif defined( __MVS__ ) || defined( __VMCMS__ ) || defined( __TESTIO__ )
1667  /* Under CMS, MVS, TSO, etc the only consistent way to handle writes is
1668  to write a fixed-length single-record file containing all the data in
1669  one record, so we can't really do anything until the data is flushed */
1670  #if 0
1671  /* No need to go to this level, a RECFM=* binary file will do just as
1672  well */
1673  char formatBuffer[ 64 + 8 ];
1674  sprintf_s( formatBuffer, 64, "wb,recfm=F,lrecl=%d,noseek",
1675  stream->bufPos );
1676  filePtr = fopen( stream->name, formatBuffer );
1677  #endif /* 0 */
1678  filePtr = fopen( stream->name, MODE_WRITE );
1679  if( filePtr == NULL )
1680  return( CRYPT_ERROR_WRITE );
1681  count = fwrite( stream->buffer, stream->bufEnd, 1, filePtr );
1682  fclose( filePtr );
1683  return( ( count != 1 ) ? CRYPT_ERROR_WRITE : CRYPT_OK );
1684 #else
1685  #error Need to add mechanism to commit data to backing store
1686  return( CRYPT_ERROR_WRITE );
1687 #endif /* Nonstandard I/O environments */
1688  }
1689 
1690 /* Change the read/write position in a file */
1691 
1692 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1693 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
1694  {
1695  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1696 
1697  REQUIRES( sIsVirtualFileStream( stream ) );
1698 
1699 #if defined( __IBM4758__ ) || defined( __MVS__ ) || \
1700  defined( __VMCMS__ ) || defined( __TESTIO__ )
1701  /* These environments move all data into an in-memory buffer when the
1702  file is opened, so there's never any need to move around in the
1703  stream */
1704  retIntError();
1705 #else
1706  #error Need to add mechanism to perform virtual seek on backing store
1707  return( sSetError( stream, CRYPT_ERROR_READ ) );
1708 #endif /* Nonstandard I/O environments */
1709  }
1710 
1711 /* Check whether a file is writeable */
1712 
1714 BOOLEAN fileReadonly( IN_STRING const char *fileName )
1715  {
1716  assert( isReadPtr( fileName, 2 ) );
1717 
1718 #if defined( __IBM4758__ ) || defined( __MVS__ ) || \
1719  defined( __VMCMS__ ) || defined( __TESTIO__ )
1720  /* Since there's no filesystem or no real access control (even under MVS
1721  et al it'll be handled at a much higher level like RACF or a SAF-
1722  compatable product), there's no concept of a read-only file, at least
1723  at a level that we can easily determine programmatically */
1724  return( FALSE );
1725 #else
1726  #error Need to add mechanism to determine readability of data in backing store
1727  return( FALSE );
1728 #endif /* Nonstandard I/O environments */
1729  }
1730 
1731 /* File deletion functions: Wipe a file from the current position to EOF,
1732  and wipe and delete a file (although it's not terribly rigorous).
1733  Vestigia nulla retrorsum */
1734 
1735 STDC_NONNULL_ARG( ( 1 ) ) \
1736 void fileClearToEOF( const STREAM *stream )
1737  {
1738  assert( isReadPtr( stream, sizeof( STREAM ) ) );
1739 
1740  REQUIRES_V( sIsVirtualFileStream( stream ) );
1741 
1742 #if defined( __IBM4758__ ) || defined( __MVS__ ) || \
1743  defined( __VMCMS__ ) || defined( __TESTIO__ )
1744  /* Data updates on these systems are atomic so there's no remaining data
1745  left to clear */
1746  UNUSED_ARG( stream );
1747 #else
1748  #error Need to add clear-to-EOF function for data in backing store
1749 #endif /* Nonstandard I/O environments */
1750  }
1751 
1752 STDC_NONNULL_ARG( ( 1 ) ) \
1753 void fileErase( IN_STRING const char *fileName )
1754  {
1755 #if defined( __IBM4758__ )
1756  sccDeletePPD( ( char * ) fileName );
1757 #elif defined( __MVS__ ) || defined( __VMCMS__ ) || defined( __TESTIO__ )
1758  FILE *filePtr;
1759  #ifdef EBCDIC_CHARS
1760  char fileNameBuffer[ MAX_PATH_LENGTH + 8 ];
1761  #endif /* EBCDIC_CHARS */
1762  int length = CRYPT_ERROR;
1763 
1764  assert( isReadPtr( fileName, 2 ) );
1765 
1766  #if defined( __MVS__ ) && defined( DDNAME_IO )
1767  /* If we're using DDNAME I/O under MVS we can't perform standard
1768  random-access file operations, the best that we can do is just
1769  delete the dataset entry */
1770  fileName = bufferToEbcdic( fileNameBuffer, fileName );
1771  remove( fileName );
1772  return;
1773  #elif defined( __MVS__ ) || defined( __VMCMS__ )
1774  /* Determine how large the file is */
1775  #ifdef EBCDIC_CHARS
1776  fileName = bufferToEbcdic( fileNameBuffer, fileName );
1777  #pragma convlit( suspend )
1778  #endif /* EBCDIC_CHARS */
1779  filePtr = fopen( fileName, MODE_READWRITE );
1780  if( filePtr != NULL )
1781  {
1782  fldata_t fileData;
1783  char fileBuffer[ MAX_PATH_LENGTH + 8 ];
1784 
1785  if( fldata( filePtr, fileBuffer, &fileData ) == 0 )
1786  length = fileData.__maxreclen;
1787  }
1788  #ifdef EBCDIC_CHARS
1789  #pragma convlit( resume )
1790  #endif /* EBCDIC_CHARS */
1791  #else
1792  /* Determine how large the file is */
1793  filePtr = fopen( fileName, MODE_READWRITE );
1794  if( filePtr != NULL )
1795  {
1796  fseek( filePtr, 0, SEEK_END );
1797  length = ( int ) ftell( filePtr );
1798  fseek( filePtr, 0, SEEK_SET );
1799  }
1800  #endif /* OS environment-specific file handling */
1801 
1802  /* If we got a length, overwrite the data. Since the file contains a
1803  single record we can't perform the write-until-done overwrite used
1804  on other OS'es, however since we're only going to be deleting short
1805  private key files using the default stream buffer is OK for this */
1806  if( length > 0 )
1807  {
1809  BYTE buffer[ STREAM_VFILE_BUFSIZE + 8 ];
1810 
1811  length = max( length, STREAM_VFILE_BUFSIZE );
1812  setMessageData( &msgData, buffer, length );
1814  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
1815  fwrite( buffer, 1, length, filePtr );
1816  }
1817  if( filePtr != NULL )
1818  {
1819  fflush( filePtr );
1820  fclose( filePtr );
1821  }
1822  remove( fileName );
1823 #else
1824  #error Need to add erase function for data in backing store
1825 #endif /* Nonstandard I/O environments */
1826  }
1827 
1828 /* Build the path to a file in the cryptlib directory */
1829 
1830 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
1831 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
1832  IN_LENGTH_SHORT const int pathMaxLen,
1833  OUT_LENGTH_SHORT_Z int *pathLen,
1834  IN_BUFFER( fileNameLen ) const char *fileName,
1835  IN_LENGTH_SHORT const int fileNameLen,
1836  IN_ENUM( BUILDPATH_OPTION ) \
1837  const BUILDPATH_OPTION_TYPE option )
1838  {
1839  assert( isWritePtr( path, pathMaxLen ) );
1840  assert( isWritePtr( pathLen, sizeof( int ) ) );
1841  assert( isReadPtr( fileName, fileNameLen ) );
1842 
1843  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
1844  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
1845  option == BUILDPATH_GETPATH ) && fileName != NULL && \
1846  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
1847  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
1848  fileNameLen == 0 ) );
1849 
1850  /* Make sure that the open fails if we can't build the path */
1851  *path = '\0';
1852 
1853  /* Build the path to the configuration file if necessary */
1854 #if defined( __IBM4758__ )
1855  if( option == BUILDPATH_RNDSEEDFILE )
1856  {
1857  /* Unlikely to really be necessary since we have a hardware RNG */
1858  strlcpy_s( path, pathMaxLen, "RANDSEED" );
1859  }
1860  else
1861  strlcpy_s( path, pathMaxLen, fileName );
1862  return( CRYPT_OK );
1863 #elif defined( __MVS__ ) || defined( __VMCMS__ ) || defined( __TESTIO__ )
1864  #if defined( DDNAME_IO )
1865  /* MVS dataset name userid.CRYPTLIB.filename. We can't use a PDS since
1866  multiple members have to be opened in write mode simultaneously */
1867  if( option == BUILDPATH_RNDSEEDFILE )
1868  strlcpy_s( path, pathMaxLen, "//RANDSEED" );
1869  else
1870  {
1871  strlcpy_s( path, pathMaxLen, "//CRYPTLIB." );
1872  strlcat_s( path, pathMaxLen, fileName );
1873  }
1874 
1875  return( CRYPT_OK );
1876  #else
1877  return( appendFilename( path, pathMaxLen, pathLen, fileName,
1878  fileNameLen, option ) );
1879  #endif /* DDNAME_IO */
1880 #else
1881  #error Need to add function to build path to config data in backing store
1882 
1883  return( CRYPT_ERROR_OPEN );
1884 #endif /* OS-specific file path creation */
1885  }
1886 
1887 /****************************************************************************
1888 * *
1889 * Palm OS File Stream Functions *
1890 * *
1891 ****************************************************************************/
1892 
1893 #elif defined( __PALMOS__ )
1894 
1895 #include <FeatureMgr.h>
1896 
1897 /* In theory it's possible for a system not to have the VFS Manager
1898  available, although this seems highly unlikely we check for it just
1899  in case using the Feature Manager */
1900 
1901 static BOOLEAN checkVFSMgr( void )
1902  {
1903  uint32_t vfsMgrVersion;
1904 
1905  return( ( FtrGet( sysFileCVFSMgr, vfsFtrIDVersion,
1906  &vfsMgrVersion ) == errNone ) ? TRUE : FALSE );
1907  }
1908 
1909 /* Open/close a file stream */
1910 
1911 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
1912 int sFileOpen( OUT STREAM *stream, IN_STRING const char *fileName,
1913  IN_FLAGS( FILE ) const int mode )
1914  {
1915  static const int modes[] = {
1916  vfsModeRead, vfsModeRead,
1917  vfsModeCreate | vfsModeExclusive | vfsModeWrite,
1918  vfsModeReadWrite
1919  };
1920  uint32_t volIterator = vfsIteratorStart;
1921  uint16_t volRefNum, openMode;
1922  status_t err;
1923 
1924  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1925  assert( isReadPtr( fileName, 2 ) );
1926 
1927  REQUIRES( mode != 0 );
1928 
1929  /* Initialise the stream structure */
1930  memset( stream, 0, sizeof( STREAM ) );
1931  stream->type = STREAM_TYPE_FILE;
1932  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
1933  stream->flags = STREAM_FLAG_READONLY;
1934  openMode = modes[ mode & FILE_FLAG_RW_MASK ];
1935 
1936  /* Make sure that VFS services are available and get the default volume
1937  to open the file on */
1938  if( !checkVFSMgr() )
1939  return( CRYPT_ERROR_OPEN );
1940  if( VFSVolumeEnumerate( &volRefNum, &volIterator ) != errNone )
1941  return( CRYPT_ERROR_OPEN );
1942 
1943  /* If we're trying to write to the file, check whether we've got
1944  permission to do so */
1945  if( ( mode & FILE_FLAG_WRITE ) && fileReadonly( fileName ) )
1946  return( CRYPT_ERROR_PERMISSION );
1947 
1948  /* Try and open the file */
1949  err = VFSFileOpen( volRefNum, fileName, openMode, &stream->fileRef );
1950  if( err == vfsErrFilePermissionDenied || err == vfsErrIsADirectory || \
1951  err == vfsErrVolumeFull )
1952  return( CRYPT_ERROR_PERMISSION );
1953  if( err == vfsErrFileNotFound )
1954  return( CRYPT_ERROR_NOTFOUND );
1955  if( err != errNone )
1956  return( CRYPT_ERROR_OPEN );
1957 
1958  return( CRYPT_OK );
1959  }
1960 
1961 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1962 int sFileClose( INOUT STREAM *stream )
1963  {
1964  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1965 
1966  REQUIRES( stream->type == STREAM_TYPE_FILE );
1967 
1968  /* Close the file and clear the stream structure */
1969  VFSFileClose( stream->fileRef );
1970  zeroise( stream, sizeof( STREAM ) );
1971 
1972  return( CRYPT_OK );
1973  }
1974 
1975 /* Read/write a block of data from/to a file stream */
1976 
1977 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
1978 int fileRead( INOUT STREAM *stream,
1979  OUT_BUFFER( length, *bytesRead ) void *buffer,
1980  IN_LENGTH const int length,
1981  OUT_LENGTH_Z int *bytesRead )
1982  {
1983  uint32_t byteCount;
1984 
1985  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1986  assert( isWritePtr( buffer, length ) );
1987  assert( isWritePtr( bytesRead, sizeof( int ) ) );
1988 
1989  REQUIRES( stream->type == STREAM_TYPE_FILE );
1990  REQUIRES( length > 0 && length < MAX_INTLENGTH );
1991 
1992  /* Clear return value */
1993  *bytesRead = 0;
1994 
1995  if( VFSFileRead( stream->fileRef, length, buffer,
1996  &byteCount ) != errNone )
1997  return( sSetError( stream, CRYPT_ERROR_READ ) );
1998  *bytesRead = byteCount;
1999 
2000  return( CRYPT_OK );
2001  }
2002 
2003 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
2004 int fileWrite( INOUT STREAM *stream,
2005  IN_BUFFER( length ) const void *buffer,
2006  IN_LENGTH const int length )
2007  {
2008  uint32_t bytesWritten;
2009 
2010  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2011  assert( isReadPtr( buffer, length ) );
2012 
2013  REQUIRES( stream->type == STREAM_TYPE_FILE );
2014  REQUIRES( length > 0 && length < MAX_INTLENGTH );
2015 
2016  if( VFSFileWrite( stream->fileRef, length, buffer,
2017  &bytesWritten ) != errNone || \
2018  bytesWritten != length )
2019  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
2020  return( CRYPT_OK );
2021  }
2022 
2023 /* Commit data in a file stream to backing storage */
2024 
2025 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
2026 int fileFlush( INOUT STREAM *stream )
2027  {
2028  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2029 
2030  REQUIRES( stream->type == STREAM_TYPE_FILE );
2031 
2032  /* There doesn't seem to be any way to force data to be written do
2033  backing store, probably because the concept of backing store is
2034  somewhat hazy in a system that's never really powered down.
2035  Probably for removable media data is committed fairly quickly to
2036  handle media removal while for fixed media it's committed as
2037  required since it can be retained in memory more or less
2038  indefinitely */
2039  return( CRYPT_OK );
2040  }
2041 
2042 /* Change the read/write position in a file */
2043 
2044 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
2045 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
2046  {
2047  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2048 
2049  REQUIRES( stream->type == STREAM_TYPE_FILE );
2050  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
2051 
2052  if( VFSFileSeek( stream->fileRef, vfsOriginBeginning,
2053  position ) != errNone )
2054  return( sSetError( stream, CRYPT_ERROR_READ ) );
2055  return( CRYPT_OK );
2056  }
2057 
2058 /* Check whether a file is writeable */
2059 
2061 BOOLEAN fileReadonly( IN_STRING const char *fileName )
2062  {
2063  FileRef fileRef;
2064  uint32_t volIterator = vfsIteratorStart;
2065  uint16_t volRefNum;
2066  status_t err;
2067 
2068  assert( isReadPtr( fileName, 2 ) );
2069 
2070  if( VFSVolumeEnumerate( &volRefNum, &volIterator ) != errNone )
2071  return( TRUE );
2072  err = VFSFileOpen( volRefNum, fileName, vfsModeRead, &fileRef );
2073  if( err == errNone )
2074  VFSFileClose( fileRef );
2075 
2076  return( ( err == vfsErrFilePermissionDenied ) ? TRUE : FALSE );
2077  }
2078 
2079 /* File deletion functions: Wipe a file from the current position to EOF,
2080  and wipe and delete a file (although it's not terribly rigorous).
2081  Vestigia nulla retrorsum */
2082 
2083 static void eraseFile( const STREAM *stream, long position, long length )
2084  {
2085  assert( isReadPtr( stream, sizeof( STREAM ) ) );
2086 
2087  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
2088  REQUIRES_V( position >= 0 && position < MAX_INTLENGTH );
2089  REQUIRES_V( length >= 0 && length < MAX_INTLENGTH );
2090  /* May be zero if a file-open failed leaving a zero-length
2091  file */
2092 
2093  /* Wipe everything past the current position in the file */
2094  while( length > 0 )
2095  {
2097  BYTE buffer[ ( BUFSIZ * 2 ) + 8 ];
2098  uint32_t bytesWritten;
2099  int bytesToWrite = min( length, BUFSIZ * 2 );
2100 
2101  /* We need to make sure that we fill the buffer with random data for
2102  each write, otherwise compressing filesystems will just compress
2103  it to nothing */
2104  setMessageData( &msgData, buffer, bytesToWrite );
2106  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
2107 
2108  if( VFSFileWrite( stream->fileRef, bytesToWrite, buffer,
2109  &bytesWritten ) != errNone )
2110  break; /* An error occurred while writing, exit */
2111  length -= bytesToWrite;
2112  }
2113 
2114  VFSFileResize( stream->fileRef, position );
2115  }
2116 
2117 STDC_NONNULL_ARG( ( 1 ) ) \
2118 void fileClearToEOF( const STREAM *stream )
2119  {
2120  uint32_t length, position;
2121 
2122  assert( isReadPtr( stream, sizeof( STREAM ) ) );
2123 
2124  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
2125 
2126  /* Wipe everything past the current position in the file */
2127  if( VFSFileSize( stream->fileRef, &length ) != errNone || \
2128  VFSFileTell( stream->fileRef, &position ) != errNone );
2129  return;
2130  length -= position;
2131  if( length <= 0 )
2132  return; /* Nothing to do, exit */
2133  eraseFile( stream, position, length );
2134  }
2135 
2136 STDC_NONNULL_ARG( ( 1 ) ) \
2137 void fileErase( IN_STRING const char *fileName )
2138  {
2139  STREAM stream;
2140  uint32_t volIterator = vfsIteratorStart, length;
2141  uint16_t volRefNum;
2142  int status;
2143 
2144  assert( isReadPtr( fileName, 2 ) );
2145 
2146  /* Try and open the file so that we can erase it. If this fails, the
2147  best that we can do is a straight unlink */
2148  if( VFSVolumeEnumerate( &volRefNum, &volIterator ) != errNone )
2149  return;
2150  status = sFileOpen( &stream, fileName,
2151  FILE_FLAG_READ | FILE_FLAG_WRITE |
2153  if( cryptStatusError( status ) )
2154  {
2155  if( status != CRYPT_ERROR_NOTFOUND )
2156  remove( fileName );
2157  return;
2158  }
2159 
2160  /* Determine the size of the file and erase it */
2161  VFSFileSize( stream.fileRef, &length );
2162  eraseFile( &stream, 0, length );
2163 
2164  /* Reset the file's attributes */
2165  VFSFileSetAttributes( stream.fileRef, 0 );
2166  VFSFileSetDate( stream.fileRef, vfsFileDateAccessed, 0 );
2167  VFSFileSetDate( stream.fileRef, vfsFileDateCreated, 0 );
2168  VFSFileSetDate( stream.fileRef, vfsFileDateModified, 0 );
2169 
2170  /* Delete the file */
2171  sFileClose( &stream );
2172  VFSFileDelete( volRefNum, fileName );
2173  }
2174 
2175 /* Build the path to a file in the cryptlib directory */
2176 
2177 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
2178 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
2179  IN_LENGTH_SHORT const int pathMaxLen,
2180  OUT_LENGTH_SHORT_Z int *pathLen,
2181  IN_BUFFER( fileNameLen ) const char *fileName,
2182  IN_LENGTH_SHORT const int fileNameLen,
2183  IN_ENUM( BUILDPATH_OPTION ) \
2184  const BUILDPATH_OPTION_TYPE option )
2185  {
2186  assert( isWritePtr( path, pathMaxLen ) );
2187  assert( isWritePtr( pathLen, sizeof( int ) ) );
2188  assert( isReadPtr( fileName, fileNameLen ) );
2189 
2190  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
2191  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
2192  option == BUILDPATH_GETPATH ) && fileName != NULL && \
2193  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
2194  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
2195  fileNameLen == 0 ) );
2196 
2197  /* Make sure that the open fails if we can't build the path */
2198  *path = '\0';
2199 
2200  /* Make sure that VFS services are available */
2201  if( !checkVFSMgr() )
2202  return( CRYPT_ERROR_NOTAVAIL );
2203 
2204  /* Build the path to the configuration file if necessary */
2205  strlcpy_s( path, pathMaxLen, "/PALM/cryptlib/" );
2206 
2207  /* If we're being asked to create the cryptlib directory and it doesn't
2208  already exist, create it now */
2209  if( option == BUILDPATH_CREATEPATH )
2210  {
2211  FileRef fileRef;
2212  uint32_t volIterator = vfsIteratorStart;
2213  uint16_t volRefNum;
2214 
2215  if( VFSVolumeEnumerate( &volRefNum, &volIterator ) != errNone )
2216  return( CRYPT_ERROR_OPEN );
2217  if( VFSFileOpen( volRefNum, path, vfsModeRead, &fileRef ) == errNone )
2218  VFSFileClose( fileRef );
2219  else
2220  {
2221  /* The directory doesn't exist, try and create it */
2222  if( VFSDirCreate( volRefNum, path ) != errNone )
2223  return( CRYPT_ERROR_OPEN );
2224  }
2225  }
2226 
2227  /* Add the filename to the path */
2228  return( appendFilename( path, pathMaxLen, pathLen, fileName,
2229  fileNameLen, option ) );
2230  }
2231 
2232 /****************************************************************************
2233 * *
2234 * ThreadX (via FileX) *
2235 * *
2236 ****************************************************************************/
2237 
2238 #elif defined( __FileX__ )
2239 
2240 /* Using FileX is a bit complicated because it has a form of level -1
2241  filesystem abstraction in which it's necessary to open the underlying
2242  device before you can work with the files stored on it. Since this
2243  process is entirely device-specific there's no way to do this from
2244  within cryptlib, so we rely on a helper function to which the caller
2245  passes an FX_MEDIA structure for the device to be used. A reference
2246  to this is stored locally and used for all operations that require a
2247  device to be specified */
2248 
2249 static FX_MEDIA *media = NULL;
2250 
2251 CHECK_RETVAL STDC_NONNULLARG( ( 1 ) ) \
2252 int setMedia( FX_MEDIA *mediaPtr )
2253  {
2254  assert( isWritePtr( mediaPtr, sizeof( FX_MEDIA ) ) );
2255 
2256  /* Remember the user-supplied media information */
2257  media = mediaPtr;
2258 
2259  return( CRYPT_OK );
2260  }
2261 
2262 /* Open/close a file stream */
2263 
2264 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
2265 int sFileOpen( OUT STREAM *stream, IN_STRING const char *fileName,
2266  IN_FLAGS( FILE ) const int mode )
2267  {
2268  static const int modes[] = { FX_OPEN_FOR_READ, FX_OPEN_FOR_READ,
2269  FX_OPEN_FOR_WRITE,
2270  FX_OPEN_FOR_READ | FX_OPEN_FOR_WRITE };
2271  UINT openStatus;
2272  int openMode;
2273 
2274  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2275  assert( isReadPtr( fileName, 2 ) );
2276 
2277  REQUIRE( mode != 0 );
2278 
2279  /* Initialise the stream structure */
2280  memset( stream, 0, sizeof( STREAM ) );
2281  stream->type = STREAM_TYPE_FILE;
2282  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
2283  stream->flags = STREAM_FLAG_READONLY;
2284  openMode = modes[ mode & FILE_FLAG_RW_MASK ];
2285 
2286  /* FileX has a somewhat strange way of creating files in which
2287  fx_file_create() is used to create a directory entry for a file
2288  and then fx_file_open() actually opens it. This nasty non-atomic
2289  open requires special-case handling for the situation where the
2290  directory-entry create succeeds but the open fails, so we have to
2291  special-case the handling for this */
2292  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_WRITE )
2293  {
2294  if( fx_file_create( media, fileName ) != FX_SUCCESS )
2295  return( CRYPT_ERROR_OPEN );
2296  if( fx_file_open( media, &stream->filePtr, fileName,
2297  FX_OPEN_FOR_WRITE ) != FX_SUCCESS )
2298  {
2299  fx_file_delete( media, fileName );
2300  return( CRYPT_ERROR_OPEN );
2301  }
2302  }
2303 
2304  /* Try and open the file */
2305  openStatus = fx_file_open( media, &stream->filePtr, fileName, openMode );
2306  if( openStatus != FX_SUCCESS )
2307  {
2308  return( ( openStatus == FX_NOT_FOUND ) ? CRYPT_ERROR_NOTFOUND : \
2309  ( openStatus == FX_ACCESS_ERROR || \
2310  openStatus == FX_WRITE_PROTECT ) ? CRYPT_ERROR_PERMISSION : \
2311  CRYPT_ERROR_OPEN );
2312  }
2313  stream->position = 0;
2314 
2315  return( CRYPT_OK );
2316  }
2317 
2318 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
2319 int sFileClose( INOUT STREAM *stream )
2320  {
2321  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2322 
2323  REQUIRES( stream->type == STREAM_TYPE_FILE );
2324 
2325  /* Close the file and clear the stream structure */
2326  fx_file_close( stream->filePtr );
2327  zeroise( stream, sizeof( STREAM ) );
2328 
2329  return( CRYPT_OK );
2330  }
2331 
2332 /* Read/write a block of data from/to a file stream */
2333 
2334 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
2335 int fileRead( INOUT STREAM *stream,
2336  OUT_BUFFER( length, *bytesRead ) void *buffer,
2337  IN_LENGTH const int length,
2338  OUT_LENGTH_Z int *bytesRead )
2339  {
2340  ULONG byteCount;
2341 
2342  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2343  assert( isWritePtr( buffer, length ) );
2344  assert( isWritePtr( bytesRead, sizeof( int ) ) );
2345 
2346  REQUIRES( stream->type == STREAM_TYPE_FILE );
2347  REQUIRES( length > 0 && length < MAX_INTLENGTH );
2348 
2349  /* Clear return value */
2350  *bytesRead = 0;
2351 
2352  if( ( fx_file_read( stream->filePtr, buffer, length, \
2353  &byteCount ) != FX_SUCCESS ) || \
2354  byteCount != length )
2355  return( sSetError( stream, CRYPT_ERROR_READ ) );
2356  stream->position += byteCount;
2357  *bytesRead = byteCount;
2358 
2359  return( CRYPT_OK );
2360  }
2361 
2362 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
2363 int fileWrite( INOUT STREAM *stream,
2364  IN_BUFFER( length ) const void *buffer,
2365  IN_LENGTH const int length )
2366  {
2367  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2368  assert( isReadPtr( buffer, length ) );
2369 
2370  REQUIRES( stream->type == STREAM_TYPE_FILE );
2371  REQUIRES( length > 0 && length < MAX_INTLENGTH );
2372 
2373  if( fx_file_write( stream->filePtr, buffer, length ) != FX_SUCCESS )
2374  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
2375  stream->position += length;
2376 
2377  return( CRYPT_OK );
2378  }
2379 
2380 /* Commit data in a file stream to backing storage */
2381 
2382 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
2383 int fileFlush( INOUT STREAM *stream )
2384  {
2385  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2386 
2387  REQUIRES( stream->type == STREAM_TYPE_FILE );
2388 
2389  if( fx_media_flush( media ) != FX_SUCCESS )
2390  return( CRYPT_ERROR_WRITE );
2391 
2392  return( CRYPT_OK );
2393  }
2394 
2395 /* Change the read/write position in a file */
2396 
2397 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
2398 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
2399  {
2400  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2401 
2402  REQUIRES( stream->type == STREAM_TYPE_FILE );
2403  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
2404 
2405  if( fx_file_seek( stream->filePtr, position ) != FX_SUCCESS )
2406  return( sSetError( stream, CRYPT_ERROR_READ ) );
2407  stream->position = position;
2408 
2409  return( CRYPT_OK );
2410  }
2411 
2412 /* Check whether a file is writeable */
2413 
2415 BOOLEAN fileReadonly( IN_STRING const char *fileName )
2416  {
2417  UINT attributes;
2418 
2419  assert( isReadPtr( fileName, 2 ) );
2420 
2421  if( fx_file_attribute_read( media, fileName, &attributes ) != FX_SUCCESS )
2422  return( TRUE );
2423  return( ( attributes & ( FX_READ_ONLY | FX_HIDDEN | FX_SYSTEM ) ) ? \
2424  TRUE : FALSE );
2425  }
2426 
2427 /* File deletion functions: Wipe a file from the current position to EOF,
2428  and wipe and delete a file (although it's not terribly rigorous).
2429  Vestigia nulla retrorsum */
2430 
2431 static void eraseFile( FX_FILE *filePtr, long position, long length )
2432  {
2433  assert( isReadPtr( stream, sizeof( STREAM ) ) );
2434 
2435  REQUIRES_V( position >= 0 && position < MAX_INTLENGTH );
2436  REQUIRES_V( length >= 0 && length < MAX_INTLENGTH );
2437  /* May be zero if a file-open failed leaving a zero-length
2438  file */
2439 
2440  /* Wipe everything past the current position in the file */
2441  while( length > 0 )
2442  {
2444  BYTE buffer[ 1024 + 8 ];
2445  int bytesToWrite = min( length, 1024 );
2446 
2447  /* We need to make sure that we fill the buffer with random data for
2448  each write, otherwise compressing filesystems will just compress
2449  it to nothing */
2450  setMessageData( &msgData, buffer, bytesToWrite );
2452  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
2453 
2454  if( fx_file_write( filePtr, buffer, bytesToWrite ) != FX_SUCCESS )
2455  break; /* An error occurred while writing, exit */
2456  length -= bytesToWrite;
2457  }
2458 
2459  /* FileX has two forms of file-truncate, one that releases the clusters
2460  beyond the truncation point and one that doesn't. Why anyone would
2461  want to truncate a file and then throw away the clusters that this
2462  frees is a mystery */
2463  fx_filetruncate_release( filePtr, position );
2464  }
2465 
2466 STDC_NONNULL_ARG( ( 1 ) ) \
2467 void fileClearToEOF( const STREAM *stream )
2468  {
2469  assert( isReadPtr( stream, sizeof( STREAM ) ) );
2470 
2471  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
2472 
2473  /* FileX provides no way to determine either the current position in a
2474  file or its length, so there's no way to use eraseFile() to clear to
2475  EOF (in theory we could remember the file's pathname on open, parse
2476  the path to get the encapsulating directory, perform a media flush to
2477  update the data on disk in the hope that this creates an accurate
2478  record of the file size rather than just a nearest-cluster-
2479  approximation until the file is closed, and then use the find-first-
2480  file results for the file's size, but this seems excessively
2481  complicated */
2482  fx_filetruncate_release( stream->filePtr, stream->position );
2483  }
2484 
2485 STDC_NONNULL_ARG( ( 1 ) ) \
2486 void fileErase( IN_STRING const char *fileName )
2487  {
2488  STREAM stream;
2489  int length, status;
2490 
2491  assert( isReadPtr( fileName, 2 ) );
2492 
2493  /* Try and open the file so that we can erase it. If this fails, the
2494  best that we can do is a straight unlink */
2495  status = sFileOpen( &stream, fileName,
2496  FILE_FLAG_READ | FILE_FLAG_WRITE | \
2498  if( cryptStatusError( status ) )
2499  {
2500  if( status != CRYPT_ERROR_NOTFOUND )
2501  remove( fileName );
2502  return;
2503  }
2504 
2505  /* Determine the size of the file and erase it. Again, because of
2506  FileX's idiotic inability to tell us anything about the file (see
2507  the comment in fileClearToEOF()) we have to perform a byte-at-a-time
2508  read until the read fails in order to determine how much data is
2509  present */
2510  for( length = 0; length < 50000; length++ )
2511  {
2512  BYTE buffer[ 1 + 8 ];
2513  int bytesRead;
2514 
2515  if( ( fx_file_read( stream->filePtr, buffer, 1, \
2516  &bytesRead ) != FX_SUCCESS ) || bytesRead != 1 )
2517  break;
2518  }
2519  fx_file_seek( stream->filePtr, 0 );
2520  if( length > 0 )
2521  eraseFile( &stream, 0, length );
2522 
2523  /* Reset the file's attributes */
2524  fx_file_attribute_set( stream->filePtr, FJ_DA_NORMAL );
2525 
2526  /* Delete the file */
2527  sFileClose( &stream );
2528  fx_file_delete( media, fileName );
2529  }
2530 
2531 /* Build the path to a file in the cryptlib directory */
2532 
2533 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
2534 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
2535  IN_LENGTH_SHORT const int pathMaxLen,
2536  OUT_LENGTH_SHORT_Z int *pathLen,
2537  IN_BUFFER( fileNameLen ) const char *fileName,
2538  IN_LENGTH_SHORT const int fileNameLen,
2539  IN_ENUM( BUILDPATH_OPTION ) \
2540  const BUILDPATH_OPTION_TYPE option )
2541  {
2542  assert( isWritePtr( path, pathMaxLen ) );
2543  assert( isWritePtr( pathLen, sizeof( int ) ) );
2544  assert( isReadPtr( fileName, fileNameLen ) );
2545 
2546  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
2547  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
2548  option == BUILDPATH_GETPATH ) && fileName != NULL && \
2549  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
2550  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
2551  fileNameLen == 0 ) );
2552 
2553  /* Make sure that the open fails if we can't build the path */
2554  *path = '\0';
2555 
2556  /* Build the path to the configuration file if necessary */
2557  strlcpy_s( path, pathMaxLen, "/cryptlib/" );
2558 
2559  /* If we're being asked to create the cryptlib directory and it doesn't
2560  already exist, create it now */
2561  if( option == BUILDPATH_CREATEPATH && \
2562  fx_directory_name_test( path ) != FX_SUCCESS )
2563  {
2564  /* The directory doesn't exist, try and create it */
2565  if( fx_directory_create( media, path ) != FX_SUCCESS )
2566  return( CRYPT_ERROR_OPEN );
2567  }
2568 
2569  /* Add the filename to the path */
2570  return( appendFilename( path, pathMaxLen, pathLen, fileName,
2571  fileNameLen, option ) );
2572  }
2573 
2574 /****************************************************************************
2575 * *
2576 * Unix/Unix-like Systems File Stream Functions *
2577 * *
2578 ****************************************************************************/
2579 
2580 #elif defined( __BEOS__ ) || defined( __ECOS__ ) || defined( __MVS__ ) || \
2581  defined( __RTEMS__ ) || defined( __SYMBIAN32__ ) || \
2582  defined( __TANDEM_NSK__ ) || defined( __TANDEM_OSS__ ) || \
2583  defined( __UNIX__ )
2584 
2585 /* Tandem doesn't have ftruncate() even though there's a manpage for it
2586  (which claims that it's prototyped in sys/types.h (!!)). unistd.h has
2587  it protected by ( _XOPEN_SOURCE_EXTENDED == 1 && _TNS_R_TARGET ), which
2588  implies that we'd better emulate it if we want to make use of it. For
2589  now we do nothing, this is just a placeholder if the Guardian native
2590  file layer isn't available */
2591 
2592 #if defined( __TANDEM_NSK__ ) || defined( __TANDEM_OSS__ )
2593 
2594 int ftruncate( int fd, off_t length )
2595  {
2596  return( 0 );
2597  }
2598 #endif /* Tandem */
2599 
2600 /* Safe file-open function */
2601 
2602 #ifndef O_NOFOLLOW /* Avoid following symlinks on open */
2603  #define O_NOFOLLOW 0
2604 #endif /* O_NOFOLLOW */
2605 #ifndef O_CLOEXEC /* Don't let forked children inherit handle */
2606  #define O_CLOEXEC 0
2607 #endif /* O_CLOEXEC */
2608 
2609 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
2610 static int openFile( INOUT STREAM *stream, IN_STRING const char *fileName,
2611  const int flags, const int openMode )
2612  {
2613  int fd, count;
2614 
2615  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2616  assert( isReadPtr( fileName, 2 ) );
2617 
2618  REQUIRES( stream->type == STREAM_TYPE_FILE );
2619  /* openMode is a unistd.h define so can't be checked against
2620  a fixed range */
2621 
2622  /* A malicious user could have exec()'d us after closing standard I/O
2623  handles (which we inherit across the exec()), which means that any
2624  new files that we open will be allocated the same handles as the
2625  former standard I/O ones. This could cause private data to be
2626  written to stdout or error messages emitted by the calling app to go
2627  into the opened file. To avoid this, we retry the open if we get the
2628  same handle as a standard I/O one.
2629 
2630  We use the O_NOFOLLOW flag to avoid following symlinks if the OS
2631  supports it. Note that this flag is dangerous to use when reading
2632  things like /dev/random because they're symlinks on some OSes, but
2633  these types of files aren't accessed through this function */
2634  for( count = 0; count < 4; count++ )
2635  {
2636  fd = open( fileName, flags, openMode | O_NOFOLLOW );
2637  if( fd < 0 )
2638  {
2639  /* If we're creating the file, the only error condition is a
2640  straight open error */
2641  if( flags & O_CREAT )
2642  return( CRYPT_ERROR_OPEN );
2643 
2644  /* Determine whether the open failed because the file doesn't
2645  exist or because we can't use that access mode */
2646  return( ( access( fileName, 0 ) < 0 ) ? \
2647  CRYPT_ERROR_NOTFOUND : CRYPT_ERROR_OPEN );
2648  }
2649 
2650  /* If we got a handle that isn't in the stdio reserved range, we're
2651  done */
2652  if( fd > 2 )
2653  break;
2654  }
2655  if( count >= 4 )
2656  {
2657  /* We still couldn't get a kosher file handle after trying to move
2658  past the standard I/O range, something's wrong */
2659  assert( DEBUG_WARN );
2660  return( CRYPT_ERROR_OPEN );
2661  }
2662 
2663  stream->fd = fd;
2664  return( CRYPT_OK );
2665  }
2666 
2667 /* Open/close a file stream */
2668 
2669 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
2670 int sFileOpen( INOUT STREAM *stream, IN_STRING const char *fileName,
2671  IN_FLAGS( FILE ) const int mode )
2672  {
2673  const int extraOpenFlags = ( mode & FILE_FLAG_EXCLUSIVE_ACCESS ) ? \
2674  O_CLOEXEC : 0;
2675 #ifdef EBCDIC_CHARS
2676  char fileNameBuffer[ MAX_PATH_LENGTH + 8 ];
2677 #endif /* EBCDIC_CHARS */
2678 #if !defined( USE_EMBEDDED_OS ) && defined( USE_FCNTL_LOCKING )
2679  struct flock flockInfo;
2680 #endif /* !USE_EMBEDDED_OS && USE_FCNTL_LOCKING */
2681 
2682  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2683  assert( isReadPtr( fileName, 2 ) );
2684 
2685  REQUIRES( mode != 0 );
2686 
2687  /* Initialise the stream structure */
2688  memset( stream, 0, sizeof( STREAM ) );
2689  stream->type = STREAM_TYPE_FILE;
2690  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
2691  stream->flags = STREAM_FLAG_READONLY;
2692 
2693  /* If we're trying to write to the file, check whether we've got
2694  permission to do so */
2695  if( ( mode & FILE_FLAG_WRITE ) && fileReadonly( fileName ) )
2696  return( CRYPT_ERROR_PERMISSION );
2697 
2698 #ifdef EBCDIC_CHARS
2699  fileName = bufferToEbcdic( fileNameBuffer, fileName );
2700 #endif /* EBCDIC_CHARS */
2701 
2702  /* Defending against writing through links is somewhat difficult since
2703  there's no atomic way to do this. What we do is lstat() the file,
2704  open it as appropriate, and if it's an existing file ftstat() it and
2705  compare various important fields to make sure that the file wasn't
2706  changed between the lstat() and the open(). If everything is OK, we
2707  then use the lstat() information to make sure that it isn't a symlink
2708  (or at least that it's a normal file) and that the link count is 1.
2709  These checks also catch other weird things like STREAMS stuff
2710  fattach()'d over files. If these checks pass and the file already
2711  exists we truncate it to mimic the effect of an open with create.
2712 
2713  There is a second type of race-condition attack in which the race is
2714  run at a very low speed instead of high speed (sometimes called a
2715  cryogenic sleep attack) in which an attacker SIGSTOP's us after the
2716  lstat() (which generally isn't possible for processes not owned by
2717  the user but is possible for setuid ones), deletes the file, waits
2718  for the inode number to roll around, creates a link with the
2719  (reused) inode, and then SIGCONT's us again. Checking for a link
2720  count > 1 catches the link problem.
2721 
2722  Another workaround would be to check the inode generation number
2723  st_gen, but virtually nothing supports this */
2724  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_WRITE )
2725  {
2726  struct stat lstatInfo;
2727  int status;
2728 
2729  /* lstat() the file. If it doesn't exist, create it with O_EXCL. If
2730  it does exist, open it for read/write and perform the fstat()
2731  check */
2732  if( lstat( fileName, &lstatInfo ) < 0 )
2733  {
2734  /* If the lstat() failed for reasons other than the file not
2735  existing, return a file open error */
2736  if( errno != ENOENT )
2737  return( CRYPT_ERROR_OPEN );
2738 
2739  /* The file doesn't exist, create it with O_EXCL to make sure
2740  that an attacker can't slip in a file between the lstat() and
2741  open(). Note that this still doesn't work for some non-
2742  local filesystems, for example it's not supported at all in
2743  NFSv2 and even for newer versions support can be hit-and-miss
2744  - under Linux for example it requires kernel versions 2.6.5
2745  or newer to work */
2746  status = openFile( stream, fileName,
2747  O_CREAT | O_EXCL | O_RDWR | extraOpenFlags,
2748  0600 );
2749  if( cryptStatusError( status ) )
2750  return( status );
2751  }
2752  else
2753  {
2754  struct stat fstatInfo;
2755 
2756  /* If it's not a normal file or there are links to it, don't
2757  even try and do anything with it */
2758  if( !S_ISREG( lstatInfo.st_mode ) || lstatInfo.st_nlink != 1 )
2759  return( CRYPT_ERROR_OPEN );
2760 
2761  /* Open an existing file */
2762  status = openFile( stream, fileName, O_RDWR | extraOpenFlags, 0 );
2763  if( cryptStatusError( status ) )
2764  return( status );
2765 
2766  /* fstat() the opened file and check that the file mode bits,
2767  inode, device, and link info match */
2768  if( fstat( stream->fd, &fstatInfo ) < 0 || \
2769  lstatInfo.st_mode != fstatInfo.st_mode || \
2770  lstatInfo.st_ino != fstatInfo.st_ino || \
2771  lstatInfo.st_dev != fstatInfo.st_dev || \
2772  lstatInfo.st_nlink != fstatInfo.st_nlink )
2773  {
2774  close( stream->fd );
2775  return( CRYPT_ERROR_OPEN );
2776  }
2777 
2778  /* If the above check was passed, we know that the lstat() and
2779  fstat() were done to the same file. Now check that it's a
2780  normal file (this isn't strictly necessary because the
2781  fstat() vs. lstat() st_mode check would also find this) and
2782  there's only one link. This also catches tricks like an
2783  attacker closing stdin/stdout so that a newly-opened file
2784  ends up with those file handles, with the result that the app
2785  using cryptlib ends up corrupting cryptlib's files when it
2786  sends data to stdout. In order to counter this we could
2787  simply repeatedly open /dev/null until we get a handle > 2,
2788  but the fstat() check will catch this in a manner that's also
2789  safe with systems that don't have a stdout (so the handle > 2
2790  check wouldn't make much sense) */
2791  if( !S_ISREG( fstatInfo.st_mode ) || fstatInfo.st_nlink != 1 )
2792  {
2793  close( stream->fd );
2794  return( CRYPT_ERROR_OPEN );
2795  }
2796 
2797  /* Turn the file into an empty file */
2798  if( ftruncate( stream->fd, 0 ) < 0 )
2799  {
2800  close( stream->fd );
2801  return( CRYPT_ERROR_OPEN );
2802  }
2803  }
2804  }
2805  else
2806  {
2807  static const int modes[] = { O_RDONLY, O_RDONLY, O_WRONLY, O_RDWR };
2808  int status;
2809 
2810  /* Open an existing file for read access */
2811  status = openFile( stream, fileName,
2812  modes[ mode & FILE_FLAG_RW_MASK ] | extraOpenFlags,
2813  0 );
2814  if( cryptStatusError( status ) )
2815  return( status );
2816  }
2817 
2818  /* Set the file access permissions so that only the owner can access it */
2819  if( mode & FILE_FLAG_PRIVATE )
2820  fchmod( stream->fd, 0600 );
2821 
2822  /* Lock the file if possible to make sure that no-one else tries to do
2823  things to it. If available we use the (BSD-style) flock(), if not we
2824  fall back to Posix fcntl() locking (both mechanisms are broken, but
2825  flock() is less broken). In addition there's lockf(), but that's
2826  just a wrapper around fcntl(), so there's no need to special-case it.
2827 
2828  fcntl() locking has two disadvantages over flock():
2829 
2830  1. Locking is per-process rather than per-thread (specifically it's
2831  based on processes and inodes rather than flock()'s file table
2832  entries, for which any new handles created via dup()/fork()/open()
2833  all refer to the same file table entry so there's a single location
2834  at which to handle locking), so another thread in the same process
2835  could still access the file. Whether this is a good thing or not
2836  is context-dependant: We want multiple threads to be able to read
2837  from the file (if one keyset handle is shared among threads), but
2838  not necessarily for multiple threads to be able to write. We could
2839  if necessary use mutexes for per-thread lock synchronisation, but
2840  this gets incredibly ugly since we then have to duplicate parts of
2841  the the system file table with per-thread mutexes, mess around with
2842  an fstat() on each file access to determine if we're accessing an
2843  already-open file, wrap all that up in more mutexes, etc etc, as
2844  well as being something that's symtomatic of a user application bug
2845  rather than normal behaviour that we can defend against.
2846 
2847  2. Closing *any* descriptor for an fcntl()-locked file releases *all*
2848  locks on the file (!!) (one manpage appropriately describes this
2849  behaviour as "the completely stupid semantics of System V and IEEE
2850  Std 1003.1-1988 (= POSIX.1)"). In other words if two threads or
2851  processes open an fcntl()-locked file for shared read access then
2852  the first close of the file releases all locks on it. Since
2853  fcntl() requires a file handle to work, the only way to determine
2854  whether a file is locked requires opening it, but as soon as we
2855  close it again (for example to abort the access if there's a lock
2856  on it) all locks are released.
2857 
2858  flock() sticks with the much more sensible 4.2BSD-based last-close
2859  semantics, however it doesn't usually work with NFS unless special
2860  hacks have been applied (for example under Linux it requires kernel
2861  versions >= 2.6.12 to work). fcntl() passes lock requests to
2862  rpc.lockd to handle, but this is its own type of mess since it's
2863  often unreliable, so it's really not much worse than flock(). In
2864  addition locking support under filesystems like AFS is often
2865  nonexistant, with the lock apparently succeeding but no lock actually
2866  being applied. Even under local filesystems, mandatory locking is
2867  only enabled if the filesystem is mounted with the "-o mand" option
2868  is used, which is rarely the case (it's off by default).
2869 
2870  Locking is almost always advisory only, but even mandatory locking
2871  can be bypassed by tricks such as copying the original, unlinking it,
2872  and renaming the copy back to the original (the unlinked - and still
2873  locked - original goes away once the handle is closed) - this
2874  mechanism is standard practice for many Unix utilities like text
2875  editors. A common mandatory locking implementation uses the sgid bit
2876  (a directory bit that wouldn't normally be used for a file) to
2877  indicate that a file is subject to locking, which another process can
2878  turn off and therefore disable the locking. Finally, mandatory
2879  locking is wierd in that an open for write (or read, on a write-
2880  locked file) will succeed, it's only a later attempt to read/write
2881  that will fail.
2882 
2883  This mess is why dotfile-locking is still so popular, but that's
2884  probably going a bit far for simple keyset accesses */
2885 #ifndef USE_EMBEDDED_OS /* Embedded systems have no locking */
2886  #ifndef USE_FCNTL_LOCKING
2887  if( flock( stream->fd, ( mode & FILE_FLAG_EXCLUSIVE_ACCESS ) ? \
2888  LOCK_EX | LOCK_NB : LOCK_SH | LOCK_NB ) < 0 && \
2889  errno == EWOULDBLOCK )
2890  {
2891  close( stream->fd );
2892  return( CRYPT_ERROR_PERMISSION );
2893  }
2894  #else
2895  memset( &flockInfo, 0, sizeof( struct flock ) );
2896  flockInfo.l_type = ( mode & FILE_FLAG_EXCLUSIVE_ACCESS ) ? \
2897  F_WRLCK : F_RDLCK;
2898  flockInfo.l_whence = SEEK_SET;
2899  flockInfo.l_start = flockInfo.l_len = 0;
2900  if( fcntl( stream->fd, F_SETLK, &flockInfo ) < 0 && \
2901  ( errno == EACCES || errno == EDEADLK ) )
2902  {
2903  /* Now we're in a bind. If we close the file and exit, the lock
2904  we've just detected on the file is released (see the comment on
2905  this utter braindamage above). OTOH if we don't close the file
2906  we'll leak the file handle, which is bad for long-running
2907  processes. Feedback from users indicates that leaking file
2908  handles is less desirable than the possiblity of having the file
2909  unlocked during an update (the former is a situation that occurs
2910  far more frequently than the latter), so we close the handle and
2911  hope that the update by the other process completes quickly */
2912  close( stream->fd );
2913  return( CRYPT_ERROR_PERMISSION );
2914  }
2915  #endif /* flock() vs. fcntl() locking */
2916 #endif /* USE_EMBEDDED_OS */
2917 
2918  return( CRYPT_OK );
2919  }
2920 
2921 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
2922 int sFileClose( INOUT STREAM *stream )
2923  {
2924  BOOLEAN closeOK = TRUE;
2925 
2926  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2927 
2928  REQUIRES( stream->type == STREAM_TYPE_FILE );
2929 
2930  /* Unlock the file if necessary. If we're using fcntl() locking there's
2931  no need to unlock the file since all locks are automatically released
2932  as soon as any handle to it is closed (see the long comment above for
2933  more on this complete braindamage) */
2934 #if !defined( USE_EMBEDDED_OS ) && !defined( USE_FCNTL_LOCKING )
2935  flock( stream->fd, LOCK_UN );
2936 #endif /* !USE_EMBEDDED_OS && !USE_FCNTL_LOCKING */
2937 
2938  /* Close the file. In theory this shouldn't really be able to fail, but
2939  NFS can elay the error reporting until this point rather than
2940  reporting it during a write when it actually occurs. Some disk quota
2941  management systems can also cause problems, since the data is
2942  buffered and the final size calculation doesn't occur until a set
2943  quantization boundary is crossed or the file is closed. AFS is even
2944  worse, it caches copies of files being worked on locally and then
2945  copies them back to the remote server, so the close can fail if the
2946  copy fails, leaving nothing on the remote server, or a previous copy,
2947  or a zero-length file.
2948 
2949  There's not too much that we can do in the case of this condition,
2950  the status itself is a bit weird because (apart from an EBADF or
2951  EINTR) the close has actually succeeded, what's failed is some other
2952  operation unrelated to the close. The fsync() that was used earlier
2953  will catch most problems, but not ones like AFS' invisible copy-to-
2954  server operation on close.
2955 
2956  The best that we can do is return a write-problem error indicator if
2957  the close fails. There's nothing that can be done to recover from
2958  this, but where possible the caller can at least try to clean up the
2959  file rather than leaving an incomplete file on disk */
2960  if( close( stream->fd ) < 0 )
2961  {
2962  assert( DEBUG_WARN );
2963  closeOK = FALSE;
2964  }
2965  zeroise( stream, sizeof( STREAM ) );
2966 
2967  return( closeOK ? CRYPT_OK : CRYPT_ERROR_WRITE );
2968  }
2969 
2970 /* Read/write a block of data from/to a file stream */
2971 
2972 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
2973 int fileRead( INOUT STREAM *stream,
2974  OUT_BUFFER( length, *bytesRead ) void *buffer,
2975  IN_LENGTH const int length,
2976  OUT_LENGTH_Z int *bytesRead )
2977  {
2978  int byteCount;
2979 
2980  assert( isWritePtr( stream, sizeof( STREAM ) ) );
2981  assert( isWritePtr( buffer, length ) );
2982  assert( isWritePtr( bytesRead, sizeof( int ) ) );
2983 
2984  REQUIRES( stream->type == STREAM_TYPE_FILE );
2985  REQUIRES( length > 0 && length < MAX_INTLENGTH );
2986 
2987  /* Clear return value */
2988  *bytesRead = 0;
2989 
2990  if( ( byteCount = read( stream->fd, buffer, length ) ) < 0 )
2991  return( sSetError( stream, CRYPT_ERROR_READ ) );
2992  *bytesRead = byteCount;
2993 
2994  return( CRYPT_OK );
2995  }
2996 
2997 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
2998 int fileWrite( INOUT STREAM *stream,
2999  IN_BUFFER( length ) const void *buffer,
3000  IN_LENGTH const int length )
3001  {
3002  assert( isWritePtr( stream, sizeof( STREAM ) ) );
3003  assert( isReadPtr( buffer, length ) );
3004 
3005  REQUIRES( stream->type == STREAM_TYPE_FILE );
3006  REQUIRES( length > 0 && length < MAX_INTLENGTH );
3007 
3008  if( write( stream->fd, buffer, length ) != length )
3009  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
3010  return( CRYPT_OK );
3011  }
3012 
3013 /* Commit data in a file stream to backing storage. Unfortunately this
3014  doesn't quite give the guarantees that it's supposed to because some
3015  drives report a successful disk flush when all they've done is committed
3016  the data to the drive's cache without actually having written it to disk
3017  yet. Directly-connected PATA/SATA drives mostly get it right, but
3018  drives behind a glue layer like Firewire, USB, or RAID controllers often
3019  ignore the SCSI SYNCHRONIZE CACHE / ATA FLUSH CACHE / FLUSH CACHE EXT /
3020  FLUSH TRACK CACHE commands (that is, the glue layer discards them before
3021  they get to the drive). To get around this problem, Apple introducted
3022  the FS_FULLFSYNC fcntl in OS X, but even this only works if the glue
3023  layer doesn't discard cache flush commands that it generates.
3024 
3025  The problem is endemic in drive design in general. In order to produce
3026  better benchmark results, drives issue write-completion notifications
3027  when the data hits the track cache, in the hope that the host will issue
3028  another write request to follow the current one so the two writes can
3029  occur back-to-back. The SCSI design solved this with tag queueing, which
3030  allowed (typically) 16 write requests to be enqueued, rather than having
3031  the host wait for each one to announce that it had completed. This was
3032  back-enginered into the ATA spec as tagged command queueing (TCQ), but
3033  ATA allowed the completion of a tagged request to depending on whether the
3034  write cache was enabled or not (it was enabled by default, since disabling
3035  it produced a ~50% performance hit). As a result, it had no effect, since
3036  the drive would still post the completion notification as soon as the data
3037  hit the cache - TCQ added more complexity with no real benefit.
3038 
3039  This was finally fixed with native command queueing (NCQ), which works
3040  more like the original SCSI tagged queueing in that there's a flag in
3041  the write command that forces the drive to only report a write-completion
3042  when the data is committed to stable media */
3043 
3044 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
3045 int fileFlush( INOUT STREAM *stream )
3046  {
3047  assert( isWritePtr( stream, sizeof( STREAM ) ) );
3048 
3049  REQUIRES( stream->type == STREAM_TYPE_FILE );
3050 
3051  return( ( fsync( stream->fd ) == 0 ) ? \
3052  CRYPT_OK : CRYPT_ERROR_WRITE );
3053  }
3054 
3055 /* Change the read/write position in a file */
3056 
3057 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
3058 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
3059  {
3060  assert( isWritePtr( stream, sizeof( STREAM ) ) );
3061 
3062  REQUIRES( stream->type == STREAM_TYPE_FILE );
3063  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
3064 
3065  if( lseek( stream->fd, position, SEEK_SET ) == ( off_t ) -1 )
3066  return( sSetError( stream, CRYPT_ERROR_READ ) );
3067  return( CRYPT_OK );
3068  }
3069 
3070 /* Check whether a file is writeable */
3071 
3073 BOOLEAN fileReadonly( IN_STRING const char *fileName )
3074  {
3075 #ifdef EBCDIC_CHARS
3076  char fileNameBuffer[ MAX_PATH_LENGTH + 8 ];
3077 
3078  assert( isReadPtr( fileName, 2 ) );
3079 
3080  fileName = bufferToEbcdic( fileNameBuffer, fileName );
3081 #else
3082  assert( isReadPtr( fileName, 2 ) );
3083 #endif /* EBCDIC_CHARS */
3084  if( access( fileName, W_OK ) < 0 && errno != ENOENT )
3085  return( TRUE );
3086 
3087  return( FALSE );
3088  }
3089 
3090 /* File deletion functions: Wipe a file from the current position to EOF,
3091  and wipe and delete a file (although it's not terribly rigorous).
3092  Vestigia nulla retrorsum */
3093 
3094 STDC_NONNULL_ARG( ( 1 ) ) \
3095 static void eraseFile( const STREAM *stream, long position, long length )
3096  {
3097  assert( isReadPtr( stream, sizeof( STREAM ) ) );
3098 
3099  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
3100  REQUIRES_V( position >= 0 && position < MAX_INTLENGTH );
3101  REQUIRES_V( length >= 0 && length < MAX_INTLENGTH );
3102  /* May be zero if a file-open failed leaving a zero-length
3103  file */
3104 
3105  /* Wipe the file. This is a fairly crude function that performs a
3106  single pass of overwriting the data with random data, it's not
3107  possible to do much better than this without getting terribly OS-
3108  specific.
3109 
3110  You'll NEVER get rid of me, Toddy */
3111  while( length > 0 )
3112  {
3114  BYTE buffer[ 1024 + 8 ];
3115  const int bytesToWrite = min( length, 1024 );
3116 
3117  /* We need to make sure that we fill the buffer with random data for
3118  each write, otherwise compressing filesystems will just compress
3119  it to nothing */
3120  setMessageData( &msgData, buffer, bytesToWrite );
3122  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
3123  if( write( stream->fd, buffer, bytesToWrite ) <= bytesToWrite )
3124  break; /* An error occurred while writing, exit */
3125  length -= bytesToWrite;
3126  }
3127  fsync( stream->fd );
3128 #ifdef __GNUC__
3129  /* Work around a persistent bogus warning in gcc. Unfortunately this
3130  generates a second warning about 'x' being unused, but it's less
3131  problematic than the return-value-unused one */
3132  { int x = ftruncate( stream->fd, position ); }
3133 #else
3134  ( void ) ftruncate( stream->fd, position );
3135 #endif /* gcc with clang bug */
3136  }
3137 
3138 STDC_NONNULL_ARG( ( 1 ) ) \
3139 void fileClearToEOF( const STREAM *stream )
3140  {
3141  struct stat fstatInfo;
3142  long position, length;
3143 
3144  assert( isReadPtr( stream, sizeof( STREAM ) ) );
3145 
3146  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
3147 
3148  /* Wipe everything past the current position in the file */
3149  if( fstat( stream->fd, &fstatInfo ) < 0 )
3150  return;
3151  position = lseek( stream->fd, 0, SEEK_CUR );
3152  length = fstatInfo.st_size - position;
3153  if( length <= 0 )
3154  return; /* Nothing to do, exit */
3155  eraseFile( stream, position, length );
3156  }
3157 
3158 STDC_NONNULL_ARG( ( 1 ) ) \
3159 void fileErase( IN_STRING const char *fileName )
3160  {
3161  STREAM stream;
3162  struct stat fstatInfo;
3163 #ifndef USE_EMBEDDED_OS /* Embedded systems have no file timestamps */
3164  #if defined( __FreeBSD__ )
3165  struct timeval timeVals[ 2 ];
3166  #elif !( defined( __APPLE__ ) || defined( __FreeBSD__ ) || \
3167  defined( __linux__ ) )
3168  struct utimbuf timeStamp;
3169  #endif /* OS-specific variable declarations */
3170 #endif /* USE_EMBEDDED_OS */
3171 #ifdef EBCDIC_CHARS
3172  char fileNameBuffer[ MAX_PATH_LENGTH + 8 ];
3173 #endif /* EBCDIC_CHARS */
3174  int status;
3175 
3176  assert( isReadPtr( fileName, 2 ) );
3177 
3178 #ifdef EBCDIC_CHARS
3179  fileName = bufferToEbcdic( fileNameBuffer, fileName );
3180 #endif /* EBCDIC_CHARS */
3181 
3182  /* Try and open the file so that we can erase it. If this fails, the
3183  best that we can do is a straight unlink */
3184  status = sFileOpen( &stream, fileName,
3185  FILE_FLAG_READ | FILE_FLAG_WRITE | \
3186  FILE_FLAG_EXCLUSIVE_ACCESS );
3187  if( cryptStatusError( status ) )
3188  {
3189  if( status != CRYPT_ERROR_NOTFOUND )
3190  unlink( fileName );
3191  return;
3192  }
3193 
3194  /* Determine the size of the file and erase it */
3195  if( fstat( stream.fd, &fstatInfo ) == 0 )
3196  eraseFile( &stream, 0, fstatInfo.st_size );
3197 
3198  /* Reset the time stamps and delete the file. On BSD filesystems that
3199  support creation times (e.g. UFS2), the handling of creation times
3200  has been kludged into utimes() by having it called twice. The first
3201  call sets the creation time provided that it's older than the
3202  current creation time (which it always is, since we set it to the
3203  epoch). The second call then works as utimes() normally would.
3204 
3205  Both the unlink() and utimes() calls use filenames rather than
3206  handles, which unfortunately makes them subject to race conditions
3207  where an attacker renames the file before the access. Some systems
3208  support the newer BSD futimes() (generally via glibc), but for the
3209  rest we're stuck with using the unsafe calls. The problem of unsafe
3210  functions however is mitigated by the fact that we're acting on
3211  files in restricted-access directories for which attackers shouldn't
3212  be able to perform renames (but see the note further on on the safe
3213  use of mkdir(), which a determined attacker could also bypass if they
3214  really wanted to), and the fact that the file data is overwritten
3215  before it's unlinked, so the most that an attacker who can bypass the
3216  directory permissions can do is cause us to delete another file in a
3217  generic DoS that they could perform anyway if they have the user's
3218  rights */
3219 #ifndef USE_EMBEDDED_OS /* Embedded systems have no file timestamps */
3220  #if defined( __APPLE__ )
3221  futimes( stream.fd, NULL );
3222  sFileClose( &stream );
3223  #elif defined( __FreeBSD__ )
3224  memset( timeVals, 0, sizeof( struct timeval ) * 2 );
3225  futimes( stream.fd, timeVals );
3226  futimes( stream.fd, timeVals );
3227  sFileClose( &stream );
3228  #elif defined( __UCLIBC__ )
3229  sFileClose( &stream );
3230  utimes( fileName, NULL ); /* uClibc doesn't have futimes() */
3231  #elif defined( __linux__ )
3232  status = 0;
3233  if( futimes( stream.fd, NULL ) < 0 )
3234  status = errno; /* futimes() isn't available on all platforms */
3235  sFileClose( &stream );
3236  if( status == ENOSYS ) /* futimes() failed, fall back to utimes() */
3237  utimes( fileName, NULL );
3238  #else
3239  sFileClose( &stream );
3240  memset( &timeStamp, 0, sizeof( struct utimbuf ) );
3241  utime( fileName, &timeStamp );
3242  #endif /* OS-specific size and date-mangling */
3243 #else
3244  sFileClose( &stream );
3245 #endif /* USE_EMBEDDED_OS */
3246  unlink( fileName );
3247  }
3248 
3249 /* Build the path to a file in the cryptlib directory */
3250 
3251 #if defined( USE_EMBEDDED_OS )
3252 
3253 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
3254 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
3255  IN_LENGTH_SHORT const int pathMaxLen,
3256  OUT_LENGTH_SHORT_Z int *pathLen,
3257  IN_BUFFER( fileNameLen ) const char *fileName,
3258  IN_LENGTH_SHORT const int fileNameLen,
3259  IN_ENUM( BUILDPATH_OPTION ) \
3260  const BUILDPATH_OPTION_TYPE option )
3261  {
3262  assert( isWritePtr( path, pathMaxLen ) );
3263  assert( isWritePtr( pathLen, sizeof( int ) ) );
3264  assert( ( fileName == NULL && fileNameLen == 0 ) || \
3265  isReadPtr( fileName, fileNameLen ) );
3266 
3267  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
3268  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
3269  option == BUILDPATH_GETPATH ) && fileName != NULL && \
3270  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
3271  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
3272  fileNameLen == 0 ) );
3273 
3274  /* Make sure that the open fails if we can't build the path */
3275  *path = '\0';
3276 
3277  /* Embedded OSes have little in the way of filesystems so rather than
3278  trying to second-guess what might be available we just dump
3279  everything in the current directory */
3280  return( appendFilename( path, pathMaxLen, pathLen, fileName,
3281  fileNameLen, option ) );
3282  }
3283 #else
3284 
3285 #include <pwd.h>
3286 
3287 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
3288 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
3289  IN_LENGTH_SHORT const int pathMaxLen,
3290  OUT_LENGTH_SHORT_Z int *pathLen,
3291  IN_BUFFER( fileNameLen ) const char *fileName,
3292  IN_LENGTH_SHORT const int fileNameLen,
3293  IN_ENUM( BUILDPATH_OPTION ) \
3294  const BUILDPATH_OPTION_TYPE option )
3295  {
3296  struct passwd *passwd;
3297  int length;
3298 #ifdef EBCDIC_CHARS
3299  char fileNameBuffer[ MAX_PATH_LENGTH + 8 ];
3300  int status;
3301 #endif /* EBCDIC_CHARS */
3302 
3303  assert( isWritePtr( path, pathMaxLen ) );
3304  assert( isWritePtr( pathLen, sizeof( int ) ) );
3305  assert( isReadPtr( fileName, fileNameLen ) );
3306 
3307  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
3308  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
3309  option == BUILDPATH_GETPATH ) && fileName != NULL && \
3310  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
3311  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
3312  fileNameLen == 0 ) );
3313 
3314  /* Make sure that the open fails if we can't build the path */
3315  *path = '\0';
3316 
3317  /* Build the path to the configuration file if necessary. In theory we
3318  could perform further checking here to ensure that all directories in
3319  the path to the target file are safe (i.e. not generally writeable)
3320  because otherwise an attacker could remove a directory in the path
3321  and substitute one of their own with the same name, which means that
3322  we'd end up writing our config data into a directory that they
3323  control, but in practice there's no easy way to enforce this because
3324  it's unclear what the definition of "safe" is since it varies
3325  depending on who the current user is, or more specifically what
3326  rights the user currently has. In addition the checking itself is
3327  subject to race conditions which makes it complex to perform (see the
3328  hoops that the file-open operation has to jump through above for a
3329  small example of this). In general when the system config is broken
3330  enough to allow this type of attack there's not much more that we can
3331  achieve with various Rube Goldberg checks, and on most systems all
3332  it'll do is lead to lots of false positives because of the unclear
3333  definition of what should be considered "safe" */
3334 #ifdef EBCDIC_CHARS
3335  fileName = bufferToEbcdic( fileNameBuffer, fileName );
3336  #pragma convlit( suspend )
3337 #endif /* EBCDIC_CHARS */
3338  /* Get the path to the user's home directory */
3339  if( ( passwd = getpwuid( getuid() ) ) == NULL )
3340  return( CRYPT_ERROR_OPEN ); /* Huh? User not in passwd file */
3341  if( ( length = strlen( passwd->pw_dir ) ) > MAX_PATH_LENGTH - 64 )
3342  return( CRYPT_ERROR_OPEN ); /* You're kidding, right? */
3343 
3344  /* Set up the path to the cryptlib directory */
3345 #if defined( __APPLE__ )
3346  if( length + 32 >= pathMaxLen )
3347  return( CRYPT_ERROR_OVERFLOW ); /* OS X uses slighly longer paths */
3348 #else
3349  if( length + 16 >= pathMaxLen )
3350  return( CRYPT_ERROR_OVERFLOW );
3351 #endif /* OS X */
3352  memcpy( path, passwd->pw_dir, length );
3353  if( path[ length - 1 ] != '/' )
3354  path[ length++ ] = '/';
3355 #if defined( __APPLE__ )
3356  /* Like Windows, OS X has a predefined location for storing user config
3357  data */
3358  strlcpy_s( path + length, pathMaxLen - length,
3359  "Library/Preferences/cryptlib" );
3360 #else
3361  strlcpy_s( path + length, pathMaxLen - length, ".cryptlib" );
3362 #endif /* OS X */
3363 
3364  /* If we're being asked to create the cryptlib directory and it doesn't
3365  already exist, create it now. In theory we could eliminate potential
3366  problems with race conditions by creating the directory and then
3367  walking up the directory tree ensuring that only root and the current
3368  user (via geteuid()) have write access (so we're guaranteed safe
3369  access to the newly-created directory), but this is a bit too
3370  restrictive in that it can fail under otherwise valid situations with
3371  group-accessible directories. Since the safe-file-open is already
3372  extremely careful to prevent race conditions, we rely on that rather
3373  than risking mysterious failures due to false positives */
3374  if( ( option == BUILDPATH_CREATEPATH ) && \
3375  access( path, F_OK ) < 0 && mkdir( path, 0700 ) < 0 )
3376  return( CRYPT_ERROR_OPEN );
3377 
3378  /* Add the filename to the path */
3379  strlcat_s( path, pathMaxLen, "/" );
3380 #ifndef EBCDIC_CHARS
3381  ANALYSER_HINT( fileName != NULL );
3382  return( appendFilename( path, pathMaxLen, pathLen, fileName,
3383  fileNameLen, option ) );
3384 #else
3385  status = appendFilenameEBCDIC( path, pathMaxLen, pathLen, fileName,
3386  fileNameLen, option );
3387  if( cryptStatusError( status ) )
3388  return( status );
3389  ebcdicToAscii( path, path, pathLen );
3390  #pragma convlit( resume )
3391 
3392  return( CRYPT_OK );
3393 #endif /* EBCDIC_CHARS */
3394  }
3395 #endif /* OS-specific filesystem handling */
3396 
3397 /****************************************************************************
3398 * *
3399 * VxWorks File Stream Functions *
3400 * *
3401 ****************************************************************************/
3402 
3403 #elif defined( __VXWORKS__ )
3404 
3405 /* Some file functions can only be performed via ioctl()'s. These include:
3406 
3407  chmod() - FIOATTRIBSET, dosFsLib, best-effort use only.
3408 
3409  flush() - FIOFLUSH, dosFsLib, rt11FsLib, fallback to FIOSYNC for nfsDrv.
3410 
3411  fstat() - FIOFSTATGET, dosFsLib, nfsDrv, rt11FsLib.
3412 
3413  ftruncate() - FIOTRUNC, dosFsLib.
3414 
3415  tell() - FIOWHERE, dosFsLib, nfsDrv, rt11FsLib.
3416 
3417  utime() - FIOTIMESET, not documented to be supported, but probably
3418  present for utime() support in dirLib, best-effort use only.
3419  For dosFsLib the only way to set the time is to install a time
3420  hook via dosFsDateTimeInstall() before accessing the file, have
3421  it report the desired time when the file is accessed, and then
3422  uninstall it again afterwards, which is too unsafe to use
3423  (it'd affect all files on the filesystem.
3424 
3425  Of these ftruncate() is the only problem, being supported only in
3426  dosFsLib */
3427 
3428 /* When performing file accesses, we use the Unix-style errno to interpret
3429  errors. Unlike some other threaded systems which use preprocessor
3430  tricks to turn errno into a function that returns a value on a per-thread
3431  basis, VxWorks stores the last error in the TCB, so that errno can read
3432  it directly from the TCB.
3433 
3434  The error status is a 32-bit value, of which the high 16 bits are the
3435  module number and the low 16 bits are the module-specific error. However,
3436  module 0 is reserved for Unix-compatible errors, allowing direct use of
3437  the standard errno.h values. This is complicated by the fact that the
3438  error may also be a module-specific one, so we need a special function to
3439  sort out the actual error details */
3440 
3441 CHECK_RETVAL \
3442 static int getErrorCode( const int defaultErrorCode )
3443  {
3444  const int moduleNo = errno >> 16;
3445  const int errNo = errno & 0xFFFFL;
3446 
3447  /* If it's a Unix-compatible error code, we can use it directly */
3448  if( moduleNo == 0 )
3449  {
3450  switch( errNo )
3451  {
3452  case EPERM:
3453  case EACCES:
3454  case EROFS:
3455  return( CRYPT_ERROR_PERMISSION );
3456  case ENOENT:
3457  return( CRYPT_ERROR_NOTFOUND );
3458  case ENOMEM:
3459  return( CRYPT_ERROR_MEMORY );
3460  case EBUSY:
3461  return( CRYPT_ERROR_TIMEOUT );
3462  case EEXIST:
3463  return( CRYPT_ERROR_DUPLICATE );
3464  }
3465  }
3466  else
3467  {
3468  /* It's a module-specific error, check whether there's anything
3469  that we can use */
3470  }
3471 
3472  return( defaultErrorCode );
3473  }
3474 
3475 /* Open/close a file stream */
3476 
3477 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
3478 int sFileOpen( OUT STREAM *stream, IN_STRING const char *fileName,
3479  IN_FLAGS( FILE ) const int mode )
3480  {
3481  assert( isWritePtr( stream, sizeof( STREAM ) ) );
3482  assert( isReadPtr( fileName, 2 ) );
3483 
3484  REQUIRES( mode != 0 );
3485 
3486  /* Initialise the stream structure */
3487  memset( stream, 0, sizeof( STREAM ) );
3488  stream->type = STREAM_TYPE_FILE;
3489  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
3490  stream->flags = STREAM_FLAG_READONLY;
3491 
3492  /* If we're trying to write to the file, check whether we've got
3493  permission to do so */
3494  if( ( mode & FILE_FLAG_WRITE ) && fileReadonly( fileName ) )
3495  return( CRYPT_ERROR_PERMISSION );
3496 
3497  /* Try and open the file. We don't have to jump through the hoops that
3498  are required for Unix because VxWorks doesn't support links (or the
3499  functions that Unix provides to detec them) */
3500  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_WRITE )
3501  {
3502  /* We're creating the file, we have to use creat() rather than
3503  open(), which can only open an existing file (well, except for
3504  NFS filesystems) */
3505  if( ( stream->fd = creat( fileName, 0600 ) ) == ERROR )
3506  return( getErrorCode( CRYPT_ERROR_OPEN ) );
3507  }
3508  else
3509  {
3510  const int mode = ( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ ) ? \
3511  O_RDONLY : O_RDWR;
3512 
3513  /* Open an existing file */
3514  if( ( stream->fd = open( fileName, mode, 0600 ) ) == ERROR )
3515  {
3516  /* The open failed, determine whether it was because the file
3517  doesn't exist or because we can't use that access mode */
3518  return( getErrorCode( CRYPT_ERROR_OPEN ) );
3519  }
3520  }
3521 
3522  return( CRYPT_OK );
3523  }
3524 
3525 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
3526 int sFileClose( INOUT STREAM *stream )
3527  {
3528  assert( isWritePtr( stream, sizeof( STREAM ) ) );
3529 
3530  REQUIRES( stream->type == STREAM_TYPE_FILE );
3531 
3532  /* Close the file and clear the stream structure */
3533  close( stream->fd );
3534  zeroise( stream, sizeof( STREAM ) );
3535 
3536  return( CRYPT_OK );
3537  }
3538 
3539 /* Read/write a block of data from/to a file stream */
3540 
3541 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
3542 int fileRead( INOUT STREAM *stream,
3543  OUT_BUFFER( length, *bytesRead ) void *buffer,
3544  IN_LENGTH const int length,
3545  OUT_LENGTH_Z int *bytesRead )
3546  {
3547  int byteCount;
3548 
3549  assert( isWritePtr( stream, sizeof( STREAM ) ) );
3550  assert( isWritePtr( buffer, length ) );
3551  assert( isWritePtr( bytesRead, sizeof( int ) ) );
3552 
3553  REQUIRES( stream->type == STREAM_TYPE_FILE );
3554  REQUIRES( length > 0 && length < MAX_INTLENGTH );
3555 
3556  /* Clear return value */
3557  *bytesRead = 0;
3558 
3559  if( ( byteCount = read( stream->fd, buffer, length ) ) == ERROR )
3560  return( sSetError( stream, CRYPT_ERROR_READ ) );
3561  *bytesRead = byteCount;
3562 
3563  return( CRYPT_OK );
3564  }
3565 
3566 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
3567 int fileWrite( INOUT STREAM *stream,
3568  IN_BUFFER( length ) const void *buffer,
3569  IN_LENGTH const int length )
3570  {
3571  assert( isWritePtr( stream, sizeof( STREAM ) ) );
3572  assert( isReadPtr( buffer, length ) );
3573 
3574  REQUIRES( stream->type == STREAM_TYPE_FILE );
3575  REQUIRES( length > 0 && length < MAX_INTLENGTH );
3576 
3577  if( write( stream->fd, buffer, length ) != length )
3578  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
3579  return( CRYPT_OK );
3580  }
3581 
3582 /* Commit data in a file stream to backing storage. We use FIOFLUSH rather
3583  then FIOSYNC, since the latter re-reads the written data into I/O buffers
3584  while all we're interested in is forcing a commit. However, nfsDrv only
3585  supports FIOSYNC, so we try that as a fallback if FIOFLUSH fails */
3586 
3587 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
3588 int fileFlush( INOUT STREAM *stream )
3589  {
3590  assert( isWritePtr( stream, sizeof( STREAM ) ) );
3591 
3592  REQUIRES( stream->type == STREAM_TYPE_FILE );
3593 
3594  return( ioctl( stream->fd, FIOFLUSH, 0 ) == ERROR && \
3595  ioctl( stream->fd, FIOSYNC, 0 ) == ERROR ? \
3596  CRYPT_ERROR_WRITE : CRYPT_OK );
3597  }
3598 
3599 /* Change the read/write position in a file */
3600 
3601 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
3602 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
3603  {
3604  assert( isWritePtr( stream, sizeof( STREAM ) ) );
3605 
3606  REQUIRES( stream->type == STREAM_TYPE_FILE );
3607  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
3608 
3609  if( lseek( stream->fd, position, SEEK_SET ) == ERROR )
3610  return( sSetError( stream, CRYPT_ERROR_READ ) );
3611  return( CRYPT_OK );
3612  }
3613 
3614 /* Check whether a file is writeable */
3615 
3617 BOOLEAN fileReadonly( IN_STRING const char *fileName )
3618  {
3619  int fd;
3620 
3621  assert( isReadPtr( fileName, 2 ) );
3622 
3623  /* The only way to tell whether a file is writeable is to try to open it
3624  for writing, since there's no access() function */
3625  if( ( fd = open( fileName, O_RDWR, 0600 ) ) == ERROR )
3626  {
3627  /* We couldn't open it, check to see whether this is because it
3628  doesn't exist or because it's not writeable */
3629  return( getErrorCode( CRYPT_ERROR_OPEN ) == CRYPT_ERROR_PERMISSION ? \
3630  TRUE : FALSE );
3631  }
3632  close( fd );
3633  return( FALSE );
3634  }
3635 
3636 /* File deletion functions: Wipe a file from the current position to EOF,
3637  and wipe and delete a file (although it's not terribly rigorous).
3638  Vestigia nulla retrorsum */
3639 
3640 STDC_NONNULL_ARG( ( 1 ) ) \
3641 static void eraseFile( const STREAM *stream, long position, long length )
3642  {
3643  assert( isReadPtr( stream, sizeof( STREAM ) ) );
3644 
3645  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
3646  REQUIRES_V( position >= 0 && position < MAX_INTLENGTH );
3647  REQUIRES_V( length >= 0 && length < MAX_INTLENGTH );
3648  /* May be zero if a file-open failed leaving a zero-length
3649  file */
3650 
3651  /* Wipe everything past the current position in the file */
3652  while( length > 0 )
3653  {
3655  BYTE buffer[ ( BUFSIZ * 2 ) + 8 ];
3656  int bytesToWrite = min( length, BUFSIZ * 2 );
3657 
3658  /* We need to make sure that we fill the buffer with random data for
3659  each write, otherwise compressing filesystems will just compress
3660  it to nothing */
3661  setMessageData( &msgData, buffer, bytesToWrite );
3663  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
3664  if( write( stream->fd, buffer, bytesToWrite ) <= bytesToWrite )
3665  break; /* An error occurred while writing, exit */
3666  length -= bytesToWrite;
3667  }
3668  ioctl( stream->fd, FIOFLUSH, 0 );
3669 
3670  /* Truncate the file and if we're erasing the entire file, reset the
3671  attributes and timestamps. We ignore return codes since some
3672  filesystems don't support these ioctl()'s */
3673  ioctl( stream->fd, FIOTRUNC, position );
3674  if( position <= 0 )
3675  {
3676  ioctl( stream->fd, FIOATTRIBSET, 0 );
3677  ioctl( stream->fd, FIOTIMESET, 0 );
3678  }
3679  }
3680 
3681 STDC_NONNULL_ARG( ( 1 ) ) \
3682 void fileClearToEOF( const STREAM *stream )
3683  {
3684  struct stat statStruct;
3685  long position, length;
3686 
3687  assert( isReadPtr( stream, sizeof( STREAM ) ) );
3688 
3689  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
3690 
3691  /* Wipe everything past the current position in the file. We use the
3692  long-winded method of determining the overall length since it doesn't
3693  require the presence of dirLib for fstat() */
3694  position = ioctl( stream->fd, FIOWHERE, 0 );
3695  if( ioctl( stream->fd, FIOFSTATGET, ( int ) &statStruct ) != ERROR )
3696  length = statStruct.st_size - position;
3697  else
3698  {
3699  /* No stat support, do it via lseek() instead */
3700  lseek( stream->fd, 0, SEEK_END );
3701  length = ioctl( stream->fd, FIOWHERE, 0 ) - position;
3702  lseek( stream->fd, position, SEEK_SET );
3703  }
3704  eraseFile( stream, position, length );
3705  }
3706 
3707 STDC_NONNULL_ARG( ( 1 ) ) \
3708 void fileErase( IN_STRING const char *fileName )
3709  {
3710  STREAM stream;
3711  struct stat statStruct;
3712  int length, status;
3713 
3714  assert( isReadPtr( fileName, 2 ) );
3715 
3716  /* Try and open the file so that we can erase it. If this fails, the
3717  best that we can do is a straight unlink */
3718  status = sFileOpen( &stream, fileName,
3719  FILE_FLAG_READ | FILE_FLAG_WRITE | \
3720  FILE_FLAG_EXCLUSIVE_ACCESS );
3721  if( cryptStatusError( status ) )
3722  {
3723  if( status != CRYPT_ERROR_NOTFOUND )
3724  remove( fileName );
3725  return;
3726  }
3727 
3728  /* Determine the size of the file and erase it. We use the long-winded
3729  method of determining the overall length since it doesn't require the
3730  presence of dirLib for fstat() */
3731  if( ioctl( stream.fd, FIOFSTATGET, ( int ) &statStruct ) != ERROR )
3732  length = statStruct.st_size;
3733  else
3734  {
3735  /* No stat support, do it via lseek() instead */
3736  lseek( stream.fd, 0, SEEK_END );
3737  length = ioctl( stream.fd, FIOWHERE, 0 );
3738  lseek( stream.fd, 0, SEEK_SET );
3739  }
3740  eraseFile( &stream, 0, length );
3741 
3742  sFileClose( &stream );
3743 
3744  /* Finally, delete the file */
3745  remove( fileName );
3746  }
3747 
3748 /* Build the path to a file in the cryptlib directory */
3749 
3750 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
3751 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
3752  IN_LENGTH_SHORT const int pathMaxLen,
3753  OUT_LENGTH_SHORT_Z int *pathLen,
3754  IN_BUFFER( fileNameLen ) const char *fileName,
3755  IN_LENGTH_SHORT const int fileNameLen,
3756  IN_ENUM( BUILDPATH_OPTION ) \
3757  const BUILDPATH_OPTION_TYPE option )
3758  {
3759  assert( isWritePtr( path, pathMaxLen ) );
3760  assert( isWritePtr( pathLen, sizeof( int ) ) );
3761  assert( isReadPtr( fileName, fileNameLen ) );
3762 
3763  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
3764  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
3765  option == BUILDPATH_GETPATH ) && fileName != NULL && \
3766  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
3767  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
3768  fileNameLen == 0 ) );
3769 
3770  /* Make sure that the open fails if we can't build the path */
3771  *path = '\0';
3772 
3773 #if 0 /* Default path is just cwd, which isn't too useful */
3774  ioDefPathGet( path );
3775 #else
3776  strlcat_s( path, pathMaxLen, "/" );
3777 #endif /* 0 */
3778 
3779  /* Add the filename to the path */
3780  return( appendFilename( path, pathMaxLen, pathLen, fileName,
3781  fileNameLen, option ) );
3782  }
3783 
3784 /****************************************************************************
3785 * *
3786 * Windows File Stream Functions *
3787 * *
3788 ****************************************************************************/
3789 
3790 #elif defined( __WIN32__ ) || defined( __WINCE__ )
3791 
3792 /* File flags to use when accessing a file and attributes to use when
3793  creating a file. For access we tell the OS that we'll be reading the
3794  file sequentially, for creation we prevent the OS from groping around
3795  inside the file. We could also be (inadvertently) opening the client
3796  side of a named pipe, which would allow a server to impersonate us if
3797  we're not careful. To handle this we set the impersonation level to
3798  SecurityAnonymous, which prevents the server from doing anything with our
3799  capabilities. Note that the pipe flag SECURITY_SQOS_PRESENT flag clashes
3800  with the file flag FILE_FLAG_OPEN_NO_RECALL (indicating that data
3801  shouldn't be moved in from remote storage if it currently resides there),
3802  this isn't likely to be a problem. The SECURITY_ANONYMOUS define
3803  evaluates to zero, which means that it won't clash with any file flags,
3804  however if future flags below the no-recall flag (0x00100000) are defined
3805  for CreateFile() care needs to be taken that they don't run down into the
3806  area used by the pipe flags around 0x000x0000 */
3807 
3808 #ifndef __WINCE__
3809  #ifndef FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
3810  #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
3811  #endif /* VC++ <= 6.0 */
3812  #define FILE_FLAGS ( FILE_FLAG_SEQUENTIAL_SCAN | \
3813  SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS )
3814  #define FILE_ATTRIBUTES FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
3815 #else
3816  /* WinCE doesn't recognise the extended file flags */
3817  #define FILE_FLAGS 0
3818  #define FILE_ATTRIBUTES 0
3819 #endif /* Win32 vs.WinCE */
3820 
3821 /* Older versions of the Windows SDK don't include the defines for system
3822  directories so we define them ourselves if necesary. Note that we use
3823  CSIDL_APPDATA, which expands to 'Application Data', rather than
3824  CSIDL_LOCAL_APPDATA, which expands to 'Local Settings/Application Data',
3825  because although the latter is technically safer (it's not part of the
3826  roaming profile, so it'll never leave the local machine), it's
3827  technically intended for less-important/discardable data and temporary
3828  files */
3829 
3830 #ifndef CSIDL_PERSONAL
3831  #define CSIDL_PERSONAL 0x05 /* 'My Documents' */
3832 #endif /* !CSIDL_PERSONAL */
3833 #ifndef CSIDL_APPDATA
3834  #define CSIDL_APPDATA 0x1A /* '<luser name>/Application Data' */
3835 #endif /* !CSIDL_APPDATA */
3836 #ifndef CSIDL_FLAG_CREATE
3837  #define CSIDL_FLAG_CREATE 0x8000 /* Force directory creation */
3838 #endif /* !CSIDL_FLAG_CREATE */
3839 #ifndef SHGFP_TYPE_CURRENT
3840  #define SHGFP_TYPE_CURRENT 0
3841 #endif /* !SHGFP_TYPE_CURRENT */
3842 
3843 /* Older versions of the Windows SDK don't include the defines for services
3844  added in Windows XP so we define them ourselves if necessary */
3845 
3846 #ifndef SECURITY_LOCAL_SERVICE_RID
3847  #define SECURITY_LOCAL_SERVICE_RID 19
3848  #define SECURITY_NETWORK_SERVICE_RID 20
3849 #endif /* !SECURITY_LOCAL_SERVICE_RID */
3850 
3851 /* Windows CE doesn't have security mechanisms, so we make it look like Win95
3852  for ACL handling purposes by overriding the getSysVar() function */
3853 
3854 #ifdef __WINCE__
3855 
3856 CHECK_RETVAL \
3857 static int localGetSysVar( const SYSVAR_TYPE type )
3858  {
3859  if( type == SYSVAR_ISWIN95 )
3860  return( TRUE );
3861  return( getSysVar( type ) );
3862  }
3863 #define getSysVar localGetSysVar
3864 #endif /* __WINCE__ */
3865 
3866 /* Check whether a user's SID is known to a server providing a network
3867  share, so that we can set file ACLs based on it */
3868 
3869 #ifndef __WINCE__
3870 
3871 #define TOKEN_BUFFER_SIZE 256
3872 #define SID_BUFFER_SIZE 256
3873 #define UNI_BUFFER_SIZE ( 256 + _MAX_PATH )
3874 #define PATH_BUFFER_SIZE ( _MAX_PATH + 16 )
3875 
3876 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
3877 static BOOLEAN isSpecialSID( INOUT SID *pUserSid )
3878  {
3879  BYTE sidBuffer[ SID_BUFFER_SIZE + 8 ];
3880  SID *pSid = ( PSID ) sidBuffer;
3881  SID_IDENTIFIER_AUTHORITY identifierAuthority = SECURITY_NT_AUTHORITY;
3882 
3883  assert( isWritePtr( pUserSid, sizeof( SID ) ) );
3884 
3885  /* Create a SID for each special-case account and check whether it
3886  matches the current user's SID. It would be easier to use
3887  IsWellKnownSid() for this check, but this only appeared in Windows
3888  XP. In addition IsWellKnowSID() contains a large (and ever-growing)
3889  number of SIDs that aren't appropriate here, it's main use is to work
3890  in conjunction with CreateWellKnownSID() to allow creation of a known
3891  SID without having to jump through the usual SID-creation hoops */
3892  InitializeSid( pSid, &identifierAuthority, 1 );
3893  *( GetSidSubAuthority( pSid, 0 ) ) = SECURITY_LOCAL_SYSTEM_RID;
3894  if( EqualSid( pSid, pUserSid ) )
3895  return( TRUE );
3896  *( GetSidSubAuthority( pSid, 0 ) ) = SECURITY_LOCAL_SERVICE_RID;
3897  if( EqualSid( pSid, pUserSid ) )
3898  return( TRUE );
3899  *( GetSidSubAuthority( pSid, 0 ) ) = SECURITY_NETWORK_SERVICE_RID;
3900  if( EqualSid( pSid, pUserSid ) )
3901  return( TRUE );
3902 
3903  return( FALSE );
3904  }
3905 
3906 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1, 2 ) ) \
3907 static const char *getUncName( OUT UNIVERSAL_NAME_INFO *nameInfo,
3908  const char *fileName )
3909  {
3910  typedef DWORD ( WINAPI *WNETGETUNIVERSALNAMEA )( LPCSTR lpLocalPath,
3911  DWORD dwInfoLevel, LPVOID lpBuffer,
3912  LPDWORD lpBufferSize );
3913  WNETGETUNIVERSALNAMEA pWNetGetUniversalNameA;
3914  HINSTANCE hMPR;
3915  DWORD uniBufSize = UNI_BUFFER_SIZE;
3916  BOOLEAN gotUNC = FALSE;
3917 
3918  assert( isWritePtr( nameInfo, sizeof( UNIVERSAL_NAME_INFO ) ) );
3919  assert( isReadPtr( fileName, sizeof( char * ) ) );
3920 
3921  /* Clear return value */
3922  memset( nameInfo, 0, sizeof( UNIVERSAL_NAME_INFO ) );
3923 
3924  /* Load the MPR library. We can't (safely) use an opportunistic
3925  GetModuleHandle() before the DynamicLoad() for this because the code
3926  that originally loaded the DLL might do a DynamicUnload() in another
3927  thread, causing the library to be removed from under us. In any case
3928  DynamicLoad() does this for us, merely incrementing the reference
3929  count if the DLL is already loaded */
3930  hMPR = DynamicLoad( "Mpr.dll" );
3931  if( hMPR == NULL )
3932  {
3933  /* Should never happen, we can't have a mapped network drive if no
3934  network is available */
3935  return( NULL );
3936  }
3937 
3938  /* Get the translated UNC name. The UNIVERSAL_NAME_INFO struct is one
3939  of those variable-length ones where the lpUniversalName member points
3940  to extra data stored off the end of the struct, so we overlay it onto
3941  a much larger buffer */
3942  pWNetGetUniversalNameA = ( WNETGETUNIVERSALNAMEA ) \
3943  GetProcAddress( hMPR, "WNetGetUniversalNameA" );
3944  if( pWNetGetUniversalNameA != NULL && \
3945  pWNetGetUniversalNameA( fileName, UNIVERSAL_NAME_INFO_LEVEL,
3946  nameInfo, &uniBufSize ) == NO_ERROR )
3947  gotUNC = TRUE;
3948  DynamicUnload( hMPR );
3949 
3950  return( gotUNC ? nameInfo->lpUniversalName : NULL );
3951  }
3952 
3953 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
3954 static BOOLEAN checkUserKnown( IN_BUFFER( fileNameLength ) const char *fileName,
3955  const int fileNameLength )
3956  {
3957  HANDLE hToken;
3958  BYTE uniBuffer[ UNI_BUFFER_SIZE + 8 ];
3959  BYTE tokenBuffer[ TOKEN_BUFFER_SIZE + 8 ];
3960  char pathBuffer[ PATH_BUFFER_SIZE + 8 ];
3961  char nameBuffer[ PATH_BUFFER_SIZE + 8 ];
3962  char domainBuffer[ PATH_BUFFER_SIZE + 8 ];
3963  const char *fileNamePtr = ( char * ) fileName;
3964  UNIVERSAL_NAME_INFO *nameInfo = ( UNIVERSAL_NAME_INFO * ) uniBuffer;
3965  TOKEN_USER *pTokenUser = ( TOKEN_USER * ) tokenBuffer;
3966  SID_NAME_USE eUse;
3967  DWORD nameBufSize = PATH_BUFFER_SIZE, domainBufSize = PATH_BUFFER_SIZE;
3968  BOOLEAN isMappedDrive = FALSE, tokenOK = FALSE;
3969  int fileNamePtrLength = fileNameLength, serverNameLength, length;
3970 
3971  assert( isReadPtr( fileName, fileNameLength ) );
3972 
3973  static_assert( sizeof( UNIVERSAL_NAME_INFO ) + _MAX_PATH <= UNI_BUFFER_SIZE, \
3974  "UNC buffer size" );
3975 
3976  REQUIRES_B( fileNameLength > 0 && fileNameLength < _MAX_PATH );
3977 
3978  /* Win95 doesn't have any ACL-based security, there's nothing to do */
3979  if( getSysVar( SYSVAR_ISWIN95 ) == TRUE )
3980  return( TRUE );
3981 
3982  /* Canonicalise the path name. This turns relative paths into absolute
3983  ones and converts forward to backwards slashes. The latter is
3984  necessary because while the Windows filesystem functions will accept
3985  Unix-style forward slashes in paths, the WNetGetUniversalName()
3986  networking function doesn't.
3987 
3988  GetFullPathName() has a weird return value where it can return a
3989  success (nonzero) status even if it fails, which occurs when the
3990  resulting string is too long to fit into the buffer. In this case it
3991  returns the required buffer size, so we have to check whether the
3992  return value falls within a certain range rather than just being
3993  nonzero */
3994  length = GetFullPathName( fileNamePtr, PATH_BUFFER_SIZE, pathBuffer, NULL );
3995  if( length > 0 && length < PATH_BUFFER_SIZE )
3996  {
3997  fileNamePtr = pathBuffer;
3998  fileNamePtrLength = length;
3999  }
4000 
4001  /* If the path is too short to contain a drive letter or UNC path, it
4002  must be local */
4003  if( fileNamePtrLength <= 2 )
4004  return( TRUE );
4005 
4006  /* If there's a drive letter present, check whether it's a local or
4007  remote drive. GetDriveType() is rather picky about what it'll accept
4008  so we have to extract just the drive letter from the path. We could
4009  also use IsNetDrive() for this, but this requires dynamically pulling
4010  it in from shell32.dll, and even then it's only present in version 5.0
4011  or later, so it's easier to use GetDriveType() */
4012  if( fileNamePtr[ 1 ] == ':' )
4013  {
4014  char drive[ 8 + 8 ];
4015 
4016  memcpy( drive, fileNamePtr, 2 );
4017  drive[ 2 ] = '\0';
4018  if( GetDriveType( drive ) != DRIVE_REMOTE )
4019  {
4020  /* It's a local drive, the user should be known */
4021  return( TRUE );
4022  }
4023  isMappedDrive = TRUE;
4024  }
4025  else
4026  {
4027  /* If it's not a UNC name, it's local (or something weird like a
4028  mapped web page to which we shouldn't be writing keys anyway) */
4029  if( memcmp( fileNamePtr, "\\\\", 2 ) )
4030  return( TRUE );
4031  }
4032 
4033  /* If it's a mapped network drive, get the name in UNC form. What to do
4034  in case of failure is a bit tricky. If we get here we know that it's
4035  a network share, but if there's some problem mapping it to a UNC (the
4036  usual reason for this will be that there's a problem with the network
4037  and the share is a cached remnant of a persistent connection), all we
4038  can do is fail safe and hope that the user is known */
4039  if( isMappedDrive )
4040  {
4041  fileNamePtr = getUncName( nameInfo, fileNamePtr );
4042  if( fileNamePtr == NULL )
4043  return( TRUE );
4044  }
4045 
4046  assert( !memcmp( fileNamePtr, "\\\\", 2 ) );
4047 
4048  /* We've got the network share in UNC form, extract the server name. If
4049  for some reason the name is still an absolute path, the following will
4050  convert it to "x:\", which is fine */
4051  for( serverNameLength = 2; \
4052  serverNameLength < fileNamePtrLength && \
4053  fileNamePtr[ serverNameLength ] != '\\'; \
4054  serverNameLength++ );
4055  if( serverNameLength <= 0 || serverNameLength >= PATH_BUFFER_SIZE - 2 )
4056  {
4057  /* Server name is too long, default to fail-safe handling */
4058  return( TRUE );
4059  }
4060  memmove( pathBuffer, fileNamePtr, serverNameLength );
4061  strlcpy_s( pathBuffer + serverNameLength,
4062  PATH_BUFFER_SIZE - serverNameLength, "\\" );
4063 
4064  /* Get the current user's SID */
4065  if( OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken ) || \
4066  OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken ) )
4067  {
4068  DWORD cbTokenUser;
4069 
4070  tokenOK = GetTokenInformation( hToken, TokenUser, pTokenUser,
4071  TOKEN_BUFFER_SIZE, &cbTokenUser );
4072  CloseHandle( hToken );
4073  }
4074  if( !tokenOK )
4075  return( TRUE ); /* Default fail-safe */
4076 
4077  /* Check whether this is a special-case account that can't be mapped to
4078  an account on the server */
4079  if( isSpecialSID( pTokenUser->User.Sid ) )
4080  {
4081  /* The user with this SID may be known to the server, but it
4082  represents a different entity on the server than it does on the
4083  local system */
4084  return( FALSE );
4085  }
4086 
4087  /* Check whether the user with this SID is known to the server. We
4088  get some additional info in the form of the eUse value, which
4089  indicates the general class of the SID (e.g. SidTypeUser,
4090  SidTypeGroup, SidTypeDomain, SidTypeAlias, etc, but these aren't of
4091  much use to us */
4092  if( !LookupAccountSid( pathBuffer, pTokenUser->User.Sid,
4093  nameBuffer, &nameBufSize,
4094  domainBuffer, &domainBufSize, &eUse ) && \
4095  GetLastError() == ERROR_NONE_MAPPED )
4096  {
4097  /* The user with this SID isn't known to the server */
4098  return( FALSE );
4099  }
4100 
4101  /* Either the user is known to the server or it's a fail-safe */
4102  return( TRUE );
4103  }
4104 #endif /* !__WINCE__ */
4105 
4106 /* Open/close a file stream */
4107 
4108 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
4109 int sFileOpen( OUT STREAM *stream, IN_STRING const char *fileName,
4110  IN_FLAGS( FILE ) const int mode )
4111  {
4112 #ifndef __WINCE__
4113  HANDLE hFile;
4114  UINT uErrorMode;
4115  const char *fileNamePtr = fileName;
4116 #else
4117  wchar_t fileNameBuffer[ _MAX_PATH + 16 ], *fileNamePtr = fileNameBuffer;
4118 #endif /* __WINCE__ */
4119  void *aclInfo = NULL;
4120  int status = CRYPT_OK;
4121 
4122  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4123  assert( isReadPtr( fileName, 2 ) );
4124 
4125  REQUIRES( mode != 0 );
4126 
4127  /* Initialise the stream structure */
4128  memset( stream, 0, sizeof( STREAM ) );
4129  stream->type = STREAM_TYPE_FILE;
4130  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
4131  stream->flags = STREAM_FLAG_READONLY;
4132 
4133  /* Convert the filename to the native character set if necessary */
4134 #ifdef __WINCE__
4135  status = asciiToUnicode( fileNameBuffer, _MAX_PATH, fileName,
4136  strlen( fileName ) + 1 );
4137  if( cryptStatusError( status ) )
4138  return( CRYPT_ERROR_OPEN );
4139 #endif /* __WINCE__ */
4140 
4141  /* Don't allow the use of escapes that disable path parsing, and make
4142  sure that the path has a sensible length. There are in theory
4143  various additional checks that we could add at this point, for
4144  example to try and detect spurious dots and spaces in the path, which
4145  are handled by Windows in unexpected ways, generally by removing
4146  them. For example "foo... . ... ..." would be opened as "foo.".
4147  This can lead to tricks like specifying a name like "foo.exe .. ..
4148  <to MAX_PATH>.txt" which is then truncated at MAX_PATH and the morse
4149  code also truncated to create "foo.exe" instead of a text file.
4150  Alternatively it's also possible to force the creation of files with
4151  trailing dots and spaces by using an alternate data stream specifier
4152  "::<name>" after the trailing junk, since ADS parsing occurs after
4153  stripping of trailing junk, so the ADS specifier protects the
4154  trailing junk. On the other hand it's not exactly clear why a user
4155  would be doing something like this with their crypto keyset, or even
4156  whether we can evade all the various other tricks they could play at
4157  the filesystem level */
4158  if( !memcmp( fileNamePtr, "\\\\", 2 ) )
4159  {
4160  const int length = strlen( ( char * ) fileNamePtr );
4161 
4162  if( length >= 4 && !memcmp( fileNamePtr, "\\\\?\\", 4 ) )
4163  return( CRYPT_ERROR_OPEN );
4164  }
4165  else
4166  {
4167  if( !memcmp( fileNamePtr, L"\\\\", 4 ) )
4168  {
4169  const int length = wcslen( ( wchar_t * ) fileNamePtr );
4170 
4171  if( length >= 8 && !memcmp( fileNamePtr, L"\\\\?\\", 8 ) )
4172  return( CRYPT_ERROR_OPEN );
4173  }
4174  }
4175 
4176  /* If we're creating the file and we don't want others to get to it, set
4177  up the security attributes to reflect this if the OS supports it.
4178  Unfortunately creating the file with ACLs doesn't always work when
4179  the file is located on a network share because what's:
4180 
4181  create file, ACL = user SID access
4182 
4183  on a local drive can become:
4184 
4185  create file, ACL = <unknown SID> access
4186 
4187  on the network share if the user is accessing it as a member of a
4188  group and their individual SID isn't known to the server. As a
4189  result, they can't read the file that they've just created. To get
4190  around this, we need to perform an incredibly convoluted check (via
4191  checkUserKnown()) to see whether the path is a network path and if
4192  so, if the user is known to the server providing the network share.
4193 
4194  An extension of this problem occurs where the user *is* known on the
4195  local and server system, but the two are logically different. This
4196  occurs for the System/LocalSystem service account and,for Windows XP
4197  and newer, LocalService and NetworkService. To handle this,
4198  checkUserKnown() also checks whether the user is running under one of
4199  these accounts */
4200 #ifndef __WINCE__
4201  if( !getSysVar( SYSVAR_ISWIN95 ) && \
4202  ( mode & FILE_FLAG_WRITE ) && ( mode & FILE_FLAG_PRIVATE ) && \
4203  checkUserKnown( fileNamePtr, strlen( fileNamePtr ) ) && \
4204  ( aclInfo = initACLInfo( FILE_GENERIC_READ | \
4205  FILE_GENERIC_WRITE ) ) == NULL )
4206  return( CRYPT_ERROR_OPEN );
4207 #endif /* __WINCE__ */
4208 
4209  /* Check that the file isn't a special file type, for example a device
4210  pseudo-file that can crash the system under Win95/98/ME/whatever.
4211  WinCE doesn't have these pseudo-files, so this function doesn't
4212  exist there. In theory we could check for the various
4213  FILE_ATTRIBUTE_xxxROM variations, but that'll be handled
4214  automatically by CreateFile(). We perform this check before we try
4215  any of the open actions since it's most likely to catch accidental
4216  access to the wrong file, and we want to have the chance to bail
4217  out before making irreversible changes like the call to DeleteFile()
4218  below. To avoid race conditions, a further check is carried out
4219  after the file is opened */
4220 #ifndef __WINCE__
4221  hFile = CreateFile( fileNamePtr, GENERIC_READ, FILE_SHARE_READ, NULL,
4222  OPEN_EXISTING, FILE_FLAGS, NULL );
4223  if( hFile != INVALID_HANDLE_VALUE )
4224  {
4225  const DWORD type = GetFileType( hFile );
4226 
4227  CloseHandle( hFile );
4228  if( type != FILE_TYPE_DISK )
4229  {
4230  if( aclInfo != NULL )
4231  freeACLInfo( aclInfo );
4232  return( CRYPT_ERROR_OPEN );
4233  }
4234  }
4235 #endif /* __WINCE__ */
4236 
4237  /* Try and open the file */
4238 #ifndef __WINCE__
4239  uErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS );
4240 #endif /* __WINCE__ */
4241  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_WRITE )
4242  {
4243  BOOL fSuccess;
4244 
4245  /* If we're creating the file, we need to remove any existing file
4246  of the same name before we try and create a new one, otherwise
4247  the OS will pick up the permissions for the existing file and
4248  apply them to the new one. This is safe because if an attacker
4249  tries to slip in a wide-open file between the delete and the
4250  create, we'll get a file-already-exists status returned that we
4251  can trap and turn into an error. Since the DeleteFile() can fail
4252  in ways that indicate that the following CreateFile() isn't going
4253  to succeed either, we check for certain failure cases and exit
4254  immediately if we encounter them */
4255  fSuccess = DeleteFile( fileNamePtr );
4256  if( !fSuccess && GetLastError() == ERROR_ACCESS_DENIED )
4257  {
4258  if( aclInfo != NULL )
4259  freeACLInfo( aclInfo );
4260  return( CRYPT_ERROR_PERMISSION );
4261  }
4262  stream->hFile = CreateFile( fileNamePtr, GENERIC_READ | GENERIC_WRITE, 0,
4263  getACLInfo( aclInfo ), CREATE_ALWAYS,
4264  FILE_ATTRIBUTES | FILE_FLAGS, NULL );
4265  if( stream->hFile != INVALID_HANDLE_VALUE && \
4266  GetLastError() == ERROR_ALREADY_EXISTS )
4267  {
4268  /* There was already something there that wasn't hit by the
4269  delete, we can't be sure that the file has the required
4270  semantics */
4271  CloseHandle( stream->hFile );
4272  DeleteFile( fileNamePtr );
4273  stream->hFile = INVALID_HANDLE_VALUE;
4274  }
4275  }
4276  else
4277  {
4278  const int openMode = ( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ ) ? \
4279  GENERIC_READ : GENERIC_READ | GENERIC_WRITE;
4280  const int shareMode = ( mode & FILE_FLAG_EXCLUSIVE_ACCESS ) ? \
4281  0 : FILE_SHARE_READ;
4282 
4283  stream->hFile = CreateFile( fileNamePtr, openMode, shareMode, NULL,
4284  OPEN_EXISTING, FILE_FLAGS, NULL );
4285 #ifndef __WINCE__
4286  if( stream->hFile != INVALID_HANDLE_VALUE && \
4287  GetFileType( stream->hFile ) != FILE_TYPE_DISK )
4288  {
4289  /* This repeats the check that we made earlier before trying
4290  to open the file, and works around a potential race condition
4291  in which an attacker creates a special file after we perform
4292  the check */
4293  CloseHandle( stream->hFile );
4294  if( aclInfo != NULL )
4295  freeACLInfo( aclInfo );
4296  SetErrorMode( uErrorMode );
4297  return( CRYPT_ERROR_OPEN );
4298  }
4299 #endif /* __WINCE__ */
4300  }
4301 #ifndef __WINCE__
4302  SetErrorMode( uErrorMode );
4303 #endif /* __WINCE__ */
4304  if( stream->hFile == INVALID_HANDLE_VALUE )
4305  {
4306  /* Translate the Win32 error code into an equivalent cryptlib error
4307  code */
4308  switch( GetLastError() )
4309  {
4310  case ERROR_FILE_NOT_FOUND:
4311  case ERROR_PATH_NOT_FOUND:
4312  status = CRYPT_ERROR_NOTFOUND;
4313  break;
4314 
4315  case ERROR_ACCESS_DENIED:
4316  status = CRYPT_ERROR_PERMISSION;
4317  break;
4318 
4319  case ERROR_BUSY:
4320  status = CRYPT_ERROR_TIMEOUT;
4321  break;
4322 
4323  default:
4324  status = CRYPT_ERROR_OPEN;
4325  }
4326  }
4327 
4328  /* In theory we could also use something like SHChangeNotify(
4329  SHCNE_CREATE, SHCNF_PATH, fileName, NULL ) at this point to tell
4330  other apps that we've created the file, but since this is a private
4331  config/key file that's not really meant to be messed with by other
4332  apps, we leave it up to them to discover that there's been a change
4333  if they really feel they need to know this */
4334 
4335  /* Clean up */
4336  if( aclInfo != NULL )
4337  freeACLInfo( aclInfo );
4338  return( status );
4339  }
4340 
4341 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
4342 int sFileClose( INOUT STREAM *stream )
4343  {
4344  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4345 
4346  REQUIRES( stream->type == STREAM_TYPE_FILE );
4347 
4348  /* Close the file and clear the stream structure */
4349  CloseHandle( stream->hFile );
4350  zeroise( stream, sizeof( STREAM ) );
4351 
4352  return( CRYPT_OK );
4353  }
4354 
4355 /* Read/write a block of data from/to a file stream */
4356 
4357 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
4358 int fileRead( INOUT STREAM *stream,
4359  OUT_BUFFER( length, *bytesRead ) void *buffer,
4360  IN_LENGTH const int length,
4361  OUT_LENGTH_Z int *bytesRead )
4362  {
4363  DWORD byteCount;
4364 
4365  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4366  assert( isWritePtr( buffer, length ) );
4367  assert( isWritePtr( bytesRead, sizeof( int ) ) );
4368 
4369  REQUIRES( stream->type == STREAM_TYPE_FILE );
4370  REQUIRES( length > 0 && length < MAX_INTLENGTH );
4371 
4372  /* Clear return value */
4373  *bytesRead = 0;
4374 
4375  if( !ReadFile( stream->hFile, buffer, length, &byteCount, NULL ) )
4376  return( sSetError( stream, CRYPT_ERROR_READ ) );
4377  *bytesRead = byteCount;
4378 
4379  return( CRYPT_OK );
4380  }
4381 
4382 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
4383 int fileWrite( INOUT STREAM *stream,
4384  IN_BUFFER( length ) const void *buffer,
4385  IN_LENGTH const int length )
4386  {
4387  DWORD bytesWritten;
4388 
4389  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4390  assert( isReadPtr( buffer, length ) );
4391 
4392  REQUIRES( stream->type == STREAM_TYPE_FILE );
4393  REQUIRES( length > 0 && length < MAX_INTLENGTH );
4394 
4395  if( !WriteFile( stream->hFile, buffer, length, &bytesWritten, NULL ) || \
4396  ( int ) bytesWritten != length )
4397  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
4398 
4399  return( CRYPT_OK );
4400  }
4401 
4402 /* Commit data in a file stream to backing storage */
4403 
4404 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
4405 int fileFlush( INOUT STREAM *stream )
4406  {
4407  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4408 
4409  REQUIRES( stream->type == STREAM_TYPE_FILE );
4410 
4411  return( FlushFileBuffers( stream->hFile ) ? CRYPT_OK : CRYPT_ERROR_WRITE );
4412  }
4413 
4414 /* Change the read/write position in a file */
4415 
4416 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
4417 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
4418  {
4419  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4420 
4421  REQUIRES( stream->type == STREAM_TYPE_FILE );
4422  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
4423 
4424  if( SetFilePointer( stream->hFile, position, NULL,
4425  FILE_BEGIN ) == 0xFFFFFFFF )
4426  return( sSetError( stream, CRYPT_ERROR_READ ) );
4427  return( CRYPT_OK );
4428  }
4429 
4430 /* Check whether a file is writeable */
4431 
4433 BOOLEAN fileReadonly( IN_STRING const char *fileName )
4434  {
4435  HANDLE hFile;
4436 #ifdef __WINCE__
4437  wchar_t fileNameBuffer[ _MAX_PATH + 16 ], *fileNamePtr = fileNameBuffer;
4438  int status;
4439 #else
4440  const char *fileNamePtr = fileName;
4441 #endif /* __WINCE__ */
4442 
4443  assert( isReadPtr( fileName, 2 ) );
4444 
4445  /* Convert the filename to the native character set if necessary */
4446 #ifdef __WINCE__
4447  status = asciiToUnicode( fileNameBuffer, _MAX_PATH, fileName,
4448  strlen( fileName ) + 1 );
4449  if( cryptStatusError( status ) )
4450  return( TRUE );
4451 #endif /* __WINCE__ */
4452 
4453  /* The only way to tell whether a file is writeable is to try to open it
4454  for writing. An access()-based check is pointless because it just
4455  calls GetFileAttributes() and checks for the read-only bit being set.
4456  Even if we wanted to check for this basic level of access, it
4457  wouldn't work because writes can still be blocked if it's a read-only
4458  file system or a network share */
4459  hFile = CreateFile( fileNamePtr, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
4460  FILE_ATTRIBUTE_NORMAL, NULL );
4461  if( hFile == INVALID_HANDLE_VALUE )
4462  {
4463  /* Translate the Win32 error code into an equivalent cryptlib error
4464  code */
4465  return( ( GetLastError() == ERROR_ACCESS_DENIED ) ? TRUE : FALSE );
4466  }
4467  CloseHandle( hFile );
4468 
4469  return( FALSE );
4470  }
4471 
4472 /* File deletion functions: Wipe a file from the current position to EOF,
4473  and wipe and delete a file (although it's not terribly rigorous).
4474  Vestigia nulla retrorsum */
4475 
4476 STDC_NONNULL_ARG( ( 1 ) ) \
4477 static void eraseFile( const STREAM *stream, long position, long length )
4478  {
4479  assert( isReadPtr( stream, sizeof( STREAM ) ) );
4480 
4481  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
4482  REQUIRES_V( position >= 0 && position < MAX_INTLENGTH );
4483  REQUIRES_V( length >= 0 && length < MAX_INTLENGTH );
4484  /* May be zero if a file-open failed leaving a zero-length
4485  file */
4486 
4487  /* Wipe the file */
4488  while( length > 0 )
4489  {
4491  BYTE buffer[ 1024 + 8 ];
4492  DWORD bytesWritten;
4493  int bytesToWrite = min( length, 1024 );
4494 
4495  /* We need to make sure that we fill the buffer with random data for
4496  each write, otherwise compressing filesystems will just compress
4497  it to nothing */
4498  setMessageData( &msgData, buffer, bytesToWrite );
4500  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
4501  WriteFile( stream->hFile, buffer, bytesToWrite, &bytesWritten, NULL );
4502  length -= bytesToWrite;
4503  }
4504 
4505  /* Truncate the file and if we're erasing the entire file, reset the
4506  timestamps. The delete just marks the file as deleted rather than
4507  actually deleting it, but there's not much information that can be
4508  recovered without a magnetic force microscope. The call to
4509  FlushFileBuffers() ensures that the changed data gets committed
4510  before the delete call comes along. If we didn't do this then the OS
4511  would drop all changes once DeleteFile() was called, leaving the
4512  original more or less intact on disk */
4513  SetFilePointer( stream->hFile, position, NULL, FILE_BEGIN );
4514  SetEndOfFile( stream->hFile );
4515  if( position <= 0 )
4516  SetFileTime( stream->hFile, 0, 0, 0 );
4517  FlushFileBuffers( stream->hFile );
4518  }
4519 
4520 STDC_NONNULL_ARG( ( 1 ) ) \
4521 void fileClearToEOF( const STREAM *stream )
4522  {
4523  long position, length;
4524 
4525  assert( isReadPtr( stream, sizeof( STREAM ) ) );
4526 
4527  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
4528 
4529  /* Wipe everything past the current position in the file */
4530  if( ( position = SetFilePointer( stream->hFile, 0, NULL,
4531  FILE_CURRENT ) ) == 0xFFFFFFFF )
4532  return;
4533  length = GetFileSize( stream->hFile, NULL ) - position;
4534  if( length <= 0 )
4535  return; /* Nothing to do, exit */
4536  eraseFile( stream, position, length );
4537  }
4538 
4539 STDC_NONNULL_ARG( ( 1 ) ) \
4540 void fileErase( IN_STRING const char *fileName )
4541  {
4542  STREAM stream;
4543 #ifdef __WINCE__
4544  wchar_t fileNameBuffer[ _MAX_PATH + 16 ], *fileNamePtr = fileNameBuffer;
4545 #else
4546  const char *fileNamePtr = fileName;
4547 #endif /* __WINCE__ */
4548  int status;
4549 
4550  assert( isReadPtr( fileName, 2 ) );
4551 
4552  /* Convert the filename to the native character set if necessary */
4553 #ifdef __WINCE__
4554  status = asciiToUnicode( fileNameBuffer, _MAX_PATH, fileName,
4555  strlen( fileName ) + 1 );
4556  if( cryptStatusError( status ) )
4557  return; /* Error converting filename string, exit */
4558 #endif /* __WINCE__ */
4559 
4560  /* Try and open the file so that we can erase it. If this fails, the
4561  best that we can do is a straight unlink */
4562  status = sFileOpen( &stream, fileName,
4563  FILE_FLAG_READ | FILE_FLAG_WRITE | \
4564  FILE_FLAG_EXCLUSIVE_ACCESS );
4565  if( cryptStatusError( status ) )
4566  {
4567  if( status != CRYPT_ERROR_NOTFOUND )
4568  DeleteFile( fileNamePtr );
4569  return;
4570  }
4571  eraseFile( &stream, 0, GetFileSize( stream.hFile, NULL ) );
4572  sFileClose( &stream );
4573  DeleteFile( fileNamePtr );
4574  }
4575 
4576 /* Build the path to a file in the cryptlib directory */
4577 
4578 #if defined( __WIN32__ )
4579 
4580 #ifdef __WIN64__
4581  #define WIN_DEFAULT_USER_HANDLE IntToPtr( -1 )
4582 #else
4583  #define WIN_DEFAULT_USER_HANDLE ( HANDLE ) -1
4584 #endif /* 32- vs. 64-bit Windows */
4585 
4586 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
4587 static int getFolderPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
4588  IN_LENGTH_SHORT const int pathMaxLen,
4589  OUT_LENGTH_SHORT_Z int *pathLen )
4590  {
4591  typedef HRESULT ( WINAPI *SHGETFOLDERPATH )( HWND hwndOwner,
4592  int nFolder, HANDLE hToken,
4593  DWORD dwFlags, LPTSTR lpszPath );
4594  SHGETFOLDERPATH pSHGetFolderPath;
4595  const int osMajorVersion = getSysVar( SYSVAR_OSMAJOR );
4596  BOOLEAN gotPath = FALSE;
4597 
4598  assert( isWritePtr( path, pathMaxLen ) );
4599  assert( isWritePtr( pathLen, sizeof( int ) ) );
4600 
4601  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
4602 
4603  /* Clear return value */
4604  memset( path, 0, min( 16, pathMaxLen ) );
4605  *pathLen = 0;
4606 
4607  /* SHGetFolderPath() doesn't have an explicit buffer-size parameter to
4608  pass to the function, it always assumes a buffer of at least MAX_PATH
4609  bytes, so before we can call it we have to ensure that we've got at
4610  least this much room in the output buffer */
4611  REQUIRES( pathMaxLen >= MAX_PATH );
4612 
4613  /* Build the path to the configuration file if necessary. We can't
4614  (safely) use an opportunistic GetModuleHandle() before the
4615  DynamicLoad() for this because the code that originally loaded the
4616  DLL might do a DynamicUnload() in another thread, causing the library
4617  to be removed from under us. In any case DynamicLoad() does this for
4618  us, merely incrementing the reference count if the DLL is already
4619  loaded */
4620  if( osMajorVersion <= 4 )
4621  {
4622  HINSTANCE hComCtl32, hSHFolder;
4623 
4624  /* Try and find the location of the closest thing that Windows has
4625  to a home directory. This is a bit of a problem function in that
4626  both the function name and parameters have changed over time, and
4627  it's only included in pre-Win2K versions of the OS via a kludge
4628  DLL that takes the call and redirects it to the appropriate
4629  function anderswhere. Under certain (very unusual) circumstances
4630  this kludge can fail if shell32.dll and comctl32.dll aren't
4631  mapped into the process' address space yet, so we have to check
4632  for the presence of these DLLs in memory as well as for the
4633  successful load of the kludge DLL. In addition the function name
4634  changed yet again for Vista to SHGetKnownFolderPath(), but the
4635  existing SHGetFolderPath() is provided as a wrapper for
4636  SHGetKnownFolderPath() so we use that in all cases to keep
4637  things simple. Finally, the use of the CSIDL_FLAG_CREATE flag
4638  can cause performance issues if it requires groping around on a
4639  network (for example due to having your profile folder redirected
4640  to a network share that's offline), but this is fairly uncommon
4641  so it shouldn't be a problem */
4642  hComCtl32 = DynamicLoad( "ComCtl32.dll" );
4643  if( hComCtl32 != NULL )
4644  {
4645  if( ( hSHFolder = DynamicLoad( "SHFolder.dll" ) ) != NULL )
4646  {
4647  pSHGetFolderPath = ( SHGETFOLDERPATH ) \
4648  GetProcAddress( hSHFolder, "SHGetFolderPathA" );
4649  if( pSHGetFolderPath != NULL && \
4650  pSHGetFolderPath( NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE,
4651  NULL, SHGFP_TYPE_CURRENT, path ) == S_OK )
4652  gotPath = TRUE;
4653  DynamicUnload( hSHFolder );
4654  }
4655  DynamicUnload( hComCtl32 );
4656  }
4657  }
4658  else
4659  {
4660  const int osMinorVersion = getSysVar( SYSVAR_OSMINOR );
4661  const BOOLEAN isXPOrNewer = ( osMajorVersion > 5 || \
4662  ( osMajorVersion == 5 && \
4663  osMinorVersion >= 1 ) ) ? TRUE : FALSE;
4664  char defaultUserPath[ MAX_PATH + 16 ];
4665  BOOLEAN isDefaultUserPath = FALSE;
4666  HINSTANCE hShell32;
4667 
4668  /* Try and find the location of the closest thing that Windows has
4669  to a home directory. Note that this call can fail in nonobvious
4670  ways in specific situations such as when we're being run as a
4671  service (with no logged-on user) and so SHGetFolderPath() has no
4672  user profile to access, which means it returns the default-user
4673  profile. In this case we don't have privileges to access it, and
4674  the CreateDirectory() call that follows will fail. There's no
4675  programmatic way that we can address this here since it's
4676  something caused by the calling application that we're part of,
4677  the caller can take various steps such as explicitly calling
4678  LoadUserProfile() to ensure that there's a user profile set when
4679  SHGetFolderPath() gets called, but LoadUserProfile() in turn
4680  requires admin or LocalSystem privs. The best that we can do
4681  here is to get the path for the default-user profile (which is
4682  only possible for WinXP or newer) and if that matches the path
4683  that the standard SHGetFolderPath() returned, record a diagnostic
4684  and return an error, since the attempt to create a subdirectory
4685  in the default-user path below will fail (and even if it doesn't
4686  fail it's not safe to continue with it since whatever we store
4687  there will be replicated for any new user that logs on) */
4688  hShell32 = DynamicLoad( "Shell32.dll" );
4689  if( hShell32 != NULL )
4690  {
4691  pSHGetFolderPath = ( SHGETFOLDERPATH ) \
4692  GetProcAddress( hShell32, "SHGetFolderPathA" );
4693  if( pSHGetFolderPath != NULL )
4694  {
4695  if( pSHGetFolderPath( NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE,
4696  NULL, SHGFP_TYPE_CURRENT, path ) == S_OK )
4697  gotPath = TRUE;
4698  if( gotPath && isXPOrNewer && \
4699  pSHGetFolderPath( NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE,
4700  WIN_DEFAULT_USER_HANDLE,
4701  SHGFP_TYPE_CURRENT,
4702  defaultUserPath ) == S_OK )
4703  {
4704  if( !strcmp( path, defaultUserPath ) )
4705  isDefaultUserPath = TRUE;
4706  }
4707  }
4708  DynamicUnload( hShell32 );
4709  }
4710  if( isDefaultUserPath )
4711  {
4712  /* We've ended up with the profile for the default user, which
4713  we can't store per-user configuration data in, warn the
4714  caller (if possible) and exit */
4715  DEBUG_PRINT(( "No Windows user profile available, is this "
4716  "application running as a service or using "
4717  "impersonation?" ));
4718  return( CRYPT_ERROR_OPEN );
4719  }
4720  }
4721  if( gotPath )
4722  {
4723  *pathLen = strlen( path );
4724  if( *pathLen < 3 )
4725  {
4726  /* Under WinNT and Win2K the LocalSystem account doesn't have
4727  its own profile so SHGetFolderPath() will report success but
4728  return a zero-length path if we're running as a service. In
4729  this case we use the nearest equivalent that LocalSystem has
4730  to its own directories, which is the Windows directory. This
4731  is safe because LocalSystem always has permission to write
4732  there */
4733  if( !GetWindowsDirectory( path, pathMaxLen - 8 ) )
4734  *path = '\0';
4735  *pathLen = strlen( path );
4736  }
4737  return( CRYPT_OK );
4738  }
4739 
4740  /* We didn't get the folder path, fall back to dumping data in the
4741  Windows directory. This will probably fail on systems where the user
4742  doesn't have privs to write there but if SHGetFolderPath() fails it's
4743  an indication that something's wrong anyway.
4744 
4745  If this too fails, we fall back to the root dir. This has the same
4746  problems as the Windows directory for non-admin users, but we try it
4747  just in case the user manually copied the config there as a last
4748  resort */
4749  if( !GetWindowsDirectory( path, pathMaxLen - 8 ) )
4750  *path = '\0';
4751  *pathLen = strlen( path );
4752 
4753  return( CRYPT_OK );
4754  }
4755 #endif /* __WIN32__ */
4756 
4757 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
4758 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
4759  IN_LENGTH_SHORT const int pathMaxLen,
4760  OUT_LENGTH_SHORT_Z int *pathLen,
4761  IN_BUFFER( fileNameLen ) const char *fileName,
4762  IN_LENGTH_SHORT const int fileNameLen,
4763  IN_ENUM( BUILDPATH_OPTION ) \
4764  const BUILDPATH_OPTION_TYPE option )
4765  {
4766 #if defined( __WIN32__ )
4767  #if defined( __BORLANDC__ ) && ( __BORLANDC__ < 0x550 )
4768  #define HRESULT DWORD /* Not defined in older BC++ headers */
4769  #endif /* BC++ before 5.5 */
4770  char *pathPtr = path;
4771  int length, status;
4772 #elif defined( __WINCE__ )
4773  wchar_t pathBuffer[ _MAX_PATH + 8 ], *pathPtr = pathBuffer;
4774  BOOLEAN gotPath = FALSE;
4775  int length;
4776 #endif /* Win32 vs. WinCE */
4777 
4778  assert( isWritePtr( path, pathMaxLen ) );
4779  assert( isWritePtr( pathLen, sizeof( int ) ) );
4780  assert( ( option == BUILDPATH_RNDSEEDFILE ) || \
4781  isReadPtr( fileName, fileNameLen ) );
4782 
4783  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
4784  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
4785  option == BUILDPATH_GETPATH ) && fileName != NULL && \
4786  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
4787  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
4788  fileNameLen == 0 ) );
4789 
4790  /* Make sure that the open fails if we can't build the path */
4791  *path = '\0';
4792 
4793 #if defined( __WIN32__ )
4794  /* Get the path to the user data folder/directory */
4795  status = getFolderPath( path, pathMaxLen, &length );
4796  if( cryptStatusError( status ) )
4797  return( status );
4798  if( length + 16 >= pathMaxLen )
4799  return( CRYPT_ERROR_OVERFLOW );
4800  strlcpy_s( pathPtr + length, pathMaxLen - length, "\\cryptlib" );
4801 #elif defined( __WINCE__ )
4802  if( SHGetSpecialFolderPath( NULL, pathPtr, CSIDL_APPDATA, TRUE ) || \
4803  SHGetSpecialFolderPath( NULL, pathPtr, CSIDL_PERSONAL, TRUE ) )
4804  {
4805  /* We have to check for the availability of two possible locations
4806  since some older PocketPC versions don't have CSIDL_APPDATA */
4807  gotPath = TRUE;
4808  }
4809  if( !gotPath )
4810  {
4811  /* This should never happen under WinCE since the get-path
4812  functionality is always available */
4813  wcscpy( pathPtr, L"\\Windows" );
4814  }
4815  length = wcslen( pathPtr );
4816 
4817  /* Make sure that the path buffer meets the minimum-length requirements.
4818  We have to check both that the Unicode version of the string fits
4819  into the Unicode path buffer and that the resulting ASCII-converted
4820  form fits into the output buffer */
4821  REQUIRES( ( length + 16 ) * sizeof( wchar_t ) <= _MAX_PATH && \
4822  length + 16 <= pathMaxLen );
4823 
4824  wcscat( pathPtr, L"\\cryptlib" );
4825 #endif /* Win32 vs. WinCE */
4826 
4827  /* If we're being asked to create the cryptlib directory and it doesn't
4828  already exist, create it now */
4829  if( ( option == BUILDPATH_CREATEPATH ) && \
4830  GetFileAttributes( pathPtr ) == 0xFFFFFFFFUL )
4831  {
4832  void *aclInfo = NULL;
4833  BOOLEAN retVal = TRUE;
4834 
4835  if( !getSysVar( SYSVAR_ISWIN95 ) && \
4836  ( aclInfo = initACLInfo( FILE_ALL_ACCESS ) ) == NULL )
4837  retVal = FALSE;
4838  else
4839  retVal = CreateDirectory( pathPtr, getACLInfo( aclInfo ) );
4840  freeACLInfo( aclInfo );
4841  if( !retVal )
4842  return( CRYPT_ERROR_OPEN );
4843  }
4844 #if defined( __WINCE__ )
4845  unicodeToAscii( path, pathMaxLen, pathPtr, wcslen( pathPtr ) + 1 );
4846 #endif /* __WINCE__ */
4847 
4848  /* Add the filename to the path */
4849  strlcat_s( path, pathMaxLen, "\\" );
4850  return( appendFilename( path, pathMaxLen, pathLen, fileName,
4851  fileNameLen, option ) );
4852  }
4853 
4854 /****************************************************************************
4855 * *
4856 * Xilinx XMK *
4857 * *
4858 ****************************************************************************/
4859 
4860 #elif defined( __XMK__ )
4861 
4862 /* Open/close a file stream */
4863 
4864 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
4865 int sFileOpen( OUT STREAM *stream, IN_STRING const char *fileName,
4866  IN_FLAGS( FILE ) const int mode )
4867  {
4868  static const int modes[] = { MFS_MODE_READ, MFS_MODE_READ,
4869  MFS_MODE_CREATE, MFS_MODE_WRITE };
4870  int openMode;
4871 
4872  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4873  assert( isReadPtr( fileName, 2 ) );
4874 
4875  REQUIRE( mode != 0 );
4876 
4877  /* Initialise the stream structure */
4878  memset( stream, 0, sizeof( STREAM ) );
4879  stream->type = STREAM_TYPE_FILE;
4880  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
4881  stream->flags = STREAM_FLAG_READONLY;
4882  openMode = modes[ mode & FILE_FLAG_RW_MASK ];
4883 
4884  /* If we're trying to read from the file, check whether it exists */
4885  if( ( mode & FILE_FLAG_READ ) && mfs_exists_file( fileName ) != 1 )
4886  return( CRYPT_ERROR_NOTFOUND );
4887 
4888  /* Try and open the file */
4889  if( ( stream->fd = mfs_file_open( fileName, openMode ) ) < 0 )
4890  return( CRYPT_ERROR_OPEN );
4891 
4892  return( CRYPT_OK );
4893  }
4894 
4895 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
4896 int sFileClose( INOUT STREAM *stream )
4897  {
4898  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4899 
4900  REQUIRES( stream->type == STREAM_TYPE_FILE );
4901 
4902  /* Close the file and clear the stream structure */
4903  mfs_file_close( stream->fd );
4904  zeroise( stream, sizeof( STREAM ) );
4905 
4906  return( CRYPT_OK );
4907  }
4908 
4909 /* Read/write a block of data from/to a file stream */
4910 
4911 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
4912 int fileRead( INOUT STREAM *stream,
4913  OUT_BUFFER( length, *bytesRead ) void *buffer,
4914  IN_LENGTH const int length,
4915  OUT_LENGTH_Z int *bytesRead )
4916  {
4917  int byteCount;
4918 
4919  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4920  assert( isWritePtr( buffer, length ) );
4921  assert( isWritePtr( bytesRead, sizeof( int ) ) );
4922 
4923  REQUIRES( stream->type == STREAM_TYPE_FILE );
4924  REQUIRES( length > 0 && length < MAX_INTLENGTH );
4925 
4926  /* Clear return value */
4927  *bytesRead = 0;
4928 
4929  if( ( byteCount = mfs_file_read( stream->fd, buffer, length ) ) < 0 )
4930  return( sSetError( stream, CRYPT_ERROR_READ ) );
4931  *bytesRead = byteCount;
4932 
4933  return( CRYPT_OK );
4934  }
4935 
4936 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
4937 int fileWrite( INOUT STREAM *stream,
4938  IN_BUFFER( length ) const void *buffer,
4939  IN_LENGTH const int length )
4940  {
4941  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4942  assert( isReadPtr( buffer, length ) );
4943 
4944  REQUIRES( stream->type == STREAM_TYPE_FILE );
4945  REQUIRES( length > 0 && length < MAX_INTLENGTH );
4946 
4947  if( mfs_file_write( stream->fd, buffer, length ) < 0 )
4948  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
4949  return( CRYPT_OK );
4950  }
4951 
4952 /* Commit data in a file stream to backing storage */
4953 
4954 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
4955 int fileFlush( INOUT STREAM *stream )
4956  {
4957  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4958 
4959  REQUIRES( stream->type == STREAM_TYPE_FILE );
4960 
4961  /* Since the backing store is flash memory and writing simply copies it
4962  to flash, there's no real way to flush data to disk */
4963  return( CRYPT_OK );
4964  }
4965 
4966 /* Change the read/write position in a file */
4967 
4968 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
4969 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
4970  {
4971  assert( isWritePtr( stream, sizeof( STREAM ) ) );
4972 
4973  REQUIRES( stream->type == STREAM_TYPE_FILE );
4974  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
4975 
4976  /* MFS doesn't support any type of writing other than appending to the
4977  end of the file, so if we try and seek in a non-readonly file we
4978  return an error */
4979  if( !( stream->flags & STREAM_FLAG_READONLY ) )
4980  {
4981  assert( DEBUG_WARN );
4982  return( CRYPT_ERROR_WRITE );
4983  }
4984 
4985  if( mfs_file_lseek( stream->fd, position, MFS_SEEK_SET ) < 0 )
4986  return( sSetError( stream, CRYPT_ERROR_READ ) );
4987  return( CRYPT_OK );
4988  }
4989 
4990 /* Check whether a file is writeable */
4991 
4993 BOOLEAN fileReadonly( IN_STRING const char *fileName )
4994  {
4995  assert( isReadPtr( fileName, 2 ) );
4996 
4997  /* All non-ROM filesystems are writeable under MFS, in theory a ROM-based
4998  FS would be non-writeable but there's no way to tell whether the
4999  underlying system is ROM or RAM */
5000  return( FALSE );
5001  }
5002 
5003 /* File deletion functions: Wipe a file from the current position to EOF,
5004  and wipe and delete a file (although it's not terribly rigorous). Since
5005  MFS doesn't support any type of file writes except appending data to an
5006  existing file, the best that we can do is to simply delete the file
5007  without trying to overwrite it */
5008 
5009 STDC_NONNULL_ARG( ( 1 ) ) \
5010 void fileClearToEOF( const STREAM *stream )
5011  {
5012  assert( isWritePtr( stream, sizeof( STREAM ) ) );
5013 
5014  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
5015 
5016  return;
5017  }
5018 
5019 STDC_NONNULL_ARG( ( 1 ) ) \
5020 void fileErase( IN_STRING const char *fileName )
5021  {
5022  STREAM stream;
5023 
5024  assert( isReadPtr( fileName, 2 ) );
5025 
5026  /* Delete the file */
5027  mfs_delete_file( fileName );
5028  }
5029 
5030 /* Build the path to a file in the cryptlib directory */
5031 
5032 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
5033 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
5034  IN_LENGTH_SHORT const int pathMaxLen,
5035  OUT_LENGTH_SHORT_Z int *pathLen,
5036  IN_BUFFER( fileNameLen ) const char *fileName,
5037  IN_LENGTH_SHORT const int fileNameLen,
5038  IN_ENUM( BUILDPATH_OPTION ) \
5039  const BUILDPATH_OPTION_TYPE option )
5040  {
5041  assert( isWritePtr( path, pathMaxLen ) );
5042  assert( isWritePtr( pathLen, sizeof( int ) ) );
5043  assert( isReadPtr( fileName, fileNameLen ) );
5044 
5045  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
5046  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
5047  option == BUILDPATH_GETPATH ) && fileName != NULL && \
5048  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
5049  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
5050  fileNameLen == 0 ) );
5051 
5052  /* Make sure that the open fails if we can't build the path */
5053  *path = '\0';
5054 
5055  /* Build the path to the configuration file if necessary */
5056  strlcpy_s( path, pathMaxLen, "/cryptlib/" );
5057 
5058  /* If we're being asked to create the cryptlib directory and it doesn't
5059  already exist, create it now */
5060  if( option == BUILDPATH_CREATEPATH && mfs_exists_file( path ) != 2 )
5061  {
5062  /* The directory doesn't exist, try and create it */
5063  if( mfs_create_dir( path ) <= 0 )
5064  return( CRYPT_ERROR_OPEN );
5065  }
5066 
5067  /* Add the filename to the path */
5068  return( appendFilename( path, pathMaxLen, pathLen, fileName,
5069  fileNameLen, option ) );
5070  }
5071 
5072 /****************************************************************************
5073 * *
5074 * Everything Else (Generic stdio) *
5075 * *
5076 ****************************************************************************/
5077 
5078 #else
5079 
5080 /* BC++ 3.1 is rather anal-retentive about not allowing extensions when in
5081  ANSI mode */
5082 
5083 #if defined( __STDC__ ) && ( __BORLANDC__ == 0x410 )
5084  #define fileno( filePtr ) ( ( filePtr )->fd )
5085 #endif /* BC++ 3.1 in ANSI mode */
5086 
5087 /* When checking whether a file is read-only we also have to check (via
5088  errno) to make sure that the file actually exists since the access check
5089  will return a false positive for a nonexistant file */
5090 
5091 #if defined( __MSDOS16__ ) || defined( __OS2__ ) || defined( __WIN16__ )
5092  #include <errno.h>
5093 #endif /* __MSDOS16__ || __OS2__ || __WIN16__ */
5094 
5095 /* Some OS'es don't define W_OK for the access check */
5096 
5097 #ifndef W_OK
5098  #define W_OK 2
5099 #endif /* W_OK */
5100 
5101 /* Watcom C under DOS supports file-time access via DOS functions */
5102 
5103 #if defined( __WATCOMC__ ) && defined( __DOS__ )
5104  #include <dos.h>
5105 
5106  struct ftime {
5107  unsigned short ft_tsec : 5; /* Two seconds */
5108  unsigned short ft_min : 6; /* Minutes */
5109  unsigned short ft_hour : 5; /* Hours */
5110  unsigned short ft_day : 5; /* Days */
5111  unsigned short ft_month : 4; /* Months */
5112  unsigned short ft_year : 7; /* Year - 1980 */
5113  };
5114 #endif /* Watcom C under DOS */
5115 
5116 /* Extra system-specific includes */
5117 
5118 #ifdef __WIN16__
5119  #include <direct.h>
5120 #endif /* Win16 */
5121 
5122 /* Open/close a file stream */
5123 
5125 int sFileOpen( OUT STREAM *stream, IN_STRING const char *fileName,
5126  IN_FLAGS( FILE ) const int mode )
5127  {
5128  static const char *modes[] = { MODE_READ, MODE_READ,
5130  const char *openMode;
5131 
5132  assert( isWritePtr( stream, sizeof( STREAM ) ) );
5133  assert( isReadPtr( fileName, 2 ) );
5134 
5135  REQUIRES( mode != 0 );
5136 
5137  /* Initialise the stream structure */
5138  memset( stream, 0, sizeof( STREAM ) );
5139  stream->type = STREAM_TYPE_FILE;
5140  if( ( mode & FILE_FLAG_RW_MASK ) == FILE_FLAG_READ )
5141  stream->flags = STREAM_FLAG_READONLY;
5142  openMode = modes[ mode & FILE_FLAG_RW_MASK ];
5143 
5144  /* If we're trying to write to the file, check whether we've got
5145  permission to do so */
5146  if( ( mode & FILE_FLAG_WRITE ) && fileReadonly( fileName ) )
5147  return( CRYPT_ERROR_PERMISSION );
5148 
5149  /* Try and open the file */
5150  stream->filePtr = fopen( fileName, openMode );
5151 #if defined( __MSDOS16__ ) || defined( __WIN16__ ) || defined( __WINCE__ ) || \
5152  defined( __OS2__ ) || defined( __SYMBIAN32__ )
5153  if( stream->filePtr == NULL )
5154  {
5155  /* The open failed, determine whether it was because the file doesn't
5156  exist or because we can't use that access mode */
5157  return( ( access( fileName, 0 ) < 0 ) ? \
5158  CRYPT_ERROR_NOTFOUND : CRYPT_ERROR_OPEN );
5159  }
5160 #elif defined( __TANDEMNSK__ )
5161  if( stream->filePtr == NULL )
5162  {
5163  return( ( errno == ENOENT ) ? \
5164  CRYPT_ERROR_NOTFOUND : CRYPT_ERROR_OPEN );
5165  }
5166 #else
5167  #error Need to add file open error-handling
5168 #endif /* OS-specific file open error-handling */
5169 
5170  return( CRYPT_OK );
5171  }
5172 
5174 int sFileClose( INOUT STREAM *stream )
5175  {
5176  assert( isWritePtr( stream, sizeof( STREAM ) ) );
5177 
5178  REQUIRES( stream->type == STREAM_TYPE_FILE );
5179 
5180  /* Close the file and clear the stream structure */
5181  fclose( stream->filePtr );
5182  zeroise( stream, sizeof( STREAM ) );
5183 
5184  return( CRYPT_OK );
5185  }
5186 
5187 /* Read/write a block of data from/to a file stream */
5188 
5190 int fileRead( INOUT STREAM *stream,
5191  OUT_BUFFER( length, *bytesRead ) void *buffer,
5192  IN_LENGTH const int length,
5193  OUT_LENGTH_Z int *bytesRead )
5194  {
5195  int byteCount;
5196 
5197  assert( isWritePtr( stream, sizeof( STREAM ) ) );
5198  assert( isWritePtr( buffer, length ) );
5199  assert( isWritePtr( bytesRead, sizeof( int ) ) );
5200 
5201  REQUIRES( stream->type == STREAM_TYPE_FILE );
5202  REQUIRES( length > 0 && length < MAX_INTLENGTH );
5203 
5204  /* Clear return value */
5205  *bytesRead = 0;
5206 
5207  if( ( byteCount = fread( buffer, 1, length, stream->filePtr ) ) < length && \
5208  ( byteCount < 0 || ferror( stream->filePtr ) ) )
5209  return( sSetError( stream, CRYPT_ERROR_READ ) );
5210  *bytesRead = byteCount;
5211 
5212  return( CRYPT_OK );
5213  }
5214 
5215 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
5216 int fileWrite( INOUT STREAM *stream,
5217  IN_BUFFER( length ) const void *buffer,
5218  IN_LENGTH const int length )
5219  {
5220  assert( isWritePtr( stream, sizeof( STREAM ) ) );
5221  assert( isReadPtr( buffer, length ) );
5222 
5223  REQUIRES( stream->type == STREAM_TYPE_FILE );
5224  REQUIRES( length > 0 && length < MAX_INTLENGTH );
5225 
5226  if( fwrite( buffer, 1, length, stream->filePtr ) != length )
5227  return( sSetError( stream, CRYPT_ERROR_WRITE ) );
5228  return( CRYPT_OK );
5229  }
5230 
5231 /* Commit data in a file stream to backing storage */
5232 
5233 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
5234 int fileFlush( INOUT STREAM *stream )
5235  {
5236  assert( isWritePtr( stream, sizeof( STREAM ) ) );
5237 
5238  REQUIRES( stream->type == STREAM_TYPE_FILE );
5239 
5240  return( fflush( stream->filePtr ) == 0 ? CRYPT_OK : CRYPT_ERROR_WRITE );
5241  }
5242 
5243 /* Change the read/write position in a file */
5244 
5245 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
5246 int fileSeek( INOUT STREAM *stream, IN_LENGTH_Z const long position )
5247  {
5248  assert( isWritePtr( stream, sizeof( STREAM ) ) );
5249 
5250  REQUIRES( stream->type == STREAM_TYPE_FILE );
5251  REQUIRES( position >= 0 && position < MAX_INTLENGTH );
5252 
5253  if( fseek( stream->filePtr, position, SEEK_SET ) )
5254  return( sSetError( stream, CRYPT_ERROR_READ ) );
5255  return( CRYPT_OK );
5256  }
5257 
5258 /* Check whether a file is writeable */
5259 
5261 BOOLEAN fileReadonly( IN_STRING const char *fileName )
5262  {
5263 #if defined( __MSDOS16__ ) || defined( __WIN16__ ) || defined( __OS2__ ) || \
5264  defined( __SYMBIAN32__ ) || defined( __BEOS__ )
5265  if( access( fileName, W_OK ) < 0 && errno != ENOENT )
5266  return( TRUE );
5267 #elif defined( __TANDEMNSK__ )
5268  FILE *filePtr;
5269 
5270  assert( isReadPtr( fileName, 2 ) );
5271 
5272  if( ( filePtr = fopen( fileName, MODE_READWRITE ) ) == NULL )
5273  {
5274  if( errno == EACCES )
5275  return( TRUE );
5276  }
5277  else
5278  fclose( filePtr );
5279 #else
5280  #error Need to add file accessibility call
5281 #endif /* OS-specific file accessibility check */
5282 
5283  return( FALSE );
5284  }
5285 
5286 /* File deletion functions: Wipe a file from the current position to EOF,
5287  and wipe and delete a file (although it's not terribly rigorous).
5288  Vestigia nulla retrorsum */
5289 
5290 static void eraseFile( const STREAM *stream, long position, long length )
5291  {
5292  int fileHandle = fileno( stream->filePtr );
5293 
5294  assert( isReadPtr( stream, sizeof( STREAM ) ) );
5295 
5296  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
5297  REQUIRES_V( position >= 0 && position < MAX_INTLENGTH );
5298  REQUIRES_V( length >= 0 && length < MAX_INTLENGTH );
5299  /* May be zero if a file-open failed leaving a zero-length
5300  file */
5301 
5302  /* Wipe everything past the current position in the file */
5303  while( length > 0 )
5304  {
5306  BYTE buffer[ ( BUFSIZ * 2 ) + 8 ];
5307  int bytesToWrite = min( length, BUFSIZ * 2 );
5308 
5309  /* We need to make sure that we fill the buffer with random data for
5310  each write, otherwise compressing filesystems will just compress
5311  it to nothing */
5312  setMessageData( &msgData, buffer, bytesToWrite );
5314  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
5315  if( fwrite( buffer, 1, bytesToWrite, stream->filePtr ) == 0 )
5316  break; /* An error occurred while writing, exit */
5317  length -= bytesToWrite;
5318  }
5319  fflush( stream->filePtr );
5320 
5321  /* Truncate the file and if we're erasing the entire file, reset the
5322  timestamps. This is only possible through a file handle on some
5323  systems, on others the caller has to do it via the filename */
5324 #if defined( __AMIGA__ )
5325  SetFileSize( fileHandle, OFFSET_BEGINNING, position );
5326 #elif defined( __MSDOS16__ ) || defined( __MSDOS32__ )
5327  chsize( fileHandle, position );
5328 #elif defined( __OS2__ )
5329  DosSetFileSize( fileHandle, position );
5330 #elif defined( __WIN16__ )
5331  _chsize( fileHandle, position );
5332 #endif /* OS-specific size mangling */
5333  if( position <= 0 )
5334  {
5335 #if defined( __MSDOS16__ ) || defined( __MSDOS32__ )
5336  struct ftime fileTime;
5337 #endif /* OS-specific variable declarations */
5338 
5339 #if defined( __MSDOS16__ ) || defined( __MSDOS32__ )
5340  memset( &fileTime, 0, sizeof( struct ftime ) );
5341  #if defined( __WATCOMC__ )
5342  _dos_setftime( fileHandle, \
5343  *( ( unsigned short * ) &fileTime + 1 ), \
5344  *( ( unsigned short * ) &fileTime ) );
5345  #else
5346  setftime( fileHandle, &fileTime );
5347  #endif /* __WATCOMC__ */
5348 #endif /* OS-specific date mangling */
5349  }
5350  }
5351 
5352 STDC_NONNULL_ARG( ( 1 ) ) \
5353 void fileClearToEOF( const STREAM *stream )
5354  {
5355  long position, length;
5356 
5357  assert( isReadPtr( stream, sizeof( STREAM ) ) );
5358 
5359  REQUIRES_V( stream->type == STREAM_TYPE_FILE );
5360 
5361  /* Wipe everything past the current position in the file */
5362  position = ftell( stream->filePtr );
5363  fseek( stream->filePtr, 0, SEEK_END );
5364  length = ftell( stream->filePtr ) - position;
5365  fseek( stream->filePtr, position, SEEK_SET );
5366  eraseFile( stream, position, length );
5367  }
5368 
5369 STDC_NONNULL_ARG( ( 1 ) ) \
5370 void fileErase( IN_STRING const char *fileName )
5371  {
5372  STREAM stream;
5373 #if defined( __AMIGA__ )
5374  struct DateStamp dateStamp;
5375 #elif defined( __OS2__ )
5376  FILESTATUS info;
5377 #elif defined( __WIN16__ )
5378  HFILE hFile;
5379 #endif /* OS-specific variable declarations */
5380  int length, status;
5381 
5382  assert( isReadPtr( fileName, 2 ) );
5383 
5384  /* Try and open the file so that we can erase it. If this fails, the
5385  best that we can do is a straight unlink */
5386  status = sFileOpen( &stream, fileName,
5387  FILE_FLAG_READ | FILE_FLAG_WRITE | \
5388  FILE_FLAG_EXCLUSIVE_ACCESS );
5389  if( cryptStatusError( status ) )
5390  {
5391  if( status != CRYPT_ERROR_NOTFOUND )
5392  remove( fileName );
5393  return;
5394  }
5395 
5396  /* Determine the size of the file and erase it */
5397  fseek( stream.filePtr, 0, SEEK_END );
5398  length = ( int ) ftell( stream.filePtr );
5399  fseek( stream.filePtr, 0, SEEK_SET );
5400  eraseFile( &stream, 0, length );
5401 
5402  /* Truncate the file to 0 bytes if we couldn't do it in eraseFile, reset
5403  the time stamps, and delete it */
5404  sFileClose( &stream );
5405 #if defined( __AMIGA__ )
5406  memset( dateStamp, 0, sizeof( struct DateStamp ) );
5407  SetFileDate( fileName, &dateStamp );
5408 #elif defined( __OS2__ )
5409  DosQueryPathInfo( ( PSZ ) fileName, FIL_STANDARD, &info, sizeof( info ) );
5410  memset( &info.fdateLastWrite, 0, sizeof( info.fdateLastWrite ) );
5411  memset( &info.ftimeLastWrite, 0, sizeof( info.ftimeLastWrite ) );
5412  memset( &info.fdateLastAccess, 0, sizeof( info.fdateLastAccess ) );
5413  memset( &info.ftimeLastAccess, 0, sizeof( info.ftimeLastAccess ) );
5414  memset( &info.fdateCreation, 0, sizeof( info.fdateCreation ) );
5415  memset( &info.ftimeCreation, 0, sizeof( info.ftimeCreation ) );
5416  DosSetPathInfo( ( PSZ ) fileName, FIL_STANDARD, &info, sizeof( info ), 0 );
5417 #elif defined( __WIN16__ )
5418  /* Under Win16 we can't really do anything without resorting to MSDOS int
5419  21h calls, the best we can do is truncate the file using _lcreat() */
5420  hFile = _lcreat( fileName, 0 );
5421  if( hFile != HFILE_ERROR )
5422  _lclose( hFile );
5423 #endif /* OS-specific size and date-mangling */
5424 
5425  /* Finally, delete the file */
5426  remove( fileName );
5427  }
5428 
5429 /* Build the path to a file in the cryptlib directory */
5430 
5431 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
5432 int fileBuildCryptlibPath( OUT_BUFFER( pathMaxLen, *pathLen ) char *path,
5433  IN_LENGTH_SHORT const int pathMaxLen,
5434  OUT_LENGTH_SHORT_Z int *pathLen,
5435  IN_BUFFER( fileNameLen ) const char *fileName,
5436  IN_LENGTH_SHORT const int fileNameLen,
5437  IN_ENUM( BUILDPATH_OPTION ) \
5438  const BUILDPATH_OPTION_TYPE option )
5439  {
5440 #if defined( __OS2__ )
5441  ULONG aulSysInfo[ 1 ] = { 0 };
5442 #elif defined( __WIN16__ )
5443  BOOLEAN gotPath = FALSE;
5444 #endif /* OS-specific info */
5445 
5446  assert( isWritePtr( path, pathMaxLen ) );
5447  assert( isWritePtr( pathLen, sizeof( int ) ) );
5448  assert( isReadPtr( fileName, fileNameLen ) );
5449 
5450  REQUIRES( pathMaxLen > 32 && pathMaxLen < MAX_INTLENGTH );
5451  REQUIRES( ( ( option == BUILDPATH_CREATEPATH || \
5452  option == BUILDPATH_GETPATH ) && fileName != NULL && \
5453  fileNameLen > 0 && fileNameLen < MAX_INTLENGTH ) || \
5454  ( option == BUILDPATH_RNDSEEDFILE && fileName == NULL && \
5455  fileNameLen == 0 ) );
5456 
5457  /* Make sure that the open fails if we can't build the path */
5458  *path = '\0';
5459 
5460  /* Build the path to the configuration file if necessary */
5461 #if defined( __MSDOS__ )
5462  strlcpy_s( path, pathMaxLen, "c:/dos/" );
5463  return( appendFilename( path, pathMaxLen, pathLen, fileName,
5464  fileNameLen, option ) );
5465 #elif defined( __WIN16__ )
5466  GetWindowsDirectory( path, pathMaxLen - 32 );
5467  strlcat_s( path, pathMaxLen, "\\cryptlib" );
5468 
5469  /* If we're being asked to create the cryptlib directory and it doesn't
5470  already exist, create it now. There's no way to check for its
5471  existence in advance, so we try and create it unconditionally but
5472  ignore EACCESS errors */
5473  if( ( option == BUILDPATH_CREATEPATH ) && \
5474  !_mkdir( path ) && ( errno != EACCES ) )
5475  return( CRYPT_ERROR_OPEN );
5476 
5477  /* Add the filename to the path */
5478  strlcat_s( path, pathMaxLen, "\\" );
5479  return( appendFilename( path, pathMaxLen, pathLen, fileName,
5480  fileNameLen, option ) );
5481 #elif defined( __OS2__ )
5482  DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, ( PVOID ) aulSysInfo,
5483  sizeof( ULONG ) ); /* Get boot drive info */
5484  if( *aulSysInfo == 0 )
5485  return( CRYPT_ERROR_OPEN ); /* No boot drive info */
5486  path[ 0 ] = *aulSysInfo + 'A' - 1;
5487  strlcpy_s( path + 1, pathMaxLen - 1, ":\\OS2\\" );
5488  return( appendFilename( path, pathMaxLen, pathLen, fileName,
5489  fileNameLen, option ) );
5490 #elif defined( __TANDEMNSK__ )
5491  strlcpy_s( path, pathMaxLen, "$system.system." );
5492  if( option == BUILDPATH_RNDSEEDFILE )
5493  strlcat_s( path, pathMaxLen, "randseed" );
5494  else
5495  strlcat_s( path, pathMaxLen, fileName );
5496  return( CRYPT_OK );
5497 #elif defined( __SYMBIAN32__ )
5498  strlcpy_s( path, pathMaxLen, "C:\\SYSTEM\\DATA\\" );
5499  return( appendFilename( path, pathMaxLen, pathLen, fileName,
5500  fileNameLen, option ) );
5501 #else
5502  #error Need to add function to build the config file path
5503 
5504  return( CRYPT_ERROR_OPEN );
5505 #endif /* OS-specific file path creation */
5506  }
5507 #endif /* OS-specific file stream handling */