mirror of
https://github.com/openssl/openssl.git
synced 2024-12-15 13:03:39 +08:00
Add nameConstraints commonName checking.
New hostname checking function asn1_valid_host() Check commonName entries against nameConstraints: any CN components in EE certificate which look like hostnames are checked against nameConstraints. Note that RFC5280 et al only require checking subject alt name against DNS name constraints. Reviewed-by: Richard Levitte <levitte@openssl.org>
This commit is contained in:
parent
1d03b7b893
commit
5bd5dcd496
@ -10,6 +10,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "internal/cryptlib.h"
|
||||
#include "internal/asn1_int.h"
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/asn1.h>
|
||||
@ -592,3 +593,53 @@ int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in)
|
||||
*out = stmp.data;
|
||||
return stmp.length;
|
||||
}
|
||||
|
||||
/* Return 1 if host is a valid hostname and 0 otherwise */
|
||||
int asn1_valid_host(const ASN1_STRING *host)
|
||||
{
|
||||
int hostlen = host->length;
|
||||
const unsigned char *hostptr = host->data;
|
||||
int type = host->type;
|
||||
int i;
|
||||
char width = -1;
|
||||
unsigned short chflags = 0, prevchflags;
|
||||
|
||||
if (type > 0 && type < 31)
|
||||
width = tag2nbyte[type];
|
||||
if (width == -1 || hostlen == 0)
|
||||
return 0;
|
||||
/* Treat UTF8String as width 1 as any MSB set is invalid */
|
||||
if (width == 0)
|
||||
width = 1;
|
||||
for (i = 0 ; i < hostlen; i+= width) {
|
||||
prevchflags = chflags;
|
||||
/* Value must be <= 0x7F: check upper bytes are all zeroes */
|
||||
if (width == 4) {
|
||||
if (*hostptr++ != 0 || *hostptr++ != 0 || *hostptr++ != 0)
|
||||
return 0;
|
||||
} else if (width == 2) {
|
||||
if (*hostptr++ != 0)
|
||||
return 0;
|
||||
}
|
||||
if (*hostptr > 0x7f)
|
||||
return 0;
|
||||
chflags = char_type[*hostptr++];
|
||||
if (!(chflags & (CHARTYPE_HOST_ANY | CHARTYPE_HOST_WILD))) {
|
||||
/* Nothing else allowed at start or end of string */
|
||||
if (i == 0 || i == hostlen - 1)
|
||||
return 0;
|
||||
/* Otherwise invalid if not dot or hyphen */
|
||||
if (!(chflags & (CHARTYPE_HOST_DOT | CHARTYPE_HOST_HYPHEN)))
|
||||
return 0;
|
||||
/*
|
||||
* If previous is dot or hyphen then illegal unless both
|
||||
* are hyphens: as .- -. .. are all illegal
|
||||
*/
|
||||
if (prevchflags & (CHARTYPE_HOST_DOT | CHARTYPE_HOST_HYPHEN)
|
||||
&& ((prevchflags & CHARTYPE_HOST_DOT)
|
||||
|| (chflags & CHARTYPE_HOST_DOT)))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -10,17 +10,25 @@
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#define CHARTYPE_HOST_ANY 4096
|
||||
#define CHARTYPE_HOST_DOT 8192
|
||||
#define CHARTYPE_HOST_HYPHEN 16384
|
||||
#define CHARTYPE_HOST_WILD 32768
|
||||
|
||||
/*
|
||||
* Mask of various character properties
|
||||
*/
|
||||
|
||||
static const unsigned short char_type[] = {
|
||||
1026, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
120, 0, 1, 40, 0, 0, 0, 16, 1040, 1040, 1024, 25, 25, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 9, 9, 16, 9, 16,
|
||||
0, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 1025, 0, 0, 0,
|
||||
0, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 0, 0, 0, 2
|
||||
1026, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 120, 0, 1, 40,
|
||||
0, 0, 0, 16, 1040, 1040, 33792, 25, 25, 16400, 8208, 16,
|
||||
4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 16, 9,
|
||||
9, 16, 9, 16, 0, 4112, 4112, 4112, 4112, 4112, 4112, 4112,
|
||||
4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112,
|
||||
4112, 4112, 4112, 4112, 4112, 4112, 4112, 0, 1025, 0, 0, 0,
|
||||
0, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112,
|
||||
4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112, 4112,
|
||||
4112, 4112, 4112, 0, 0, 0, 0, 2
|
||||
};
|
||||
|
@ -22,6 +22,10 @@ my $PSTRING_CHAR = 0x10; # Valid PrintableString character
|
||||
my $RFC2253_FIRST_ESC = 0x20; # Escaped with \ if first character
|
||||
my $RFC2253_LAST_ESC = 0x40; # Escaped with \ if last character
|
||||
my $RFC2254_ESC = 0x400; # Character escaped \XX
|
||||
my $HOST_ANY = 0x1000; # Valid hostname character anywhere in label
|
||||
my $HOST_DOT = 0x2000; # Dot: hostname label separator
|
||||
my $HOST_HYPHEN = 0x4000; # Hyphen: not valid at start or end.
|
||||
my $HOST_WILD = 0x8000; # Wildcard character
|
||||
|
||||
for($i = 0; $i < 128; $i++) {
|
||||
# Set the RFC2253 escape characters (control)
|
||||
@ -34,7 +38,7 @@ for($i = 0; $i < 128; $i++) {
|
||||
if( ( ( $i >= ord("a")) && ( $i <= ord("z")) )
|
||||
|| ( ( $i >= ord("A")) && ( $i <= ord("Z")) )
|
||||
|| ( ( $i >= ord("0")) && ( $i <= ord("9")) ) ) {
|
||||
$arr[$i] |= $PSTRING_CHAR;
|
||||
$arr[$i] |= $PSTRING_CHAR | $HOST_ANY;
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +62,7 @@ $arr[ord(";")] |= $NOESC_QUOTE | $RFC2253_ESC;
|
||||
$arr[0] |= $RFC2254_ESC;
|
||||
$arr[ord("(")] |= $RFC2254_ESC;
|
||||
$arr[ord(")")] |= $RFC2254_ESC;
|
||||
$arr[ord("*")] |= $RFC2254_ESC;
|
||||
$arr[ord("*")] |= $RFC2254_ESC | $HOST_WILD;
|
||||
$arr[ord("\\")] |= $RFC2254_ESC;
|
||||
|
||||
# Remaining PrintableString characters
|
||||
@ -69,8 +73,8 @@ $arr[ord("(")] |= $PSTRING_CHAR;
|
||||
$arr[ord(")")] |= $PSTRING_CHAR;
|
||||
$arr[ord("+")] |= $PSTRING_CHAR;
|
||||
$arr[ord(",")] |= $PSTRING_CHAR;
|
||||
$arr[ord("-")] |= $PSTRING_CHAR;
|
||||
$arr[ord(".")] |= $PSTRING_CHAR;
|
||||
$arr[ord("-")] |= $PSTRING_CHAR | $HOST_HYPHEN;
|
||||
$arr[ord(".")] |= $PSTRING_CHAR | $HOST_DOT;
|
||||
$arr[ord("/")] |= $PSTRING_CHAR;
|
||||
$arr[ord(":")] |= $PSTRING_CHAR;
|
||||
$arr[ord("=")] |= $PSTRING_CHAR;
|
||||
@ -91,6 +95,11 @@ print <<EOF;
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#define CHARTYPE_HOST_ANY $HOST_ANY
|
||||
#define CHARTYPE_HOST_DOT $HOST_DOT
|
||||
#define CHARTYPE_HOST_HYPHEN $HOST_HYPHEN
|
||||
#define CHARTYPE_HOST_WILD $HOST_WILD
|
||||
|
||||
/*
|
||||
* Mask of various character properties
|
||||
*/
|
||||
@ -100,8 +109,8 @@ EOF
|
||||
|
||||
print " ";
|
||||
for($i = 0; $i < 128; $i++) {
|
||||
print("\n ") if($i && (($i % 16) == 0));
|
||||
printf(" %2d", $arr[$i]);
|
||||
print("\n ") if($i && (($i % 12) == 0));
|
||||
printf(" %4d", $arr[$i]);
|
||||
print(",") if ($i != 127);
|
||||
}
|
||||
print("\n};\n");
|
||||
|
@ -89,3 +89,5 @@ struct asn1_pctx_st {
|
||||
unsigned long oid_flags;
|
||||
unsigned long str_flags;
|
||||
} /* ASN1_PCTX */ ;
|
||||
|
||||
int asn1_valid_host(const ASN1_STRING *host);
|
||||
|
@ -651,6 +651,10 @@ static int check_name_constraints(X509_STORE_CTX *ctx)
|
||||
if (nc) {
|
||||
int rv = NAME_CONSTRAINTS_check(x, nc);
|
||||
|
||||
/* If EE certificate check commonName too */
|
||||
if (rv == X509_V_OK && i == 0)
|
||||
rv = NAME_CONSTRAINTS_check_CN(x, nc);
|
||||
|
||||
switch (rv) {
|
||||
case X509_V_OK:
|
||||
break;
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include "internal/cryptlib.h"
|
||||
#include "internal/asn1_int.h"
|
||||
#include <openssl/asn1t.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/x509v3.h>
|
||||
@ -226,6 +227,51 @@ int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)
|
||||
|
||||
}
|
||||
|
||||
int NAME_CONSTRAINTS_check_CN(X509 *x, NAME_CONSTRAINTS *nc)
|
||||
{
|
||||
int r, i;
|
||||
X509_NAME *nm;
|
||||
|
||||
ASN1_STRING stmp;
|
||||
GENERAL_NAME gntmp;
|
||||
stmp.flags = 0;
|
||||
stmp.type = V_ASN1_IA5STRING;
|
||||
gntmp.type = GEN_DNS;
|
||||
gntmp.d.dNSName = &stmp;
|
||||
|
||||
nm = X509_get_subject_name(x);
|
||||
|
||||
/* Process any commonName attributes in subject name */
|
||||
|
||||
for (i = -1;;) {
|
||||
X509_NAME_ENTRY *ne;
|
||||
ASN1_STRING *hn;
|
||||
i = X509_NAME_get_index_by_NID(nm, NID_commonName, i);
|
||||
if (i == -1)
|
||||
break;
|
||||
ne = X509_NAME_get_entry(nm, i);
|
||||
hn = X509_NAME_ENTRY_get_data(ne);
|
||||
/* Only process attributes that look like host names */
|
||||
if (asn1_valid_host(hn)) {
|
||||
unsigned char *h;
|
||||
int hlen = ASN1_STRING_to_UTF8(&h, hn);
|
||||
if (hlen <= 0)
|
||||
return X509_V_ERR_OUT_OF_MEM;
|
||||
|
||||
stmp.length = hlen;
|
||||
stmp.data = h;
|
||||
|
||||
r = nc_match(&gntmp, nc);
|
||||
|
||||
OPENSSL_free(h);
|
||||
|
||||
if (r != X509_V_OK)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return X509_V_OK;
|
||||
}
|
||||
|
||||
static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
|
||||
{
|
||||
GENERAL_SUBTREE *sub;
|
||||
|
@ -524,6 +524,7 @@ DECLARE_ASN1_FUNCTIONS(ISSUING_DIST_POINT)
|
||||
int DIST_POINT_set_dpname(DIST_POINT_NAME *dpn, X509_NAME *iname);
|
||||
|
||||
int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc);
|
||||
int NAME_CONSTRAINTS_check_CN(X509 *x, NAME_CONSTRAINTS *nc);
|
||||
|
||||
DECLARE_ASN1_FUNCTIONS(ACCESS_DESCRIPTION)
|
||||
DECLARE_ASN1_FUNCTIONS(AUTHORITY_INFO_ACCESS)
|
||||
|
Loading…
Reference in New Issue
Block a user