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 * ) &paramBlock );
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