Browse Source

[crypto] Add support for subjectAltName and wildcard certificates

Originally-implemented-by: Alex Chernyakhovsky <achernya@google.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 10 years ago
parent
commit
f10726c8bb
4 changed files with 160 additions and 8 deletions
  1. 132
    8
      src/crypto/x509.c
  2. 5
    0
      src/include/ipxe/asn1.h
  3. 14
    0
      src/include/ipxe/x509.h
  4. 9
    0
      src/tests/x509_test.c

+ 132
- 8
src/crypto/x509.c View File

@@ -603,7 +603,7 @@ static int x509_parse_ocsp ( struct x509_certificate *cert,
603 603
 
604 604
 	/* Enter accessLocation */
605 605
 	memcpy ( uri, raw, sizeof ( *uri ) );
606
-	if ( ( rc = asn1_enter ( uri, ASN1_IMPLICIT_TAG ( 6 ) ) ) != 0 ) {
606
+	if ( ( rc = asn1_enter ( uri, X509_GENERAL_NAME_URI ) ) != 0 ) {
607 607
 		DBGC ( cert, "X509 %p OCSP does not contain "
608 608
 		       "uniformResourceIdentifier:\n", cert );
609 609
 		DBGC_HDA ( cert, 0, raw->data, raw->len );
@@ -708,6 +708,33 @@ static int x509_parse_authority_info_access ( struct x509_certificate *cert,
708 708
 	return 0;
709 709
 }
710 710
 
711
+/**
712
+ * Parse X.509 certificate subject alternative name
713
+ *
714
+ * @v cert		X.509 certificate
715
+ * @v raw		ASN.1 cursor
716
+ * @ret rc		Return status code
717
+ */
718
+static int x509_parse_subject_alt_name ( struct x509_certificate *cert,
719
+					 const struct asn1_cursor *raw ) {
720
+	struct x509_subject_alt_name *alt_name = &cert->extensions.alt_name;
721
+	struct asn1_cursor *names = &alt_name->names;
722
+	int rc;
723
+
724
+	/* Enter subjectAltName */
725
+	memcpy ( names, raw, sizeof ( *names ) );
726
+	if ( ( rc = asn1_enter ( names, ASN1_SEQUENCE ) ) != 0 ) {
727
+		DBGC ( cert, "X509 %p invalid subjectAltName: %s\n",
728
+		       cert, strerror ( rc ) );
729
+		DBGC_HDA ( cert, 0, raw->data, raw->len );
730
+		return rc;
731
+	}
732
+	DBGC2 ( cert, "X509 %p has subjectAltName:\n", cert );
733
+	DBGC2_HDA ( cert, 0, names->data, names->len );
734
+
735
+	return 0;
736
+}
737
+
711 738
 /** "id-ce-basicConstraints" object identifier */
712 739
 static uint8_t oid_ce_basic_constraints[] =
713 740
 	{ ASN1_OID_BASICCONSTRAINTS };
@@ -724,6 +751,10 @@ static uint8_t oid_ce_ext_key_usage[] =
724 751
 static uint8_t oid_pe_authority_info_access[] =
725 752
 	{ ASN1_OID_AUTHORITYINFOACCESS };
726 753
 
754
+/** "id-ce-subjectAltName" object identifier */
755
+static uint8_t oid_ce_subject_alt_name[] =
756
+	{ ASN1_OID_SUBJECTALTNAME };
757
+
727 758
 /** Supported certificate extensions */
728 759
 static struct x509_extension x509_extensions[] = {
729 760
 	{
@@ -746,6 +777,11 @@ static struct x509_extension x509_extensions[] = {
746 777
 		.oid = ASN1_OID_CURSOR ( oid_pe_authority_info_access ),
747 778
 		.parse = x509_parse_authority_info_access,
748 779
 	},
780
+	{
781
+		.name = "subjectAltName",
782
+		.oid = ASN1_OID_CURSOR ( oid_ce_subject_alt_name ),
783
+		.parse = x509_parse_subject_alt_name,
784
+	},
749 785
 };
750 786
 
751 787
 /**
@@ -1340,6 +1376,82 @@ int x509_validate ( struct x509_certificate *cert,
1340 1376
 	return 0;
1341 1377
 }
1342 1378
 
1379
+/**
1380
+ * Check X.509 certificate alternative dNSName
1381
+ *
1382
+ * @v cert		X.509 certificate
1383
+ * @v raw		ASN.1 cursor
1384
+ * @v name		Name
1385
+ * @ret rc		Return status code
1386
+ */
1387
+static int x509_check_dnsname ( struct x509_certificate *cert,
1388
+				const struct asn1_cursor *raw,
1389
+				const char *name ) {
1390
+	const char *fullname = name;
1391
+	const char *dnsname = raw->data;
1392
+	size_t len = raw->len;
1393
+
1394
+	/* Check for wildcards */
1395
+	if ( ( len >= 2 ) && ( dnsname[0] == '*' ) && ( dnsname[1] == '.' ) ) {
1396
+
1397
+		/* Skip initial "*." */
1398
+		dnsname += 2;
1399
+		len -= 2;
1400
+
1401
+		/* Skip initial portion of name to be tested */
1402
+		name = strchr ( name, '.' );
1403
+		if ( ! name )
1404
+			return -ENOENT;
1405
+		name++;
1406
+	}
1407
+
1408
+	/* Compare names */
1409
+	if ( ! ( ( strlen ( name ) == len ) &&
1410
+		 ( memcmp ( name, dnsname, len ) == 0 ) ) )
1411
+		return -ENOENT;
1412
+
1413
+	if ( name == fullname ) {
1414
+		DBGC2 ( cert, "X509 %p \"%s\" subjectAltName matches \"%s\"\n",
1415
+			cert, x509_name ( cert ), name );
1416
+	} else {
1417
+		DBGC2 ( cert, "X509 %p \"%s\" subjectAltName matches \"%s\" "
1418
+			"(via \"*.%s\")\n", cert, x509_name ( cert ),
1419
+			fullname, name );
1420
+	}
1421
+	return 0;
1422
+}
1423
+
1424
+/**
1425
+ * Check X.509 certificate alternative name
1426
+ *
1427
+ * @v cert		X.509 certificate
1428
+ * @v raw		ASN.1 cursor
1429
+ * @v name		Name
1430
+ * @ret rc		Return status code
1431
+ */
1432
+static int x509_check_alt_name ( struct x509_certificate *cert,
1433
+				 const struct asn1_cursor *raw,
1434
+				 const char *name ) {
1435
+	struct asn1_cursor alt_name;
1436
+	unsigned int type;
1437
+
1438
+	/* Enter generalName */
1439
+	memcpy ( &alt_name, raw, sizeof ( alt_name ) );
1440
+	type = asn1_type ( &alt_name );
1441
+	asn1_enter_any ( &alt_name );
1442
+
1443
+	/* Check this name */
1444
+	switch ( type ) {
1445
+	case X509_GENERAL_NAME_DNS :
1446
+		return x509_check_dnsname ( cert, &alt_name, name );
1447
+	default:
1448
+		DBGC2 ( cert, "X509 %p \"%s\" unknown name of type %#02x:\n",
1449
+			cert, x509_name ( cert ), type );
1450
+		DBGC2_HDA ( cert, 0, alt_name.data, alt_name.len );
1451
+		return -ENOTSUP;
1452
+	}
1453
+}
1454
+
1343 1455
 /**
1344 1456
  * Check X.509 certificate name
1345 1457
  *
@@ -1349,17 +1461,29 @@ int x509_validate ( struct x509_certificate *cert,
1349 1461
  */
1350 1462
 int x509_check_name ( struct x509_certificate *cert, const char *name ) {
1351 1463
 	struct asn1_cursor *common_name = &cert->subject.common_name;
1352
-	size_t len = strlen ( name );
1464
+	struct asn1_cursor alt_name;
1465
+	int rc;
1353 1466
 
1354 1467
 	/* Check commonName */
1355
-	if ( ! ( ( len == common_name->len ) &&
1356
-		 ( memcmp ( name, common_name->data, len ) == 0 ) ) ) {
1357
-		DBGC ( cert, "X509 %p \"%s\" does not match name \"%s\"\n",
1358
-		       cert, x509_name ( cert ), name );
1359
-		return -EACCES_WRONG_NAME;
1468
+	if ( ( strlen ( name ) == common_name->len ) &&
1469
+	     ( memcmp ( name, common_name->data, common_name->len ) == 0 ) ) {
1470
+		DBGC2 ( cert, "X509 %p \"%s\" commonName matches \"%s\"\n",
1471
+			cert, x509_name ( cert ), name );
1472
+		return 0;
1360 1473
 	}
1361 1474
 
1362
-	return 0;
1475
+	/* Check any subjectAlternativeNames */
1476
+	memcpy ( &alt_name, &cert->extensions.alt_name.names,
1477
+		 sizeof ( alt_name ) );
1478
+	for ( ; alt_name.len ; asn1_skip_any ( &alt_name ) ) {
1479
+		if ( ( rc = x509_check_alt_name ( cert, &alt_name,
1480
+						  name ) ) == 0 )
1481
+			return 0;
1482
+	}
1483
+
1484
+	DBGC ( cert, "X509 %p \"%s\" does not match name \"%s\"\n",
1485
+	       cert, x509_name ( cert ), name );
1486
+	return -EACCES_WRONG_NAME;
1363 1487
 }
1364 1488
 
1365 1489
 /**

+ 5
- 0
src/include/ipxe/asn1.h View File

@@ -222,6 +222,11 @@ struct asn1_builder_header {
222 222
 	ASN1_OID_SINGLE ( 5 ), ASN1_OID_SINGLE ( 7 ),		\
223 223
 	ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 9 )
224 224
 
225
+/** ASN.1 OID for id-ce-subjectAltName (2.5.29.17) */
226
+#define ASN1_OID_SUBJECTALTNAME					\
227
+	ASN1_OID_INITIAL ( 2, 5 ), ASN1_OID_SINGLE ( 29 ),	\
228
+	ASN1_OID_SINGLE ( 17 )
229
+
225 230
 /** Define an ASN.1 cursor containing an OID */
226 231
 #define ASN1_OID_CURSOR( oid_value ) {				\
227 232
 		.data = oid_value,				\

+ 14
- 0
src/include/ipxe/x509.h View File

@@ -136,6 +136,18 @@ struct x509_authority_info_access {
136 136
 	struct x509_ocsp_responder ocsp;
137 137
 };
138 138
 
139
+/** X.509 certificate subject alternative name */
140
+struct x509_subject_alt_name {
141
+	/** Names */
142
+	struct asn1_cursor names;
143
+};
144
+
145
+/** X.509 certificate general name types */
146
+enum x509_general_name_types {
147
+	X509_GENERAL_NAME_DNS = ASN1_IMPLICIT_TAG ( 2 ),
148
+	X509_GENERAL_NAME_URI = ASN1_IMPLICIT_TAG ( 6 ),
149
+};
150
+
139 151
 /** An X.509 certificate extensions set */
140 152
 struct x509_extensions {
141 153
 	/** Basic constraints */
@@ -146,6 +158,8 @@ struct x509_extensions {
146 158
 	struct x509_extended_key_usage ext_usage;
147 159
 	/** Authority information access */
148 160
 	struct x509_authority_info_access auth_info;
161
+	/** Subject alternative name */
162
+	struct x509_subject_alt_name alt_name;
149 163
 };
150 164
 
151 165
 /** A link in an X.509 certificate chain */

+ 9
- 0
src/tests/x509_test.c View File

@@ -1023,7 +1023,16 @@ static void x509_test_exec ( void ) {
1023 1023
 
1024 1024
 	/* Check certificate names */
1025 1025
 	x509_check_name_ok ( &server_crt, "boot.test.ipxe.org" );
1026
+	x509_check_name_ok ( &server_crt, "demo.test.ipxe.org" );
1026 1027
 	x509_check_name_fail_ok ( &server_crt, "incorrect.test.ipxe.org" );
1028
+	x509_check_name_ok ( &server_crt, "anything.alt.test.ipxe.org" );
1029
+	x509_check_name_ok ( &server_crt, "wildcard.alt.test.ipxe.org" );
1030
+	x509_check_name_fail_ok ( &server_crt, "sub.domain.alt.test.ipxe.org" );
1031
+	x509_check_name_fail_ok ( &server_crt, "alt.test.ipxe.org" );
1032
+	x509_check_name_fail_ok ( &server_crt, "test.ipxe.org" );
1033
+	x509_check_name_fail_ok ( &server_crt, "ipxe.org" );
1034
+	x509_check_name_fail_ok ( &server_crt, "org" );
1035
+	x509_check_name_fail_ok ( &server_crt, "" );
1027 1036
 
1028 1037
 	/* Parse all certificate chains */
1029 1038
 	x509_chain_ok ( &server_chain );

Loading…
Cancel
Save