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

📄 dbms.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************************
*																			*
*						 cryptlib DBMS Backend Interface					*
*						Copyright Peter Gutmann 1996-2007					*
*																			*
****************************************************************************/

#include <ctype.h>
#include <stdarg.h>
#if defined( INC_ALL )
  #include "crypt.h"
  #include "keyset.h"
  #include "dbms.h"
  #include "rpc.h"
#else
  #include "crypt.h"
  #include "keyset/keyset.h"
  #include "keyset/dbms.h"
  #include "misc/rpc.h"
#endif /* Compiler-specific includes */

#ifdef USE_DBMS

/****************************************************************************
*																			*
*						Backend Database Access Functions					*
*																			*
****************************************************************************/

/* Dispatch functions for various database types.  ODBC is the native keyset
   for Windows and (frequently) Unix, a cryptlib-native plugin is the 
   fallback for Unix, and the rest are only accessible via database network
   plugins */

#ifdef USE_ODBC
  int initDispatchODBC( DBMS_INFO *dbmsInfo );
#else
  #define initDispatchODBC( dbmsInfo )		CRYPT_ERROR
#endif /* USE_ODBC */
#if defined( USE_DATABASE )
  int initDispatchDatabase( DBMS_INFO *dbmsInfo );
#else
  #define initDispatchDatabase( dbmsInfo )	CRYPT_ERROR
#endif /* General database interface */
#ifdef USE_DATABASE_PLUGIN
  int initDispatchNet( DBMS_INFO *dbmsInfo );
#else
  #define initDispatchNet( dbmsInfo )		CRYPT_ERROR
#endif /* USE_DATABASE_PLUGIN */

/* Database access functions */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 5 ) ) \
static int openDatabase( INOUT DBMS_INFO *dbmsInfo, 
						 IN_BUFFER( nameLen ) const char *name,
						 IN_LENGTH_NAME const int nameLen, 
						 IN_ENUM_OPT( CRYPT_KEYOPT ) \
							const CRYPT_KEYOPT_TYPE options, 
						 OUT_FLAGS_Z( DBMS_FEATURE ) int *featureFlags )
	{
	DBMS_STATE_INFO *dbmsStateInfo = dbmsInfo->stateInfo;
	int status;

	assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
	assert( isReadPtr( name, nameLen ) );
	assert( isWritePtr( featureFlags, sizeof( int ) ) );

	REQUIRES( nameLen >= MIN_NAME_LENGTH && \
			  nameLen < MAX_ATTRIBUTE_SIZE );
	REQUIRES( options >= CRYPT_KEYOPT_NONE && options < CRYPT_KEYOPT_LAST );

	/* Clear return value */
	*featureFlags = DBMS_FEATURE_FLAG_NONE;

	status = dbmsInfo->openDatabaseBackend( dbmsStateInfo, name, nameLen, 
											options, featureFlags );
	if( cryptStatusError( status ) )
		return( status );

	/* Make long-term information returned as a back-end interface-specific
	   feature flags persistent if necessary */
	if( *featureFlags & DBMS_FEATURE_FLAG_BINARYBLOBS )
		dbmsInfo->flags |= DBMS_FLAG_BINARYBLOBS;

	return( CRYPT_OK );
	}

STDC_NONNULL_ARG( ( 1 ) ) \
static void closeDatabase( INOUT DBMS_INFO *dbmsInfo )
	{
	DBMS_STATE_INFO *dbmsStateInfo = dbmsInfo->stateInfo;

	assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );

	dbmsInfo->closeDatabaseBackend( dbmsStateInfo );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int performUpdate( INOUT DBMS_INFO *dbmsInfo, 
						  IN_STRING_OPT const char *command,
						  IN_ARRAY_OPT( BOUND_DATA_MAXITEMS ) \
							const BOUND_DATA *boundData,
						  IN_ENUM( DBMS_UPDATE ) \
							const DBMS_UPDATE_TYPE updateType )
	{
	DBMS_STATE_INFO *dbmsStateInfo = dbmsInfo->stateInfo;
	const int commandLength = ( command != NULL ) ? strlen( command ) : 0;
	int status;

	assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
	assert( ( command == NULL && commandLength == 0 && \
			  updateType == DBMS_UPDATE_ABORT ) || \
			isReadPtr( command, commandLength ) );
	assert( ( boundData == NULL ) || \
			isReadPtr( boundData, \
					   sizeof( BOUND_DATA ) * BOUND_DATA_MAXITEMS ) );

	REQUIRES( ( updateType == DBMS_UPDATE_ABORT && \
				command == NULL && commandLength == 0 ) || \
			  ( updateType != DBMS_UPDATE_ABORT && \
				command != NULL && \
				commandLength > 0 && commandLength < MAX_INTLENGTH_SHORT ) );
	REQUIRES( updateType > DBMS_UPDATE_NONE && \
			  updateType < DBMS_UPDATE_LAST );

	/* If we're trying to abort a transaction that was never begun, don't
	   do anything */
	if( updateType == DBMS_UPDATE_ABORT && \
		!( dbmsInfo->flags & DBMS_FLAG_UPDATEACTIVE ) )
		return( CRYPT_OK );

	/* Process the update */
	status = dbmsInfo->performUpdateBackend( dbmsStateInfo, command, 
											 commandLength, boundData, 
											 updateType );
	if( cryptStatusOK( status ) )
		{
		/* 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 );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int performStaticUpdate( INOUT DBMS_INFO *dbmsInfo, 
								IN_STRING const char *command )
	{
	return( performUpdate( dbmsInfo, command, NULL, DBMS_UPDATE_NORMAL ) );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int performQuery( INOUT DBMS_INFO *dbmsInfo, 
						 IN_STRING_OPT const char *command,
						 OUT_BUFFER_OPT( dataMaxLength, *dataLength ) \
							char *data, 
						 IN_LENGTH_SHORT_Z const int dataMaxLength, 
						 OUT_LENGTH_SHORT_Z int *dataLength, 
						 IN_ARRAY_OPT( BOUND_DATA_MAXITEMS ) \
							const BOUND_DATA *boundData,
						 IN_ENUM_OPT( DBMS_CACHEDQUERY ) \
							const DBMS_CACHEDQUERY_TYPE queryEntry,
						 IN_ENUM( DBMS_QUERY ) const DBMS_QUERY_TYPE queryType )
	{
	DBMS_STATE_INFO *dbmsStateInfo = dbmsInfo->stateInfo;
	const int commandLength = ( command != NULL ) ? strlen( command ) : 0;
	int status;

	assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
	assert( ( command == NULL && commandLength == 0 && \
			  ( queryType == DBMS_QUERY_CONTINUE || \
				queryType == DBMS_QUERY_CANCEL ) ) || \
			isReadPtr( command, commandLength ) );
	assert( ( data == NULL && dataLength == NULL ) || \
			isWritePtr( data, MAX_QUERY_RESULT_SIZE ) );
	assert( ( boundData == NULL ) || \
			isReadPtr( boundData, \
					   sizeof( BOUND_DATA ) * BOUND_DATA_MAXITEMS ) );

	REQUIRES( ( ( queryType == DBMS_QUERY_CONTINUE || \
				  queryType == DBMS_QUERY_CANCEL ) && \
				command == NULL && commandLength == 0 ) || \
			  ( ( queryType == DBMS_QUERY_START || \
				  queryType == DBMS_QUERY_CHECK || \
				  queryType == DBMS_QUERY_NORMAL ) && \
				command != NULL && \
				commandLength > 0 && commandLength < MAX_INTLENGTH_SHORT ) );
	REQUIRES( ( data == NULL && dataMaxLength == 0 && \
				dataLength == NULL ) || \
			  ( data != NULL && dataMaxLength >= 16 && \
				dataMaxLength < MAX_INTLENGTH_SHORT && \
				dataLength != NULL ) );
	REQUIRES( queryEntry >= DBMS_CACHEDQUERY_NONE && \
			  queryEntry < DBMS_CACHEDQUERY_LAST );
	REQUIRES( queryType > DBMS_QUERY_NONE && \
			  queryType < DBMS_QUERY_LAST );

	/* Additional state checks: If we're starting a new query or performing
	   a point query there can't already be one active and if we're
	   continuing or cancelling an existing query there has to be one
	   already active */
	REQUIRES( ( ( queryType == DBMS_QUERY_START || \
				  queryType == DBMS_QUERY_CHECK || \
				  queryType == DBMS_QUERY_NORMAL ) && \
				!( dbmsInfo->flags & DBMS_FLAG_QUERYACTIVE ) ) ||
			  ( ( queryType == DBMS_QUERY_CONTINUE || \
				  queryType == DBMS_QUERY_CANCEL ) && \
				( dbmsInfo->flags & DBMS_FLAG_QUERYACTIVE ) ) );

	/* Clear return value */
	if( data != NULL )
		{
		memset( data, 0, min( 16, dataMaxLength ) );
		*dataLength = 0;
		}

	/* Process the query */
	status = dbmsInfo->performQueryBackend( dbmsStateInfo, command, 
											commandLength, data, dataMaxLength, 
											dataLength, boundData, 
											queryEntry, queryType );
	if( cryptStatusError( status ) )
		return( status );

	/* Sanity-check the result data from the back-end */
	if( dataLength != NULL && \
		( *dataLength <= 0 || *dataLength > MAX_QUERY_RESULT_SIZE ) )
		{
		assert( DEBUG_WARN );
		memset( data, 0, min( 16, dataMaxLength ) );
		*dataLength = 0;
		return( CRYPT_ERROR_BADDATA );
		}

	/* Update the state information based on the query that we've just 
	   performed */
	if( queryType == DBMS_QUERY_START  )
		dbmsInfo->flags |= DBMS_FLAG_QUERYACTIVE;
	if( queryType == DBMS_QUERY_CANCEL )
		dbmsInfo->flags &= ~DBMS_FLAG_QUERYACTIVE;
	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int performStaticQuery( INOUT DBMS_INFO *dbmsInfo, 
							   IN_STRING_OPT const char *command,
							   IN_ENUM_OPT( DBMS_CACHEDQUERY ) \
								const DBMS_CACHEDQUERY_TYPE queryEntry,
							   IN_ENUM( DBMS_QUERY ) \
								const DBMS_QUERY_TYPE queryType )
	{
	return( performQuery( dbmsInfo, command, NULL, 0, NULL, NULL, 
						  queryEntry, queryType ) );
	}

/****************************************************************************
*																			*
*								SQL Rewrite Routines						*
*																			*
****************************************************************************/

/* In order to allow general certificate database queries we have to be able 
   to process user-supplied query strings.  The cryptlib manual contains 
   strong warnings about the correct way to do this (if it's done at all), 
   the best that we can do is apply assorted safety checks of the query data 
   to try and reduce the chances of SQL injection.  Unfortunately this can 
   get arbitrarily complicated:

	';	The standard SQL-injection method, used with values like
		'foo; DROP TABLE bar', or '1=1' to return all entries in a table.

	--	Comment delimiter (other values also exist, e.g. MySQL's '#') to
		truncate queries beyond the end of the injected SQL.

	char(0xNN)	Bypass the first level of filtering, e.g. char(0x41)
		produces the banned character '.

   One additional check that we could do is to try and explicitly strip
   SQL keywords from queries but this is somewhat problematic because apart 
   from the usual trickery (e.g. embedding one SQL keyword inside another so 
   that stripping SELECT from SELSELECTECT will still leave the outer 
   SELECT, requiring recursive stripping, or taking advantage of the fact 
   that VARBINARY values are implicitly cast to VARCHARS so that 0x42434445 
   would turn into ABCD, or further escaping the encoding with values like 
   'sel'+'ect') there are also any number of backend-specific custom 
   keywords and ways of escaping keywords that we can't know about and 
   therefore can't easily strip */

#define	SQL_ESCAPE	'\''

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int copyChar( OUT_BUFFER( bufMaxLen, *bufPos ) char *buffer, 
					 IN_LENGTH_SHORT const int bufMaxLen, 
					 OUT_LENGTH_SHORT_Z int *bufPos, 
					 IN_BYTE const int ch, const BOOLEAN escapeQuotes )
	{
	int position = 0;

	assert( isWritePtr( buffer, bufMaxLen ) );
	assert( isWritePtr( bufPos, sizeof( int ) ) );

	REQUIRES( bufMaxLen > 0 && bufMaxLen < MAX_INTLENGTH_SHORT );
	REQUIRES( ch >= 0 && ch <= 0xFF );

	/* Clear return value */
	*bufPos = 0;

	/* If it's a control character, skip it */
	if( ( ch & 0x7F ) < ' ' )
		return( CRYPT_OK );

	/* Escape metacharacters that could be misused in queries.  We catch the 
	   obvious ' and ; as well as the less obvious %, which could be used to 
	   hide other metacharacters, and \, used by some databases (e.g. MySQL)
	   as an escape.  Note that none of these characters are valid in base64, 
	   which makes it safe to escape them in the few instances where they do 
	   occur */
	if( ( ch == '\'' && escapeQuotes ) || \
		ch == '\\' || ch == ';' || ch == '%' )
		{
		/* Escape the character */
		buffer[ position++ ] = SQL_ESCAPE;
		if( position >= bufMaxLen )
			return( CRYPT_ERROR_OVERFLOW );
		}

	/* Bypass various dangerous SQL "enhancements".  For Windows ODBC the 
	   driver will execute anything delimited by '|'s as an expression (an 
	   example being '|shell("cmd /c echo " & chr(124) & " format c:")|'), 
	   because of this we strip gazintas.  Since ODBC uses '{' and '}' as 
	   escape delimiters we also strip these */
	if( ch != '|' && ch != '{' && ch != '}' )
		buffer[ position++ ] = ch;

	/* Make sure that we haven't overflowed the output buffer.  This 
	   overflowing can be done deliberately, for example by using large 
	   numbers of escape chars (which are in turn escaped) to force 
	   truncation of the query beyond the injected SQL if the processing 
	   simply stops at a given point */
	if( position >= bufMaxLen )
		return( CRYPT_ERROR_OVERFLOW );

⌨️ 快捷键说明

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