📄 dbxdbms.c
字号:
/****************************************************************************
* *
* cryptlib DBMS Backend Interface *
* Copyright Peter Gutmann 1996-2002 *
* *
****************************************************************************/
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "keyset.h"
#include "dbxdbx.h"
#include "rpc.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../keyset/keyset.h"
#include "../keyset/dbxdbx.h"
#include "../misc/rpc.h"
#else
#include "crypt.h"
#include "keyset/keyset.h"
#include "keyset/dbxdbx.h"
#include "misc/rpc.h"
#endif /* Compiler-specific includes */
#ifdef USE_DBMS
/****************************************************************************
* *
* Network Database Interface Routines *
* *
****************************************************************************/
#ifdef USE_TCP
static void netEncodeError( BYTE *buffer, const int status )
{
putMessageType( buffer, COMMAND_RESULT, 0, 1, 0 );
putMessageLength( buffer + COMMAND_WORDSIZE, COMMAND_WORDSIZE );
putMessageWord( buffer + COMMAND_WORD1_OFFSET, status );
}
void netProcessCommand( void *stateInfo, BYTE *buffer )
{
DBMS_STATE_INFO *dbmsInfo = ( DBMS_STATE_INFO * ) stateInfo;
COMMAND_INFO cmd;
int length, status;
memset( &cmd, 0, sizeof( COMMAND_INFO ) );
/* Get the messge information from the header */
getMessageType( buffer, cmd.type, cmd.flags,
cmd.noArgs, cmd.noStrArgs );
length = getMessageLength( buffer + COMMAND_WORDSIZE );
if( cmd.type == DBX_COMMAND_OPEN )
{
NET_CONNECT_INFO connectInfo;
BYTE *bufPtr = buffer + COMMAND_FIXED_DATA_SIZE + COMMAND_WORDSIZE;
int nameLen;
/* Get the length of the server name and null-terminate it */
nameLen = getMessageWord( bufPtr );
bufPtr += COMMAND_WORDSIZE;
bufPtr[ nameLen ] = '\0';
/* Connect to the plugin */
initNetConnectInfo( &connectInfo, DEFAULTUSER_OBJECT_HANDLE,
CRYPT_ERROR, CRYPT_ERROR, NET_OPTION_HOSTNAME );
connectInfo.name = bufPtr;
status = sNetConnect( &dbmsInfo->stream, STREAM_PROTOCOL_TCPIP,
&connectInfo, dbmsInfo->errorMessage,
&dbmsInfo->errorCode );
if( cryptStatusError( status ) )
{
netEncodeError( buffer, status );
return;
}
}
/* Send the command to the plugin and read back the response */
status = swrite( &dbmsInfo->stream, buffer,
COMMAND_FIXED_DATA_SIZE + COMMAND_WORDSIZE + length );
if( cryptStatusOK( status ) )
status = sread( &dbmsInfo->stream, buffer, COMMAND_FIXED_DATA_SIZE );
if( !cryptStatusError( status ) )
{
/* Perform a consistency check on the returned data */
getMessageType( buffer, cmd.type, cmd.flags,
cmd.noArgs, cmd.noStrArgs );
length = getMessageLength( buffer + COMMAND_WORDSIZE );
if( !dbxCheckCommandInfo( &cmd, length ) || \
cmd.type != COMMAND_RESULT )
status = CRYPT_ERROR_BADDATA;
}
if( !cryptStatusError( status ) )
/* Read the rest of the message */
status = sread( &dbmsInfo->stream, buffer + COMMAND_FIXED_DATA_SIZE,
length );
/* If it's a close command, terminate the connection to the plugin. We
don't do any error checking once we get this far since there's not
much we can do at this point any more */
if( cmd.type == DBX_COMMAND_CLOSE )
sNetDisconnect( &dbmsInfo->stream );
else
if( cryptStatusError( status ) )
netEncodeError( buffer, status );
}
#endif /* USE_TCP */
/****************************************************************************
* *
* Database RPC Routines *
* *
****************************************************************************/
/* Dispatch functions for various database types. ODBC is the native keyset
for Windows, a cryptlib-native plugin (usually MySQL) is the native
keyset for Unix, the rest are only accessible via database network
plugins */
#ifdef __WINDOWS__
void odbcProcessCommand( void *stateInfo, BYTE *buffer );
#else
#define odbcProcessCommand NULL
#endif /* __WINDOWS__ */
#if defined( USE_MYSQL ) || defined( USE_ORACLE ) || defined( USE_POSTGRES )
void databaseProcessCommand( void *stateInfo, BYTE *buffer );
#else
#define databaseProcessCommand NULL
#endif /* General database interface */
#ifndef USE_TCP
#define netProcessCommand NULL
#endif /* USE_TCP */
/* Make sure we can fit the largest possible SQL query into the RPC buffer */
#if MAX_SQL_QUERY_SIZE + 256 >= DBX_IO_BUFSIZE
#error Database RPC buffer size is too small, increase DBX_IO_BUFSIZE and rebuild
#endif /* SQL query size larger than RPC buffer size */
/* Dispatch data to the backend */
static int dispatchCommand( COMMAND_INFO *cmd, void *stateInfo,
DISPATCH_FUNCTION dispatchFunction )
{
COMMAND_INFO sentCmd = *cmd;
BYTE buffer[ DBX_IO_BUFSIZE ], *bufPtr = buffer;
BYTE header[ COMMAND_FIXED_DATA_SIZE ];
const int payloadLength = ( cmd->noArgs * COMMAND_WORDSIZE ) + \
( cmd->noStrArgs * COMMAND_WORDSIZE ) + \
cmd->strArgLen[ 0 ] + cmd->strArgLen[ 1 ] + \
cmd->strArgLen[ 2 ];
long resultLength;
int i;
assert( payloadLength + 32 < DBX_IO_BUFSIZE );
assert( dispatchFunction != NULL );
/* Clear the return value */
memset( cmd, 0, sizeof( COMMAND_INFO ) );
/* Write the header and message fields to the buffer */
putMessageType( bufPtr, sentCmd.type, sentCmd.flags,
sentCmd.noArgs, sentCmd.noStrArgs );
putMessageLength( bufPtr + COMMAND_WORDSIZE, payloadLength );
bufPtr += COMMAND_FIXED_DATA_SIZE;
for( i = 0; i < sentCmd.noArgs; i++ )
{
putMessageWord( bufPtr, sentCmd.arg[ i ] );
bufPtr += COMMAND_WORDSIZE;
}
for( i = 0; i < sentCmd.noStrArgs; i++ )
{
const int argLength = sentCmd.strArgLen[ i ];
putMessageWord( bufPtr, argLength );
if( argLength > 0 )
memcpy( bufPtr + COMMAND_WORDSIZE, sentCmd.strArg[ i ],
argLength );
bufPtr += COMMAND_WORDSIZE + argLength;
}
/* Send the command to the server and read the server's message header */
dispatchFunction( stateInfo, buffer );
memcpy( header, buffer, COMMAND_FIXED_DATA_SIZE );
/* Process the fixed message header and make sure it's valid */
getMessageType( header, cmd->type, cmd->flags,
cmd->noArgs, cmd->noStrArgs );
resultLength = getMessageLength( header + COMMAND_WORDSIZE );
if( !dbxCheckCommandInfo( cmd, resultLength ) || \
cmd->type != COMMAND_RESULT )
return( CRYPT_ERROR );
if( ( cmd->noStrArgs && cmd->strArgLen[ 0 ] ) && \
( sentCmd.type != DBX_COMMAND_QUERY && \
sentCmd.type != DBX_COMMAND_GETERRORINFO ) )
/* Only these commands can return data */
return( CRYPT_ERROR );
/* Read the rest of the server's message */
bufPtr = buffer + COMMAND_FIXED_DATA_SIZE;
for( i = 0; i < cmd->noArgs; i++ )
{
cmd->arg[ i ] = getMessageWord( bufPtr );
bufPtr += COMMAND_WORDSIZE;
}
for( i = 0; i < cmd->noStrArgs; i++ )
{
cmd->strArgLen[ i ] = getMessageWord( bufPtr );
cmd->strArg[ i ] = bufPtr + COMMAND_WORDSIZE;
bufPtr += COMMAND_WORDSIZE + cmd->strArgLen[ i ];
}
/* The first value returned is the status code, if it's nonzero return
it to the caller, otherwise move the other values down */
if( cryptStatusError( cmd->arg[ 0 ] ) )
return( cmd->arg[ 0 ] );
assert( cryptStatusOK( cmd->arg[ 0 ] ) );
for( i = 1; i < cmd->noArgs; i++ )
cmd->arg[ i - 1 ] = cmd->arg[ i ];
cmd->arg[ i ] = 0;
cmd->noArgs--;
/* Copy any string arg data back to the caller */
if( cmd->noStrArgs && cmd->strArgLen[ 0 ] )
{
const int maxBufSize = ( sentCmd.type == DBX_COMMAND_QUERY ) ? \
MAX_QUERY_RESULT_SIZE : MAX_ERRMSG_SIZE;
const int argIndex = sentCmd.noStrArgs;
memcpy( sentCmd.strArg[ argIndex ], cmd->strArg[ 0 ],
min( cmd->strArgLen[ 0 ], maxBufSize ) );
cmd->strArg[ 0 ] = sentCmd.strArg[ argIndex ];
}
return( CRYPT_OK );
}
/* Database access functions */
static int openDatabase( DBMS_INFO *dbmsInfo, const char *name,
const int options )
{
static const COMMAND_INFO cmdTemplate = \
{ DBX_COMMAND_OPEN, COMMAND_FLAG_NONE, 1, 1 };
COMMAND_INFO cmd;
int status;
/* Dispatch the command */
memcpy( &cmd, &cmdTemplate, sizeof( COMMAND_INFO ) );
cmd.arg[ 0 ] = options;
cmd.strArg[ 0 ] = ( void * ) name;
cmd.strArgLen[ 0 ] = strlen( name );
status = dispatchCommand( &cmd, dbmsInfo->stateInfo,
dbmsInfo->dispatchFunction );
if( cryptStatusOK( status ) && \
( cmd.arg[ 0 ] & DBMS_HAS_BINARYBLOBS ) )
dbmsInfo->flags |= DBMS_FLAG_BINARYBLOBS;
return( status );
}
static void closeDatabase( DBMS_INFO *dbmsInfo )
{
static const COMMAND_INFO cmdTemplate = \
{ DBX_COMMAND_CLOSE, COMMAND_FLAG_NONE, 0, 0 };
COMMAND_INFO cmd;
/* Dispatch the command */
memcpy( &cmd, &cmdTemplate, sizeof( COMMAND_INFO ) );
dispatchCommand( &cmd, dbmsInfo->stateInfo, dbmsInfo->dispatchFunction );
}
static void performErrorQuery( DBMS_INFO *dbmsInfo )
{
static const COMMAND_INFO cmdTemplate = \
{ DBX_COMMAND_GETERRORINFO, COMMAND_FLAG_NONE, 0, 0 };
COMMAND_INFO cmd;
int status;
/* Clear the return values */
memset( dbmsInfo->errorMessage, 0, MAX_ERRMSG_SIZE );
dbmsInfo->errorCode = 0;
/* Dispatch the command */
memcpy( &cmd, &cmdTemplate, sizeof( COMMAND_INFO ) );
cmd.strArg[ 0 ] = dbmsInfo->errorMessage;
cmd.strArgLen[ 0 ] = 0;
status = dispatchCommand( &cmd, dbmsInfo->stateInfo,
dbmsInfo->dispatchFunction );
if( cryptStatusOK( status ) )
{
dbmsInfo->errorCode = cmd.arg[ 0 ];
dbmsInfo->errorMessage[ cmd.strArgLen[ 0 ] ] = '\0';
}
}
static int performUpdate( DBMS_INFO *dbmsInfo, const char *command,
const void *boundData, const int boundDataLength,
const time_t boundDate,
const DBMS_UPDATE_TYPE updateType )
{
static const COMMAND_INFO cmdTemplate = \
{ DBX_COMMAND_UPDATE, COMMAND_FLAG_NONE, 1, 0 };
COMMAND_INFO cmd;
BYTE encodedDate[ 8 ];
int argIndex = 1, status;
/* If we're trying to abort a transaction which was never begun, don't
do anything */
if( updateType == DBMS_UPDATE_ABORT && \
!( dbmsInfo->flags & DBMS_FLAG_UPDATEACTIVE ) )
return( CRYPT_OK );
/* Dispatch the command */
memcpy( &cmd, &cmdTemplate, sizeof( COMMAND_INFO ) );
cmd.arg[ 0 ] = updateType;
if( command != NULL )
{
cmd.noStrArgs = 1;
cmd.strArg[ 0 ] = ( void * ) command;
cmd.strArgLen[ 0 ] = strlen( command );
}
if( boundDate )
{
#ifndef _BIG_WORDS
assert( sizeof( time_t ) <= 4 );
#endif /* !_BIG_WORDS */
/* Encode the date as a 64-bit value */
memset( encodedDate, 0, 8 );
#ifdef _BIG_WORDS
encodedDate[ 3 ] = ( BYTE )( ( boundDate >> 32 ) & 0xFF );
#endif /* _BIG_WORDS */
encodedDate[ 4 ] = ( BYTE )( ( boundDate >> 24 ) & 0xFF );
encodedDate[ 5 ] = ( BYTE )( ( boundDate >> 16 ) & 0xFF );
encodedDate[ 6 ] = ( BYTE )( ( boundDate >> 8 ) & 0xFF );
encodedDate[ 7 ] = ( BYTE )( ( boundDate ) & 0xFF );
cmd.noStrArgs++;
cmd.strArg[ argIndex ] = encodedDate;
cmd.strArgLen[ argIndex++ ] = 8;
}
if( boundData != NULL )
{
/* Copy the bound data into non-ephemeral storage where it'll be
accessible to the backend */
memcpy( dbmsInfo->boundData, boundData, boundDataLength );
cmd.noStrArgs++;
cmd.strArg[ argIndex ] = dbmsInfo->boundData;
cmd.strArgLen[ argIndex++ ] = boundDataLength;
}
status = dispatchCommand( &cmd, dbmsInfo->stateInfo,
dbmsInfo->dispatchFunction );
if( cryptStatusError( status ) )
performErrorQuery( dbmsInfo );
else
{
/* If we're starting or ending an update, record the update state */
if( updateType == DBMS_UPDATE_BEGIN )
dbmsInfo->flags |= DBMS_FLAG_UPDATEACTIVE;
if( updateType == DBMS_UPDATE_COMMIT || \
updateType == DBMS_UPDATE_ABORT )
dbmsInfo->flags &= ~DBMS_FLAG_UPDATEACTIVE;
}
return( status );
}
static int performStaticUpdate( DBMS_INFO *dbmsInfo, const char *command )
{
return( performUpdate( dbmsInfo, command, NULL, 0, 0,
DBMS_UPDATE_NORMAL ) );
}
static int performQuery( DBMS_INFO *dbmsInfo, const char *command,
char *data, int *dataLength, const time_t date,
const DBMS_QUERY_TYPE queryType )
{
static const COMMAND_INFO cmdTemplate = \
{ DBX_COMMAND_QUERY, COMMAND_FLAG_NONE, 1, 1 };
COMMAND_INFO cmd;
BYTE encodedDate[ 8 ];
int argIndex = 1, status;
/* Clear return value */
if( data != NULL )
{
memset( data, 0, 16 );
*dataLength = -1;
}
/* Dispatch the command */
memcpy( &cmd, &cmdTemplate, sizeof( COMMAND_INFO ) );
cmd.arg[ 0 ] = queryType;
if( command != NULL )
{
cmd.strArg[ 0 ] = ( void * ) command;
cmd.strArgLen[ 0 ] = strlen( command );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -