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

📄 stream.c

📁 提供了很多种加密算法和CA认证及相关服务如CMP、OCSP等的开发
💻 C
📖 第 1 页 / 共 5 页
字号:
/****************************************************************************
*																			*
*							Stream I/O Functions							*
*						Copyright Peter Gutmann 1993-2002					*
*																			*
****************************************************************************/

#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 which 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 <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
  #include "stream.h"
#elif defined( INC_CHILD )
  #include "stream.h"
#else
  #include "keymgmt/stream.h"
#endif /* Compiler-specific includes */
#if defined( __UNIX__ )
  #include <errno.h>
  #include <fcntl.h>
  #include <sys/types.h>
  #include <sys/file.h>
  #include <sys/stat.h>
  #if !( ( defined( sun ) && OSVERSION == 4 ) || defined( __linux__ ) || \
		   defined( __bsdi__ ) || defined( __FreeBSD__ ) || \
		   defined( __OpenBSD__ ) || defined( __hpux ) || \
		   defined( _M_XENIX ) )
	#include <sys/mode.h>
  #endif /* SunOS || Linux || *BSD || Phux || SCO/UnixWare */
  #include <unistd.h>
  #if defined( sun ) || defined( _M_XENIX ) || defined( __SCO_VERSION__ ) || \
	  defined( __linux__ ) || defined( __osf__ ) || defined( __bsdi__ ) || \
	  defined( __FreeBSD__ ) || defined( __OpenBSD__ ) || defined( _AIX )
	#include <utime.h>			/* It's a SYSV thing... */
  #endif /* SYSV Unixen */
  #if ( defined( sun ) && ( OSVERSION >= 5 ) ) || \
	  defined( _M_XENIX ) || defined( __SCO_VERSION__ ) || \
	  defined( __hpux ) || defined( _AIX )
	#define flock( a, b )		/* No flock() */
    /* 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 which breaks it.  In addition
	   UnixWare (== SCO) supports something called flockfile() but this only
	   provides thread-level locking which isn't useful */
  #endif /* Slowaris || SCO/UnixWare || PHUX || Aches */
  #if ( defined( _M_XENIX ) && ( OSVERSION == 3 ) )
	#define ftruncate( a, b )	chsize( a, b )
  #endif /* SCO */
#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( __WIN32__ )
  /* The size of the buffer for Win32 ACLs */
  #define ACL_BUFFER_SIZE		1024
#elif defined( __IBM4758__ )
  #include <scc_err.h>
  #include <scc_int.h>
#elif defined( __TANDEM__ )
  #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 */

/* Some environments place severe restrictions on what can be done with file
   I/O, either having no filesystem at all or having one with characteristics
   which don't fit the stdio model.  For these systems we used our own in-
   memory buffer and make them look like memory streams until they're
   flushed, at which point they're written to backing store (flash RAM/
   EEPROM/DASD/whatever non-FS storage is being used) in one go.

   For streams with the sensitive bit set we don't expand the buffer size
   (because the original was probably in protected memory) for non-sensitive
   streams we do expand the size if necessary.  This means we have to choose
   a suitably large buffer for sensitive streams (private keys), but one
   which isn't too big, 16K is about right (typical private key files with
   cert chains are 2K) */

#if defined( __VMCMS__ ) || defined( __IBM4758__ )
  #define NO_STDIO
  #define STREAM_BUFSIZE	16384
#endif /* Nonstandard file I/O systems */

/****************************************************************************
*																			*
*								Utility Functions							*
*																			*
****************************************************************************/

/* In environments where we're provinding emulated I/O, we need to expand the
   write buffer on demand when it fills up.  The following routine does a
   safe realloc() which wipes the original buffer */

#ifdef NO_STDIO

static int expandBuffer( STREAM *stream, const int length )
	{
	void *newBuffer;
	int newSize = stream->bufSize + STREAM_BUFSIZE;

	/* Determine how much to expand the buffer by.  If it's a small buffer
	   allocated when we initially read a file and it doesn't look like we'll
	   be overflowing a standard-size buffer, we first expand it up to
	   STREAM_BUFSIZE before increasing it in STREAM_BUFSIZE steps */
	if( stream->bufSize < STREAM_BUFSIZE && \
		stream->bufPos + length < STREAM_BUFSIZE - 1024 )
		newSize = STREAM_BUFSIZE;

	/* Allocate the buffer and copy the new data across.  If the malloc
	   fails we return CRYPT_ERROR_OVERFLOW rather than CRYPT_ERROR_MEMORY
	   since the former is more appropriate for the emulated-I/O environment */
	if( ( newBuffer = malloc( stream->bufSize + STREAM_BUFSIZE ) ) == NULL )
		{
		stream->status = CRYPT_ERROR_OVERFLOW;
		return( CRYPT_ERROR_OVERFLOW );
		}
	memcpy( newBuffer, stream->buffer, stream->bufSize );
	zeroise( stream->buffer, stream->bufSize );
	free( stream->buffer );
	stream->buffer = newBuffer;
	stream->bufSize = newSize;

	return( CRYPT_OK );
	}
