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