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
 #define EINFO_EACCES_STALE						\
58
 #define EINFO_EACCES_STALE						\
59
 	__einfo_uniqify ( EINFO_EACCES, 0x04,				\
59
 	__einfo_uniqify ( EINFO_EACCES, 0x04,				\
60
 			  "Stale (or premature) OCSP repsonse" )
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
 #define EPROTO_MALFORMED_REQUEST					\
76
 #define EPROTO_MALFORMED_REQUEST					\
62
 	__einfo_error ( EINFO_EPROTO_MALFORMED_REQUEST )
77
 	__einfo_error ( EINFO_EPROTO_MALFORMED_REQUEST )
63
 #define EINFO_EPROTO_MALFORMED_REQUEST					\
78
 #define EINFO_EPROTO_MALFORMED_REQUEST					\
355
 		DBGC ( ocsp, "OCSP %p \"%s\" response type not supported:\n",
370
 		DBGC ( ocsp, "OCSP %p \"%s\" response type not supported:\n",
356
 		       ocsp, ocsp->cert->subject.name );
371
 		       ocsp, ocsp->cert->subject.name );
357
 		DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
372
 		DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
358
-		return -ENOTSUP;
373
+		return -ENOTSUP_RESPONSE_TYPE;
359
 	}
374
 	}
360
 
375
 
361
 	return 0;
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
  * Parse OCSP certificate ID
462
  * Parse OCSP certificate ID
366
  *
463
  *
484
 	/* Skip version, if present */
581
 	/* Skip version, if present */
485
 	asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
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
 	asn1_skip_any ( &cursor );
587
 	asn1_skip_any ( &cursor );
489
 
588
 
490
 	/* Skip producedAt */
589
 	/* Skip producedAt */
508
 			      const struct asn1_cursor *raw ) {
607
 			      const struct asn1_cursor *raw ) {
509
 	struct ocsp_response *response = &ocsp->response;
608
 	struct ocsp_response *response = &ocsp->response;
510
 	struct asn1_cursor cursor;
609
 	struct asn1_cursor cursor;
610
+	struct x509_certificate *cert;
511
 	int rc;
611
 	int rc;
512
 
612
 
513
 	/* Enter certs */
613
 	/* Enter certs */
519
 	 * multiple certificates, but the protocol requires that the
619
 	 * multiple certificates, but the protocol requires that the
520
 	 * OCSP signing certificate must either be the issuer itself,
620
 	 * OCSP signing certificate must either be the issuer itself,
521
 	 * or must be directly issued by the issuer (see RFC2560
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
 #define OCSP_STATUS_SIG_REQUIRED	0x05
28
 #define OCSP_STATUS_SIG_REQUIRED	0x05
29
 #define OCSP_STATUS_UNAUTHORIZED	0x06
29
 #define OCSP_STATUS_UNAUTHORIZED	0x06
30
 
30
 
31
+struct ocsp_check;
32
+
31
 /** An OCSP request */
33
 /** An OCSP request */
32
 struct ocsp_request {
34
 struct ocsp_request {
33
 	/** Request builder */
35
 	/** Request builder */
36
 	struct asn1_cursor cert_id;
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
 /** An OCSP response */
56
 /** An OCSP response */
40
 struct ocsp_response {
57
 struct ocsp_response {
41
 	/** Raw response */
58
 	/** Raw response */
42
 	void *data;
59
 	void *data;
43
 	/** Raw tbsResponseData */
60
 	/** Raw tbsResponseData */
44
 	struct asn1_cursor tbs;
61
 	struct asn1_cursor tbs;
62
+	/** Responder */
63
+	struct ocsp_responder responder;
45
 	/** Time at which status is known to be correct */
64
 	/** Time at which status is known to be correct */
46
 	time_t this_update;
65
 	time_t this_update;
47
 	/** Time at which newer status information will be available */
66
 	/** Time at which newer status information will be available */

Loading…
Cancel
Save