#endif /* NO_STDIO */

/* When working with a network stream we need several helper functions to 
   handle higher-level protocols and proxying */

#ifdef NET_TCP

/* Prototypes for functions in net_tcp.c */

BOOLEAN networkingOK( void );
int getIPAddress( STREAM *stream, BYTE *buffer, const char *server );
int openSocket( STREAM *stream, const char *server, const int port );
void closeSocket( STREAM *stream );
int readSocket( STREAM *stream, BYTE *buffer, int length );
int writeSocket( STREAM *stream, const BYTE *buffer, int length );

/* Open a connection, taking proxying requirements into account */

static int connectStream( STREAM *stream )
	{
	RESOURCE_DATA msgData;
	BYTE socksBuffer[ 64 + CRYPT_MAX_TEXTSIZE ], *bufPtr = socksBuffer;
	const char *hostNamePtr = stream->host;
	char proxyName[ MAX_URL_SIZE + 1 ], userName[ CRYPT_MAX_TEXTSIZE + 1 ];
	unsigned short localPort = stream->port;
	int length, status;

	/* If it's a server-side stream, open a listening socket and return */
	if( stream->isServer )
		return( openSocket( stream, hostNamePtr, localPort ) );

	/* Check whether there's a socks proxy configured, however we don't use 
	   it if it's a local connection */
	setResourceData( &msgData, proxyName, MAX_URL_SIZE );
	status = krnlSendMessage( DEFAULTUSER_OBJECT_HANDLE, 
							  RESOURCE_IMESSAGE_GETATTRIBUTE_S, &msgData, 
							  CRYPT_OPTION_NET_SOCKS_SERVER );
	if( cryptStatusOK( status ) && \
		strcmp( hostNamePtr, "127.0.0.1" ) && \
		stricmp( hostNamePtr, "localhost" ) )
		{
		/* We found a proxy, get the user name, defaulting to "cryptlib" if
		   there's none set */
		proxyName[ msgData.length ] = '\0';
		setResourceData( &msgData, userName, CRYPT_MAX_TEXTSIZE );
		status = krnlSendMessage( DEFAULTUSER_OBJECT_HANDLE, 
								  RESOURCE_IMESSAGE_GETATTRIBUTE_S, &msgData, 
								  CRYPT_OPTION_NET_SOCKS_USERNAME );
		if( cryptStatusOK( status ) )
			userName[ msgData.length ] = '\0';
		else
			strcpy( userName, "cryptlib" );
		hostNamePtr = proxyName;
		localPort = 1080;	/* SOCKS port */
		}

	/* Try and open the connection to the remote server */
	status = openSocket( stream, hostNamePtr, localPort );
	if( cryptStatusError( status ) )
		return( status );

	/* If we're not using a proxy, we're done */
	if( hostNamePtr == stream->host )
		return( CRYPT_OK );

	/* Build up the socks request string:
		BYTE: version = 4
		BYTE: command = 1 (connect)
		WORD: port
		LONG: IP address
		STRING: userName + '\0' */
	*bufPtr++ = 4; *bufPtr++ = 1;
	mputBWord( bufPtr, stream->port );
	status = getIPAddress( stream, bufPtr, stream->host );
	strcpy( bufPtr + 4, userName );
	length = 1 + 1 + 2 + 4 + strlen( userName ) + 1;
	if( cryptStatusError( status ) )
		{
		closeSocket( stream );
		stream->status = CRYPT_ERROR_NOTFOUND;
		return( CRYPT_ERROR_NOTFOUND );
		}

	/* Send the data to the server and read back the reply */
	status = writeSocket( stream, socksBuffer, length );
	if( cryptStatusOK( status ) )
		status = readSocket( stream, socksBuffer, 8 );
	if( cryptStatusError( status ) )
		{
		/* The involvement of a proxy complicates matters somewhat because
		   we can usually connect to the proxy OK but may run into problems
		   going from the proxy to the remote server, so if we get an error
		   at this stage (which will typically show up as a read error from
		   the proxy) we report it as an open error instead */
		if( status == CRYPT_ERROR_READ )
			status = CRYPT_ERROR_OPEN;
		closeSocket( stream );
		stream->status = status;
		return( status );
		}

	/* Make sure everything is OK, the second returned byte should be 90 */
	if( socksBuffer[ 1 ] != 90 )
		{
		int i;

		closeSocket( stream );
		strcpy( stream->errorMessage, "Socks proxy returned" );
		for( i = 0; i < 8; i++ )
			sprintf( stream->errorMessage + 20 + ( i * 3 ), 
					 " %02X", socksBuffer[ i ] );
		strcat( stream->errorMessage, "." );
		stream->errorCode = socksBuffer[ 1 ];
		stream->status = CRYPT_ERROR_OPEN;
		return( CRYPT_ERROR_OPEN );
		}

	return( CRYPT_OK );
	}

