提交 e9746e03 编写于 作者: D Dr. Stephen Henson

Initial support for name constraints certificate extension.

TODO: robustness checking on name forms.
上级 ab9c689a
...@@ -4,6 +4,18 @@ ...@@ -4,6 +4,18 @@
Changes between 0.9.8i and 0.9.9 [xx XXX xxxx] Changes between 0.9.8i and 0.9.9 [xx XXX xxxx]
*) Fixes to pathlength constraint, self issued certificate handling,
policy processing to align with RFC3280 and PKITS tests.
This work was sponsored by Google.
[Steve Henson]
*) Support for name constraints certificate extension. DN, email, DNS
and URI types are currently supported.
This work was sponsored by Google.
[Steve Henson]
*) To cater for systems that provide a pointer-based thread ID rather *) To cater for systems that provide a pointer-based thread ID rather
than numeric, deprecate the current numeric thread ID mechanism and than numeric, deprecate the current numeric thread ID mechanism and
replace it with a structure and associated callback type. This replace it with a structure and associated callback type. This
...@@ -31,6 +43,8 @@ ...@@ -31,6 +43,8 @@
*) Initial support for different CRL issuing certificates. This covers a *) Initial support for different CRL issuing certificates. This covers a
simple case where the self issued certificates in the chain exist and simple case where the self issued certificates in the chain exist and
the real CRL issuer is higher in the existing chain. the real CRL issuer is higher in the existing chain.
This work was sponsored by Google.
[Steve Henson] [Steve Henson]
*) Removed effectively defunct crypto/store from the build. *) Removed effectively defunct crypto/store from the build.
......
...@@ -116,6 +116,8 @@ static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, ...@@ -116,6 +116,8 @@ static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
AUTHORITY_KEYID_free(ret->akid); AUTHORITY_KEYID_free(ret->akid);
CRL_DIST_POINTS_free(ret->crldp); CRL_DIST_POINTS_free(ret->crldp);
policy_cache_free(ret->policy_cache); policy_cache_free(ret->policy_cache);
GENERAL_NAMES_free(ret->altname);
NAME_CONSTRAINTS_free(ret->nc);
#ifndef OPENSSL_NO_RFC3779 #ifndef OPENSSL_NO_RFC3779
sk_IPAddressFamily_pop_free(ret->rfc3779_addr, IPAddressFamily_free); sk_IPAddressFamily_pop_free(ret->rfc3779_addr, IPAddressFamily_free);
ASIdentifiers_free(ret->rfc3779_asid); ASIdentifiers_free(ret->rfc3779_asid);
......
...@@ -177,6 +177,7 @@ typedef struct X509_POLICY_CACHE_st X509_POLICY_CACHE; ...@@ -177,6 +177,7 @@ typedef struct X509_POLICY_CACHE_st X509_POLICY_CACHE;
typedef struct AUTHORITY_KEYID_st AUTHORITY_KEYID; typedef struct AUTHORITY_KEYID_st AUTHORITY_KEYID;
typedef struct DIST_POINT_st DIST_POINT; typedef struct DIST_POINT_st DIST_POINT;
typedef struct ISSUING_DIST_POINT_st ISSUING_DIST_POINT; typedef struct ISSUING_DIST_POINT_st ISSUING_DIST_POINT;
typedef struct NAME_CONSTRAINTS_st NAME_CONSTRAINTS;
/* If placed in pkcs12.h, we end up with a circular depency with pkcs7.h */ /* If placed in pkcs12.h, we end up with a circular depency with pkcs7.h */
#define DECLARE_PKCS12_STACK_OF(type) /* Nothing */ #define DECLARE_PKCS12_STACK_OF(type) /* Nothing */
......
...@@ -294,6 +294,8 @@ struct x509_st ...@@ -294,6 +294,8 @@ struct x509_st
AUTHORITY_KEYID *akid; AUTHORITY_KEYID *akid;
X509_POLICY_CACHE *policy_cache; X509_POLICY_CACHE *policy_cache;
STACK_OF(DIST_POINT) *crldp; STACK_OF(DIST_POINT) *crldp;
STACK_OF(GENERAL_NAME) *altname;
NAME_CONSTRAINTS *nc;
#ifndef OPENSSL_NO_RFC3779 #ifndef OPENSSL_NO_RFC3779
STACK_OF(IPAddressFamily) *rfc3779_addr; STACK_OF(IPAddressFamily) *rfc3779_addr;
struct ASIdentifiers_st *rfc3779_asid; struct ASIdentifiers_st *rfc3779_asid;
......
...@@ -168,6 +168,20 @@ const char *X509_verify_cert_error_string(long n) ...@@ -168,6 +168,20 @@ const char *X509_verify_cert_error_string(long n)
return("Unsupported extension feature"); return("Unsupported extension feature");
case X509_V_ERR_UNNESTED_RESOURCE: case X509_V_ERR_UNNESTED_RESOURCE:
return("RFC 3779 resource not subset of parent's resources"); return("RFC 3779 resource not subset of parent's resources");
case X509_V_ERR_PERMITTED_VIOLATION:
return("permitted subtree violation");
case X509_V_ERR_EXCLUDED_VIOLATION:
return("excluded subtree violation");
case X509_V_ERR_SUBTREE_MINMAX:
return("name constraints minimum and maximum not supported");
case X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE:
return("unsupported name constraint type");
case X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX:
return("unsupported or invalid name constraint syntax");
case X509_V_ERR_UNSUPPORTED_NAME_SYNTAX:
return("unsupported or invalid name syntax");
default: default:
BIO_snprintf(buf,sizeof buf,"error number %ld",n); BIO_snprintf(buf,sizeof buf,"error number %ld",n);
return(buf); return(buf);
......
...@@ -74,6 +74,7 @@ static int null_callback(int ok,X509_STORE_CTX *e); ...@@ -74,6 +74,7 @@ static int null_callback(int ok,X509_STORE_CTX *e);
static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer);
static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x); static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x);
static int check_chain_extensions(X509_STORE_CTX *ctx); static int check_chain_extensions(X509_STORE_CTX *ctx);
static int check_name_constraints(X509_STORE_CTX *ctx);
static int check_trust(X509_STORE_CTX *ctx); static int check_trust(X509_STORE_CTX *ctx);
static int check_revocation(X509_STORE_CTX *ctx); static int check_revocation(X509_STORE_CTX *ctx);
static int check_cert(X509_STORE_CTX *ctx); static int check_cert(X509_STORE_CTX *ctx);
...@@ -291,6 +292,12 @@ int X509_verify_cert(X509_STORE_CTX *ctx) ...@@ -291,6 +292,12 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
if (!ok) goto end; if (!ok) goto end;
/* Check name constraints */
ok = check_name_constraints(ctx);
if (!ok) goto end;
/* The chain extensions are OK: check trust */ /* The chain extensions are OK: check trust */
if (param->trust > 0) ok = check_trust(ctx); if (param->trust > 0) ok = check_trust(ctx);
...@@ -538,6 +545,42 @@ static int check_chain_extensions(X509_STORE_CTX *ctx) ...@@ -538,6 +545,42 @@ static int check_chain_extensions(X509_STORE_CTX *ctx)
#endif #endif
} }
static int check_name_constraints(X509_STORE_CTX *ctx)
{
X509 *x;
int i, j, rv;
/* Check name constraints for all certificates */
for (i = sk_X509_num(ctx->chain) - 1; i >= 0; i--)
{
x = sk_X509_value(ctx->chain, i);
/* Ignore self issued certs unless last in chain */
if (i && (x->ex_flags & EXFLAG_SI))
continue;
/* Check against constraints for all certificates higher in
* chain including trust anchor. Trust anchor not strictly
* speaking needed but if it includes constraints it is to be
* assumed it expects them to be obeyed.
*/
for (j = sk_X509_num(ctx->chain) - 1; j > i; j--)
{
NAME_CONSTRAINTS *nc = sk_X509_value(ctx->chain, j)->nc;
if (nc)
{
rv = NAME_CONSTRAINTS_check(x, nc);
if (rv != X509_V_OK)
{
ctx->error = rv;
ctx->error_depth = i;
ctx->current_cert = x;
if (!ctx->verify_cb(0,ctx))
return 0;
}
}
}
}
return 1;
}
static int check_trust(X509_STORE_CTX *ctx) static int check_trust(X509_STORE_CTX *ctx)
{ {
#ifdef OPENSSL_NO_CHAIN_VERIFY #ifdef OPENSSL_NO_CHAIN_VERIFY
......
...@@ -341,6 +341,13 @@ void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth); ...@@ -341,6 +341,13 @@ void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth);
#define X509_V_ERR_UNNESTED_RESOURCE 46 #define X509_V_ERR_UNNESTED_RESOURCE 46
#define X509_V_ERR_PERMITTED_VIOLATION 47
#define X509_V_ERR_EXCLUDED_VIOLATION 48
#define X509_V_ERR_SUBTREE_MINMAX 49
#define X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE 51
#define X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX 52
#define X509_V_ERR_UNSUPPORTED_NAME_SYNTAX 53
/* The application is not happy */ /* The application is not happy */
#define X509_V_ERR_APPLICATION_VERIFICATION 50 #define X509_V_ERR_APPLICATION_VERIFICATION 50
......
...@@ -72,6 +72,13 @@ static int do_i2r_name_constraints(X509V3_EXT_METHOD *method, ...@@ -72,6 +72,13 @@ static int do_i2r_name_constraints(X509V3_EXT_METHOD *method,
BIO *bp, int ind, char *name); BIO *bp, int ind, char *name);
static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip); static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip);
static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc);
static int nc_match_single(GENERAL_NAME *sub, GENERAL_NAME *gen);
static int nc_dn(X509_NAME *sub, X509_NAME *nm);
static int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns);
static int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml);
static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base);
const X509V3_EXT_METHOD v3_name_constraints = { const X509V3_EXT_METHOD v3_name_constraints = {
NID_name_constraints, 0, NID_name_constraints, 0,
ASN1_ITEM_ref(NAME_CONSTRAINTS), ASN1_ITEM_ref(NAME_CONSTRAINTS),
...@@ -218,3 +225,277 @@ static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip) ...@@ -218,3 +225,277 @@ static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip)
return 1; return 1;
} }
/* Check a certificate conforms to a specified set of constraints.
* Return values:
* X509_V_OK: All constraints obeyed.
* X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation.
* X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation.
* X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type.
* X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: Unsupported constraint type.
* X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: bad unsupported constraint syntax.
* X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: bad or unsupported syntax of name
*/
int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)
{
int r, i;
X509_NAME *nm;
nm = X509_get_subject_name(x);
if (X509_NAME_entry_count(nm) > 0)
{
GENERAL_NAME gntmp;
gntmp.type = GEN_DIRNAME;
gntmp.d.directoryName = nm;
r = nc_match(&gntmp, nc);
if (r != X509_V_OK)
return r;
gntmp.type = GEN_EMAIL;
/* Process any email address attributes in subject name */
for (i = -1;;)
{
X509_NAME_ENTRY *ne;
i = X509_NAME_get_index_by_NID(nm,
NID_pkcs9_emailAddress,
i);
if (i == -1)
break;
ne = X509_NAME_get_entry(nm, i);
gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne);
if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING)
return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
r = nc_match(&gntmp, nc);
if (r != X509_V_OK)
return r;
}
}
for (i = 0; i < sk_GENERAL_NAME_num(x->altname); i++)
{
GENERAL_NAME *gen = sk_GENERAL_NAME_value(x->altname, i);
r = nc_match(gen, nc);
if (r != X509_V_OK)
return r;
}
return X509_V_OK;
}
static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
{
GENERAL_SUBTREE *sub;
int i, r, match = 0;
/* Permitted subtrees: if any subtrees exist of matching the type
* at least one subtree must match.
*/
for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++)
{
sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i);
if (gen->type != sub->base->type)
continue;
if (sub->minimum || sub->maximum)
return X509_V_ERR_SUBTREE_MINMAX;
/* If we already have a match don't bother trying any more */
if (match == 2)
continue;
if (match == 0)
match = 1;
r = nc_match_single(gen, sub->base);
if (r == X509_V_OK)
match = 2;
else if (r != X509_V_ERR_PERMITTED_VIOLATION)
return r;
}
if (match == 1)
return X509_V_ERR_PERMITTED_VIOLATION;
/* Excluded subtrees: must not match any of these */
for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++)
{
sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i);
if (gen->type != sub->base->type)
continue;
if (sub->minimum || sub->maximum)
return X509_V_ERR_SUBTREE_MINMAX;
r = nc_match_single(gen, sub->base);
if (r == X509_V_OK)
return X509_V_ERR_EXCLUDED_VIOLATION;
else if (r != X509_V_ERR_PERMITTED_VIOLATION)
return r;
}
return X509_V_OK;
}
static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base)
{
switch(base->type)
{
case GEN_DIRNAME:
return nc_dn(gen->d.directoryName, base->d.directoryName);
case GEN_DNS:
return nc_dns(gen->d.dNSName, base->d.dNSName);
case GEN_EMAIL:
return nc_email(gen->d.rfc822Name, base->d.rfc822Name);
case GEN_URI:
return nc_uri(gen->d.uniformResourceIdentifier,
base->d.uniformResourceIdentifier);
default:
return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE;
}
}
/* directoryName name constraint matching.
* The canonical encoding of X509_NAME makes this comparison easy. It is
* matched if the subtree is a subset of the name.
*/
static int nc_dn(X509_NAME *nm, X509_NAME *base)
{
if (base->canon_enclen > nm->canon_enclen)
return X509_V_ERR_PERMITTED_VIOLATION;
if (memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen))
return X509_V_ERR_PERMITTED_VIOLATION;
return X509_V_OK;
}
static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base)
{
char *baseptr = (char *)base->data;
char *dnsptr = (char *)dns->data;
/* Empty matches everything */
if (!*baseptr)
return X509_V_OK;
/* Otherwise can add zero or more components on the left so
* compare RHS and if dns is longer and expect '.' as preceding
* character.
*/
if (dns->length > base->length)
{
dnsptr += dns->length - base->length;
if (dnsptr[-1] != '.')
return X509_V_ERR_PERMITTED_VIOLATION;
}
if (strcasecmp(baseptr, dnsptr))
return X509_V_ERR_PERMITTED_VIOLATION;
return X509_V_OK;
}
static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base)
{
const char *baseptr = (char *)base->data;
const char *emlptr = (char *)eml->data;
const char *baseat = strchr(baseptr, '@');
const char *emlat = strchr(emlptr, '@');
if (!emlat)
return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
/* Special case: inital '.' is RHS match */
if (!baseat && (*baseptr == '.'))
{
if (eml->length > base->length)
{
emlptr += eml->length - base->length;
if (!strcasecmp(baseptr, emlptr))
return X509_V_OK;
}
return X509_V_ERR_PERMITTED_VIOLATION;
}
/* If we have anything before '@' match local part */
if (baseat)
{
if (baseat != baseptr)
{
if ((baseat - baseptr) != (emlat - emlptr))
return X509_V_ERR_PERMITTED_VIOLATION;
/* Case sensitive match of local part */
if (strncmp(baseptr, emlptr, emlat - emlptr))
return X509_V_ERR_PERMITTED_VIOLATION;
}
/* Position base after '@' */
baseptr = baseat + 1;
}
emlptr = emlat + 1;
/* Just have hostname left to match: case insensitive */
if (strcasecmp(baseptr, emlptr))
return X509_V_ERR_PERMITTED_VIOLATION;
return X509_V_OK;
}
static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base)
{
const char *baseptr = (char *)base->data;
const char *hostptr = (char *)uri->data;
const char *p = strchr(hostptr, ':');
size_t hostlen;
/* Check for foo:// and skip past it */
if (!p || (p[1] != '/') || (p[2] != '/'))
return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
hostptr = p + 3;
/* Determine length of hostname part of URI */
/* Look for a port indicator as end of hostname first */
p = strchr(hostptr, ':');
/* Otherwise look for trailing slash */
if (!p)
p = strchr(hostptr, '/');
if (!p)
hostlen = strlen(hostptr);
else
hostlen = p - hostptr;
if (hostlen == 0)
return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
/* Special case: inital '.' is RHS match */
if (*baseptr == '.')
{
if (hostlen > base->length)
{
p = hostptr + hostlen - base->length;
if (!strncasecmp(p, baseptr, base->length))
return X509_V_OK;
}
return X509_V_ERR_PERMITTED_VIOLATION;
}
if ((base->length != hostlen) || strncasecmp(hostptr, baseptr, hostlen))
return X509_V_ERR_PERMITTED_VIOLATION;
return X509_V_OK;
}
...@@ -295,6 +295,7 @@ int X509_supported_extension(X509_EXTENSION *ex) ...@@ -295,6 +295,7 @@ int X509_supported_extension(X509_EXTENSION *ex)
#endif #endif
NID_policy_constraints, /* 401 */ NID_policy_constraints, /* 401 */
NID_proxyCertInfo, /* 663 */ NID_proxyCertInfo, /* 663 */
NID_name_constraints, /* 666 */
NID_inhibit_any_policy /* 748 */ NID_inhibit_any_policy /* 748 */
}; };
...@@ -448,6 +449,10 @@ static void x509v3_cache_extensions(X509 *x) ...@@ -448,6 +449,10 @@ static void x509v3_cache_extensions(X509 *x)
} }
x->skid =X509_get_ext_d2i(x, NID_subject_key_identifier, NULL, NULL); x->skid =X509_get_ext_d2i(x, NID_subject_key_identifier, NULL, NULL);
x->akid =X509_get_ext_d2i(x, NID_authority_key_identifier, NULL, NULL); x->akid =X509_get_ext_d2i(x, NID_authority_key_identifier, NULL, NULL);
x->altname = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
x->nc = X509_get_ext_d2i(x, NID_name_constraints, &i, NULL);
if (!x->nc && (i != -1))
x->ex_flags |= EXFLAG_INVALID;
setup_crldp(x); setup_crldp(x);
......
...@@ -305,10 +305,10 @@ typedef struct GENERAL_SUBTREE_st { ...@@ -305,10 +305,10 @@ typedef struct GENERAL_SUBTREE_st {
DECLARE_STACK_OF(GENERAL_SUBTREE) DECLARE_STACK_OF(GENERAL_SUBTREE)
typedef struct NAME_CONSTRAINTS_st { struct NAME_CONSTRAINTS_st {
STACK_OF(GENERAL_SUBTREE) *permittedSubtrees; STACK_OF(GENERAL_SUBTREE) *permittedSubtrees;
STACK_OF(GENERAL_SUBTREE) *excludedSubtrees; STACK_OF(GENERAL_SUBTREE) *excludedSubtrees;
} NAME_CONSTRAINTS; };
typedef struct POLICY_CONSTRAINTS_st { typedef struct POLICY_CONSTRAINTS_st {
ASN1_INTEGER *requireExplicitPolicy; ASN1_INTEGER *requireExplicitPolicy;
...@@ -549,6 +549,8 @@ DECLARE_ASN1_FUNCTIONS(ISSUING_DIST_POINT) ...@@ -549,6 +549,8 @@ DECLARE_ASN1_FUNCTIONS(ISSUING_DIST_POINT)
int DIST_POINT_set_dpname(DIST_POINT_NAME *dpn, X509_NAME *iname); int DIST_POINT_set_dpname(DIST_POINT_NAME *dpn, X509_NAME *iname);
int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc);
DECLARE_ASN1_FUNCTIONS(ACCESS_DESCRIPTION) DECLARE_ASN1_FUNCTIONS(ACCESS_DESCRIPTION)
DECLARE_ASN1_FUNCTIONS(AUTHORITY_INFO_ACCESS) DECLARE_ASN1_FUNCTIONS(AUTHORITY_INFO_ACCESS)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册