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

📄 lib_dbms.c

📁 提供了很多种加密算法和CA认证及相关服务如CMP、OCSP等的开发
💻 C
📖 第 1 页 / 共 5 页
字号:
			return( status );
			}
		status = performStaticUpdate( &keysetInfo->keysetDBMS,
					"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( cryptStatusError( status ) && status != CRYPT_ERROR_DUPLICATE )
			{
			/* Undo the previous table creations */
			performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE certificates" );
			performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE CRLs" );
			performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE pkiUsers" );
			return( status );
			}
		status = performStaticUpdate( &keysetInfo->keysetDBMS,
					"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 ) && status != CRYPT_ERROR_DUPLICATE )
			{
			/* Undo the previous table creations */
			performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE certificates" );
			performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE CRLs" );
			performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE pkiUsers" );
			performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE certRequests" );
			return( status );
			}
		}

	/* Create an index for the email address, nameID, issuerID, keyID, and
	   certID in the certificates table, the issuerID 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), and the certID in the cert log (this 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
	   (since there could be multiple certs present which 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 = performStaticUpdate( &keysetInfo->keysetDBMS,
					"CREATE INDEX emailIdx ON certificates(email)" );
	if( cryptStatusOK( status ) )
		status = performStaticUpdate( &keysetInfo->keysetDBMS,
					"CREATE INDEX nameIDIdx ON certificates(nameID)" );
	if( cryptStatusOK( status ) )
		status = performStaticUpdate( &keysetInfo->keysetDBMS,
					"CREATE UNIQUE INDEX issuerIDIdx ON certificates(issuerID)" );
	if( cryptStatusOK( status ) )
		status = performStaticUpdate( &keysetInfo->keysetDBMS,
					"CREATE UNIQUE INDEX keyIDIdx ON certificates(keyID)" );
	if( cryptStatusOK( status ) )
		status = performStaticUpdate( &keysetInfo->keysetDBMS,
					"CREATE UNIQUE INDEX certIDIdx ON certificates(certID)" );
	if( cryptStatusOK( status ) )
		status = performStaticUpdate( &keysetInfo->keysetDBMS,
					"CREATE UNIQUE INDEX crlIssuerIDIdx ON CRLs (issuerID)" );
	if( cryptStatusOK( status ) && keysetInfo->keysetDBMS.isCertStore )
		status = performStaticUpdate( &keysetInfo->keysetDBMS,
					"CREATE UNIQUE INDEX logCertIDIdx ON certLog (certID)" );
	if( cryptStatusOK( status ) && keysetInfo->keysetDBMS.isCertStore )
		status = performStaticUpdate( &keysetInfo->keysetDBMS,
					"CREATE UNIQUE INDEX userKeyIDIdx ON pkiUsers (keyID)" );
	if( cryptStatusOK( status ) && keysetInfo->keysetDBMS.isCertStore )
		{
		char dummyCertID[ MAX_ENCODED_DBXKEYID_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( keysetInfo, CRYPT_CERTACTION_CREATE,
								dummyCertID, NULL, NULL, NULL, 0,
								DBMS_UPDATE_NORMAL );
		}
	if( cryptStatusError( status ) )
		{
		/* Undo the creation of the various tables */
		performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE certificates" );
		performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE CRLs" );
		if( keysetInfo->keysetDBMS.isCertStore )
			{
			performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE pkiUsers" );
			performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE certRequests" );
			performStaticUpdate( &keysetInfo->keysetDBMS,
					"DROP TABLE certLog" );
			}
		return( CRYPT_ERROR_WRITE );
		}

	return( CRYPT_OK );
	}

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

/* 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 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 any usage is OK */
	return( TRUE );
	}

/* 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 */

static int getItemData( KEYSET_INFO *keysetInfo,
						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;

	/* Make sure we can never explicitly fetch anything with an ID which
	   indicates it's physically but not logically present (for example
	   certificates which have been created but not fully issued yet, cert
	   items which are on hold, and similar items) */
	if( keyValue != NULL && \
		( !memcmp( keyValue, "--", 2 ) || !memcmp( keyValue, "++", 2 ) ) )
		return( CRYPT_ERROR_NOTFOUND );

	/* If we have binary blob support, fetch the data directly into the
	   certificate buffer */
	if( keysetInfo->keysetDBMS.hasBinaryBlobs )
		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 it ever becomes necessary
	   to store multiple generations of identical certs in a data source, the
	   straight select can be replaced by one which has "... ORDER BY validTo
	   DESCENDING" to pick the newest cert, this isn't done by default since
	   it results in extra overhead both in the database (it has to sort the
	   query results) an here (we have to do a multi-cert query for every
	   fetch) */
	if( stateInfo != NULL )
		{
		formatSQL( sqlBuffer, ( itemType == KEYMGMT_ITEM_REQUEST ) ? \
					"SELECT certData FROM certRequests WHERE $ = '$'" : \
							  ( itemType == KEYMGMT_ITEM_PKIUSER ) ? \
					"SELECT certData FROM pkiUsers WHERE $ = '$'" : \
					"SELECT certData FROM certificates WHERE $ = '$'",
				   keyName, keyValue );
		if( multiCertQuery )
			{
			/* We're fetching a collection of certs in order to pick out the
			   one we want, submit the query to start the fetch */
			status = performQuery( &keysetInfo->keysetDBMS, 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 key data if
		   necessary */
		status = performQuery( &keysetInfo->keysetDBMS, sqlBufPtr, keyPtr,
							   &keyLength, 0, queryType );
		if( cryptStatusOK( status ) && \
			!keysetInfo->keysetDBMS.hasBinaryBlobs )
			{
			keyLength = base64decode( certificate, keyBuffer, keyLength,
									  CRYPT_CERTFORMAT_NONE );
			if( !keyLength )
				status = CRYPT_ERROR_BADDATA;
			}
		if( cryptStatusError( status ) )
			{
			if( status == CRYPT_ERROR_COMPLETE )
				{
				if( stateInfo == NULL )
					/* We were performing an ongoing query, this has now
					   completed */
					keysetInfo->queryInProgress = FALSE;
				if( multiCertQuery )
					/* Convert the error code to a more appropriate value */
					status = CRYPT_ERROR_NOTFOUND;
				}
			return( 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 */
	if( multiCertQuery )
		performStaticQuery( &keysetInfo->keysetDBMS, NULL,
							DBMS_QUERY_CANCEL );

	/* Create a certificate object from the encoded cert */
	setMessageCreateObjectIndirectInfo( &createInfo, certificate, keyLength );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
							  RESOURCE_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 )
	{
	char keyIDbuffer[ CRYPT_MAX_TEXTSIZE * 2 ];
	const char *keyName = NULL;
	int status;

	/* If it's a general query, submit the query to the database */

⌨️ 快捷键说明

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