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