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
 
603
 
604
 	/* Enter accessLocation */
604
 	/* Enter accessLocation */
605
 	memcpy ( uri, raw, sizeof ( *uri ) );
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
 		DBGC ( cert, "X509 %p OCSP does not contain "
607
 		DBGC ( cert, "X509 %p OCSP does not contain "
608
 		       "uniformResourceIdentifier:\n", cert );
608
 		       "uniformResourceIdentifier:\n", cert );
609
 		DBGC_HDA ( cert, 0, raw->data, raw->len );
609
 		DBGC_HDA ( cert, 0, raw->data, raw->len );
708
 	return 0;
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
 /** "id-ce-basicConstraints" object identifier */
738
 /** "id-ce-basicConstraints" object identifier */
712
 static uint8_t oid_ce_basic_constraints[] =
739
 static uint8_t oid_ce_basic_constraints[] =
713
 	{ ASN1_OID_BASICCONSTRAINTS };
740
 	{ ASN1_OID_BASICCONSTRAINTS };
724
 static uint8_t oid_pe_authority_info_access[] =
751
 static uint8_t oid_pe_authority_info_access[] =
725
 	{ ASN1_OID_AUTHORITYINFOACCESS };
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
 /** Supported certificate extensions */
758
 /** Supported certificate extensions */
728
 static struct x509_extension x509_extensions[] = {
759
 static struct x509_extension x509_extensions[] = {
729
 	{
760
 	{
746
 		.oid = ASN1_OID_CURSOR ( oid_pe_authority_info_access ),
777
 		.oid = ASN1_OID_CURSOR ( oid_pe_authority_info_access ),
747
 		.parse = x509_parse_authority_info_access,
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
 	return 0;
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
  * Check X.509 certificate name
1456
  * Check X.509 certificate name
1345
  *
1457
  *
1349
  */
1461
  */
1350
 int x509_check_name ( struct x509_certificate *cert, const char *name ) {
1462
 int x509_check_name ( struct x509_certificate *cert, const char *name ) {
1351
 	struct asn1_cursor *common_name = &cert->subject.common_name;
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
 	/* Check commonName */
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
 	ASN1_OID_SINGLE ( 5 ), ASN1_OID_SINGLE ( 7 ),		\
222
 	ASN1_OID_SINGLE ( 5 ), ASN1_OID_SINGLE ( 7 ),		\
223
 	ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 9 )
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
 /** Define an ASN.1 cursor containing an OID */
230
 /** Define an ASN.1 cursor containing an OID */
226
 #define ASN1_OID_CURSOR( oid_value ) {				\
231
 #define ASN1_OID_CURSOR( oid_value ) {				\
227
 		.data = oid_value,				\
232
 		.data = oid_value,				\

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

136
 	struct x509_ocsp_responder ocsp;
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
 /** An X.509 certificate extensions set */
151
 /** An X.509 certificate extensions set */
140
 struct x509_extensions {
152
 struct x509_extensions {
141
 	/** Basic constraints */
153
 	/** Basic constraints */
146
 	struct x509_extended_key_usage ext_usage;
158
 	struct x509_extended_key_usage ext_usage;
147
 	/** Authority information access */
159
 	/** Authority information access */
148
 	struct x509_authority_info_access auth_info;
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
 /** A link in an X.509 certificate chain */
165
 /** A link in an X.509 certificate chain */

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

1023
 
1023
 
1024
 	/* Check certificate names */
1024
 	/* Check certificate names */
1025
 	x509_check_name_ok ( &server_crt, "boot.test.ipxe.org" );
1025
 	x509_check_name_ok ( &server_crt, "boot.test.ipxe.org" );
1026
+	x509_check_name_ok ( &server_crt, "demo.test.ipxe.org" );
1026
 	x509_check_name_fail_ok ( &server_crt, "incorrect.test.ipxe.org" );
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
 	/* Parse all certificate chains */
1037
 	/* Parse all certificate chains */
1029
 	x509_chain_ok ( &server_chain );
1038
 	x509_chain_ok ( &server_chain );

Loading…
Cancel
Save