📄 dbxdbx.c
字号:
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 );
}
/* Create a new key database */
static int createDatabase( DBMS_INFO *dbmsInfo )
{
int updateProgress = 0, status;
/* Create tables for certs, CRLs, cert requests, PKI users, and CA logs.
We use CHAR rather than VARCHAR for the ID fields since these always
have a fixed length and CHAR is faster than VARCHAR. In addition we
make as many columns as possible NOT NULL since these fields should
always be present, and because this is faster for most databases. The
BLOB type is nonstandard, this is rewritten by the database interface
layer to the type which is appropriate for the database */
status = dbmsStaticUpdate(
"CREATE TABLE certificates ("
"C CHAR(2), "
"SP VARCHAR(64), "
"L VARCHAR(64), "
"O VARCHAR(64), "
"OU VARCHAR(64), "
"CN VARCHAR(64), "
"email VARCHAR(64), "
"validTo DATETIME NOT NULL, "
"nameID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL, "
"issuerID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL, "
"keyID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL, "
"certID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL, "
"certData BLOB NOT NULL)" );
if( cryptStatusError( status ) )
return( status );
if( isCertStore( dbmsInfo ) )
/* The cert store contains in addition to the other CRL fields the
certificate expiry time which is used to remove the entry from
the CRL table once the certificate has expired anyway, the nameID
which is used to force clustering of entries for each CA, and the
ID of the cert being revoked, which isn't available if we're
creating it from a raw CRL */
status = dbmsStaticUpdate(
"CREATE TABLE CRLs ("
"expiryDate DATETIME NOT NULL, "
"nameID CHAR(" TEXT_DBXKEYID_SIZE ") PRIMARY KEY NOT NULL, "
"issuerID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL,"
"certID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL, "
"certData BLOB NOT NULL)" );
else
status = dbmsStaticUpdate(
"CREATE TABLE CRLs ("
"issuerID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL,"
"certData BLOB NOT NULL)" );
if( cryptStatusOK( status ) && isCertStore( dbmsInfo ) )
{
updateProgress++;
status = dbmsStaticUpdate(
"CREATE TABLE pkiUsers ("
"C CHAR(2), "
"SP VARCHAR(64), "
"L VARCHAR(64), "
"O VARCHAR(64), "
"OU VARCHAR(64), "
"CN VARCHAR(64), "
"nameID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL, "
"keyID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL, "
"certID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL, "
"certData BLOB NOT NULL)" );
}
if( cryptStatusOK( status ) && isCertStore( dbmsInfo ) )
{
updateProgress++;
status = dbmsStaticUpdate(
"CREATE TABLE certRequests ("
"type SMALLINT NOT NULL, "
"C CHAR(2), "
"SP VARCHAR(64), "
"L VARCHAR(64), "
"O VARCHAR(64), "
"OU VARCHAR(64), "
"CN VARCHAR(64), "
"email VARCHAR(64), "
"certID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL, "
"certData BLOB NOT NULL)" );
}
if( cryptStatusOK( status ) && isCertStore( dbmsInfo ) )
{
updateProgress++;
status = dbmsStaticUpdate(
"CREATE TABLE certLog ("
"action SMALLINT NOT NULL, "
"actionTime DATETIME NOT NULL, "
"certID CHAR(" TEXT_DBXKEYID_SIZE ") NOT NULL, "
"reqCertID CHAR(" TEXT_DBXKEYID_SIZE "), "
"subjCertID CHAR(" TEXT_DBXKEYID_SIZE "), "
"certData BLOB)" );
}
if( cryptStatusError( status ) )
{
/* Undo the previous table creations */
dbmsStaticUpdate( "DROP TABLE certificates" );
if( updateProgress > 0 )
dbmsStaticUpdate( "DROP TABLE CRLs" );
if( updateProgress > 1 )
dbmsStaticUpdate( "DROP TABLE pkiUsers" );
if( updateProgress > 2 )
dbmsStaticUpdate( "DROP TABLE certRequests" );
return( status );
}
/* Create an index for the email address, nameID, issuerID, keyID, and
certID in the certificates table, the issuerID and certID in the CRLs
table (the CRL nameID isn't indexed since we only use it for linear
scans, however it's designated the primary key to ensure that rows are
clustered around it), the nameID and keyID in the PKI users table (the
former isn't used but is made a UNIQUE INDEX to ensure that the same
entry can't be added more than once) and the certID in the cert log
(this also isn't used but is made a UNIQUE INDEX to ensure that the
same entry can't be added more than once). We have to give these
unique names since some databases don't allow two indexes to have the
same name, even if they're in a different table. Since most of the
fields in the tables are supposed to be unique, we can specify this
for the indexes we're creating, however we can't do it for the email
address or the nameID in the certs table since there could be multiple
certs present that differ only in key usage. We don't index the other
tables since indexes consume space and we don't expect to access any
of these much */
status = dbmsStaticUpdate(
"CREATE INDEX emailIdx ON certificates(email)" );
if( cryptStatusOK( status ) )
status = dbmsStaticUpdate(
"CREATE INDEX nameIDIdx ON certificates(nameID)" );
if( cryptStatusOK( status ) )
status = dbmsStaticUpdate(
"CREATE UNIQUE INDEX issuerIDIdx ON certificates(issuerID)" );
if( cryptStatusOK( status ) )
status = dbmsStaticUpdate(
"CREATE UNIQUE INDEX keyIDIdx ON certificates(keyID)" );
if( cryptStatusOK( status ) )
status = dbmsStaticUpdate(
"CREATE UNIQUE INDEX certIDIdx ON certificates(certID)" );
if( cryptStatusOK( status ) )
status = dbmsStaticUpdate(
"CREATE UNIQUE INDEX crlIssuerIDIdx ON CRLs (issuerID)" );
if( cryptStatusOK( status ) && isCertStore( dbmsInfo ) )
status = dbmsStaticUpdate(
"CREATE UNIQUE INDEX crlCertIDIdx ON CRLs (certID)" );
if( cryptStatusOK( status ) && isCertStore( dbmsInfo ) )
status = dbmsStaticUpdate(
"CREATE UNIQUE INDEX userKeyIDIdx ON pkiUsers (keyID)" );
if( cryptStatusOK( status ) && isCertStore( dbmsInfo ) )
status = dbmsStaticUpdate(
"CREATE UNIQUE INDEX userNameIDIdx ON pkiUsers (nameID)" );
if( cryptStatusOK( status ) && isCertStore( dbmsInfo ) )
status = dbmsStaticUpdate(
"CREATE UNIQUE INDEX logCertIDIdx ON certLog (certID)" );
if( cryptStatusOK( status ) && isCertStore( dbmsInfo ) )
{
char dummyCertID[ DBXKEYID_BUFFER_SIZE ];
/* Create a special dummy certID with an out-of-band value to mark
the first entry in the log */
memset( dummyCertID, '-', MAX_ENCODED_DBXKEYID_SIZE );
dummyCertID[ MAX_ENCODED_DBXKEYID_SIZE - 1 ] = '\0';
/* Add the initial log entry recording the creation of the log */
status = updateCertLog( dbmsInfo, CRYPT_CERTACTION_CREATE,
dummyCertID, NULL, NULL, NULL, 0,
DBMS_UPDATE_NORMAL );
}
if( cryptStatusError( status ) )
{
/* Undo the creation of the various tables */
dbmsStaticUpdate( "DROP TABLE certificates" );
dbmsStaticUpdate( "DROP TABLE CRLs" );
if( isCertStore( dbmsInfo ) )
{
dbmsStaticUpdate( "DROP TABLE pkiUsers" );
dbmsStaticUpdate( "DROP TABLE certRequests" );
dbmsStaticUpdate( "DROP TABLE certLog" );
}
return( CRYPT_ERROR_WRITE );
}
return( CRYPT_OK );
}
/****************************************************************************
* *
* 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 */
int getItemData( DBMS_INFO *dbmsInfo, CRYPT_CERTIFICATE *iCertificate,
int *stateInfo, const char *keyName, const char *keyValue,
const KEYMGMT_ITEM_TYPE itemType, const int options )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
const BOOLEAN multiCertQuery = ( options & KEYMGMT_MASK_USAGEOPTIONS ) ? \
TRUE : FALSE;
const DBMS_QUERY_TYPE queryType = \
( stateInfo == NULL || multiCertQuery ) ? \
DBMS_QUERY_CONTINUE : DBMS_QUERY_NORMAL;
BYTE certificate[ MAX_CERT_SIZE ];
BOOLEAN continueFetch;
char keyBuffer[ MAX_QUERY_RESULT_SIZE ], *keyPtr = keyBuffer;
char sqlBuffer[ MAX_SQL_QUERY_SIZE ], *sqlBufPtr = NULL;
int keyLength, status;
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, "--", 2 ) || !memcmp( keyValue, "++", 2 ) ) )
/* Eheu, litteras istas reperire non possum */
return( CRYPT_ERROR_NOTFOUND );
/* If we have binary blob support, fetch the data directly into the
certificate buffer */
if( hasBinaryBlobs( dbmsInfo ) )
keyPtr = ( char * ) certificate;
/* If this isn't an ongoing fetch from a query submitted earlier, prepare
and submit the query to fetch the data */
if( stateInfo != NULL )
{
dbmsFormatSQL( sqlBuffer,
"SELECT certData FROM $ WHERE $ = '$'",
getTableName( itemType ), keyName, keyValue );
if( multiCertQuery )
{
/* We're fetching a collection of certs in order to pick out the
one that we want, submit the query to start the fetch */
status = dbmsQuery( sqlBuffer, NULL, NULL, 0, DBMS_QUERY_START );
if( cryptStatusError( status ) )
return( status );
}
else
/* It's a point query, submit it as we do the fetch */
sqlBufPtr = sqlBuffer;
}
do
{
/* Retrieve the record and base64-decode the binary cert data if
necessary */
status = dbmsQuery( sqlBufPtr, keyPtr, &keyLength, 0, queryType );
if( cryptStatusOK( status ) && !hasBinaryBlobs( dbmsInfo ) )
{
keyLength = base64decode( certificate, keyBuffer, keyLength,
CRYPT_CERTFORMAT_NONE );
if( !keyLength )
status = CRYPT_ERROR_BADDATA;
}
if( cryptStatusError( status ) )
/* Convert the error code to a more appropriate value if
appropriate */
return( ( multiCertQuery && ( status == CRYPT_ERROR_COMPLETE ) ) ? \
CRYPT_ERROR_NOTFOUND : status );
/* 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 we can't explicitly fetch
it. If it's a point query this means we didn't find anything,
otherwise we try again with the next result */
if( certificate[ 0 ] == 0xFF )
{
if( sqlBufPtr == sqlBuffer )
/* Point query, we found something but it isn't there.
"Can't you understand English you arse, we're not at home"
-- Jeremy Black, "The Boys from Brazil" */
return( CRYPT_ERROR_NOTFOUND );
continueFetch = TRUE;
}
else
/* If more than one cert is present and the requested key usage
doesn't match the one indicated in the cert, try again */
if( multiCertQuery && \
!checkCertUsage( certificate, keyLength, options ) )
continueFetch = TRUE;
else
/* We got what we wanted, exit */
continueFetch = FALSE;
}
while( continueFetch );
/* If we've been looking through multiple certs, cancel the outstanding
query, which is still in progress */
if( multiCertQuery )
dbmsStaticQuery( NULL, DBMS_QUERY_CANCEL );
/* Create a certificate object from the encoded cert. If we're reading
revocation information the data is a single CRL entry so we have to
tell the cert import code to treat it as a special case of a CRL. If
we're reading a request, it could be one of several types so we have
to use autodetection rather than specifying an exact format */
setMessageCreateObjectIndirectInfo( &createInfo, certificate, keyLength,
( itemType == KEYMGMT_ITEM_PUBLICKEY || \
itemType == KEYMGMT_ITEM_NONE ) ? CRYPT_CERTTYPE_CERTIFICATE : \
( itemType == KEYMGMT_ITEM_REQUEST ) ? CRYPT_CERTTYPE_NONE : \
( itemType == KEYMGMT_ITEM_PKIUSER ) ? CRYPT_CERTTYPE_PKIUSER : \
( itemType == KEYMGMT_ITEM_REVOCATIONINFO ) ? CERTFORMAT_REVINFO : \
CRYPT_CERTTYPE_NONE );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT_INDIRECT,
&createInfo, OBJECT_TYPE_CERTIFICATE );
if( cryptStatusError( status ) )
return( status );
*iCertificate = createInfo.cryptHandle;
/* If this was a read with state held externally, remember where we got
to so we can fetch the next cert in the sequence */
if( stateInfo != NULL )
*stateInfo = *iCertificate;
return( CRYPT_OK );
}
static int getFirstItemFunction( KEYSET_INFO *keysetInfo,
CRYPT_CERTIFICATE *iCertificate,
int *stateInfo,
const CRYPT_KEYID_TYPE keyIDtype,
const void *keyID, const int keyIDlength,
const KEYMGMT_ITEM_TYPE itemType,
const int options )
{
DBMS_INFO *dbmsInfo = keysetInfo->keysetDBMS;
char keyIDbuffer[ CRYPT_MAX_TEXTSIZE * 2 ];
int status;
/* If it's a general query, submit the query to the database */
if( stateInfo == NULL )
{
char sqlBuffer[ MAX_SQL_QUERY_SIZE ];
int sqlLength;
assert( itemType == KEYMGMT_ITEM_PUBLICKEY || \
itemType == KEYMGMT_ITEM_REQUEST );
assert( options == KEYMGMT_FLAG_NONE );
if( keyIDlength > MAX_SQL_QUERY_SIZE - 64 )
return( CRYPT_ARGERROR_STR1 );
/* If we're cancelling an existing query, pass it on down */
if( keyIDlength == 6 && !strCompare( keyID, "cancel", keyIDlength ) )
{
status = dbmsStaticQuery( NULL, DBMS_QUERY_CANCEL );
return( status );
}
assert( !keysetInfo->isBusyFunction( keysetInfo ) );
/* Rewrite the user-supplied portion of the query using the actual
column names and append it to the SELECT statement */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -