1 /****************************************************************************
2 *																			*
3 *					cryptlib DBMS CA Certificate Misc Interface				*
4 *						Copyright Peter Gutmann 1996-2007					*
5 *																			*
6 ****************************************************************************/
7 
8 #include <stdio.h>		/* For snprintf() */
9 #if defined( INC_ALL )
10   #include "crypt.h"
11   #include "asn1.h"
12   #include "dbms.h"
13   #include "keyset.h"
14 #else
15   #include "crypt.h"
16   #include "enc_dec/asn1.h"
17   #include "keyset/dbms.h"
18   #include "keyset/keyset.h"
19 #endif /* Compiler-specific includes */
20 
21 #ifdef USE_DBMS
22 
23 /****************************************************************************
24 *																			*
25 *								Utility Functions							*
26 *																			*
27 ****************************************************************************/
28 
29 #if 0
30 
31 /* Get the ultimate successor certificate for one that's been superseded */
32 
33 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
34 static int getSuccessorCert( INOUT DBMS_INFO *dbmsInfo,
35 							 OUT_HANDLE_OPT CRYPT_CERTIFICATE *iCertificate,
36 							 IN_BUFFER( initialCertIDlength ) \
37 								const char *initialCertID,
38 							 IN_LENGTH_SHORT const int initialCertIDlength )
39 	{
40 	char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
41 	int chainingLevel, status;
42 
43 	assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
44 	assert( isWritePtr( *iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
45 	assert( isReadPtr( initialCertID, initialCertIDlength ) );
46 
47 	/* Walk through the chain of renewals in the certificate store log until
48 	   we find the ultimate successor certificate to the current one */
49 	memcpy( certID, initialCertID, initialCertIDlength );
50 	for( chainingLevel = 0, status = CRYPT_ERROR_NOTFOUND;
51 		 status == CRYPT_ERROR_NOTFOUND && \
52 			chainingLevel < FAILSAFE_ITERATIONS_MED;
53 		 chainingLevel++ )
54 		{
55 		BYTE keyCertID[ DBXKEYID_SIZE + 8 ];
56 		char certData[ MAX_QUERY_RESULT_SIZE + 8 ];
57 		int certDataLength, length, dummy;
58 
59 		/* Find the request to renew this certificate */
60 		status = dbmsQuery(
61 			"SELECT certID FROM certLog WHERE subjCertID = ? "
62 			"AND action = " TEXT_CERTACTION_REQUEST_RENEWAL,
63 							certData, &certDataLength, certID,
64 							strlen( certID ), 0, DBMS_CACHEDQUERY_NONE,
65 							DBMS_QUERY_NORMAL );
66 		if( cryptStatusError( status ) )
67 			return( status );
68 
69 		/* Find the resulting certificate */
70 		memcpy( certID, certData,
71 				min( certDataLength, ENCODED_DBXKEYID_SIZE + 1 ) );
72 		certID[ MAX_ENCODED_DBXKEYID_SIZE ] = '\0';
73 		status = dbmsQuery(
74 			"SELECT certID FROM certLog WHERE reqCertID = ? "
75 				"AND action = " TEXT_CERTACTION_CERT_CREATION,
76 							certData, &certDataLength, certID,
77 							strlen( certID ), 0, DBMS_CACHEDQUERY_NONE,
78 							DBMS_QUERY_NORMAL );
79 		if( cryptStatusOK( status ) )
80 			{
81 			status = length = \
82 				base64decode( keyCertID, certData,
83 							  min( certDataLength, ENCODED_DBXKEYID_SIZE ),
84 							  CRYPT_CERTFORMAT_NONE );
85 			assert( !cryptStatusError( status ) );
86 			}
87 		if( cryptStatusError( status ) )
88 			return( status );
89 
90 		/* Try and get the replacement certificate */
91 		status = getItemData( dbmsInfo, iCertificate, &dummy,
92 							  getKeyName( CRYPT_IKEYID_CERTID ),
93 							  keyCertID, length, KEYMGMT_ITEM_PUBLICKEY,
94 							  KEYMGMT_FLAG_NONE, errorInfo );
95 		}
96 	if( chainingLevel >= FAILSAFE_ITERATIONS_MED )
97 		{
98 		/* We've chained through too many entries, bail out */
99 		return( CRYPT_ERROR_OVERFLOW );
100 		}
101 
102 	return( status );
103 	}
104 #endif /* 0 */
105 
106 /****************************************************************************
107 *																			*
108 *								Logging Functions							*
109 *																			*
110 ****************************************************************************/
111 
112 /* Add an entry to the CA log */
113 
114 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
updateCertLog(INOUT DBMS_INFO * dbmsInfo,IN_ENUM (CRYPT_CERTACTION)const CRYPT_CERTACTION_TYPE action,IN_BUFFER_OPT (certIDlength)const char * certID,IN_LENGTH_SHORT_Z const int certIDlength,IN_BUFFER_OPT (reqCertIDlength)const char * reqCertID,IN_LENGTH_SHORT_Z const int reqCertIDlength,IN_BUFFER_OPT (subjCertIDlength)const char * subjCertID,IN_LENGTH_SHORT_Z const int subjCertIDlength,IN_BUFFER_OPT (dataLength)const void * data,IN_LENGTH_SHORT_Z const int dataLength,IN_ENUM (DBMS_UPDATE)const DBMS_UPDATE_TYPE updateType)115 int updateCertLog( INOUT DBMS_INFO *dbmsInfo,
116 				   IN_ENUM( CRYPT_CERTACTION ) const CRYPT_CERTACTION_TYPE action,
117 				   IN_BUFFER_OPT( certIDlength ) const char *certID,
118 				   IN_LENGTH_SHORT_Z const int certIDlength,
119 				   IN_BUFFER_OPT( reqCertIDlength ) const char *reqCertID,
120 				   IN_LENGTH_SHORT_Z const int reqCertIDlength,
121 				   IN_BUFFER_OPT( subjCertIDlength ) const char *subjCertID,
122 				   IN_LENGTH_SHORT_Z const int subjCertIDlength,
123 				   IN_BUFFER_OPT( dataLength ) const void *data,
124 				   IN_LENGTH_SHORT_Z const int dataLength,
125 				   IN_ENUM( DBMS_UPDATE ) const DBMS_UPDATE_TYPE updateType )
126 	{
127 	BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
128 	char sqlBuffer[ MAX_SQL_QUERY_SIZE + 8 ];
129 	char certIDbuffer[ ENCODED_DBXKEYID_SIZE + 8 ];
130 	char encodedCertData[ MAX_ENCODED_CERT_SIZE + 8 ];
131 	const time_t boundDate = getApproxTime();
132 	int localCertIDlength = certIDlength, sqlOffset, sqlLength, boundDataIndex;
133 
134 	assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
135 	assert( ( certID == NULL && certIDlength == 0 ) || \
136 			isReadPtr( certID, certIDlength ) );
137 	assert( ( reqCertID == NULL && reqCertIDlength == 0 ) || \
138 			isReadPtr( reqCertID, reqCertIDlength ) );
139 	assert( ( subjCertID == NULL && subjCertIDlength == 0 ) || \
140 			isReadPtr( subjCertID, subjCertIDlength ) );
141 	assert( ( data == NULL && dataLength == 0 ) || \
142 			isReadPtr( data, dataLength ) );
143 
144 	REQUIRES( action > CRYPT_CERTACTION_NONE && \
145 			  action < CRYPT_CERTACTION_LAST );
146 	REQUIRES( ( certID == NULL && certIDlength == 0 ) || \
147 			  ( certID != NULL && \
148 				certIDlength > 0 && \
149 				certIDlength < MAX_INTLENGTH_SHORT ) );
150 	REQUIRES( ( reqCertID == NULL && reqCertIDlength == 0 ) || \
151 			  ( reqCertID != NULL && \
152 				reqCertIDlength > 0 && \
153 				reqCertIDlength < MAX_INTLENGTH_SHORT ) );
154 	REQUIRES( ( subjCertID == NULL && subjCertIDlength == 0 ) || \
155 			  ( subjCertID != NULL && \
156 				subjCertIDlength > 0 && \
157 				subjCertIDlength < MAX_INTLENGTH_SHORT ) );
158 	REQUIRES( ( data == NULL && dataLength == 0 ) || \
159 			  ( data != NULL && \
160 				dataLength > 0 && \
161 				dataLength < MAX_INTLENGTH_SHORT ) );
162 	REQUIRES( updateType > DBMS_UPDATE_NONE && \
163 			  updateType < DBMS_UPDATE_LAST );
164 
165 	/* Build up the necessary SQL format string required to insert the log
166 	   entry.  This is complicated somewhat by the fact that some of the
167 	   values may be NULL so we have to insert them by naming the columns
168 	   (some databases allow the use of the DEFAULT keyword but this isn't
169 	   standardised enough to be safe) */
170 	strlcpy_s( sqlBuffer, MAX_SQL_QUERY_SIZE,
171 			  "INSERT INTO certLog (action, actionTime, certID" );
172 	if( reqCertID != NULL )
173 		strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ", reqCertID" );
174 	if( subjCertID != NULL )
175 		strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ", subjCertID" );
176 	if( data != NULL )
177 		strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ", certData" );
178 	strlcat_s( sqlBuffer, MAX_SQL_QUERY_SIZE, ") VALUES (" );
179 	sqlOffset = strlen( sqlBuffer );
180 	sqlLength = MAX_SQL_QUERY_SIZE - sqlOffset;
181 	sprintf_s( sqlBuffer + sqlOffset, sqlLength, "%d, ?, ?", action );
182 	if( reqCertID != NULL )
183 		strlcat_s( sqlBuffer + sqlOffset, sqlLength, ", ?" );
184 	if( subjCertID != NULL )
185 		strlcat_s( sqlBuffer + sqlOffset, sqlLength, ", ?" );
186 	if( data != NULL )
187 		strlcat_s( sqlBuffer + sqlOffset, sqlLength, ", ?" );
188 	strlcat_s( sqlBuffer + sqlOffset, sqlLength, ")" );
189 
190 	/* If we're not worried about the certID we just insert a nonce value
191 	   which is used to meet the constraints for a unique entry.  In order
192 	   to ensure that it doesn't clash with a real certID we set the first
193 	   four characters to an out-of-band value */
194 	if( certID == NULL )
195 		{
196 		MESSAGE_DATA msgData;
197 		BYTE nonce[ KEYID_SIZE + 8 ];
198 		int status;
199 
200 		setMessageData( &msgData, nonce, KEYID_SIZE );
201 		status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
202 								  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
203 		if( cryptStatusOK( status ) )
204 			status = base64encode( certIDbuffer, ENCODED_DBXKEYID_SIZE,
205 								   &localCertIDlength, nonce, DBXKEYID_SIZE,
206 								   CRYPT_CERTTYPE_NONE );
207 		if( cryptStatusError( status ) )
208 			{
209 			/* Normally this is a should-never-occur error, however if
210 			   cryptlib has been shut down from another thread the kernel
211 			   will fail all non shutdown-related calls with a permission
212 			   error.  To avoid false alarms, we mask out failures due to
213 			   permission errors */
214 			assert( ( status == CRYPT_ERROR_PERMISSION ) || DEBUG_WARN );
215 			return( status );
216 			}
217 		memset( certIDbuffer, '-', 4 );
218 		certID = certIDbuffer;
219 		}
220 
221 	/* Set up the parameter information and update the certificate store
222 	   log */
223 	initBoundData( boundDataPtr );
224 	setBoundDataDate( boundDataPtr, 0, &boundDate );
225 	setBoundData( boundDataPtr, 1, certID, localCertIDlength );
226 	boundDataIndex = 2;
227 	if( reqCertID != NULL )
228 		setBoundData( boundDataPtr, boundDataIndex++, reqCertID,
229 					  reqCertIDlength );
230 	if( subjCertID != NULL )
231 		setBoundData( boundDataPtr, boundDataIndex++, subjCertID,
232 					  subjCertIDlength );
233 	if( data != NULL )
234 		{
235 		if( hasBinaryBlobs( dbmsInfo ) )
236 			{
237 			setBoundDataBlob( boundDataPtr, boundDataIndex,
238 							  data, dataLength );
239 			}
240 		else
241 			{
242 			int encodedDataLength, status;
243 
244 			status = base64encode( encodedCertData, MAX_ENCODED_CERT_SIZE,
245 								   &encodedDataLength, data, dataLength,
246 								   CRYPT_CERTTYPE_NONE );
247 			if( cryptStatusError( status ) )
248 				{
249 				DEBUG_DIAG(( "Couldn't base64-encode data" ));
250 				assert( DEBUG_WARN );
251 				return( status );
252 				}
253 			setBoundData( boundDataPtr, boundDataIndex,
254 						  encodedCertData, encodedDataLength );
255 			}
256 		}
257 	return( dbmsUpdate( sqlBuffer, boundDataPtr, updateType ) );
258 	}
259 
260 RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
updateCertErrorLog(INOUT DBMS_INFO * dbmsInfo,IN_ERROR const int errorStatus,IN_STRING const char * errorString,IN_BUFFER_OPT (certIDlength)const char * certID,IN_LENGTH_SHORT_Z const int certIDlength,IN_BUFFER_OPT (reqCertIDlength)const char * reqCertID,IN_LENGTH_SHORT_Z const int reqCertIDlength,IN_BUFFER_OPT (subjCertIDlength)const char * subjCertID,IN_LENGTH_SHORT_Z const int subjCertIDlength,IN_BUFFER_OPT (dataLength)const void * data,IN_LENGTH_SHORT_Z const int dataLength)261 int updateCertErrorLog( INOUT DBMS_INFO *dbmsInfo,
262 						IN_ERROR const int errorStatus,
263 						IN_STRING const char *errorString,
264 						IN_BUFFER_OPT( certIDlength ) const char *certID,
265 						IN_LENGTH_SHORT_Z const int certIDlength,
266 						IN_BUFFER_OPT( reqCertIDlength ) const char *reqCertID,
267 						IN_LENGTH_SHORT_Z const int reqCertIDlength,
268 						IN_BUFFER_OPT( subjCertIDlength ) const char *subjCertID,
269 						IN_LENGTH_SHORT_Z const int subjCertIDlength,
270 						IN_BUFFER_OPT( dataLength ) const void *data,
271 						IN_LENGTH_SHORT_Z const int dataLength )
272 	{
273 	STREAM stream;
274 	BYTE errorData[ 64 + MAX_CERT_SIZE + 8 ];
275 	const int errorStringLength = strlen( errorString );
276 	int errorDataLength DUMMY_INIT, status;
277 
278 	assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
279 	assert( ( certID == NULL && certIDlength == 0 ) || \
280 			isReadPtr( certID, certIDlength ) );
281 	assert( ( reqCertID == NULL && reqCertIDlength == 0 ) || \
282 			isReadPtr( reqCertID, reqCertIDlength ) );
283 	assert( ( subjCertID == NULL && subjCertIDlength == 0 ) || \
284 			isReadPtr( subjCertID, subjCertIDlength ) );
285 	assert( ( data == NULL && dataLength == 0 ) || \
286 			isReadPtr( data, dataLength ) );
287 
288 	ANALYSER_HINT_STRING( errorString );
289 
290 	REQUIRES( cryptStatusError( errorStatus ) );
291 	REQUIRES( errorString != NULL );
292 	REQUIRES( ( certID == NULL && certIDlength == 0 ) || \
293 			  ( certID != NULL && \
294 				certIDlength > 0 && \
295 				certIDlength < MAX_INTLENGTH_SHORT ) );
296 	REQUIRES( ( reqCertID == NULL && reqCertIDlength == 0 ) || \
297 			  ( reqCertID != NULL && \
298 				reqCertIDlength > 0 && \
299 				reqCertIDlength < MAX_INTLENGTH_SHORT ) );
300 	REQUIRES( ( subjCertID == NULL && subjCertIDlength == 0 ) || \
301 			  ( subjCertID != NULL && \
302 				subjCertIDlength > 0 && \
303 				subjCertIDlength < MAX_INTLENGTH_SHORT ) );
304 	REQUIRES( ( data == NULL && dataLength == 0 ) || \
305 			  ( data != NULL && \
306 				dataLength > 0 && \
307 				dataLength < MAX_INTLENGTH_SHORT ) );
308 
309 	/* Encode the error information:
310 
311 		SEQUENCE {
312 			errorStatus	INTEGER,
313 			errorString	UTF8String,
314 			certData	ANY OPTIONAL
315 			}
316 
317 	   Note that the buffer we use is slightly larger than MAX_CERT_SIZE in
318 	   order to accomodate the error status information alongside the
319 	   largest possible certificate, in theory this means that if the database
320 	   back-end doesn't support binary blobs there won't be enough room in
321 	   the logging code to text-encode this worst-case scenario, but the use
322 	   of non-binary-blob capable database should be fairly rare so it's
323 	   easier to just rely on the logging code to catch this unlikely
324 	   scenario than to try and special-case around it */
325 	sMemOpen( &stream, errorData, 64 + MAX_CERT_SIZE );
326 	writeSequence( &stream, sizeofShortInteger( -errorStatus ) + \
327 							( int ) sizeofObject( errorStringLength ) + \
328 							dataLength );
329 	writeShortInteger( &stream, -errorStatus, DEFAULT_TAG );
330 	status = writeCharacterString( &stream, errorString, errorStringLength,
331 								   BER_STRING_UTF8 );
332 	if( dataLength > 0 )
333 		status = swrite( &stream, data, dataLength );
334 	if( cryptStatusOK( status ) )
335 		errorDataLength = stell( &stream );
336 	sMemDisconnect( &stream );
337 	if( cryptStatusError( status ) )
338 		{
339 		sMemOpen( &stream, errorData, MAX_CERT_SIZE );
340 		writeSequence( &stream, sizeofObject( 1 ) + sizeofObject( 31 ) );
341 		writeShortInteger( &stream, -( CRYPT_ERROR_FAILED ), DEFAULT_TAG );
342 		status = writeCharacterString( &stream,
343 									   "Error writing error information", 31,
344 									   BER_STRING_UTF8 );
345 		if( cryptStatusOK( status ) )
346 			errorDataLength = stell( &stream );
347 		sMemDisconnect( &stream );
348 		}
349 	ENSURES( cryptStatusOK( status ) );
350 
351 	/* Update the certificate store log with the error information as the
352 	   data value */
353 	return( updateCertLog( dbmsInfo, CRYPT_CERTACTION_ERROR,
354 						   certID, certIDlength, reqCertID, reqCertIDlength,
355 						   subjCertID, subjCertIDlength,
356 						   errorData, errorDataLength, DBMS_UPDATE_NORMAL ) );
357 	}
358 
359 RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
updateCertErrorLogMsg(INOUT DBMS_INFO * dbmsInfo,IN_ERROR const int errorStatus,IN_STRING const char * errorString)360 int updateCertErrorLogMsg( INOUT DBMS_INFO *dbmsInfo,
361 						   IN_ERROR const int errorStatus,
362 						   IN_STRING const char *errorString )
363 	{
364 	assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
365 
366 	return( updateCertErrorLog( dbmsInfo, errorStatus, errorString,
367 								NULL, 0, NULL, 0, NULL, 0, NULL, 0 ) );
368 	}
369 
370 /****************************************************************************
371 *																			*
372 *							Miscellaneous CA Functions						*
373 *																			*
374 ****************************************************************************/
375 
376 /* Get the PKI user that originally authorised the issuance of a certificate.
377    This can involve chaining back through multiple generations of
378    certificates, for example to check authorisation on a revocation request
379    we might have to go through:
380 
381 	rev_req:	get reqCertID = update_req
382 	update_req:	get reqCertID = cert_req
383 	cert_req:	get reqCertID = init_req
384 	init_req:	get reqCertID = pki_user */
385 
386 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 5 ) ) \
caGetIssuingUser(INOUT DBMS_INFO * dbmsInfo,OUT_HANDLE_OPT CRYPT_CERTIFICATE * iPkiUser,IN_BUFFER (initialCertIDlength)const char * initialCertID,IN_LENGTH_FIXED (ENCODED_DBXKEYID_SIZE)const int initialCertIDlength,INOUT ERROR_INFO * errorInfo)387 int caGetIssuingUser( INOUT DBMS_INFO *dbmsInfo,
388 					  OUT_HANDLE_OPT CRYPT_CERTIFICATE *iPkiUser,
389 					  IN_BUFFER( initialCertIDlength ) const char *initialCertID,
390 					  IN_LENGTH_FIXED( ENCODED_DBXKEYID_SIZE ) \
391 							const int initialCertIDlength,
392 					  INOUT ERROR_INFO *errorInfo )
393 	{
394 	char certID[ ENCODED_DBXKEYID_SIZE + 8 ];
395 	int certIDlength, chainingLevel, dummy, status;
396 
397 	assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
398 	assert( isWritePtr( iPkiUser, sizeof( CRYPT_CERTIFICATE ) ) );
399 	assert( isReadPtr( initialCertID, ENCODED_DBXKEYID_SIZE ) );
400 
401 	REQUIRES( initialCertIDlength == ENCODED_DBXKEYID_SIZE );
402 	REQUIRES( errorInfo != NULL );
403 
404 	/* Clear return value */
405 	*iPkiUser = CRYPT_ERROR;
406 
407 	/* Walk through the chain of updates in the certificate store log until
408 	   we find the PKI user that authorised the first certificate issue */
409 	memcpy( certID, initialCertID, initialCertIDlength );
410 	certIDlength = initialCertIDlength;
411 	for( chainingLevel = 0; chainingLevel < FAILSAFE_ITERATIONS_MED;
412 		 chainingLevel++ )
413 		{
414 		BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
415 		BYTE certData[ MAX_QUERY_RESULT_SIZE + 8 ];
416 		int certDataLength;
417 
418 		/* Find out whether this is a PKI user.  The comparison for the
419 		   action type is a bit odd since some back-ends will return the
420 		   action as text and some as a binary numeric value.  Rather than
421 		   relying on the back-end glue code to perform the appropriate
422 		   conversion we just check for either value type */
423 		initBoundData( boundDataPtr );
424 		setBoundData( boundDataPtr, 0, certID, certIDlength );
425 		status = dbmsQuery(
426 			"SELECT action FROM certLog WHERE certID = ?",
427 							certData, MAX_QUERY_RESULT_SIZE, &certDataLength,
428 							boundDataPtr, DBMS_CACHEDQUERY_NONE,
429 							DBMS_QUERY_NORMAL );
430 		if( cryptStatusError( status ) )
431 			return( status );
432 		if( certData[ 0 ] == CRYPT_CERTACTION_ADDUSER || \
433 			certData[ 0 ] == TEXTCH_CERTACTION_ADDUSER )
434 			{
435 			/* We've found the PKI user, we're done */
436 			break;
437 			}
438 
439 		/* Find the certificate that was issued, recorded either as a
440 		   CERTACTION_CERT_CREATION for a multi-phase CMP-based certificate
441 		   creation or a CERTACTION_ISSUE_CERT for a one-step creation */
442 		status = dbmsQuery(
443 			"SELECT reqCertID FROM certLog WHERE certID = ?",
444 							certData, MAX_QUERY_RESULT_SIZE, &certDataLength,
445 							boundDataPtr, DBMS_CACHEDQUERY_NONE,
446 							DBMS_QUERY_NORMAL );
447 		if( cryptStatusError( status ) )
448 			return( status );
449 		certIDlength = min( certDataLength, ENCODED_DBXKEYID_SIZE );
450 		memcpy( certID, certData, certIDlength );
451 
452 		/* Find the request to issue this certificate.  For a CMP-based issue
453 		   this will have an authorising object (found in the next iteration
454 		   through the loop), for a one-step issue it won't */
455 		initBoundData( boundDataPtr );
456 		setBoundData( boundDataPtr, 0, certID, certIDlength );
457 		status = dbmsQuery(
458 			"SELECT reqCertID FROM certLog WHERE certID = ?",
459 							certData, MAX_QUERY_RESULT_SIZE, &certDataLength,
460 							boundDataPtr, DBMS_CACHEDQUERY_NONE,
461 							DBMS_QUERY_NORMAL );
462 		if( cryptStatusError( status ) )
463 			return( status );
464 		certIDlength = min( certDataLength, ENCODED_DBXKEYID_SIZE );
465 		memcpy( certID, certData, certIDlength );
466 		}
467 	if( chainingLevel >= FAILSAFE_ITERATIONS_MED )
468 		{
469 		/* We've chained through too many entries, bail out */
470 		return( CRYPT_ERROR_OVERFLOW );
471 		}
472 
473 	/* We've found the original PKI user, get the user information */
474 	return( getItemData( dbmsInfo, iPkiUser, &dummy, KEYMGMT_ITEM_PKIUSER,
475 						 CRYPT_IKEYID_CERTID, certID, certIDlength,
476 						 KEYMGMT_FLAG_NONE, errorInfo ) );
477 	}
478 
479 /****************************************************************************
480 *																			*
481 *						CA Certificate Management Interface					*
482 *																			*
483 ****************************************************************************/
484 
485 /* Perform a certificate management operation */
486 
487 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
certMgmtFunction(INOUT KEYSET_INFO * keysetInfoPtr,OUT_OPT_HANDLE_OPT CRYPT_CERTIFICATE * iCertificate,IN_HANDLE_OPT const CRYPT_CERTIFICATE caKey,IN_HANDLE_OPT const CRYPT_CERTIFICATE request,IN_ENUM (CRYPT_CERTACTION)const CRYPT_CERTACTION_TYPE action)488 static int certMgmtFunction( INOUT KEYSET_INFO *keysetInfoPtr,
489 							 OUT_OPT_HANDLE_OPT CRYPT_CERTIFICATE *iCertificate,
490 							 IN_HANDLE_OPT const CRYPT_CERTIFICATE caKey,
491 							 IN_HANDLE_OPT const CRYPT_CERTIFICATE request,
492 							 IN_ENUM( CRYPT_CERTACTION ) \
493 								const CRYPT_CERTACTION_TYPE action )
494 	{
495 	BOUND_DATA boundData[ BOUND_DATA_MAXITEMS ], *boundDataPtr = boundData;
496 	DBMS_INFO *dbmsInfo = keysetInfoPtr->keysetDBMS;
497 	char reqCertID[ ENCODED_DBXKEYID_SIZE + 8 ];
498 	int reqCertIDlength, status;
499 
500 	assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
501 	assert( ( iCertificate == NULL ) || \
502 			isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
503 
504 	REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
505 	REQUIRES( ( action != CRYPT_CERTACTION_ISSUE_CRL &&
506 				action != CRYPT_CERTACTION_CERT_CREATION ) || \
507 			  ( ( action == CRYPT_CERTACTION_ISSUE_CRL || \
508 				  action == CRYPT_CERTACTION_CERT_CREATION ) && \
509 				iCertificate != NULL ) );
510 			  /* An ISSUE_CERT can have iCertificate == NULL, which leaves
511 			     the issued certificate in the store without returning it
512 				 to the caller */
513 	REQUIRES( ( caKey == CRYPT_UNUSED ) || isHandleRangeValid( caKey ) );
514 	REQUIRES( ( request == CRYPT_UNUSED ) || isHandleRangeValid( request ) );
515 	REQUIRES( action > CRYPT_CERTACTION_NONE && \
516 			  action < CRYPT_CERTACTION_LAST );
517 
518 	/* In order for various SQL query strings to use the correct values the
519 	   type values have to match their text equivalents defined at the start
520 	   of this file.  Since we can't check this at compile time we have to
521 	   do it here via an assertion */
522 	static_assert_opt( TEXT_CERTTYPE_REQUEST_CERT[ 0 ] - '0' == \
523 							CRYPT_CERTTYPE_REQUEST_CERT, \
524 					   "SQL data type" );
525 	static_assert_opt( TEXT_CERTTYPE_REQUEST_REVOCATION[ 0 ] - '0' == \
526 							CRYPT_CERTTYPE_REQUEST_REVOCATION, \
527 					   "SQL data type" );
528 	static_assert_opt( TEXT_CERTACTION_CREATE[ 0 ] - '0' == \
529 							CRYPT_CERTACTION_CREATE, \
530 					   "SQL data type" );
531 	static_assert( TEXTCH_CERTACTION_ADDUSER - '0' == \
532 						CRYPT_CERTACTION_ADDUSER, \
533 				   "SQL data type" );
534 	static_assert_opt( TEXT_CERTACTION_REQUEST_CERT[ 0 ] - '0' == \
535 							CRYPT_CERTACTION_REQUEST_CERT, \
536 					   "SQL data type" );
537 	static_assert( TEXTCH_CERTACTION_REQUEST_CERT - '0' == \
538 						CRYPT_CERTACTION_REQUEST_CERT, \
539 				   "SQL data type" );
540 	static_assert_opt( TEXT_CERTACTION_REQUEST_RENEWAL[ 0 ] - '0' == \
541 							CRYPT_CERTACTION_REQUEST_RENEWAL, \
542 					   "SQL data type" );
543 	static_assert( TEXTCH_CERTACTION_REQUEST_RENEWAL - '0' == \
544 				   CRYPT_CERTACTION_REQUEST_RENEWAL, \
545 				   "SQL data type" );
546 	static_assert_opt( TEXT_CERTACTION_CERT_CREATION[ 0 ] - '0' == \
547 							CRYPT_CERTACTION_CERT_CREATION / 10, \
548 					   "SQL data type" );
549 	static_assert_opt( TEXT_CERTACTION_CERT_CREATION[ 1 ] - '0' == \
550 							CRYPT_CERTACTION_CERT_CREATION % 10, \
551 					   "SQL data type" );
552 
553 	/* Clear return value */
554 	if( iCertificate != NULL )
555 		*iCertificate = CRYPT_ERROR;
556 
557 	/* If it's a simple certificate expire or cleanup, there are no
558 	   parameters to check so we can perform the action immediately */
559 	if( action == CRYPT_CERTACTION_EXPIRE_CERT || \
560 		action == CRYPT_CERTACTION_CLEANUP )
561 		{
562 		REQUIRES( caKey == CRYPT_UNUSED );
563 		REQUIRES( request == CRYPT_UNUSED );
564 
565 		return( caCleanup( dbmsInfo, action, KEYSET_ERRINFO ) );
566 		}
567 
568 	/* If it's the completion of a certificate creation, process it */
569 	if( action == CRYPT_CERTACTION_CERT_CREATION_COMPLETE || \
570 		action == CRYPT_CERTACTION_CERT_CREATION_DROP || \
571 		action == CRYPT_CERTACTION_CERT_CREATION_REVERSE )
572 		{
573 		REQUIRES( caKey == CRYPT_UNUSED );
574 		REQUIRES( isHandleRangeValid( request ) );
575 
576 		return( caIssueCertComplete( dbmsInfo, request, action,
577 									 KEYSET_ERRINFO ) );
578 		}
579 
580 	/* Check that the CA key that we've been passed is in order.  These
581 	   checks are performed automatically during the issue process by the
582 	   kernel when we try and convert the request into a certificate,
583 	   however we perform them explicitly here so that we can return a more
584 	   meaningful error message to the caller */
585 	if( action == CRYPT_CERTACTION_ISSUE_CRL )
586 		{
587 		int value;
588 
589 		REQUIRES( isHandleRangeValid( caKey ) );
590 
591 		/* If we're issuing a CRL, the key must be capable of CRL signing */
592 		status = krnlSendMessage( caKey, IMESSAGE_GETATTRIBUTE, &value,
593 								  CRYPT_CERTINFO_KEYUSAGE );
594 		if( cryptStatusError( status ) || \
595 			!( value & CRYPT_KEYUSAGE_CRLSIGN ) )
596 			{
597 			retExtArg( CAMGMT_ARGERROR_CAKEY,
598 					   ( CAMGMT_ARGERROR_CAKEY, KEYSET_ERRINFO,
599 						 "CA certificate isn't valid for CRL signing" ) );
600 			}
601 		}
602 	else
603 		{
604 		/* For anything other than a revocation action (which just updates
605 		   the certificate store without doing anything else) the key must
606 		   be a CA key */
607 		if( action != CRYPT_CERTACTION_REVOKE_CERT )
608 			{
609 			REQUIRES( isHandleRangeValid( caKey ) );
610 
611 			status = krnlSendMessage( caKey, IMESSAGE_CHECK, NULL,
612 									  MESSAGE_CHECK_CA );
613 			if( cryptStatusError( status ) )
614 				{
615 				retExtArg( CAMGMT_ARGERROR_CAKEY,
616 						   ( CAMGMT_ARGERROR_CAKEY, KEYSET_ERRINFO,
617 							 "CA certificate isn't valid for certificate "
618 							 "signing" ) );
619 				}
620 			}
621 		}
622 
623 	/* If it's a CRL issue it's a read-only operation on the CRL store for
624 	   which we only need the CA certificate (there's no request involved) */
625 	if( action == CRYPT_CERTACTION_ISSUE_CRL )
626 		{
627 		REQUIRES( isHandleRangeValid( caKey ) );
628 		REQUIRES( request == CRYPT_UNUSED );
629 
630 		return( caIssueCRL( dbmsInfo, iCertificate, caKey, KEYSET_ERRINFO ) );
631 		}
632 
633 	/* Beyond this point we need to be working with a valid request */
634 	REQUIRES( isHandleRangeValid( request ) );
635 
636 	/* We're processing an action that requires an explicit certificate
637 	   request, perform further checks on the request */
638 	if( !checkRequest( request, action ) )
639 		{
640 		retExtArg( CAMGMT_ARGERROR_REQUEST,
641 				   ( CAMGMT_ARGERROR_REQUEST, KEYSET_ERRINFO,
642 					 "Certificate request information "
643 					 "inconsistent/invalid" ) );
644 		}
645 
646 	/* Make sure that the request is present in the request table in order
647 	   to issue a certificate for it.  Again, this will be checked later but
648 	   we can return a more meaningful error here */
649 	status = getKeyID( reqCertID, ENCODED_DBXKEYID_SIZE, &reqCertIDlength,
650 					   request, CRYPT_CERTINFO_FINGERPRINT_SHA1 );
651 	if( cryptStatusError( status ) )
652 		return( CAMGMT_ARGERROR_REQUEST );
653 	initBoundData( boundDataPtr );
654 	setBoundData( boundDataPtr, 0, reqCertID, reqCertIDlength );
655 	status = dbmsQuery(
656 		"SELECT certData FROM certRequests WHERE certID = ?",
657 						NULL, 0, NULL, boundDataPtr,
658 						DBMS_CACHEDQUERY_NONE, DBMS_QUERY_CHECK );
659 	if( cryptStatusError( status ) )
660 		{
661 		retExt( CRYPT_ERROR_NOTFOUND,
662 				( CRYPT_ERROR_NOTFOUND, KEYSET_ERRINFO,
663 				  "Certificate request doesn't correspond to any existing "
664 				  "request in the certificate store" ) );
665 		}
666 
667 	/* If it's a revocation request, process it */
668 	if( action == CRYPT_CERTACTION_REVOKE_CERT )
669 		{
670 		REQUIRES( caKey == CRYPT_UNUSED );
671 
672 		return( caRevokeCert( dbmsInfo, request, CRYPT_UNUSED,
673 							  CRYPT_CERTACTION_REVOKE_CERT, KEYSET_ERRINFO ) );
674 		}
675 
676 	/* It's a certificate issue request, issue the certificate */
677 	REQUIRES( action == CRYPT_CERTACTION_ISSUE_CERT || \
678 			  action == CRYPT_CERTACTION_CERT_CREATION );
679 	REQUIRES( isHandleRangeValid( caKey ) );
680 
681 	return( caIssueCert( dbmsInfo, iCertificate, caKey, request, action,
682 						 KEYSET_ERRINFO ) );
683 	}
684 
685 /* Set up the function pointers to the keyset methods */
686 
687 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
initDBMSCA(INOUT KEYSET_INFO * keysetInfoPtr)688 int initDBMSCA( INOUT KEYSET_INFO *keysetInfoPtr )
689 	{
690 	assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
691 
692 	REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
693 
694 	keysetInfoPtr->keysetDBMS->certMgmtFunction = certMgmtFunction;
695 
696 	return( CRYPT_OK );
697 	}
698 #endif /* USE_DBMS */
699