⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 str_file.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 5 页
字号:
/****************************************************************************
*																			*
*							File Stream I/O Functions						*
*						Copyright Peter Gutmann 1993-2003					*
*																			*
****************************************************************************/

#if defined( __UNIX__ ) && defined( __linux__ )
  /* In order for the fileReadonly() check to work we need to be able to
	 check errno, however for this to work the headers that specify that
	 threading is being used must be the first headers included
	 (specifically, the include order has to be pthread.h, unistd.h,
	 everything else) or errno.h, which is pulled in by stdlib.h, gets
	 set up as an extern int rather than a function */
  #include "crypt.h"
#endif /* Older Linux broken include-file dependencies */
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
  #include "stream.h"
#elif defined( INC_CHILD )
  #include "stream.h"
#else
  #include "misc/stream.h"
#endif /* Compiler-specific includes */
#if defined( __BEOS__ ) || defined( __SYMBIAN32__ ) || \
	defined( __TANDEMOSS__ ) || defined( __UNIX__ )
  #include <errno.h>
  #include <fcntl.h>
  #include <sys/types.h>
  #include <sys/file.h>
  #include <sys/stat.h>
  #if !( defined( __APPLE__ ) || defined( __BEOS__ ) || \
		 defined( __bsdi__ ) || defined( __CYGWIN__ ) || \
		 defined( __FreeBSD__ ) || defined( __hpux ) || \
		 defined( __linux__ ) || defined( _M_XENIX ) || \
		 defined( __MVS__ ) || defined( __OpenBSD__ ) || \
		 ( defined( sun ) && OSVERSION == 4 ) || \
		 defined ( __SYMBIAN32__ ) )
	#include <sys/mode.h>
  #endif /* Vaguely non-SYSV-ish systems */
  #include <unistd.h>
  #if defined( _AIX ) || defined( __alpha__ ) || defined( __BEOS__ ) || \
	  defined( __bsdi__ ) || defined( __FreeBSD__ ) || \
	  defined( __linux__ ) || defined( _MPRAS ) || defined( __MVS__ ) || \
	  defined( _M_XENIX ) || defined( __OpenBSD__ ) || \
	  defined( __osf__ ) || defined( __SCO_VERSION__ ) || defined( sun )
	#include <utime.h>			/* It's a SYSV thing... */
  #endif /* SYSV Unixen */
  #ifdef __APPLE__
	#include <sys/time.h>
  #endif /* OS X */
  #ifdef __CYGWIN__
	#include <sys/utime.h>
  #endif /* __CYGWIN__ */
  #if defined( _AIX ) || defined( __BEOS__ ) || defined( __CYGWIN__ ) || \
	  defined( __hpux ) || defined( _MPRAS ) || defined( __MVS__ ) || \
	  defined( _M_XENIX ) || defined( __SCO_VERSION__ ) || \
	  ( defined( sun ) && ( OSVERSION >= 5 ) )
	#define USE_FCNTL_LOCKING
    /* By default we try and use flock()-locking, if this isn't available we
	   fall back to fcntl() locking (see the long comment further on).
	   Actually Slowaris does have flock(), but there are lots of warnings
	   in the manpage about using it only on BSD platforms, and the result
	   won't work with any of the system libraries.  SunOS did support it
	   without any problems, it's only Slowaris that breaks it.  In addition
	   UnixWare (== SCO) supports something called flockfile() but this only
	   provides thread-level locking that isn't useful */
  #endif /* Some older SYSV-ish systems */
  #if ( defined( _M_XENIX ) && ( OSVERSION == 3 ) )
	#define ftruncate( a, b )	chsize( a, b )
  #endif /* SCO */
  #if defined( __CYGWIN__ )
	#define LOCK_SH		1
	#define LOCK_EX		2
	#define LOCK_NB		4
	#define LOCK_UN		8
  #endif /* Cygwin */
#elif defined( __AMIGA__ )
  #include <proto/dos.h>
#elif defined( __MSDOS16__ ) || defined( __WIN16__ )
  #include <io.h>
#elif defined( __OS2__ )
  #define INCL_DOSFILEMGR	/* DosQueryPathInfo(),DosSetFileSize(),DosSetPathInfo */
  #define INCL_DOSMISC		/* DosQuerySysInfo() */
  #include <os2.h>			/* FILESTATUS */
  #include <io.h>
#elif defined( __IBM4758__ )
  #include <scc_err.h>
  #include <scc_int.h>
#elif defined( __TANDEMNSK__ )
  #include <errno.h>
#elif defined( __MAC__ )
  #include <Script.h>
  #if defined __MWERKS__
    #pragma mpwc_relax off
    #pragma extended_errorcheck on
  #endif
#endif /* OS-specific includes and defines */

/* In order to get enhanced control over things like file security and 
   buffering we can't use stdio but have to rely on using OS-level file 
   routines, which is essential for working with things like ACL's for 
   sensitive files and forcing disk writes for files we want to erase.  
   Without the forced disk write the data in the cache doesn't get flushed 
   before the file delete request arrives, after which it's discarded rather 
   than being written, so the file never gets overwritten.  In addition some 
   embedded environments don't support stdio so we have to supply our own 
   alternatives.

   When implementing the following for new systems there are certain things
   that you need to ensure to guarantee error-free operation:

	- File permissions should be set as indicated by the file open flags.

	- File sharing controls (shared vs. exclusive access locks) should be
	  implemented.

	- If the file is locked for exclusive access, the open call should either
	  block until the lock is released (they're never held for more than a
	  fraction of a second) or return CRYPT_ERROR_TIMEOUT depending on how
	  the OS handles locks */

/****************************************************************************
*																			*
*							Windows File Stream Functions					*
*																			*
****************************************************************************/

#if defined( __WIN32__ )

/* File flags to use when accessing a file and attributes to use when
   creating a file.  For access we tell the OS that we'll be reading the 
   file sequentially, for creation we prevent the OS from groping around 
   inside the file */

#ifndef FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
  #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED	0x00002000
#endif /* VC++ <= 6.0 */
#define FILE_FLAGS			FILE_FLAG_SEQUENTIAL_SCAN
#define FILE_ATTRIBUTES		FILE_ATTRIBUTE_NOT_CONTENT_INDEXED

/* Older versions of the Windows SDK don't include the defines for system 
   directories so we define them ourselves if necesary */

#ifndef CSIDL_PERSONAL
  #define CSIDL_PERSONAL		0x05	/* 'My Documents' */
  #define CSIDL_APPDATA			0x1A	/* '<luser name>/Application Data' */
#endif /* !CSIDL_PERSONAL */
#ifndef CSIDL_FLAG_CREATE
  #define CSIDL_FLAG_CREATE		0x8000	/* Force directory creation */
#endif /* !CSIDL_FLAG_CREATE */
#ifndef SHGFP_TYPE_CURRENT
  #define SHGFP_TYPE_CURRENT	0
#endif /* !SHGFP_TYPE_CURRENT */

/* Check whether a user's SID is known to a server providing a network 
   share, so that we can set file ACLs based on it */

#define TOKEN_BUFFER_SIZE	256
#define UNI_BUFFER_SIZE		( 256 + _MAX_PATH )
#define PATH_BUFFER_SIZE	( _MAX_PATH + 16 )

static BOOLEAN checkUserKnown( const char *fileName )
	{
	HANDLE hToken;
	BYTE uniBuffer[ UNI_BUFFER_SIZE ], tokenBuffer[ TOKEN_BUFFER_SIZE ];
	char pathBuffer[ PATH_BUFFER_SIZE ], nameBuffer[ PATH_BUFFER_SIZE ];
	char domainBuffer[ PATH_BUFFER_SIZE ], *fileNamePtr;
    UNIVERSAL_NAME_INFO *nameInfo = ( UNIVERSAL_NAME_INFO * ) uniBuffer;
	TOKEN_USER *pTokenUser = ( TOKEN_USER * ) tokenBuffer;
	SID_NAME_USE eUse;
	BOOLEAN isMappedDrive = FALSE, tokenOK = FALSE, retVal;
	int uniBufSize = UNI_BUFFER_SIZE, nameBufSize = PATH_BUFFER_SIZE;
	int domainBufSize = PATH_BUFFER_SIZE, serverNameLength;

	assert( sizeof( UNIVERSAL_NAME_INFO ) + _MAX_PATH <= UNI_BUFFER_SIZE );

	/* Win95 doesn't have any ACL-based security, there's nothing to do */
	if( isWin95 )
		return( TRUE );

	/* Canonicalise the path name.  This turns relative paths into absolute 
	   ones and converts forward to backwards slashes.  The latter is
	   necessary because while the Windows filesystem functions will accept 
	   Unix-style forward slashes in paths, the WNetGetUniversalName() 
	   networking function doesn't */
	if( GetFullPathName( fileName, PATH_BUFFER_SIZE, pathBuffer, 
						 &fileNamePtr ) )
		fileName = pathBuffer;

	/* If the path is too short to contain a drive letter or UNC path, it 
	   must be local */
	if( strlen( fileName ) <= 2 )
		return( TRUE );

	/* If there's a drive letter present, check whether it's a local or
	   remote drive.  GetDriveType() is rather picky about what it'll accept 
	   so we have to extract just the drive letter from the path */
	if( fileName[ 1 ] == ':' )
		{
		char drive[ 8 ];

		memcpy( drive, fileName, 2 );
		drive[ 2 ] = '\0';
		if( GetDriveType( drive ) != DRIVE_REMOTE )
			/* It's a local drive, the user should be known */
			return( TRUE );
		isMappedDrive = TRUE;
		}
	else
		/* If it's not a UNC name, it's local (or something weird like a 
		   mapped web page to which we shouldn't be writing keys anyway) */
		if( memcmp( fileName, "\\\\", 2 ) )
			return( TRUE );

	/* If it's a mapped network drive, get the name in UNC form.  What to do
	   in case of failure is a bit tricky.  If we get here we know that it's 
	   a network share, but if there's some problem mapping it to a UNC (the 
	   usual reason for this will be that there's a problem with the network 
	   and the share is a cached remnant of a persistent connection), all we 
	   can do is fail safe and hope that the user is known */
	if( isMappedDrive )
		{
		typedef DWORD ( WINAPI *WNETGETUNIVERSALNAMEA )( LPCSTR lpLocalPath,
										DWORD dwInfoLevel, LPVOID lpBuffer,
										LPDWORD lpBufferSize );
		WNETGETUNIVERSALNAMEA pWNetGetUniversalNameA;
		HINSTANCE hMPR;
		BOOLEAN loadedMPR = FALSE, gotUNC = FALSE;

		if( ( hMPR = GetModuleHandle( "Mpr.dll" ) ) == NULL )
			{
		 	hMPR = LoadLibrary( "Mpr.dll" );
			loadedMPR = TRUE;
			}
		if( hMPR == NULL )
			/* Should never happen, we can't have a mapped network drive if
			   no network is available */
			return( TRUE );		/* Default fail-safe */

		/* Get the translated UNC name.  The UNIVERSAL_NAME_INFO struct is
		   one of those variable-length ones where the lpUniversalName 
		   member points to extra data stored off the end of the struct, so
		   we overlay it onto a much larger buffer */
		pWNetGetUniversalNameA = ( WNETGETUNIVERSALNAMEA ) \
								 GetProcAddress( hMPR, "WNetGetUniversalNameA" );
		if( pWNetGetUniversalNameA != NULL && \
			pWNetGetUniversalNameA( fileName, UNIVERSAL_NAME_INFO_LEVEL, 
									nameInfo, &uniBufSize ) == NO_ERROR )
			{
			fileName = nameInfo->lpUniversalName;
			gotUNC = TRUE;
			}
		if( loadedMPR )
			FreeLibrary( hMPR );
		if( !gotUNC )
			return( TRUE );		/* Default fail-safe */
		}
	assert( !memcmp( fileName, "\\\\", 2 ) );

	/* We've got the network share in UNC form, extract the server name.  If
	   for some reason the name is still an absolute path, the following will 
	   convert it to "x:\", which is fine */
	for( serverNameLength = 2; \
		 fileName[ serverNameLength ] && fileName[ serverNameLength ] != '\\'; \
		 serverNameLength++ );
	memmove( pathBuffer, fileName, serverNameLength );
	memcpy( pathBuffer + serverNameLength, "\\", 2 );

	/* Check whether the current user's SID is known to the server */
	if( OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken ) || \
		OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken ) )
		{
		DWORD cbTokenUser;

		tokenOK = GetTokenInformation( hToken, TokenUser, pTokenUser, 
									   TOKEN_BUFFER_SIZE, &cbTokenUser );
		CloseHandle( hToken );
		}
	if( !tokenOK )
		return( TRUE );			/* Default fail-safe */
	retVal = LookupAccountSid( pathBuffer, pTokenUser->User.Sid, 
							   nameBuffer, &nameBufSize, 
							   domainBuffer, &domainBufSize, &eUse );
	if( !retVal && GetLastError() == ERROR_NONE_MAPPED )
		/* The user with this SID isn't known to the server */
		return( FALSE );

	/* Either the user is known to the server or it's a fail-safe */
	return( TRUE );
	}

/* Open/close a file stream */

int sFileOpen( STREAM *stream, const char *fileName, const int mode )
	{
	HANDLE hFile;
	UINT uErrorMode;
	void *aclInfo = NULL;
	int status = CRYPT_OK;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( fileName != NULL );
	assert( mode != 0 );

	/* Initialise the stream structure */
	memset( stream, 0, sizeof( STREAM ) );
	stream->type = STREAM_TYPE_FILE;
	if( ( mode & FILE_RW_MASK ) == FILE_READ )
		stream->flags = STREAM_FLAG_READONLY;

	/* Don't allow escapes to disable path parsing, and make sure that the 
	   path has a sensible length.  The latter is both to avoid possible 
	   overflows in the Windows filesystem functions and because some of the 
	   filesystem checks need to copy the name into a fixed-size temporary 
	   buffer when they canonicalise it */
	if( !strncmp( fileName, "\\\\?\\", 4 ) )
		return( CRYPT_ERROR_OPEN );
	if( strlen( fileName ) > _MAX_PATH )
		return( CRYPT_ERROR_OPEN );

	/* If we're creating the file and we don't want others to get to it, set
	   up the security attributes to reflect this if the OS supports it.  
	   Unfortunately creating the file with ACLs doesn't always work when 
	   the file is located on a network share because what's:

		create file, ACL = user SID access

	   on a local drive can become:

		create file, ACL = <unknown SID> access

	   on the network share if the user is accessing it as a member of a 
	   group and their individual SID isn't known to the server.  As a 
	   result, they can't read the file that they've just created.  To get 
	   around this, we need to perform an incredibly convoluted check (via
	   checkUserKnown()) to see whether the path is a network path and if 
	   so, if the user is known to the server providing the network share */
	if( !isWin95 && ( mode & FILE_WRITE ) && ( mode & FILE_PRIVATE ) && \
		checkUserKnown( fileName ) && \
		( aclInfo = initACLInfo( FILE_GENERIC_READ | \
								 FILE_GENERIC_WRITE ) ) == NULL )
		return( CRYPT_ERROR_OPEN );

	/* Check that the file isn't a special file type, for example a device
	   pseudo-file that can crash the system under Win95/98/ME/whatever */
	hFile = CreateFile( fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
						OPEN_EXISTING, FILE_FLAGS, NULL );
	if( hFile != INVALID_HANDLE_VALUE )
		{
		const DWORD type = GetFileType( hFile );

		CloseHandle( hFile );
		if( type != FILE_TYPE_DISK )
			{
			freeACLInfo( aclInfo );
			return( CRYPT_ERROR_OPEN );
			}
		}

	/* Try and open the file */
	uErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS );
	if( ( mode & FILE_RW_MASK ) == FILE_WRITE )
		{
		BOOLEAN isNetworkShare = FALSE;

		/* If we're creating the file, we need to remove any existing file
		   of the same name before we try and create a new one, otherwise
		   the OS will pick up the permissions for the existing file and
		   apply them to the new one.  This is safe because if an attacker
		   tries to slip in a wide-open file between the delete and the
		   create, we'll get a file-already-exists status returned that we
		   can trap and turn into an error */
		DeleteFile( fileName );
		stream->hFile = CreateFile( fileName, GENERIC_READ | GENERIC_WRITE, 0,
									getACLInfo( aclInfo ), CREATE_ALWAYS, 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -