📄 dbms.c
字号:
va_list argPtr;
char *formatPtr = ( char * ) format;
int bufPos = 0;
va_start( argPtr, format );
while( *formatPtr )
{
if( *formatPtr == '$' )
{
char *strPtr = va_arg( argPtr, char * );
assert( strPtr != NULL ); /* Catch a shortage of args */
/* Copy the string to the output buffer with conversion of any
special characters that are used by SQL */
while( *strPtr )
{
int ch = *strPtr++;
/* If it's a control character, skip it */
if( ( ch & 0x7F ) < ' ' )
continue;
/* Escape metacharacters that could be misused in queries,
for example by specifying a key 'foo; DROP TABLE bar' or
similar shenanigans. We catch the obvious ' and ;, as
well as the less obvious %, which could be used to hide
other metacharacters. 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 == '\'' || ch == '\\' || ch == ';' || ch == '%' )
/* Escape the character */
buffer[ bufPos++ ] = SQL_ESCAPE;
/* 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 if we're running under Windoze. In addition
generic ODBC uses '{' and '}' as escape delimiters, so we
also strip these */
#if defined( __WINDOWS__ )
if( ch != '|' && ch != '{' && ch != '}' )
#elif defined( USE_ODBC )
if( ch != '{' && ch != '}' )
#endif /* Database-specific dangerous escape sequences */
buffer[ bufPos++ ] = ch;
/* Make sure that we haven't overflowed the input buffer.
We check for MAX_SQL_QUERY_SIZE - 3 rather than
MAX_SQL_QUERY_SIZE - 2 in case the next character needs
escaping, which expands it to two chars
(MAX_SQL_QUERY_SIZE - 1 is used for the '\0'). Note
that we refuse a query if it overflows rather than
trying to truncate it to a safe length, both because
it's better to fail than to try the query anyway in
truncated form, and because this could be used by an
attacker to ensure that the truncation occurs in the
middle of an escape sequence that de-fangs a dangerous
character, thus negating the escaping */
if( bufPos > MAX_SQL_QUERY_SIZE - 3 )
{
bufPos = 0;
formatPtr = "\x00\x00"; /* Force exit on outer loop */
break;
}
}
formatPtr++;
}
else
{
/* Just copy the char over, with a length check */
if( bufPos > MAX_SQL_QUERY_SIZE - 1 )
{
bufPos = 0;
break;
}
buffer[ bufPos++ ] = *formatPtr++;
}
}
buffer[ bufPos++ ] = '\0'; /* Add der terminador */
va_end( argPtr );
}
/* Format input parameters into SQL queries, replacing meta-values with
actual column names */
int dbmsFormatQuery( char *output, const char *input, const int inputLength,
const int maxLength )
{
int inPos = 0, outPos = 0, status = CRYPT_OK;
while( inPos < inputLength )
{
if( input[ inPos ] == '$' )
{
const int fieldPos = inPos + 1;
const char *fieldName = input + fieldPos;
const char *outputFieldName;
int length;
inPos++; /* Skip '$' */
/* Extract the field name and translate it into the table
column name */
while( isAlpha( input[ inPos ] ) )
inPos++;
length = inPos - fieldPos;
if( length <= 0 || length > 7 )
{
status = CRYPT_ERROR_BADDATA;
break;
}
if( !strCompare( fieldName, "C", length ) )
outputFieldName = "C";
else
if( !strCompare( fieldName, "SP", length ) )
outputFieldName = "SP";
else
if( !strCompare( fieldName, "L", length ) )
outputFieldName = "L";
else
if( !strCompare( fieldName, "O", length ) )
outputFieldName = "O";
else
if( !strCompare( fieldName, "OU", length ) )
outputFieldName = "OU";
else
if( !strCompare( fieldName, "CN", length ) )
outputFieldName = "CN";
else
if( !strCompare( fieldName, "email", length ) || \
!strCompare( fieldName, "uri", length ) )
outputFieldName = "email";
else
if( !strCompare( fieldName, "date", length ) )
outputFieldName = "validTo";
else
{
status = CRYPT_ERROR_BADDATA;
break;
}
length = strlen( outputFieldName );
/* Copy the translated name to the output buffer */
if( outPos + length >= maxLength - 1 )
{
status = CRYPT_ERROR_OVERFLOW;
break;
}
memcpy( output + outPos, outputFieldName, length );
outPos += length;
}
else
{
const char ch = input[ inPos++ ];
/* Just copy the char over, with a length check */
if( outPos > maxLength - 1 )
{
status = CRYPT_ERROR_OVERFLOW;
break;
}
/* Safety checks from formatSQL. We don't escape single quotes
because we use those ourselves in SQL queries */
if( ( ch & 0x7F ) < ' ' )
continue;
if( ch == '\\' || ch == ';' || ch == '%' )
/* Escape the character */
output[ outPos++ ] = SQL_ESCAPE;
#if defined( __WINDOWS__ )
if( ch != '|' && ch != '{' && ch != '}' )
#elif defined( USE_ODBC )
if( ch != '{' && ch != '}' )
#endif /* Database-specific dangerous escape sequences */
output[ outPos++ ] = ch;
}
}
if( cryptStatusError( status ) )
outPos = 0;
output[ outPos++ ] = '\0'; /* Add der terminador */
return( status );
}
/* Parse a user-supplied database name into individual components, used by
the database back-end connect functions. We don't do a syntax check
(since the exact syntax is database-specific) but merely break the single
string up into any recognisable components. The database back-end can
determine whether the format is valid or not. The general format that we
look for is:
[generic name]
user:pass
user@server
user:pass@server
user:pass@server/name
One distinction that we make is that if there's something after an '@'
and there's no server/name separator present, we treat it as a name
rather than a server. In other words @foo results in name=foo, while
@foo/bar results in server=foo, name=bar. This is because the most
common situation we have to handle is ODBC, which identifies the database
by name rather than by server.
Some database types use a magic ID value to indicate the use of a C-style
string for an arg instead of taking an actual length arg, if the caller
supplies one of these magic IDs we use that for the "length" rather than
the actual string length */
int dbmsParseName( DBMS_NAME_INFO *nameInfo, const char *name,
const int lengthMarker )
{
const char *namePtr, *argEndPtr;
int length;
memset( nameInfo, 0, sizeof( DBMS_NAME_INFO ) );
/* Check for a complex database name */
if( ( namePtr = strchr( name, ':' ) ) == NULL && \
( namePtr = strchr( name, '@' ) ) == NULL )
{
/* It's a straightforward name, use it directly */
nameInfo->name = ( char * ) name;
nameInfo->nameLen = lengthMarker ? lengthMarker : strlen( name );
return( CRYPT_OK );
}
/* Extract the user name */
length = min( namePtr - name, CRYPT_MAX_TEXTSIZE );
if( length <= 0 )
return( CRYPT_ERROR_OPEN );
memcpy( nameInfo->userBuffer, name, length );
nameInfo->userBuffer[ length ] = '\0';
nameInfo->user = nameInfo->userBuffer;
nameInfo->userLen = lengthMarker ? lengthMarker : length;
/* We're either at the server name or password, extract the password
if there is one */
if( *namePtr++ == ':' )
{
argEndPtr = strchr( namePtr, '@' );
if( argEndPtr == NULL )
argEndPtr = strchr( namePtr, '\0' );
length = min( argEndPtr - namePtr, CRYPT_MAX_TEXTSIZE );
if( length <= 0 )
return( CRYPT_ERROR_OPEN );
memcpy( nameInfo->passwordBuffer, namePtr, length );
nameInfo->passwordBuffer[ length ] = '\0';
nameInfo->password = nameInfo->passwordBuffer;
nameInfo->passwordLen = lengthMarker ? lengthMarker : length;
if( !*argEndPtr )
return( CRYPT_OK );
namePtr = argEndPtr + 1;
}
/* Separate the server and database name if necessary */
argEndPtr = strchr( namePtr, '/' );
if( argEndPtr != NULL )
{
/* There's a distinction between the server name and database name,
extract the server name */
length = min( argEndPtr - namePtr, CRYPT_MAX_TEXTSIZE );
if( length <= 0 )
return( CRYPT_ERROR_OPEN );
memcpy( nameInfo->serverBuffer, namePtr, length );
nameInfo->serverBuffer[ length ] = '\0';
nameInfo->server = nameInfo->serverBuffer;
nameInfo->serverLen = lengthMarker ? lengthMarker : length;
namePtr = argEndPtr + 1;
}
/* Extract the database name if there is one */
if( *namePtr )
{
length = strlen( namePtr );
memcpy( nameInfo->nameBuffer, namePtr, length );
nameInfo->nameBuffer[ length ] = '\0';
nameInfo->name = nameInfo->nameBuffer;
nameInfo->nameLen = lengthMarker ? lengthMarker : length;
}
return( CRYPT_OK );
}
/* Initialise and shut down a session with a database back-end */
int initDbxSession( KEYSET_INFO *keysetInfo, const CRYPT_KEYSET_TYPE type )
{
DBMS_INFO *dbmsInfo = keysetInfo->keysetDBMS;
int status = CRYPT_ERROR;
/* Select the appropriate dispatch function for the keyset type */
switch( type )
{
case CRYPT_KEYSET_ODBC:
case CRYPT_KEYSET_ODBC_STORE:
status = initDispatchODBC( dbmsInfo );
break;
case CRYPT_KEYSET_DATABASE:
case CRYPT_KEYSET_DATABASE_STORE:
status = initDispatchDatabase( dbmsInfo );
break;
case CRYPT_KEYSET_PLUGIN:
case CRYPT_KEYSET_PLUGIN_STORE:
status = initDispatchNet( dbmsInfo );
break;
default:
assert( NOTREACHED );
}
if( cryptStatusError( status ) )
return( CRYPT_ARGERROR_NUM1 );
/* Set up the remaining function pointers */
dbmsInfo->openDatabaseFunction = openDatabase;
dbmsInfo->closeDatabaseFunction = closeDatabase;
dbmsInfo->performUpdateFunction = performUpdate;
dbmsInfo->performStaticUpdateFunction = performStaticUpdate;
dbmsInfo->performQueryFunction = performQuery;
dbmsInfo->performStaticQueryFunction = performStaticQuery;
/* Allocate the database session state information */
if( ( keysetInfo->keyData = \
clAlloc( "initDbxSession", sizeof( DBMS_STATE_INFO ) ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
memset( keysetInfo->keyData, 0, sizeof( DBMS_STATE_INFO ) );
keysetInfo->keyDataSize = sizeof( DBMS_STATE_INFO );
dbmsInfo->stateInfo = keysetInfo->keyData;
if( type == CRYPT_KEYSET_ODBC_STORE || \
type == CRYPT_KEYSET_DATABASE_STORE || \
type == CRYPT_KEYSET_PLUGIN_STORE )
dbmsInfo->flags |= DBMS_FLAG_CERTSTORE | DBMS_FLAG_CERTSTORE_FIELDS;
return( CRYPT_OK );
}
int endDbxSession( KEYSET_INFO *keysetInfo )
{
/* Free the database session state information if necessary */
if( keysetInfo->keyData != NULL )
{
memset( keysetInfo->keyData, 0, keysetInfo->keyDataSize );
clFree( "endDbxSession", keysetInfo->keyData );
keysetInfo->keyData = NULL;
}
return( CRYPT_OK );
}
#endif /* USE_DBMS */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -