📄 dbx_rd.c
字号:
/****************************************************************************
* *
* cryptlib DBMS Interface *
* Copyright Peter Gutmann 1996-2004 *
* *
****************************************************************************/
#include <stdarg.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "keyset.h"
#include "dbms.h"
#include "asn1.h"
#include "rpc.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../keyset/keyset.h"
#include "../keyset/dbms.h"
#include "../misc/asn1.h"
#include "../misc/rpc.h"
#else
#include "crypt.h"
#include "keyset/keyset.h"
#include "keyset/dbms.h"
#include "misc/asn1.h"
#include "misc/rpc.h"
#endif /* Compiler-specific includes */
#ifdef USE_DBMS
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* The most common query types can be performed using cached access plans
and query data. The following function determines whether a particular
query can be performed using one of these cached queries, returning the
cache entry for the query if so */
DBMS_CACHEDQUERY_TYPE getCachedQueryType( const KEYMGMT_ITEM_TYPE itemType,
const CRYPT_KEYID_TYPE keyIDtype )
{
/* If we're not reading from the standard certs table, the query won't
be cached */
if( itemType != KEYMGMT_ITEM_PUBLICKEY )
return( DBMS_CACHEDQUERY_NONE );
/* Check whether we're querying on a cacheable key value type. An ID
type of CRYPT_KEYID_LAST is a special case which denotes that we're
doing a query on name ID, this is used for getNext() and is very
common (it follows most cert reads and is used to see if we can build
a chain), so we make it cacheable */
switch( keyIDtype )
{
case CRYPT_KEYID_URI:
return( DBMS_CACHEDQUERY_URI );
case CRYPT_IKEYID_ISSUERID:
return( DBMS_CACHEDQUERY_ISSUERID );
case CRYPT_IKEYID_CERTID:
return( DBMS_CACHEDQUERY_CERTID );
case CRYPT_KEYID_LAST:
return( DBMS_CACHEDQUERY_NAMEID );
}
return( DBMS_CACHEDQUERY_NONE );
}
/* Check an encoded cert for a matching key usage. The semantics of key
usage flags are vague in the sense that the query "Is this key valid for
X" is easily resolved, but the query "Which key is appropriate for X" is
NP-hard due to the potential existence of unbounded numbers of
certificates with usage semantics expressed in an arbitrary number of
ways. For now we distinguish between signing and encryption keys (this,
at least, is feasible) by doing a quick check for keyUsage if we get
multiple certs with the same DN and choosing the one with the appropriate
key usage.
Rather than performing a relatively expensive cert import for each cert,
we find the keyUsage by doing an optimised search through the cert data
for its encoded form. The pattern that we look for is:
OID 06 03 55 1D 0F
BOOLEAN (optional)
OCTET STRING { 04 (4 or 5)
BIT STRING 03 (2 or 3) nn (value) */
static BOOLEAN checkCertUsage( const BYTE *certificate, const int length,
const int requestedUsage )
{
int i;
assert( requestedUsage & KEYMGMT_MASK_USAGEOPTIONS );
/* Scan the payload portion of the cert for the keyUsage extension */
for( i = 256; i < length - 64; i++ )
{
int keyUsage;
/* Look for the OID. This potentially skips two bytes at a
time, but this is safe since the preceding bytes can never
contain either of these two values (they're 0x30 + 11...15) */
if( certificate[ i++ ] != BER_OBJECT_IDENTIFIER || \
certificate[ i++ ] != 3 )
continue;
if( memcmp( certificate + i, "\x55\x1D\x0F", 3 ) )
continue;
i += 3;
/* We've found the OID (with 1.1e-12 error probability), skip
the critical flag if necessary */
if( certificate[ i ] == BER_BOOLEAN )
i += 3;
/* Check for the OCTET STRING wrapper and BIT STRING */
if( certificate[ i++ ] != BER_OCTETSTRING || \
( certificate[ i ] != 4 && certificate[ i ] != 5 ) || \
certificate[ ++i ] != BER_BITSTRING )
continue;
keyUsage = certificate[ i + 3 ];
/* We've got to the BIT STRING payload, check whether the requested
usage is allowed. This is somewhat ugly since it hardcodes in
the bit values, but it's difficult to handle otherwise without
resorting to interpresting the encoded ASN.1 */
if( requestedUsage & KEYMGMT_FLAG_USAGE_CRYPT )
{
if( keyUsage & 0x20 )
return( TRUE );
}
else
if( keyUsage & 0x80 )
return( TRUE );
/* The requested usage isn't permitted by this cert */
return( FALSE );
}
/* No key usage found, assume that any usage is OK */
return( TRUE );
}
/****************************************************************************
* *
* Database Access Functions *
* *
****************************************************************************/
/* Fetch a sequence of certs from a data source. This is called in one of
two ways, either indirectly by the certificate code to fetch the first and
subsequent certs in a chain or directly by the user after submitting a
query to the keyset (which doesn't return any data) to read the results of
the query. The schema for calls is:
state = NULL: query( NULL, &data, CONTINUE );
state, point query: query( SQL, &data, NORMAL );
state, multi-cert: query( SQL, &data, START );
query( NULL, &data, CONTINUE ); */
int getItemData( DBMS_INFO *dbmsInfo, CRYPT_CERTIFICATE *iCertificate,
int *stateInfo, const CRYPT_KEYID_TYPE keyIDtype,
const char *keyValue, const int keyValueLength,
const KEYMGMT_ITEM_TYPE itemType, const int options )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
const DBMS_CACHEDQUERY_TYPE cachedQueryType = \
getCachedQueryType( itemType, keyIDtype );
BYTE certificate[ MAX_CERT_SIZE + BASE64_OVFL_SIZE ];
char certDataBuffer[ MAX_QUERY_RESULT_SIZE ];
void *certDataPtr = certDataBuffer;
char sqlBuffer[ MAX_SQL_QUERY_SIZE ], *sqlBufPtr;
DBMS_QUERY_TYPE queryType;
BOOLEAN multiCertQuery = ( options & KEYMGMT_MASK_USAGEOPTIONS ) ? \
TRUE : FALSE;
BOOLEAN continueFetch = TRUE;
int certDataLength, iterationCount = 0, status;
assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
assert( isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
assert( ( keyValueLength > 2 && \
isReadPtr( keyValue, keyValueLength ) && \
( keyIDtype > CRYPT_KEYID_NONE && \
keyIDtype <= CRYPT_KEYID_LAST ) ) || \
( keyValueLength == 0 && keyValue == NULL && \
keyIDtype == CRYPT_KEYID_NONE ) );
assert( itemType == KEYMGMT_ITEM_NONE || \
itemType == KEYMGMT_ITEM_PUBLICKEY || \
itemType == KEYMGMT_ITEM_REQUEST || \
itemType == KEYMGMT_ITEM_PKIUSER || \
itemType == KEYMGMT_ITEM_REVOCATIONINFO );
/* Make sure that we can never explicitly fetch anything with an ID that
indicates that it's physically but not logically present, for example
certificates that have been created but not fully issued yet, cert
items that are on hold, and similar items */
if( keyValue != NULL && \
( !memcmp( keyValue, KEYID_ESC1, KEYID_ESC_SIZE ) || \
!memcmp( keyValue, KEYID_ESC2, KEYID_ESC_SIZE ) ) )
/* Eheu, litteras istas reperire non possum */
return( CRYPT_ERROR_NOTFOUND );
/* Perform a slight optimisation to eliminate unnecessary multi-cert
queries: If we're querying by certID or issuerID only one cert can
ever match, so there's no need to perform a multi-cert query even if
key usage options are specified */
if( keyIDtype == CRYPT_IKEYID_ISSUERID || \
keyIDtype == CRYPT_IKEYID_CERTID )
multiCertQuery = FALSE;
/* If we have binary blob support, fetch the data directly into the
certificate buffer */
if( hasBinaryBlobs( dbmsInfo ) )
certDataPtr = certificate;
/* Set the query to begin the fetch */
if( stateInfo != NULL )
{
dbmsFormatSQL( sqlBuffer,
"SELECT certData FROM $ WHERE $ = ?",
getTableName( itemType ),
( keyIDtype == CRYPT_KEYID_LAST ) ? \
"nameID" : getKeyName( keyIDtype ) );
sqlBufPtr = sqlBuffer;
queryType = multiCertQuery ? DBMS_QUERY_START : DBMS_QUERY_NORMAL;
}
else
{
/* It's an ongoing query, just fetch the next set of results */
sqlBufPtr = NULL;
queryType = DBMS_QUERY_CONTINUE;
}
/* Retrieve the results from the query */
while( continueFetch && iterationCount++ < 100 )
{
/* Retrieve the record and base64-decode the binary cert data if
necessary */
status = dbmsQuery( sqlBufPtr, certDataPtr, &certDataLength,
keyValue, keyValueLength, 0, cachedQueryType,
queryType );
if( cryptStatusOK( status ) && !hasBinaryBlobs( dbmsInfo ) )
{
certDataLength = base64decode( certificate, MAX_CERT_SIZE,
certDataBuffer, certDataLength,
CRYPT_CERTFORMAT_NONE );
if( cryptStatusError( certDataLength ) )
status = certDataLength;
}
if( cryptStatusError( status ) )
/* Convert the error code to a more appropriate value if
appropriate */
return( ( multiCertQuery && ( status == CRYPT_ERROR_COMPLETE ) ) ? \
CRYPT_ERROR_NOTFOUND : status );
/* We've started the fetch, from now on we're only fetching further
results */
sqlBufPtr = NULL;
if( queryType == DBMS_QUERY_START )
queryType = DBMS_QUERY_CONTINUE;
assert( certDataLength > 16 );
assert( ( ( stateInfo != NULL ) && \
( queryType == DBMS_QUERY_NORMAL || \
queryType == DBMS_QUERY_CONTINUE ) ) || \
( ( stateInfo == NULL ) && \
( queryType == DBMS_QUERY_CONTINUE ) ) );
/* If the first byte of the cert data is 0xFF, this is an item which
is physically but not logically present (see the comment above in
the check for the keyValue), which means that we can't explicitly
fetch it (te audire non possum, musa sapientum fixa est in aure).
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -