Browse Source

[crypto] Accept OCSP responses containing multiple certificates

RFC2560 mandates that a valid OCSP response will contain exactly one
relevant certificate.  However, some OCSP responders include
extraneous certificates.  iPXE currently assumes that the first
certificate in the OCSP response is the relevant certificate; OCSP
checks will therefore fail if the responder includes the extraneous
certificates before the relevant certificate.

Fix by using the responder ID to identify the relevant certificate.

Reported-by: Christian Stroehmeier <stroemi@mail.uni-paderborn.de>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 11 years ago
parent
commit
0036fdd5c5
2 changed files with 151 additions and 13 deletions
  1. 132
    13
      src/crypto/ocsp.c
  2. 19
    0
      src/include/ipxe/ocsp.h

+ 132
- 13
src/crypto/ocsp.c View File

@@ -58,6 +58,21 @@ FILE_LICENCE ( GPL2_OR_LATER );
58 58
 #define EINFO_EACCES_STALE						\
59 59
 	__einfo_uniqify ( EINFO_EACCES, 0x04,				\
60 60
 			  "Stale (or premature) OCSP repsonse" )
61
+#define EACCES_NO_RESPONDER						\
62
+	__einfo_error ( EINFO_EACCES_NO_RESPONDER )
63
+#define EINFO_EACCES_NO_RESPONDER					\
64
+	__einfo_uniqify ( EINFO_EACCES, 0x05,				\
65
+			  "Missing OCSP responder certificate" )
66
+#define ENOTSUP_RESPONSE_TYPE						\
67
+	__einfo_error ( EINFO_ENOTSUP_RESPONSE_TYPE )
68
+#define EINFO_ENOTSUP_RESPONSE_TYPE					\
69
+	__einfo_uniqify ( EINFO_ENOTSUP, 0x01,				\
70
+			  "Unsupported OCSP response type" )
71
+#define ENOTSUP_RESPONDER_ID						\
72
+	__einfo_error ( EINFO_ENOTSUP_RESPONDER_ID )
73
+#define EINFO_ENOTSUP_RESPONDER_ID					\
74
+	__einfo_uniqify ( EINFO_ENOTSUP, 0x02,				\
75
+			  "Unsupported OCSP responder ID" )
61 76
 #define EPROTO_MALFORMED_REQUEST					\
62 77
 	__einfo_error ( EINFO_EPROTO_MALFORMED_REQUEST )
63 78
 #define EINFO_EPROTO_MALFORMED_REQUEST					\
@@ -355,12 +370,94 @@ static int ocsp_parse_response_type ( struct ocsp_check *ocsp,
355 370
 		DBGC ( ocsp, "OCSP %p \"%s\" response type not supported:\n",
356 371
 		       ocsp, ocsp->cert->subject.name );
357 372
 		DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
358
-		return -ENOTSUP;
373
+		return -ENOTSUP_RESPONSE_TYPE;
359 374
 	}
360 375
 
361 376
 	return 0;
362 377
 }
363 378
 
379
+/**
380
+ * Compare responder's certificate name
381
+ *
382
+ * @v ocsp		OCSP check
383
+ * @v cert		Certificate
384
+ * @ret difference	Difference as returned by memcmp()
385
+ */
386
+static int ocsp_compare_responder_name ( struct ocsp_check *ocsp,
387
+					 struct x509_certificate *cert ) {
388
+	struct ocsp_responder *responder = &ocsp->response.responder;
389
+
390
+	/* Compare responder ID with certificate's subject */
391
+	return asn1_compare ( &responder->id, &cert->subject.raw );
392
+}
393
+
394
+/**
395
+ * Compare responder's certificate public key hash
396
+ *
397
+ * @v ocsp		OCSP check
398
+ * @v cert		Certificate
399
+ * @ret difference	Difference as returned by memcmp()
400
+ */
401
+static int ocsp_compare_responder_key_hash ( struct ocsp_check *ocsp,
402
+					     struct x509_certificate *cert ) {
403
+	struct ocsp_responder *responder = &ocsp->response.responder;
404
+	uint8_t ctx[SHA1_CTX_SIZE];
405
+	uint8_t digest[SHA1_DIGEST_SIZE];
406
+	int difference;
407
+
408
+	/* Sanity check */
409
+	difference = ( sizeof ( digest ) - responder->id.len );
410
+	if ( difference )
411
+		return difference;
412
+
413
+	/* Generate SHA1 hash of certificate's public key */
414
+	digest_init ( &sha1_algorithm, ctx );
415
+	digest_update ( &sha1_algorithm, ctx,
416
+			cert->subject.public_key.raw_bits.data,
417
+			cert->subject.public_key.raw_bits.len );
418
+	digest_final ( &sha1_algorithm, ctx, digest );
419
+
420
+	/* Compare responder ID with SHA1 hash of certificate's public key */
421
+	return memcmp ( digest, responder->id.data, sizeof ( digest ) );
422
+}
423
+
424
+/**
425
+ * Parse OCSP responder ID
426
+ *
427
+ * @v ocsp		OCSP check
428
+ * @v raw		ASN.1 cursor
429
+ * @ret rc		Return status code
430
+ */
431
+static int ocsp_parse_responder_id ( struct ocsp_check *ocsp,
432
+				     const struct asn1_cursor *raw ) {
433
+	struct ocsp_responder *responder = &ocsp->response.responder;
434
+	struct asn1_cursor *responder_id = &responder->id;
435
+	unsigned int type;
436
+
437
+	/* Enter responder ID */
438
+	memcpy ( responder_id, raw, sizeof ( *responder_id ) );
439
+	type = asn1_type ( responder_id );
440
+	asn1_enter_any ( responder_id );
441
+
442
+	/* Identify responder ID type */
443
+	switch ( type ) {
444
+	case ASN1_EXPLICIT_TAG ( 1 ) :
445
+		DBGC2 ( ocsp, "OCSP %p \"%s\" responder identified by name\n",
446
+			ocsp, ocsp->cert->subject.name );
447
+		responder->compare = ocsp_compare_responder_name;
448
+		return 0;
449
+	case ASN1_EXPLICIT_TAG ( 2 ) :
450
+		DBGC2 ( ocsp, "OCSP %p \"%s\" responder identified by key "
451
+			"hash\n", ocsp, ocsp->cert->subject.name );
452
+		responder->compare = ocsp_compare_responder_key_hash;
453
+		return 0;
454
+	default:
455
+		DBGC ( ocsp, "OCSP %p \"%s\" unsupported responder ID type "
456
+		       "%d\n", ocsp, ocsp->cert->subject.name, type );
457
+		return -ENOTSUP_RESPONDER_ID;
458
+	}
459
+}
460
+
364 461
 /**
365 462
  * Parse OCSP certificate ID
366 463
  *
@@ -484,7 +581,9 @@ static int ocsp_parse_tbs_response_data ( struct ocsp_check *ocsp,
484 581
 	/* Skip version, if present */
485 582
 	asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
486 583
 
487
-	/* Skip responderID */
584
+	/* Parse responderID */
585
+	if ( ( rc = ocsp_parse_responder_id ( ocsp, &cursor ) ) != 0 )
586
+		return rc;
488 587
 	asn1_skip_any ( &cursor );
489 588
 
490 589
 	/* Skip producedAt */
@@ -508,6 +607,7 @@ static int ocsp_parse_certs ( struct ocsp_check *ocsp,
508 607
 			      const struct asn1_cursor *raw ) {
509 608
 	struct ocsp_response *response = &ocsp->response;
510 609
 	struct asn1_cursor cursor;
610
+	struct x509_certificate *cert;
511 611
 	int rc;
512 612
 
513 613
 	/* Enter certs */
@@ -519,20 +619,39 @@ static int ocsp_parse_certs ( struct ocsp_check *ocsp,
519 619
 	 * multiple certificates, but the protocol requires that the
520 620
 	 * OCSP signing certificate must either be the issuer itself,
521 621
 	 * or must be directly issued by the issuer (see RFC2560
522
-	 * section 4.2.2.2 "Authorized Responders").
622
+	 * section 4.2.2.2 "Authorized Responders").  We therefore
623
+	 * need to identify only the single certificate matching the
624
+	 * Responder ID.
523 625
 	 */
524
-	if ( ( cursor.len != 0 ) &&
525
-	     ( ( rc = x509_certificate ( cursor.data, cursor.len,
526
-					 &response->signer ) ) != 0 ) ) {
527
-		DBGC ( ocsp, "OCSP %p \"%s\" could not parse certificate: "
528
-		       "%s\n", ocsp, ocsp->cert->subject.name, strerror ( rc ));
529
-		DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
530
-		return rc;
626
+	while ( cursor.len ) {
627
+
628
+		/* Parse certificate */
629
+		if ( ( rc = x509_certificate ( cursor.data, cursor.len,
630
+					       &cert ) ) != 0 ) {
631
+			DBGC ( ocsp, "OCSP %p \"%s\" could not parse "
632
+			       "certificate: %s\n", ocsp,
633
+			       ocsp->cert->subject.name, strerror ( rc ) );
634
+			DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
635
+			return rc;
636
+		}
637
+
638
+		/* Use if this certificate matches the responder ID */
639
+		if ( response->responder.compare ( ocsp, cert ) == 0 ) {
640
+			response->signer = cert;
641
+			DBGC2 ( ocsp, "OCSP %p \"%s\" response is signed by "
642
+				"\"%s\"\n", ocsp, ocsp->cert->subject.name,
643
+				response->signer->subject.name );
644
+			return 0;
645
+		}
646
+
647
+		/* Otherwise, discard this certificate */
648
+		x509_put ( cert );
649
+		asn1_skip_any ( &cursor );
531 650
 	}
532
-	DBGC2 ( ocsp, "OCSP %p \"%s\" response is signed by \"%s\"\n", ocsp,
533
-		ocsp->cert->subject.name, response->signer->subject.name );
534 651
 
535
-	return 0;
652
+	DBGC ( ocsp, "OCSP %p \"%s\" missing responder certificate\n",
653
+	       ocsp, ocsp->cert->subject.name );
654
+	return -EACCES_NO_RESPONDER;
536 655
 }
537 656
 
538 657
 /**

+ 19
- 0
src/include/ipxe/ocsp.h View File

@@ -28,6 +28,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
28 28
 #define OCSP_STATUS_SIG_REQUIRED	0x05
29 29
 #define OCSP_STATUS_UNAUTHORIZED	0x06
30 30
 
31
+struct ocsp_check;
32
+
31 33
 /** An OCSP request */
32 34
 struct ocsp_request {
33 35
 	/** Request builder */
@@ -36,12 +38,29 @@ struct ocsp_request {
36 38
 	struct asn1_cursor cert_id;
37 39
 };
38 40
 
41
+/** An OCSP responder */
42
+struct ocsp_responder {
43
+	/**
44
+	 * Check if certificate is the responder's certificate
45
+	 *
46
+	 * @v ocsp		OCSP check
47
+	 * @v cert		Certificate
48
+	 * @ret difference	Difference as returned by memcmp()
49
+	 */
50
+	int ( * compare ) ( struct ocsp_check *ocsp,
51
+			    struct x509_certificate *cert );
52
+	/** Responder ID */
53
+	struct asn1_cursor id;
54
+};
55
+
39 56
 /** An OCSP response */
40 57
 struct ocsp_response {
41 58
 	/** Raw response */
42 59
 	void *data;
43 60
 	/** Raw tbsResponseData */
44 61
 	struct asn1_cursor tbs;
62
+	/** Responder */
63
+	struct ocsp_responder responder;
45 64
 	/** Time at which status is known to be correct */
46 65
 	time_t this_update;
47 66
 	/** Time at which newer status information will be available */

Loading…
Cancel
Save