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

Initial support for name constraints certificate extension.

TODO: robustness checking on name forms.
上级 ab9c689a
......@@ -4,6 +4,18 @@
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
than numeric, deprecate the current numeric thread ID mechanism and
replace it with a structure and associated callback type. This
......@@ -31,6 +43,8 @@
*) Initial support for different CRL issuing certificates. This covers a
simple case where the self issued certificates in the chain exist and
the real CRL issuer is higher in the existing chain.
This work was sponsored by Google.
[Steve Henson]
*) 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,
AUTHORITY_KEYID_free(ret->akid);
CRL_DIST_POINTS_free(ret->crldp);
policy_cache_free(ret->policy_cache);
GENERAL_NAMES_free(ret->altname);
NAME_CONSTRAINTS_free(ret->nc);
#ifndef OPENSSL_NO_RFC3779
sk_IPAddressFamily_pop_free(ret->rfc3779_addr, IPAddressFamily_free);
ASIdentifiers_free(ret->rfc3779_asid);
......
......@@ -177,6 +177,7 @@ typedef struct X509_POLICY_CACHE_st X509_POLICY_CACHE;
typedef struct AUTHORITY_KEYID_st AUTHORITY_KEYID;
typedef struct DIST_POINT_st 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 */
#define DECLARE_PKCS12_STACK_OF(type) /* Nothing */
......
......@@ -294,6 +294,8 @@ struct x509_st
AUTHORITY_KEYID *akid;
X509_POLICY_CACHE *policy_cache;
STACK_OF(DIST_POINT) *crldp;
STACK_OF(GENERAL_NAME) *altname;
NAME_CONSTRAINTS *nc;
#ifndef OPENSSL_NO_RFC3779
STACK_OF(IPAddressFamily) *rfc3779_addr;
struct ASIdentifiers_st *rfc3779_asid;
......
......@@ -168,6 +168,20 @@ const char *X509_verify_cert_error_string(long n)
return("Unsupported extension feature");
case X509_V_ERR_UNNESTED_RESOURCE:
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:
BIO_snprintf(buf,sizeof buf,"error number %ld",n);
return(buf);
......
......@@ -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 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_name_constraints(X509_STORE_CTX *ctx);
static int check_trust(X509_STORE_CTX *ctx);
static int check_revocation(X509_STORE_CTX *ctx);
static int check_cert(X509_STORE_CTX *ctx);
......@@ -291,6 +292,12 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
if (!ok) goto end;
/* Check name constraints */
ok = check_name_constraints(ctx);
if (!ok) goto end;
/* The chain extensions are OK: check trust */
if (param->trust > 0) ok = check_trust(ctx);
......@@ -538,6 +545,42 @@ static int check_chain_extensions(X509_STORE_CTX *ctx)
#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)
{
#ifdef OPENSSL_NO_CHAIN_VERIFY
......
......@@ -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_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 */
#define X509_V_ERR_APPLICATION_VERIFICATION 50
......
......@@ -72,6 +72,13 @@ static int do_i2r_name_constraints(X509V3_EXT_METHOD *method,
BIO *bp, int ind, char *name);
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 = {
NID_name_constraints, 0,
ASN1_ITEM_ref(NAME_CONSTRAINTS),
......@@ -218,3 +225,277 @@ static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip)
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)
#endif
NID_policy_constraints, /* 401 */
NID_proxyCertInfo, /* 663 */
NID_name_constraints, /* 666 */
NID_inhibit_any_policy /* 748 */
};
......@@ -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->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);
......
......@@ -305,10 +305,10 @@ typedef struct GENERAL_SUBTREE_st {
DECLARE_STACK_OF(GENERAL_SUBTREE)
typedef struct NAME_CONSTRAINTS_st {
struct NAME_CONSTRAINTS_st {
STACK_OF(GENERAL_SUBTREE) *permittedSubtrees;
STACK_OF(GENERAL_SUBTREE) *excludedSubtrees;
} NAME_CONSTRAINTS;
};
typedef struct POLICY_CONSTRAINTS_st {
ASN1_INTEGER *requireExplicitPolicy;
......@@ -549,6 +549,8 @@ 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);
DECLARE_ASN1_FUNCTIONS(ACCESS_DESCRIPTION)
DECLARE_ASN1_FUNCTIONS(AUTHORITY_INFO_ACCESS)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册