📄 stream.c
字号:
/****************************************************************************
* *
* 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 + -