1 /****************************************************************************
2 * *
3 * Certificate Revocation Routines *
4 * Copyright Peter Gutmann 1996-2008 *
5 * *
6 ****************************************************************************/
7
8 #if defined( INC_ALL )
9 #include "cert.h"
10 #include "asn1_ext.h"
11 #else
12 #include "cert/cert.h"
13 #include "enc_dec/asn1_ext.h"
14 #endif /* Compiler-specific includes */
15
16 /* The maximum length of ID that can be stored in a REVOCATION_INFO entry.
17 Larger IDs require external storage */
18
19 #define MAX_ID_SIZE 128
20
21 /* Usually when we add revocation information we perform various checks such
22 as making sure that we're not adding duplicate information, however when
23 processing the mega-CRLs from some CAs this becomes prohibitively
24 expensive. To solve this problem we perform checking up to a certain
25 number of entries and after that just drop in any further entries as is
26 in order to provide same-day service. The following value defines the
27 number of CRL entries at which we stop performing checks when we add new
28 entries */
29
30 #define CRL_SORT_LIMIT 1024
31
32 /* Context-specific tags for OCSP certificate identifier types */
33
34 enum { CTAG_OI_CERTIFICATE, CTAG_OI_CERTIDWITHSIG, CTAG_OI_RTCS };
35
36 /* OCSP certificate status values */
37
38 enum { OCSP_STATUS_NOTREVOKED, OCSP_STATUS_REVOKED, OCSP_STATUS_UNKNOWN };
39
40 #ifdef USE_CERTREV
41
42 /****************************************************************************
43 * *
44 * Add/Delete/Check Revocation Information *
45 * *
46 ****************************************************************************/
47
48 /* Find an entry in a revocation list. This is done using a linear search,
49 which isn't very optimal but anyone trying to do anything useful with
50 mega-CRLs (or with CRLs in general) is in more trouble than basic search
51 algorithm choice. In other words it doesn't really make much difference
52 whether we have an optimal or suboptimal implementation of a
53 fundamentally broken mechanism like CRLs.
54
55 The value is either a serialNumber or a hash of some form (issuerID,
56 certHash), we don't bother distinguishing the exact type since the
57 chances of a hash collision are virtually nonexistant */
58
59 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
findRevocationEntry(const REVOCATION_INFO * listPtr,OUT_PTR_OPT REVOCATION_INFO ** insertPoint,IN_BUFFER (valueLength)const void * value,IN_LENGTH_SHORT const int valueLength,const BOOLEAN sortEntries)60 static int findRevocationEntry( const REVOCATION_INFO *listPtr,
61 OUT_PTR_OPT REVOCATION_INFO **insertPoint,
62 IN_BUFFER( valueLength ) const void *value,
63 IN_LENGTH_SHORT const int valueLength,
64 const BOOLEAN sortEntries )
65 {
66 const REVOCATION_INFO *prevElement = NULL;
67 const int idCheck = checksumData( value, valueLength );
68 int iterationCount;
69
70 assert( isReadPtr( listPtr, sizeof( REVOCATION_INFO ) ) );
71 assert( isWritePtr( insertPoint, sizeof( REVOCATION_INFO * ) ) );
72 assert( isReadPtr( value, valueLength ) );
73
74 REQUIRES( valueLength > 0 && valueLength < MAX_INTLENGTH_SHORT );
75
76 /* Clear return value */
77 *insertPoint = NULL;
78
79 /* Find the correct place in the list to insert the new element and check
80 for duplicates. If requested we sort the entries by serial number
81 (or more generally data value) for no adequately explored reason
82 (some implementations can optimise the searching of CRLs based on
83 this but since there's no agreement on whether to do it or not you
84 can't tell whether it's safe to rely on it). In addition we bound
85 the loop with FAILSAFE_ITERATIONS_MAX rather than the more usual
86 FAILSAFE_ITERATIONS_LARGE since CRLs can grow enormous */
87 for( iterationCount = 0;
88 listPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
89 listPtr = listPtr->next, iterationCount++ )
90 {
91 if( ( sortEntries || idCheck == listPtr->idCheck ) && \
92 listPtr->idLength == valueLength )
93 {
94 const int compareStatus = memcmp( listPtr->id,
95 value, valueLength );
96
97 if( !compareStatus )
98 {
99 /* We found a matching entry, tell the caller which one it
100 is */
101 *insertPoint = ( REVOCATION_INFO * ) listPtr;
102 return( CRYPT_OK );
103 }
104 if( sortEntries && compareStatus > 0 )
105 break; /* Insert before this point */
106 }
107 else
108 {
109 if( sortEntries && listPtr->idLength > valueLength )
110 break; /* Insert before this point */
111 }
112
113 prevElement = listPtr;
114 }
115 ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
116
117 /* We can't find a matching entry, return the revocation entry that we
118 should insert the new value after */
119 *insertPoint = ( REVOCATION_INFO * ) prevElement;
120 return( CRYPT_ERROR_NOTFOUND );
121 }
122
123 /* Add an entry to a revocation list */
124
125 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
addRevocationEntry(INOUT_PTR REVOCATION_INFO ** listHeadPtrPtr,OUT_OPT_PTR_COND REVOCATION_INFO ** newEntryPosition,IN_KEYID_OPT const CRYPT_KEYID_TYPE valueType,IN_BUFFER (valueLength)const void * value,IN_LENGTH_SHORT const int valueLength,const BOOLEAN noCheck)126 int addRevocationEntry( INOUT_PTR REVOCATION_INFO **listHeadPtrPtr,
127 OUT_OPT_PTR_COND REVOCATION_INFO **newEntryPosition,
128 IN_KEYID_OPT const CRYPT_KEYID_TYPE valueType,
129 IN_BUFFER( valueLength ) const void *value,
130 IN_LENGTH_SHORT const int valueLength,
131 const BOOLEAN noCheck )
132 {
133 REVOCATION_INFO *newElement, *insertPoint;
134
135 assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
136 assert( isWritePtr( newEntryPosition, sizeof( REVOCATION_INFO * ) ) );
137 assert( isReadPtr( value, valueLength ) );
138
139 REQUIRES( valueType == CRYPT_KEYID_NONE || \
140 valueType == CRYPT_IKEYID_CERTID || \
141 valueType == CRYPT_IKEYID_ISSUERID || \
142 valueType == CRYPT_IKEYID_ISSUERANDSERIALNUMBER );
143 REQUIRES( valueLength > 0 && valueLength < MAX_INTLENGTH_SHORT );
144
145 /* Clear return value */
146 *newEntryPosition = NULL;
147
148 /* Find the insertion point for the new entry unless we're reading data
149 from a large pre-encoded CRL (indicated by the caller setting the
150 noCheck flag), in which case we just drop it in at the start. The
151 absence of checking is necessary in order to provide same-day service
152 for large CRLs */
153 if( !noCheck && *listHeadPtrPtr != NULL && \
154 cryptStatusOK( \
155 findRevocationEntry( *listHeadPtrPtr, &insertPoint, value,
156 valueLength, TRUE ) ) )
157 {
158 /* If we get an OK status it means that we've found an existing
159 entry that matches the one being added, we can't add it again */
160 return( CRYPT_ERROR_DUPLICATE );
161 }
162 else
163 {
164 /* Insert the new element at the start */
165 insertPoint = NULL;
166 }
167
168 /* Allocate memory for the new element and copy the information across */
169 if( ( newElement = ( REVOCATION_INFO * ) \
170 clAlloc( "addRevocationEntry", \
171 sizeof( REVOCATION_INFO ) + valueLength ) ) == NULL )
172 return( CRYPT_ERROR_MEMORY );
173 initVarStruct( newElement, REVOCATION_INFO, valueLength );
174 newElement->id = newElement->value; /* Varstruct expects field name 'value' */
175 newElement->idType = valueType;
176 memcpy( newElement->id, value, valueLength );
177 newElement->idLength = valueLength;
178 newElement->idCheck = checksumData( value, valueLength );
179
180 /* Insert the new element into the list */
181 insertSingleListElement( listHeadPtrPtr, insertPoint, newElement );
182 *newEntryPosition = newElement;
183
184 return( CRYPT_OK );
185 }
186
187 /* Delete a revocation list */
188
189 STDC_NONNULL_ARG( ( 1 ) ) \
deleteRevocationEntries(INOUT_PTR REVOCATION_INFO ** listHeadPtrPtr)190 void deleteRevocationEntries( INOUT_PTR REVOCATION_INFO **listHeadPtrPtr )
191 {
192 REVOCATION_INFO *entryListPtr = *listHeadPtrPtr;
193 int iterationCount;
194
195 assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
196
197 *listHeadPtrPtr = NULL;
198
199 /* Destroy any remaining list items */
200 for( iterationCount = 0;
201 entryListPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
202 iterationCount++ )
203 {
204 REVOCATION_INFO *itemToFree = entryListPtr;
205
206 entryListPtr = entryListPtr->next;
207 if( itemToFree->attributes != NULL )
208 deleteAttributes( &itemToFree->attributes );
209 zeroise( itemToFree, sizeof( REVOCATION_INFO ) );
210 clFree( "deleteRevocationEntries", itemToFree );
211 }
212 }
213
214 /* Prepare the entries in a revocation list prior to encoding them */
215
216 CHECK_RETVAL STDC_NONNULL_ARG( ( 3, 5, 6 ) ) \
prepareRevocationEntries(INOUT_OPT REVOCATION_INFO * listPtr,const time_t defaultTime,OUT_PTR_xCOND REVOCATION_INFO ** errorEntry,const BOOLEAN isSingleEntry,OUT_ENUM_OPT (CRYPT_ATTRIBUTE)CRYPT_ATTRIBUTE_TYPE * errorLocus,OUT_ENUM_OPT (CRYPT_ERRTYPE)CRYPT_ERRTYPE_TYPE * errorType)217 int prepareRevocationEntries( INOUT_OPT REVOCATION_INFO *listPtr,
218 const time_t defaultTime,
219 OUT_PTR_xCOND REVOCATION_INFO **errorEntry,
220 const BOOLEAN isSingleEntry,
221 OUT_ENUM_OPT( CRYPT_ATTRIBUTE ) \
222 CRYPT_ATTRIBUTE_TYPE *errorLocus,
223 OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
224 CRYPT_ERRTYPE_TYPE *errorType )
225 {
226 REVOCATION_INFO *revocationEntry;
227 const time_t currentTime = ( defaultTime > MIN_TIME_VALUE ) ? \
228 defaultTime : getApproxTime();
229 int value, iterationCount, status;
230
231 assert( listPtr == NULL || \
232 isReadPtr( listPtr, sizeof( REVOCATION_INFO ) ) );
233 assert( isWritePtr( errorEntry, sizeof( REVOCATION_INFO * ) ) );
234 assert( isWritePtr( errorLocus, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
235 assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
236
237 /* Clear return value */
238 *errorEntry = NULL;
239
240 /* If the revocation list is empty there's nothing to do */
241 if( listPtr == NULL )
242 return( CRYPT_OK );
243
244 /* Set the revocation time if this hasn't already been set. If there's a
245 default time set we use that otherwise we use the current time */
246 for( revocationEntry = listPtr, iterationCount = 0;
247 revocationEntry != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
248 revocationEntry = revocationEntry->next, iterationCount++ )
249 {
250 if( revocationEntry->revocationTime <= MIN_TIME_VALUE )
251 revocationEntry->revocationTime = currentTime;
252
253 /* Check whether the certificate was revoked with a reason of
254 neverValid, which requires special handling of dates because
255 X.509 doesn't formally define a neverValid reason, assuming that
256 all CAs are perfect and never issue certificates in error. The
257 general idea is to set the two to the same value with the
258 invalidity date (which should be earlier than the revocation date,
259 at least in a sanely-run CA) taking precedence. A revocation
260 with this reason code will in general only be issued by the
261 cryptlib CA (where it's required to handle problems in the CMP
262 protocol) and this always sets the invalidity date so in almost
263 all cases we'll be setting the revocation date to the
264 (CA-specified) invalidity date, which is the date of issue of the
265 certificate being revoked */
266 status = getAttributeFieldValue( revocationEntry->attributes,
267 CRYPT_CERTINFO_CRLREASON,
268 CRYPT_ATTRIBUTE_NONE, &value );
269 if( cryptStatusOK( status ) && value == CRYPT_CRLREASON_NEVERVALID )
270 {
271 time_t invalidityDate;
272
273 /* The certificate was revoked with the neverValid code, see if
274 there's an invalidity date present */
275 status = getAttributeFieldTime( revocationEntry->attributes,
276 CRYPT_CERTINFO_INVALIDITYDATE,
277 CRYPT_ATTRIBUTE_NONE,
278 &invalidityDate );
279 if( cryptStatusError( status ) )
280 {
281 /* There's no invalidity date present, set it to the same as
282 the revocation date */
283 status = addAttributeFieldString( &revocationEntry->attributes,
284 CRYPT_CERTINFO_INVALIDITYDATE,
285 CRYPT_ATTRIBUTE_NONE,
286 &revocationEntry->revocationTime,
287 sizeof( time_t ), 0,
288 errorLocus, errorType );
289 if( cryptStatusError( status ) )
290 {
291 /* Remember the entry that caused the problem */
292 *errorEntry = revocationEntry;
293 return( status );
294 }
295 }
296 else
297 {
298 /* There's an invalidity date present, make sure that the
299 revocation date is the same as the invalidity date */
300 revocationEntry->revocationTime = invalidityDate;
301 }
302 }
303
304 /* If we're only processing a single CRL entry rather than an
305 entire revocation list we're done */
306 if( isSingleEntry )
307 break;
308 }
309 ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
310
311 /* Check the attributes for each entry in a revocation list */
312 for( revocationEntry = listPtr, iterationCount = 0;
313 revocationEntry != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
314 revocationEntry = revocationEntry->next, iterationCount++ )
315 {
316 if( revocationEntry->attributes != NULL )
317 {
318 status = checkAttributes( ATTRIBUTE_CERTIFICATE,
319 revocationEntry->attributes,
320 errorLocus, errorType );
321 if( cryptStatusError( status ) )
322 {
323 /* Remember the entry that caused the problem */
324 *errorEntry = revocationEntry;
325 return( status );
326 }
327 }
328
329 /* If we're only processing a single CRL entry rather than an
330 entire revocation list we're done */
331 if( isSingleEntry )
332 break;
333 }
334 ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
335
336 return( CRYPT_OK );
337 }
338
339 /****************************************************************************
340 * *
341 * Revocation-Checking Functions *
342 * *
343 ****************************************************************************/
344
345 /* Check whether a certificate has been revoked */
346
347 typedef CHECK_RETVAL int ( *CHECKREVOCATIONFUNCTION ) \
348 ( const CERT_INFO *certInfoPtr, \
349 INOUT CERT_INFO *revocationInfoPtr ) \
350 STDC_NONNULL_ARG( ( 1, 2 ) );
351
352 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
checkRevocationCRL(const CERT_INFO * certInfoPtr,INOUT CERT_INFO * revocationInfoPtr)353 static int checkRevocationCRL( const CERT_INFO *certInfoPtr,
354 INOUT CERT_INFO *revocationInfoPtr )
355 {
356 CERT_REV_INFO *certRevInfo = revocationInfoPtr->cCertRev;
357 REVOCATION_INFO *revocationEntry;
358 int status;
359
360 assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
361 assert( isWritePtr( revocationInfoPtr, sizeof( CERT_INFO ) ) );
362
363 REQUIRES( revocationInfoPtr->type == CRYPT_CERTTYPE_CRL );
364
365 /* If there's no revocation information present then the certificate
366 can't have been revoked */
367 if( certRevInfo->revocations == NULL )
368 return( CRYPT_OK );
369
370 /* If the issuers differ then the certificate can't be in this CRL */
371 if( ( revocationInfoPtr->issuerDNsize != certInfoPtr->issuerDNsize || \
372 memcmp( revocationInfoPtr->issuerDNptr, certInfoPtr->issuerDNptr,
373 revocationInfoPtr->issuerDNsize ) ) )
374 return( CRYPT_OK );
375
376 /* Check whether there's an entry for this certificate in the list */
377 status = findRevocationEntry( certRevInfo->revocations, &revocationEntry,
378 certInfoPtr->cCertCert->serialNumber,
379 certInfoPtr->cCertCert->serialNumberLength,
380 FALSE );
381 if( cryptStatusError( status ) )
382 {
383 /* No CRL entry, the certificate is OK */
384 return( CRYPT_OK );
385 }
386 ENSURES( revocationEntry != NULL );
387
388 /* Select the entry that contains the revocation information and return
389 the certificate's status */
390 certRevInfo->currentRevocation = revocationEntry;
391 return( CRYPT_ERROR_INVALID );
392 }
393
394 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
checkRevocationOCSP(const CERT_INFO * certInfoPtr,INOUT CERT_INFO * revocationInfoPtr)395 static int checkRevocationOCSP( const CERT_INFO *certInfoPtr,
396 INOUT CERT_INFO *revocationInfoPtr )
397 {
398 CERT_REV_INFO *certRevInfo = revocationInfoPtr->cCertRev;
399 REVOCATION_INFO *revocationEntry;
400 int status;
401
402 assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
403 assert( isWritePtr( revocationInfoPtr, sizeof( CERT_INFO ) ) );
404
405 REQUIRES( revocationInfoPtr->type == CRYPT_CERTTYPE_OCSP_RESPONSE );
406
407 /* If there's no revocation information present then the certificate
408 can't have been revoked */
409 if( certRevInfo->revocations == NULL )
410 return( CRYPT_OK );
411
412 /* Check whether there's an entry for this certificate in the list */
413 status = findRevocationEntry( certRevInfo->revocations, &revocationEntry,
414 certInfoPtr->cCertCert->serialNumber,
415 certInfoPtr->cCertCert->serialNumberLength,
416 FALSE );
417 if( cryptStatusError( status ) )
418 {
419 /* No revocation entry, the certificate is OK */
420 return( CRYPT_OK );
421 }
422 ENSURES( revocationEntry != NULL );
423
424 /* Select the entry that contains the revocation information and return
425 the certificate's revocation status. Because of the inability of a
426 blacklist to return a proper status, we have to map anything other
427 than "revoked" to "OK" */
428 certRevInfo->currentRevocation = revocationEntry;
429 return( ( revocationEntry->status == CRYPT_OCSPSTATUS_REVOKED ) ? \
430 CRYPT_ERROR_INVALID : CRYPT_OK );
431 }
432
433 /* Check a certificate against a CRL or OCSP response (effectively the same
434 thing, an OCSP response is just a custom CRL) */
435
436 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
checkCRL(INOUT CERT_INFO * certInfoPtr,IN_HANDLE const CRYPT_CERTIFICATE iCryptCRL)437 int checkCRL( INOUT CERT_INFO *certInfoPtr,
438 IN_HANDLE const CRYPT_CERTIFICATE iCryptCRL )
439 {
440 CHECKREVOCATIONFUNCTION checkRevocationFunction;
441 CERT_INFO *crlInfoPtr;
442 int i, status;
443
444 assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
445
446 REQUIRES( isHandleRangeValid( iCryptCRL ) );
447
448 /* Check that the CRL/OCSP response is a complete signed CRL/response
449 and not just a newly-created object */
450 status = krnlAcquireObject( iCryptCRL, OBJECT_TYPE_CERTIFICATE,
451 ( void ** ) &crlInfoPtr,
452 CRYPT_ARGERROR_VALUE );
453 if( cryptStatusError( status ) )
454 return( status );
455 if( crlInfoPtr->certificate == NULL )
456 {
457 krnlReleaseObject( crlInfoPtr->objectHandle );
458 return( CRYPT_ERROR_NOTINITED );
459 }
460 ANALYSER_HINT( crlInfoPtr != NULL );
461 checkRevocationFunction = ( crlInfoPtr->type == CRYPT_CERTTYPE_CRL ) ? \
462 checkRevocationCRL : checkRevocationOCSP;
463
464 /* Check the base certificate against the CRL/OCSP response. If it's
465 been revoked or there's only a single certificate present, exit */
466 status = checkRevocationFunction( certInfoPtr, crlInfoPtr );
467 if( cryptStatusError( status ) || \
468 certInfoPtr->type != CRYPT_CERTTYPE_CERTCHAIN )
469 {
470 krnlReleaseObject( crlInfoPtr->objectHandle );
471 return( status );
472 }
473
474 /* It's a certificate chain, check every remaining certificate in the
475 chain against the CRL/OCSP response. In theory this is pointless
476 because a CRL can only contain information for a single certificate
477 in the chain, however the caller may have passed us a CRL for an
478 intermediate certificate (in which case the check for the leaf
479 certificate was pointless). In any case it's easier to just do the
480 check for all certificates than to determine which certificate the
481 CRL applies to so we check for all certificates */
482 for( i = 0; i < certInfoPtr->cCertCert->chainEnd && \
483 i < MAX_CHAINLENGTH; i++ )
484 {
485 CERT_INFO *certChainInfoPtr;
486
487 /* Check this certificate against the CRL/OCSP response */
488 status = krnlAcquireObject( certInfoPtr->cCertCert->chain[ i ],
489 OBJECT_TYPE_CERTIFICATE,
490 ( void ** ) &certChainInfoPtr,
491 CRYPT_ERROR_SIGNALLED );
492 if( cryptStatusOK( status ) )
493 {
494 status = checkRevocationFunction( certChainInfoPtr, crlInfoPtr );
495 krnlReleaseObject( certChainInfoPtr->objectHandle );
496 }
497
498 /* If the certificate has been revoked remember which one is the
499 revoked certificate and exit */
500 if( cryptStatusError( status ) )
501 {
502 certInfoPtr->cCertCert->chainPos = i;
503 break;
504 }
505 }
506 ENSURES( i < MAX_CHAINLENGTH );
507
508 krnlReleaseObject( crlInfoPtr->objectHandle );
509 return( status );
510 }
511
512 /****************************************************************************
513 * *
514 * Read/write CRL Information *
515 * *
516 ****************************************************************************/
517
518 /* Read/write CRL entries:
519
520 RevokedCert ::= SEQUENCE {
521 userCertificate CertificalSerialNumber,
522 revocationDate UTCTime,
523 extensions Extensions OPTIONAL
524 } */
525
526 CHECK_RETVAL_LENGTH_SHORT STDC_NONNULL_ARG( ( 1 ) ) \
sizeofCRLentry(INOUT REVOCATION_INFO * crlEntry)527 int sizeofCRLentry( INOUT REVOCATION_INFO *crlEntry )
528 {
529 int status;
530
531 assert( isWritePtr( crlEntry, sizeof( REVOCATION_INFO ) ) );
532
533 /* Remember the encoded attribute size for later when we write the
534 attributes */
535 status = \
536 crlEntry->attributeSize = sizeofAttributes( crlEntry->attributes,
537 CRYPT_CERTTYPE_NONE );
538 if( cryptStatusError( status ) )
539 return( status );
540
541 return( ( int ) sizeofObject( \
542 sizeofInteger( crlEntry->id, crlEntry->idLength ) + \
543 sizeofUTCTime() + \
544 ( ( crlEntry->attributeSize > 0 ) ? \
545 ( int ) sizeofObject( crlEntry->attributeSize ) : 0 ) ) );
546 }
547
548 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
readCRLentry(INOUT STREAM * stream,INOUT_PTR REVOCATION_INFO ** listHeadPtrPtr,IN_LENGTH_Z const int entryNo,OUT_ENUM_OPT (CRYPT_ATTRIBUTE)CRYPT_ATTRIBUTE_TYPE * errorLocus,OUT_ENUM_OPT (CRYPT_ERRTYPE)CRYPT_ERRTYPE_TYPE * errorType)549 int readCRLentry( INOUT STREAM *stream,
550 INOUT_PTR REVOCATION_INFO **listHeadPtrPtr,
551 IN_LENGTH_Z const int entryNo,
552 OUT_ENUM_OPT( CRYPT_ATTRIBUTE ) \
553 CRYPT_ATTRIBUTE_TYPE *errorLocus,
554 OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
555 CRYPT_ERRTYPE_TYPE *errorType )
556 {
557 REVOCATION_INFO *currentEntry;
558 BYTE serialNumber[ MAX_SERIALNO_SIZE + 8 ];
559 int serialNumberLength, endPos, length, status;
560 time_t revocationTime;
561
562 assert( isWritePtr( stream, sizeof( STREAM ) ) );
563 assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
564 assert( isWritePtr( errorLocus, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
565 assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
566
567 REQUIRES( entryNo >= 0 && entryNo < MAX_INTLENGTH );
568
569 /* Clear return values */
570 *errorLocus = CRYPT_ATTRIBUTE_NONE;
571 *errorType = CRYPT_ERRTYPE_NONE;
572
573 /* Determine the overall size of the entry */
574 status = readSequence( stream, &length );
575 if( cryptStatusError( status ) )
576 return( status );
577 endPos = stell( stream ) + length;
578
579 /* Read the integer component of the serial number (limited to a sane
580 length) and the revocation time */
581 readInteger( stream, serialNumber, MAX_SERIALNO_SIZE,
582 &serialNumberLength );
583 status = readUTCTime( stream, &revocationTime );
584 if( cryptStatusError( status ) )
585 return( status );
586
587 /* Add the entry to the revocation information list. The ID type isn't
588 quite an issueAndSerialNumber but the checking code eventually
589 converts it into this form using the supplied issuer certificate DN */
590 status = addRevocationEntry( listHeadPtrPtr, ¤tEntry,
591 CRYPT_IKEYID_ISSUERANDSERIALNUMBER,
592 serialNumber, serialNumberLength,
593 ( entryNo > CRL_SORT_LIMIT ) ? TRUE : FALSE );
594 if( cryptStatusError( status ) )
595 return( status );
596 currentEntry->revocationTime = revocationTime;
597
598 /* Read the extensions if there are any present. Since these are per-
599 entry extensions we read the extensions themselves as
600 CRYPT_CERTTYPE_NONE rather than CRYPT_CERTTYPE_CRL to make sure
601 that they're processed as required */
602 if( stell( stream ) <= endPos - MIN_ATTRIBUTE_SIZE )
603 {
604 status = readAttributes( stream, ¤tEntry->attributes,
605 CRYPT_CERTTYPE_NONE, length,
606 errorLocus, errorType );
607 if( cryptStatusError( status ) )
608 return( status );
609 }
610
611 return( CRYPT_OK );
612 }
613
614 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
writeCRLentry(INOUT STREAM * stream,const REVOCATION_INFO * crlEntry)615 int writeCRLentry( INOUT STREAM *stream,
616 const REVOCATION_INFO *crlEntry )
617 {
618 const int revocationLength = \
619 sizeofInteger( crlEntry->id, crlEntry->idLength ) + \
620 sizeofUTCTime() + \
621 ( ( crlEntry->attributeSize > 0 ) ? \
622 ( int ) sizeofObject( crlEntry->attributeSize ) : 0 );
623 int status;
624
625 assert( isWritePtr( stream, sizeof( STREAM ) ) );
626 assert( isReadPtr( crlEntry, sizeof( REVOCATION_INFO ) ) );
627
628 /* Write the CRL entry */
629 writeSequence( stream, revocationLength );
630 writeInteger( stream, crlEntry->id, crlEntry->idLength, DEFAULT_TAG );
631 status = writeUTCTime( stream, crlEntry->revocationTime, DEFAULT_TAG );
632 if( cryptStatusError( status ) || crlEntry->attributeSize <= 0 )
633 return( status );
634
635 /* Write the per-entry extensions. Since these are per-entry extensions
636 rather than overall CRL extensions we write them as CRYPT_CERTTYPE_NONE
637 rather than CRYPT_CERTTYPE_CRL to make sure that they're processed as
638 required */
639 return( writeAttributes( stream, crlEntry->attributes,
640 CRYPT_CERTTYPE_NONE, crlEntry->attributeSize ) );
641 }
642
643 /****************************************************************************
644 * *
645 * OCSP-specific Functions *
646 * *
647 ****************************************************************************/
648
649 /* Copy a revocation list from an OCSP request to a response */
650
651 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
copyRevocationEntries(INOUT_PTR REVOCATION_INFO ** destListHeadPtrPtr,const REVOCATION_INFO * srcListPtr)652 int copyRevocationEntries( INOUT_PTR REVOCATION_INFO **destListHeadPtrPtr,
653 const REVOCATION_INFO *srcListPtr )
654 {
655 const REVOCATION_INFO *srcListCursor;
656 REVOCATION_INFO *destListCursor DUMMY_INIT_PTR;
657 int iterationCount;
658
659 assert( isWritePtr( destListHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
660 assert( isReadPtr( srcListPtr, sizeof( REVOCATION_INFO ) ) );
661
662 /* Sanity check to make sure that the destination list doesn't already
663 exist, which would cause the copy loop below to fail */
664 REQUIRES( *destListHeadPtrPtr == NULL );
665
666 /* Copy all revocation entries from source to destination */
667 for( srcListCursor = srcListPtr, iterationCount = 0;
668 srcListCursor != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
669 srcListCursor = srcListCursor->next, iterationCount++ )
670 {
671 REVOCATION_INFO *newElement;
672
673 /* Allocate the new entry and copy the data from the existing one
674 across. We don't copy the attributes because there aren't any
675 that should be carried from request to response */
676 if( ( newElement = ( REVOCATION_INFO * ) \
677 clAlloc( "copyRevocationEntries",
678 sizeof( REVOCATION_INFO ) + \
679 srcListCursor->idLength ) ) == NULL )
680 return( CRYPT_ERROR_MEMORY );
681 copyVarStruct( newElement, srcListCursor, REVOCATION_INFO );
682 newElement->id = newElement->value; /* Varstruct expects field name 'value' */
683 newElement->attributes = NULL;
684 newElement->next = NULL;
685
686 /* Set the status to 'unknown' by default, this means that any
687 entries that we can't do anything with automatically get the
688 correct status associated with them */
689 newElement->status = CRYPT_OCSPSTATUS_UNKNOWN;
690
691 /* Link the new element into the list */
692 insertSingleListElement( destListHeadPtrPtr, destListCursor,
693 newElement );
694 destListCursor = newElement;
695 }
696 ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
697
698 return( CRYPT_OK );
699 }
700
701 /* Check the entries in an OCSP response object against a certificate store.
702 The semantics for this one are a bit odd, the source information for the
703 check is from a request but the destination information is in a response.
704 Since we don't have a copy-and-verify function we do the checking from
705 the response even though technically it's the request data that's being
706 checked */
707
708 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
checkOCSPResponse(INOUT CERT_INFO * certInfoPtr,IN_HANDLE const CRYPT_KEYSET iCryptKeyset)709 int checkOCSPResponse( INOUT CERT_INFO *certInfoPtr,
710 IN_HANDLE const CRYPT_KEYSET iCryptKeyset )
711 {
712 REVOCATION_INFO *revocationInfo;
713 BOOLEAN isRevoked = FALSE;
714 int iterationCount;
715
716 assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
717
718 REQUIRES( isHandleRangeValid( iCryptKeyset ) );
719
720 /* Walk down the list of revocation entries fetching status information
721 on each one from the certificate store */
722 for( revocationInfo = certInfoPtr->cCertRev->revocations,
723 iterationCount = 0;
724 revocationInfo != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
725 revocationInfo = revocationInfo->next, iterationCount++ )
726 {
727 CRYPT_KEYID_TYPE idType = revocationInfo->idType;
728 MESSAGE_KEYMGMT_INFO getkeyInfo;
729 CERT_INFO *crlEntryInfoPtr;
730 const BYTE *id = revocationInfo->id;
731 int status;
732
733 REQUIRES( revocationInfo->idType == CRYPT_KEYID_NONE || \
734 revocationInfo->idType == CRYPT_IKEYID_CERTID || \
735 revocationInfo->idType == CRYPT_IKEYID_ISSUERID );
736
737 /* If it's an OCSPv1 ID and there's no alternate ID information
738 present we can't really do anything with it because the one-way
739 hashing process required by the standard destroys the certificate
740 identifying information */
741 if( idType == CRYPT_KEYID_NONE )
742 {
743 if( revocationInfo->altIdType == CRYPT_KEYID_NONE )
744 {
745 revocationInfo->status = CRYPT_OCSPSTATUS_UNKNOWN;
746 continue;
747 }
748
749 /* There's an alternate ID present, use that instead */
750 idType = revocationInfo->altIdType;
751 id = revocationInfo->altID;
752 }
753
754 /* Determine the revocation status of the object. Unfortunately
755 because of the way OCSP returns status information we can't just
756 return a yes/no response but have to perform multiple queries to
757 determine whether a certificate is not revoked, revoked, or
758 unknown. Optimising the query strategy is complicated by the
759 fact that although in theory the most common status will be
760 not-revoked we could also get a large number of status-unknown
761 queries, for example if a widely-deployed implementation which is
762 pointed at a cryptlib-based server gets its ID-hashing wrong and
763 submits huge numbers of queries with IDs that match no known
764 certificate. The best we can do is assume that a not-revoked
765 status will be the most common and if that fails fall back to a
766 revoked status check */
767 setMessageKeymgmtInfo( &getkeyInfo, idType, id, KEYID_SIZE,
768 NULL, 0, KEYMGMT_FLAG_CHECK_ONLY );
769 status = krnlSendMessage( iCryptKeyset, IMESSAGE_KEY_GETKEY,
770 &getkeyInfo, KEYMGMT_ITEM_PUBLICKEY );
771 if( cryptStatusOK( status ) )
772 {
773 /* The certificate is present and not revoked/OK, we're done */
774 revocationInfo->status = CRYPT_OCSPSTATUS_NOTREVOKED;
775 continue;
776 }
777
778 /* The certificate isn't a currently active one, if it weren't for
779 the need to return the CRL-based OCSP status values we could just
780 return not-OK now but as it is we have to differentiate between
781 revoked and unknown so we perform a second query, this time of
782 the revocation information */
783 setMessageKeymgmtInfo( &getkeyInfo, idType, id, KEYID_SIZE,
784 NULL, 0, KEYMGMT_FLAG_NONE );
785 status = krnlSendMessage( iCryptKeyset, IMESSAGE_KEY_GETKEY,
786 &getkeyInfo, KEYMGMT_ITEM_REVOCATIONINFO );
787 if( cryptStatusError( status ) )
788 {
789 /* No revocation information found, status is unknown */
790 revocationInfo->status = CRYPT_OCSPSTATUS_UNKNOWN;
791 continue;
792 }
793
794 /* The certificate has been revoked, copy the revocation information
795 across from the CRL entry. Error handling here gets a bit
796 complicated because we're supposed to be a (relatively) reliable
797 server, in the (highly unlikely) event that the call fails it's
798 not clear that we should abort the entire operation just because
799 we can't get the specific details for a single object (we already
800 know its overall status, which is 'revoked') so we skip reporting
801 the low-level details if there's a problem and continue */
802 status = krnlAcquireObject( getkeyInfo.cryptHandle,
803 OBJECT_TYPE_CERTIFICATE,
804 ( void ** ) &crlEntryInfoPtr,
805 CRYPT_ERROR_SIGNALLED );
806 if( cryptStatusOK( status ) )
807 {
808 const REVOCATION_INFO *crlRevocationInfo;
809
810 crlRevocationInfo = crlEntryInfoPtr->cCertRev->revocations;
811 if( crlRevocationInfo != NULL )
812 {
813 revocationInfo->revocationTime = \
814 crlRevocationInfo->revocationTime;
815 if( crlRevocationInfo->attributes != NULL )
816 {
817 /* We don't check for problems in copying the attributes
818 since bailing out at this late stage is worse than
819 missing a few obscure annotations to the revocation */
820 ( void ) copyRevocationAttributes( &revocationInfo->attributes,
821 crlRevocationInfo->attributes );
822 }
823 }
824 krnlReleaseObject( crlEntryInfoPtr->objectHandle );
825 }
826 krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
827
828 /* Record the fact that we've seen at least one revoked certificate */
829 revocationInfo->status = CRYPT_OCSPSTATUS_REVOKED;
830 isRevoked = TRUE;
831 }
832 ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
833
834 /* If at least one certificate was revoked indicate this to the caller.
835 Note that if there are multiple certificates present in the query then
836 it's up to the caller to step through the list to find out which ones
837 were revoked */
838 return( isRevoked ? CRYPT_ERROR_INVALID : CRYPT_OK );
839 }
840
841 /****************************************************************************
842 * *
843 * Read/write OCSP Information *
844 * *
845 ****************************************************************************/
846
847 /* Read/write an OCSP certificate ID:
848
849 CertID ::= CHOICE {
850 certID SEQUENCE {
851 hashAlgo AlgorithmIdentifier,
852 iNameHash OCTET STRING, -- Hash of issuerName
853 iKeyHash OCTET STRING, -- Hash of issuer SPKI w/o tag+len
854 serialNo INTEGER
855 },
856 certificate [0] EXPLICIT [0] EXPLICIT Certificate,
857 certIdWithSignature
858 [1] EXPLICIT SEQUENCE {
859 iAndS IssuerAndSerialNumber,
860 tbsCertHash BIT STRING,
861 certSig SEQUENCE {
862 sigAlgo AlgorithmIdentifier,
863 sigVal BIT STRING
864 }
865 }
866 } */
867
868 CHECK_RETVAL_RANGE( 0, 1024 ) STDC_NONNULL_ARG( ( 1 ) ) \
sizeofOcspID(const REVOCATION_INFO * ocspEntry)869 static int sizeofOcspID( const REVOCATION_INFO *ocspEntry )
870 {
871 assert( isReadPtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
872
873 REQUIRES( ocspEntry->idType == CRYPT_KEYID_NONE );
874
875 /* For now we don't try and handle anything except the v1 ID since the
876 status of v2 is uncertain (it doesn't add anything to v1 except even
877 more broken IDs) */
878 return( ocspEntry->idLength );
879 }
880
881 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 5 ) ) \
882 static int readOcspID( INOUT STREAM *stream,
883 OUT_ENUM_OPT( CRYPT_KEYID ) CRYPT_KEYID_TYPE *idType,
884 OUT_BUFFER( idMaxLen, *idLen ) BYTE *id,
885 IN_LENGTH_SHORT_MIN( 16 ) const int idMaxLen,
886 OUT_LENGTH_BOUNDED_Z( idMaxLen ) int *idLen )
887 {
888 HASH_FUNCTION_ATOMIC hashFunctionAtomic;
889 void *dataPtr DUMMY_INIT_PTR;
890 const int hashedIDlen = min( idMaxLen, KEYID_SIZE );
891 int length DUMMY_INIT, tag, status;
892
893 assert( isWritePtr( stream, sizeof( STREAM ) ) );
894 assert( isWritePtr( idType, sizeof( CRYPT_KEYID_TYPE ) ) );
895 assert( isWritePtr( id, idMaxLen ) );
896 assert( isWritePtr( idLen, sizeof( int ) ) );
897
898 REQUIRES( idMaxLen >= 16 && idMaxLen < MAX_INTLENGTH_SHORT );
899
900 getHashAtomicParameters( CRYPT_ALGO_SHA1, 0, &hashFunctionAtomic, NULL );
901
902 /* Clear return values */
903 *idType = CRYPT_KEYID_NONE;
904 memset( id, 0, min( 16, idMaxLen ) );
905 *idLen = 0;
906
907 /* Read the ID */
908 status = tag = peekTag( stream );
909 if( cryptStatusError( status ) )
910 return( status );
911 switch( tag )
912 {
913 case BER_SEQUENCE:
914 /* We can't really do anything with v1 IDs since the one-way
915 hashing process destroys any chance of being able to work
916 with them and the fact that no useful certificate information
917 is hashed means that we can't use them to identify a
918 certificate. As a result the following ID type will always
919 produce a result of "unknown" */
920 *idType = CRYPT_KEYID_NONE;
921 status = getStreamObjectLength( stream, &length );
922 if( cryptStatusError( status ) )
923 return( status );
924 if( length < 8 )
925 return( CRYPT_ERROR_UNDERFLOW );
926 if( length > idMaxLen )
927 return( CRYPT_ERROR_OVERFLOW );
928 *idLen = length;
929 return( sread( stream, id, length ) );
930
931 case MAKE_CTAG( CTAG_OI_CERTIFICATE ):
932 /* Convert the certificate to a certID */
933 *idType = CRYPT_IKEYID_CERTID;
934 *idLen = hashedIDlen;
935 readConstructed( stream, NULL, CTAG_OI_CERTIFICATE );
936 status = readConstructed( stream, &length, 0 );
937 if( cryptStatusOK( status ) && length <= 0 )
938 status = CRYPT_ERROR_BADDATA; /* length may be 0 */
939 if( cryptStatusOK( status ) )
940 status = sMemGetDataBlock( stream, &dataPtr, length );
941 if( cryptStatusError( status ) )
942 return( status );
943 ANALYSER_HINT( dataPtr != NULL );
944 hashFunctionAtomic( id, hashedIDlen, dataPtr, length );
945 return( readUniversal( stream ) );
946
947 case MAKE_CTAG( CTAG_OI_CERTIDWITHSIG ):
948 /* A bizarro ID dreamed up by Denis Pinkas that manages to carry
949 over all the problems of the v1 ID without being compatible
950 with it. It's almost as unworkable as the v1 original but we
951 can convert the iAndS to an issuerID and use that */
952 *idType = CRYPT_IKEYID_ISSUERID;
953 *idLen = hashedIDlen;
954 readConstructed( stream, NULL, CTAG_OI_CERTIDWITHSIG );
955 status = readSequence( stream, NULL );
956 if( cryptStatusOK( status ) )
957 status = getStreamObjectLength( stream, &length );
958 if( cryptStatusOK( status ) )
959 status = sMemGetDataBlock( stream, &dataPtr, length );
960 if( cryptStatusError( status ) )
961 return( status );
962 ANALYSER_HINT( dataPtr != NULL );
963 hashFunctionAtomic( id, hashedIDlen, dataPtr, length );
964 sSkip( stream, length, MAX_INTLENGTH_SHORT ); /* issuerAndSerialNumber */
965 readUniversal( stream ); /* tbsCertificateHash */
966 return( readUniversal( stream ) ); /* certSignature */
967 }
968
969 return( CRYPT_ERROR_BADDATA );
970 }
971
972 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
writeOcspID(INOUT STREAM * stream,const REVOCATION_INFO * ocspEntry)973 static int writeOcspID( INOUT STREAM *stream,
974 const REVOCATION_INFO *ocspEntry )
975 {
976 assert( isWritePtr( stream, sizeof( STREAM ) ) );
977 assert( isReadPtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
978
979 return( swrite( stream, ocspEntry->id, ocspEntry->idLength ) );
980 }
981
982 /* Read/write an OCSP request entry:
983
984 Entry ::= SEQUENCE { -- Request
985 certID CertID,
986 extensions [0] EXPLICIT Extensions OPTIONAL
987 } */
988
989 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
sizeofOcspRequestEntry(INOUT REVOCATION_INFO * ocspEntry)990 int sizeofOcspRequestEntry( INOUT REVOCATION_INFO *ocspEntry )
991 {
992 const int ocspIDsize = sizeofOcspID( ocspEntry );
993 int status;
994
995 assert( isWritePtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
996
997 REQUIRES( ocspEntry->idType == CRYPT_KEYID_NONE );
998 REQUIRES( ocspIDsize > 0 && ocspIDsize < MAX_INTLENGTH_SHORT );
999
1000 /* Remember the encoded attribute size for later when we write the
1001 attributes */
1002 status = \
1003 ocspEntry->attributeSize = sizeofAttributes( ocspEntry->attributes,
1004 CRYPT_CERTTYPE_NONE );
1005 if( cryptStatusError( status ) )
1006 return( status );
1007
1008 return( ( int ) \
1009 sizeofObject( ocspIDsize + \
1010 ( ( ocspEntry->attributeSize > 0 ) ? \
1011 ( int ) \
1012 sizeofObject( \
1013 sizeofObject( ocspEntry->attributeSize ) ) : 0 ) ) );
1014 }
1015
1016 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
readOcspRequestEntry(INOUT STREAM * stream,INOUT_PTR REVOCATION_INFO ** listHeadPtrPtr,INOUT CERT_INFO * certInfoPtr)1017 int readOcspRequestEntry( INOUT STREAM *stream,
1018 INOUT_PTR REVOCATION_INFO **listHeadPtrPtr,
1019 INOUT CERT_INFO *certInfoPtr )
1020 {
1021 const ATTRIBUTE_PTR *attributePtr;
1022 REVOCATION_INFO *currentEntry;
1023 STREAM certIdStream;
1024 BYTE idBuffer[ MAX_ID_SIZE + 8 ];
1025 CRYPT_KEYID_TYPE idType;
1026 void *certIdPtr;
1027 int endPos, certIdLength, length, status;
1028
1029 assert( isWritePtr( stream, sizeof( STREAM ) ) );
1030 assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
1031 assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
1032
1033 /* Determine the overall size of the entry */
1034 status = readSequence( stream, &length );
1035 if( cryptStatusError( status ) )
1036 return( status );
1037 endPos = stell( stream ) + length;
1038
1039 /* Read the ID information */
1040 status = readOcspID( stream, &idType, idBuffer, MAX_ID_SIZE, &length );
1041 if( cryptStatusError( status ) )
1042 return( status );
1043
1044 /* Add the entry to the revocation information list */
1045 status = addRevocationEntry( listHeadPtrPtr, ¤tEntry, idType,
1046 idBuffer, length, FALSE );
1047 if( cryptStatusError( status ) || \
1048 stell( stream ) > endPos - MIN_ATTRIBUTE_SIZE )
1049 return( status );
1050
1051 /* Read the extensions. Since these are per-entry extensions rather
1052 than overall OCSP extensions we read the wrapper here and read the
1053 extensions themselves as CRYPT_CERTTYPE_NONE rather than
1054 CRYPT_CERTTYPE_OCSP to make sure that they're processed as required.
1055 Note that these are per-request-entry extensions rather than overall
1056 per-request extensions so the tag is CTAG_OR_SR_EXTENSIONS rather
1057 than CTAG_OR_EXTENSIONS */
1058 status = readConstructed( stream, &length, CTAG_OR_SR_EXTENSIONS );
1059 if( cryptStatusOK( status ) && length > 0 )
1060 {
1061 status = readAttributes( stream, ¤tEntry->attributes,
1062 CRYPT_CERTTYPE_NONE, length,
1063 &certInfoPtr->errorLocus,
1064 &certInfoPtr->errorType );
1065 }
1066 if( cryptStatusError( status ) )
1067 return( status );
1068
1069 /* OCSPv1 uses a braindamaged certificate identification method that
1070 breaks the certificate information up into bits and hashes some while
1071 leaving others intact, making it impossible to identify the
1072 certificate from it. To try and fix this, if the request includes an
1073 ESSCertID we use that to make it look like there was a proper ID
1074 present */
1075 if( currentEntry->idType != CRYPT_KEYID_NONE )
1076 return( CRYPT_OK ); /* Proper ID present, we're done */
1077 attributePtr = findAttribute( currentEntry->attributes,
1078 CRYPT_CERTINFO_CMS_SIGNINGCERT_ESSCERTID,
1079 TRUE );
1080 if( attributePtr == NULL )
1081 return( CRYPT_OK ); /* No ESSCertID present, can't continue */
1082
1083 /* Extract the ID information from the ESSCertID and save it alongside
1084 the OCSP ID, which we need to retain for use in the response */
1085 status = getAttributeDataPtr( attributePtr, &certIdPtr, &certIdLength );
1086 if( cryptStatusError( status ) )
1087 return( status );
1088 ANALYSER_HINT( certIdPtr != NULL );
1089 sMemConnect( &certIdStream, certIdPtr, certIdLength );
1090 readSequence( &certIdStream, NULL );
1091 status = readOctetString( &certIdStream, idBuffer, &length, KEYID_SIZE,
1092 KEYID_SIZE );
1093 if( cryptStatusOK( status ) )
1094 {
1095 currentEntry->altIdType = CRYPT_IKEYID_CERTID;
1096 memcpy( currentEntry->altID, idBuffer, length );
1097 }
1098 sMemDisconnect( &certIdStream );
1099
1100 return( CRYPT_OK );
1101 }
1102
1103 STDC_NONNULL_ARG( ( 1, 2 ) ) \
writeOcspRequestEntry(INOUT STREAM * stream,const REVOCATION_INFO * ocspEntry)1104 int writeOcspRequestEntry( INOUT STREAM *stream,
1105 const REVOCATION_INFO *ocspEntry )
1106 {
1107 const int attributeSize = ( ocspEntry->attributeSize > 0 ) ? \
1108 ( int ) sizeofObject( \
1109 sizeofObject( ocspEntry->attributeSize ) ) : 0;
1110 const int ocspIDsize = sizeofOcspID( ocspEntry );
1111 int status;
1112
1113 assert( isWritePtr( stream, sizeof( STREAM ) ) );
1114 assert( isReadPtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
1115
1116 if( cryptStatusError( ocspIDsize ) )
1117 return( ocspIDsize );
1118
1119 /* Write the header and ID information */
1120 writeSequence( stream, ocspIDsize + attributeSize );
1121 status = writeOcspID( stream, ocspEntry );
1122 if( cryptStatusError( status ) || ocspEntry->attributeSize <= 0 )
1123 return( status );
1124
1125 /* Write the per-entry extensions. Since these are per-entry extensions
1126 rather than overall OCSP extensions we write them as
1127 CRYPT_CERTTYPE_NONE rather than CRYPT_CERTTYPE_OCSP to make sure that
1128 they're processed as required. Note that these are per-request-entry
1129 extensions rather than overall per-request extensions so the tag is
1130 CTAG_OR_SR_EXTENSIONS rather than CTAG_OR_EXTENSIONS */
1131 writeConstructed( stream, sizeofObject( ocspEntry->attributeSize ),
1132 CTAG_OR_SR_EXTENSIONS );
1133 return( writeAttributes( stream, ocspEntry->attributes,
1134 CRYPT_CERTTYPE_NONE, ocspEntry->attributeSize ) );
1135 }
1136
1137 /* Read/write an OCSP response entry:
1138
1139 Entry ::= SEQUENCE {
1140 certID CertID,
1141 certStatus CHOICE {
1142 notRevd [0] IMPLICIT NULL,
1143 revd [1] SEQUENCE {
1144 revTime GeneralizedTime,
1145 revReas [0] EXPLICIT CRLReason Optional
1146 },
1147 unknown [2] IMPLICIT NULL
1148 },
1149 thisUpdate GeneralizedTime,
1150 extensions [1] EXPLICIT Extensions OPTIONAL
1151 } */
1152
1153 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
sizeofOcspResponseEntry(INOUT REVOCATION_INFO * ocspEntry)1154 int sizeofOcspResponseEntry( INOUT REVOCATION_INFO *ocspEntry )
1155 {
1156 const int ocspIDsize = sizeofOcspID( ocspEntry );
1157 int certStatusSize = 0, status;
1158
1159 assert( isWritePtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
1160
1161 REQUIRES( ocspIDsize > 0 && ocspIDsize < MAX_INTLENGTH_SHORT );
1162
1163 /* Remember the encoded attribute size for later when we write the
1164 attributes */
1165 status = \
1166 ocspEntry->attributeSize = sizeofAttributes( ocspEntry->attributes,
1167 CRYPT_CERTTYPE_NONE );
1168 if( cryptStatusError( status ) )
1169 return( status );
1170
1171 /* Determine the size of the certificate status field */
1172 certStatusSize = ( ocspEntry->status != CRYPT_OCSPSTATUS_REVOKED ) ? \
1173 sizeofNull() : ( int ) sizeofObject( sizeofGeneralizedTime() );
1174
1175 return( ( int ) \
1176 sizeofObject( ocspIDsize + \
1177 certStatusSize + sizeofGeneralizedTime() ) + \
1178 ( ( ocspEntry->attributeSize > 0 ) ? \
1179 ( int ) sizeofObject( ocspEntry->attributeSize ) : 0 ) );
1180 }
1181
1182 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
readOcspResponseEntry(INOUT STREAM * stream,INOUT_PTR REVOCATION_INFO ** listHeadPtrPtr,INOUT CERT_INFO * certInfoPtr)1183 int readOcspResponseEntry( INOUT STREAM *stream,
1184 INOUT_PTR REVOCATION_INFO **listHeadPtrPtr,
1185 INOUT CERT_INFO *certInfoPtr )
1186 {
1187 REVOCATION_INFO *currentEntry;
1188 BYTE idBuffer[ MAX_ID_SIZE + 8 ];
1189 CRYPT_KEYID_TYPE idType;
1190 int endPos, length, crlReason = 0, tag, status;
1191
1192 assert( isWritePtr( stream, sizeof( STREAM ) ) );
1193 assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
1194 assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
1195
1196 /* Determine the overall size of the entry */
1197 status = readSequence( stream, &length );
1198 if( cryptStatusError( status ) )
1199 return( status );
1200 endPos = stell( stream ) + length;
1201
1202 /* Read the ID information */
1203 status = readOcspID( stream, &idType, idBuffer, MAX_ID_SIZE, &length );
1204 if( cryptStatusError( status ) )
1205 return( status );
1206
1207 /* If we're reading an OCSPv1 ID then the ability to recover any useful
1208 elements in it apart from the serial number has been destroyed by
1209 passing them through a one-way hash function, so we dig down into the
1210 ID to extract just the serial number. See the comment in
1211 checkRevocationOCSP() on the implications of this */
1212 if( idType == CRYPT_KEYID_NONE )
1213 {
1214 STREAM memStream;
1215 BYTE integer[ MAX_SERIALNO_SIZE + 8 ];
1216 int integerLength DUMMY_INIT;
1217
1218 static_assert( MAX_SERIALNO_SIZE <= MAX_ID_SIZE, "Buffer size" );
1219
1220 /* Dig down into the ID to extract the serial number and replace the
1221 overall ID with that */
1222 sMemConnect( &memStream, idBuffer, length );
1223 readSequence( &memStream, NULL );
1224 readUniversal( &memStream ); /* AlgoID */
1225 readUniversal( &memStream ); /* Hashed issuer DN */
1226 status = readUniversal( &memStream ); /* Hashed partial issuer key */
1227 if( cryptStatusOK( status ) )
1228 {
1229 status = readInteger( &memStream, integer, MAX_SERIALNO_SIZE,
1230 &integerLength );
1231 }
1232 sMemDisconnect( &memStream );
1233 if( cryptStatusError( status ) )
1234 return( status );
1235 if( integerLength <= 0 )
1236 {
1237 /* Some certificates may have a serial number of zero, which is
1238 turned into a zero-length integer by the ASN.1 read code
1239 since it truncates leading zeroes that are added due to ASN.1
1240 encoding requirements. If we get a zero-length integer we
1241 turn it into a single zero byte */
1242 idBuffer[ 0 ] = 0;
1243 length = 1;
1244 }
1245 else
1246 {
1247 memcpy( idBuffer, integer, integerLength );
1248 length = integerLength;
1249 }
1250 }
1251
1252 /* Add the entry to the revocation information list */
1253 status = addRevocationEntry( listHeadPtrPtr, ¤tEntry, idType,
1254 idBuffer, length, FALSE );
1255 if( cryptStatusError( status ) )
1256 return( status );
1257
1258 /* Read the status information */
1259 status = tag = peekTag( stream );
1260 if( cryptStatusError( status ) )
1261 return( status );
1262 switch( tag )
1263 {
1264 case MAKE_CTAG_PRIMITIVE( OCSP_STATUS_NOTREVOKED ):
1265 currentEntry->status = CRYPT_OCSPSTATUS_NOTREVOKED;
1266 status = readUniversal( stream );
1267 break;
1268
1269 case MAKE_CTAG( OCSP_STATUS_REVOKED ):
1270 currentEntry->status = CRYPT_OCSPSTATUS_REVOKED;
1271 readConstructed( stream, NULL, OCSP_STATUS_REVOKED );
1272 status = readGeneralizedTime( stream,
1273 ¤tEntry->revocationTime );
1274 if( checkStatusPeekTag( stream, status, tag ) && \
1275 tag == MAKE_CTAG( 0 ) )
1276 {
1277 /* Remember the crlReason for later */
1278 readConstructed( stream, NULL, 0 );
1279 status = readEnumerated( stream, &crlReason );
1280 }
1281 break;
1282
1283 case MAKE_CTAG_PRIMITIVE( OCSP_STATUS_UNKNOWN ):
1284 currentEntry->status = CRYPT_OCSPSTATUS_UNKNOWN;
1285 status = readUniversal( stream );
1286 break;
1287
1288 default:
1289 return( CRYPT_ERROR_BADDATA );
1290 }
1291 if( cryptStatusError( status ) )
1292 return( status );
1293 status = readGeneralizedTime( stream, &certInfoPtr->startTime );
1294 if( checkStatusPeekTag( stream, status, tag ) && \
1295 tag == MAKE_CTAG( 0 ) )
1296 {
1297 readConstructed( stream, NULL, 0 );
1298 status = readGeneralizedTime( stream, &certInfoPtr->endTime );
1299 }
1300 if( cryptStatusError( status ) )
1301 return( status );
1302
1303 /* Read the extensions if there are any present. Since these are per-
1304 entry extensions rather than overall OCSP extensions we read the
1305 wrapper here and read the extensions themselves as CRYPT_CERTTYPE_NONE
1306 rather than CRYPT_CERTTYPE_OCSP to make sure that they're processed
1307 as required */
1308 if( stell( stream ) <= endPos - MIN_ATTRIBUTE_SIZE )
1309 {
1310 status = readConstructed( stream, &length, CTAG_OP_EXTENSIONS );
1311 if( cryptStatusOK( status ) && length > 0 )
1312 {
1313 status = readAttributes( stream, ¤tEntry->attributes,
1314 CRYPT_CERTTYPE_NONE, length,
1315 &certInfoPtr->errorLocus, &certInfoPtr->errorType );
1316 }
1317 if( cryptStatusError( status ) )
1318 return( status );
1319 }
1320
1321 /* If there's a crlReason present in the response and none as an
1322 extension add it as an extension (OCSP allows the same information
1323 to be specified in two different places, to make it easier we always
1324 return it as a crlReason extension, however some implementations
1325 return it in both places so we have to make sure that we don't try and
1326 add it a second time) */
1327 if( findAttributeField( currentEntry->attributes,
1328 CRYPT_CERTINFO_CRLREASON,
1329 CRYPT_ATTRIBUTE_NONE ) == NULL )
1330 {
1331 status = addAttributeField( ¤tEntry->attributes,
1332 CRYPT_CERTINFO_CRLREASON,
1333 CRYPT_ATTRIBUTE_NONE, crlReason, 0,
1334 &certInfoPtr->errorLocus,
1335 &certInfoPtr->errorType );
1336 if( cryptStatusError( status ) )
1337 return( status );
1338 }
1339
1340 return( CRYPT_OK );
1341 }
1342
1343 STDC_NONNULL_ARG( ( 1, 2 ) ) \
writeOcspResponseEntry(INOUT STREAM * stream,const REVOCATION_INFO * ocspEntry,const time_t entryTime)1344 int writeOcspResponseEntry( INOUT STREAM *stream,
1345 const REVOCATION_INFO *ocspEntry,
1346 const time_t entryTime )
1347 {
1348 const int ocspIDsize = sizeofOcspID( ocspEntry );
1349 int certStatusSize, status;
1350
1351 assert( isWritePtr( stream, sizeof( STREAM ) ) );
1352 assert( isReadPtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
1353
1354 REQUIRES( ocspIDsize > 0 && ocspIDsize < MAX_INTLENGTH_SHORT );
1355
1356 /* Determine the size of the certificate status field */
1357 certStatusSize = ( ocspEntry->status != CRYPT_OCSPSTATUS_REVOKED ) ? \
1358 sizeofNull() : ( int ) sizeofObject( sizeofGeneralizedTime() );
1359
1360 /* Write the header and ID information */
1361 writeSequence( stream, ocspIDsize + \
1362 certStatusSize + sizeofGeneralizedTime() + \
1363 ( ( ocspEntry->attributeSize > 0 ) ? \
1364 ( int ) sizeofObject( ocspEntry->attributeSize ) : 0 ) );
1365 status = writeOcspID( stream, ocspEntry );
1366 if( cryptStatusError( status ) )
1367 return( status );
1368
1369 /* Write the certificate status */
1370 if( ocspEntry->status == CRYPT_OCSPSTATUS_REVOKED )
1371 {
1372 writeConstructed( stream, sizeofGeneralizedTime(),
1373 CRYPT_OCSPSTATUS_REVOKED );
1374 writeGeneralizedTime( stream, ocspEntry->revocationTime,
1375 DEFAULT_TAG );
1376 }
1377 else
1378 {
1379 /* An other-than-revoked status is communicated as a tagged NULL
1380 value. For no known reason this portion of OCSP uses implicit
1381 tagging, since it's the one part of the PDU in which an
1382 explicit tag would actually make sense */
1383 writeNull( stream, ocspEntry->status );
1384 }
1385
1386 /* Write the current update time, which should be the current time.
1387 Since new status information is always available we don't write a
1388 nextUpdate time (in fact there is some disagreement over whether
1389 these times are based on CRL information, responder information, the
1390 response dispatch time, or a mixture of the above, implementations
1391 can be found that return all manner of peculiar values here) */
1392 status = writeGeneralizedTime( stream, entryTime, DEFAULT_TAG );
1393 if( cryptStatusError( status ) || ocspEntry->attributeSize <= 0 )
1394 return( status );
1395
1396 /* Write the per-entry extensions. Since these are per-entry extensions
1397 rather than overall OCSP extensions we write them as
1398 CRYPT_CERTTYPE_NONE rather than CRYPT_CERTTYPE_OCSP to make sure that
1399 they're processed as required */
1400 return( writeAttributes( stream, ocspEntry->attributes,
1401 CRYPT_CERTTYPE_NONE, ocspEntry->attributeSize ) );
1402 }
1403 #endif /* USE_CERTREV */
1404