/* When reading text-oriented data over a network we don't know how much 
   more data is to come so we have to read a byte at a time looking for an 
   EOL.  This is horribly inefficient but there's no easy alternative without 
   resorting to low-level, nonportable solutions such as peeking into the 
   input stream.  In addition we can't use the simple optimisation of reading 
   two bytes at a time because some servers only send a LF even though the 
   RFC requires a CRLF */

#define HTTP_LINEBUF_SIZE	256

static int readToEOL( STREAM *stream )
	{
	BYTE ch;

	do
		{
		int status;

		status = readSocket( stream, &ch, 1 );
		if( cryptStatusError( status ) )
			{
			stream->status = status;
			return( status );
			}
		}
	while( ch != '\n' );

	return( CRYPT_OK );
	}

static int readLine( STREAM *stream, char *buffer )
	{
	BOOLEAN eol = FALSE;
	int bufPos = 0, status;

	do
		{
		BYTE ch;

		status = readSocket( stream, &ch, 1 );
		if( cryptStatusError( status ) )
			return( status );
		if( ch == '\n' )
			eol = TRUE;
		else
			if( ch != '\r' )
				buffer[ bufPos++ ] = ch;
		}
	while( !eol && bufPos < HTTP_LINEBUF_SIZE - 1 );
	if( !eol )
		{
		/* We haven't found an EOL yet after filling the line buffer,
		   discard anything else */
		status = readToEOL( stream );
		if( cryptStatusError( status ) )
			return( status );
		}
	buffer[ bufPos ] = '\0';

	return( bufPos );
	}

/* Read and write the CMP-over-TCP header, which kludges on extra bits and 
   pieces which were left out of CMP itself.  The TCP protocol version isn't 
   really 10, this is a kludge to work around the fact that the original RFC 
   2510 protocol doesn't work properly so it was necessary to create an 
   artifically huge version number to ensure non-compatibility with earlier 
   implementations (this says it all for the design of CMP as a whole) */

#define CMP_TCP_VERSION		10		/* CMP-over-TCP version */

enum { CMPMSG_PKIREQ, CMPMSG_POLLREP, CMPMSG_POLLREQ, CMPMSG_FINREP, 
	   CMPMSG_DUMMY, CMPMSG_PKIREP, CMPMSG_ERRORMSGREP };

static int writeCmpHeader( BYTE *buffer, const int length, 
						   const BOOLEAN lastMessage )
	{
	BYTE *bufPtr = buffer;
	long lengthVal = length + 3;

	/* Write the header:
		LONG: length
		BYTE: version = 10
		BYTE: flags = 0
		BYTE: message type = 0
		BYTE[]: data */
	mputBLong( bufPtr, lengthVal );
	*bufPtr++ = CMP_TCP_VERSION;
	*bufPtr++ = lastMessage;
	*bufPtr++ = CMPMSG_PKIREQ;

	return( 7 );
	}

static int readCmpHeader( STREAM *stream, int *length, const int maxLength )
	{
	BYTE buffer[ 8 ], *bufPtr = buffer;
	int headerType, status;
	long localLength;

	/* Clear return value */
	*length = CRYPT_ERROR;

	/* Read the fixed-length header fields */
	status = readSocket( stream, bufPtr, 7 );
	if( cryptStatusError( status ) )
		return( status );
	localLength = mgetBLong( bufPtr );
	if( localLength < 7 || localLength > maxLength || \
		*bufPtr++ != CMP_TCP_VERSION )
		return( CRYPT_ERROR_BADDATA );
	bufPtr++;				/* Skip flags */
	headerType = *bufPtr++;
	if( headerType < CMPMSG_PKIREQ || headerType > CMPMSG_ERRORMSGREP )
		return( CRYPT_ERROR_BADDATA );
	localLength -= 3;

	/* Handle individual header types */
	if( headerType == CMPMSG_PKIREQ || headerType == CMPMSG_PKIREP )
		{
		/* It's a normal reply, return the length of the payload */
		*length = localLength;
		return( CRYPT_OK );
		}
	if( headerType == CMPMSG_ERRORMSGREP )
		{
		BYTE errorBuffer[ 64 + MAX_ERRMSG_SIZE ];
		int dataLength;

		/* Read as much of the error status info as we can:
			WORD: error code
			WORD: length
			BYTE[]: unknown
			BYTE[]: error string filling remainder of packet */
		localLength = min( localLength, 64 + MAX_ERRMSG_SIZE );
		status = readSocket( stream, errorBuffer, localLength );
		if( cryptStatusError( status ) )
			return( status );
		bufPtr = errorBuffer;
		stream->errorCode = mgetBWord( bufPtr );
		dataLength = mgetBWord( bufPtr );
		if( dataLength < 0 )
			return( CRYPT_ERROR_BADDATA );
		localLength -= sizeof( WORD ) + sizeof( WORD ) + dataLength;
		bufPtr += dataLength;	/* Skip unknown data block */

⌨️ 快捷键说明

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