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, &currentEntry,
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, &currentEntry->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, &currentEntry, 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, &currentEntry->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, &currentEntry, 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 										  &currentEntry->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, &currentEntry->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( &currentEntry->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