/* v3_purp.c */
-/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
* project 2001.
*/
/* ====================================================================
- * Copyright (c) 1999-2001 The OpenSSL Project. All rights reserved.
+ * Copyright (c) 1999-2004 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
static void x509v3_cache_extensions(X509 *x);
-static int ca_check(const X509 *x);
static int check_ssl_ca(const X509 *x);
static int check_purpose_ssl_client(const X509_PURPOSE *xp, const X509 *x, int ca);
static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x, int ca);
static int check_purpose_smime_sign(const X509_PURPOSE *xp, const X509 *x, int ca);
static int check_purpose_smime_encrypt(const X509_PURPOSE *xp, const X509 *x, int ca);
static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, int ca);
+static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x, int ca);
static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca);
static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca);
{X509_PURPOSE_CRL_SIGN, X509_TRUST_COMPAT, 0, check_purpose_crl_sign, "CRL signing", "crlsign", NULL},
{X509_PURPOSE_ANY, X509_TRUST_DEFAULT, 0, no_check, "Any Purpose", "any", NULL},
{X509_PURPOSE_OCSP_HELPER, X509_TRUST_COMPAT, 0, ocsp_helper, "OCSP helper", "ocsphelper", NULL},
+ {X509_PURPOSE_TIMESTAMP_SIGN, X509_TRUST_TSA, 0, check_purpose_timestamp_sign, "Time Stamp signing", "timestampsign", NULL},
};
#define X509_PURPOSE_COUNT (sizeof(xstandard)/sizeof(X509_PURPOSE))
X509_PURPOSE * X509_PURPOSE_get0(int idx)
{
if(idx < 0) return NULL;
- if(idx < X509_PURPOSE_COUNT) return xstandard + idx;
+ if(idx < (int)X509_PURPOSE_COUNT) return xstandard + idx;
return sk_X509_PURPOSE_value(xptable, idx - X509_PURPOSE_COUNT);
}
void X509_PURPOSE_cleanup(void)
{
- int i;
+ unsigned int i;
sk_X509_PURPOSE_pop_free(xptable, xptable_free);
for(i = 0; i < X509_PURPOSE_COUNT; i++) xptable_free(xstandard + i);
xptable = NULL;
return xp->trust;
}
-static int nid_cmp(int *a, int *b)
+static int nid_cmp(const int *a, const int *b)
{
return *a - *b;
}
+DECLARE_OBJ_BSEARCH_CMP_FN(int, int, nid);
+IMPLEMENT_OBJ_BSEARCH_CMP_FN(int, int, nid);
+
int X509_supported_extension(X509_EXTENSION *ex)
{
/* This table is a list of the NIDs of supported extensions:
* searched using bsearch.
*/
- static int supported_nids[] = {
+ static const int supported_nids[] = {
NID_netscape_cert_type, /* 71 */
NID_key_usage, /* 83 */
NID_subject_alt_name, /* 85 */
NID_basic_constraints, /* 87 */
- NID_ext_key_usage /* 126 */
+ NID_certificate_policies, /* 89 */
+ NID_ext_key_usage, /* 126 */
+#ifndef OPENSSL_NO_RFC3779
+ NID_sbgp_ipAddrBlock, /* 290 */
+ NID_sbgp_autonomousSysNum, /* 291 */
+#endif
+ NID_policy_constraints, /* 401 */
+ NID_proxyCertInfo, /* 663 */
+ NID_name_constraints, /* 666 */
+ NID_policy_mappings, /* 747 */
+ NID_inhibit_any_policy /* 748 */
};
- int ex_nid;
-
- ex_nid = OBJ_obj2nid(X509_EXTENSION_get_object(ex));
+ int ex_nid = OBJ_obj2nid(X509_EXTENSION_get_object(ex));
if (ex_nid == NID_undef)
return 0;
- if (OBJ_bsearch((char *)&ex_nid, (char *)supported_nids,
- sizeof(supported_nids)/sizeof(int), sizeof(int),
- (int (*)(const void *, const void *))nid_cmp))
+ if (OBJ_bsearch_nid(&ex_nid, supported_nids,
+ sizeof(supported_nids)/sizeof(int)))
return 1;
return 0;
}
-
+
+static void setup_dp(X509 *x, DIST_POINT *dp)
+ {
+ X509_NAME *iname = NULL;
+ int i;
+ if (dp->reasons)
+ {
+ if (dp->reasons->length > 0)
+ dp->dp_reasons = dp->reasons->data[0];
+ if (dp->reasons->length > 1)
+ dp->dp_reasons |= (dp->reasons->data[1] << 8);
+ dp->dp_reasons &= CRLDP_ALL_REASONS;
+ }
+ else
+ dp->dp_reasons = CRLDP_ALL_REASONS;
+ if (!dp->distpoint || (dp->distpoint->type != 1))
+ return;
+ for (i = 0; i < sk_GENERAL_NAME_num(dp->CRLissuer); i++)
+ {
+ GENERAL_NAME *gen = sk_GENERAL_NAME_value(dp->CRLissuer, i);
+ if (gen->type == GEN_DIRNAME)
+ {
+ iname = gen->d.directoryName;
+ break;
+ }
+ }
+ if (!iname)
+ iname = X509_get_issuer_name(x);
+
+ DIST_POINT_set_dpname(dp->distpoint, iname);
+
+ }
+
+static void setup_crldp(X509 *x)
+ {
+ int i;
+ x->crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, NULL, NULL);
+ for (i = 0; i < sk_DIST_POINT_num(x->crldp); i++)
+ setup_dp(x, sk_DIST_POINT_value(x->crldp, i));
+ }
static void x509v3_cache_extensions(X509 *x)
{
BASIC_CONSTRAINTS *bs;
+ PROXY_CERT_INFO_EXTENSION *pci;
ASN1_BIT_STRING *usage;
ASN1_BIT_STRING *ns;
EXTENDED_KEY_USAGE *extusage;
#ifndef OPENSSL_NO_SHA
X509_digest(x, EVP_sha1(), x->sha1_hash, NULL);
#endif
- /* Does subject name match issuer ? */
- if(!X509_NAME_cmp(X509_get_subject_name(x), X509_get_issuer_name(x)))
- x->ex_flags |= EXFLAG_SS;
/* V1 should mean no extensions ... */
if(!X509_get_version(x)) x->ex_flags |= EXFLAG_V1;
/* Handle basic constraints */
BASIC_CONSTRAINTS_free(bs);
x->ex_flags |= EXFLAG_BCONS;
}
+ /* Handle proxy certificates */
+ if((pci=X509_get_ext_d2i(x, NID_proxyCertInfo, NULL, NULL))) {
+ if (x->ex_flags & EXFLAG_CA
+ || X509_get_ext_by_NID(x, NID_subject_alt_name, 0) >= 0
+ || X509_get_ext_by_NID(x, NID_issuer_alt_name, 0) >= 0) {
+ x->ex_flags |= EXFLAG_INVALID;
+ }
+ if (pci->pcPathLengthConstraint) {
+ x->ex_pcpathlen =
+ ASN1_INTEGER_get(pci->pcPathLengthConstraint);
+ } else x->ex_pcpathlen = -1;
+ PROXY_CERT_INFO_EXTENSION_free(pci);
+ x->ex_flags |= EXFLAG_PROXY;
+ }
/* Handle key usage */
if((usage=X509_get_ext_d2i(x, NID_key_usage, NULL, NULL))) {
if(usage->length > 0) {
case NID_time_stamp:
x->ex_xkusage |= XKU_TIMESTAMP;
break;
+
+ case NID_dvcs:
+ x->ex_xkusage |= XKU_DVCS;
+ break;
+
+ case NID_anyExtendedKeyUsage:
+ x->ex_xkusage |= XKU_ANYEKU;
+ break;
}
}
sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free);
}
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);
+ /* Does subject name match issuer ? */
+ if(!X509_NAME_cmp(X509_get_subject_name(x), X509_get_issuer_name(x)))
+ {
+ x->ex_flags |= EXFLAG_SI;
+ /* If SKID matches AKID also indicate self signed */
+ if (X509_check_akid(x, x->akid) == X509_V_OK)
+ x->ex_flags |= EXFLAG_SS;
+ }
+ 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);
+
+#ifndef OPENSSL_NO_RFC3779
+ x->rfc3779_addr =X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, NULL, NULL);
+ x->rfc3779_asid =X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum,
+ NULL, NULL);
+#endif
for (i = 0; i < X509_get_ext_count(x); i++)
{
ex = X509_get_ext(x, i);
if (!X509_EXTENSION_get_critical(ex))
continue;
+ if (OBJ_obj2nid(X509_EXTENSION_get_object(ex))
+ == NID_freshest_crl)
+ x->ex_flags |= EXFLAG_FRESHEST;
if (!X509_supported_extension(ex))
{
x->ex_flags |= EXFLAG_CRITICAL;
* 1 is a CA
* 2 basicConstraints absent so "maybe" a CA
* 3 basicConstraints absent but self signed V1.
+ * 4 basicConstraints absent but keyUsage present and keyCertSign asserted.
*/
#define V1_ROOT (EXFLAG_V1|EXFLAG_SS)
#define ns_reject(x, usage) \
(((x)->ex_flags & EXFLAG_NSCERT) && !((x)->ex_nscert & (usage)))
-static int ca_check(const X509 *x)
+static int check_ca(const X509 *x)
{
/* keyUsage if present should allow cert signing */
if(ku_reject(x, KU_KEY_CERT_SIGN)) return 0;
/* If basicConstraints says not a CA then say so */
else return 0;
} else {
+ /* we support V1 roots for... uh, I don't really know why. */
if((x->ex_flags & V1_ROOT) == V1_ROOT) return 3;
/* If key usage present it must have certSign so tolerate it */
- else if (x->ex_flags & EXFLAG_KUSAGE) return 3;
- else return 2;
+ else if (x->ex_flags & EXFLAG_KUSAGE) return 4;
+ /* Older certificates could have Netscape-specific CA types */
+ else if (x->ex_flags & EXFLAG_NSCERT
+ && x->ex_nscert & NS_ANY_CA) return 5;
+ /* can this still be regarded a CA certificate? I doubt it */
+ return 0;
}
}
+int X509_check_ca(X509 *x)
+{
+ if(!(x->ex_flags & EXFLAG_SET)) {
+ CRYPTO_w_lock(CRYPTO_LOCK_X509);
+ x509v3_cache_extensions(x);
+ CRYPTO_w_unlock(CRYPTO_LOCK_X509);
+ }
+
+ return check_ca(x);
+}
+
/* Check SSL CA: common checks for SSL client and server */
static int check_ssl_ca(const X509 *x)
{
int ca_ret;
- ca_ret = ca_check(x);
+ ca_ret = check_ca(x);
if(!ca_ret) return 0;
/* check nsCertType if present */
- if(x->ex_flags & EXFLAG_NSCERT) {
- if(x->ex_nscert & NS_SSL_CA) return ca_ret;
- return 0;
- }
- if(ca_ret != 2) return ca_ret;
+ if(ca_ret != 5 || x->ex_nscert & NS_SSL_CA) return ca_ret;
else return 0;
}
{
if(xku_reject(x,XKU_SSL_CLIENT)) return 0;
if(ca) return check_ssl_ca(x);
- /* We need to do digital signatures with it */
- if(ku_reject(x,KU_DIGITAL_SIGNATURE)) return 0;
+ /* We need to do digital signatures or key agreement */
+ if(ku_reject(x,KU_DIGITAL_SIGNATURE|KU_KEY_AGREEMENT)) return 0;
/* nsCertType if present should allow SSL client use */
if(ns_reject(x, NS_SSL_CLIENT)) return 0;
return 1;
}
+/* Key usage needed for TLS/SSL server: digital signature, encipherment or
+ * key agreement. The ssl code can check this more thoroughly for individual
+ * key types.
+ */
+#define KU_TLS \
+ KU_DIGITAL_SIGNATURE|KU_KEY_ENCIPHERMENT|KU_KEY_AGREEMENT
static int check_purpose_ssl_server(const X509_PURPOSE *xp, const X509 *x, int ca)
{
if(ca) return check_ssl_ca(x);
if(ns_reject(x, NS_SSL_SERVER)) return 0;
- /* Now as for keyUsage: we'll at least need to sign OR encipher */
- if(ku_reject(x, KU_DIGITAL_SIGNATURE|KU_KEY_ENCIPHERMENT)) return 0;
+ if(ku_reject(x, KU_TLS)) return 0;
return 1;
if(xku_reject(x,XKU_SMIME)) return 0;
if(ca) {
int ca_ret;
- ca_ret = ca_check(x);
+ ca_ret = check_ca(x);
if(!ca_ret) return 0;
/* check nsCertType if present */
- if(x->ex_flags & EXFLAG_NSCERT) {
- if(x->ex_nscert & NS_SMIME_CA) return ca_ret;
- return 0;
- }
- if(ca_ret != 2) return ca_ret;
+ if(ca_ret != 5 || x->ex_nscert & NS_SMIME_CA) return ca_ret;
else return 0;
}
if(x->ex_flags & EXFLAG_NSCERT) {
{
if(ca) {
int ca_ret;
- if((ca_ret = ca_check(x)) != 2) return ca_ret;
+ if((ca_ret = check_ca(x)) != 2) return ca_ret;
else return 0;
}
if(ku_reject(x, KU_CRL_SIGN)) return 0;
static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca)
{
- /* Must be a valid CA */
- if(ca) {
- int ca_ret;
- ca_ret = ca_check(x);
- if(ca_ret != 2) return ca_ret;
- if(x->ex_flags & EXFLAG_NSCERT) {
- if(x->ex_nscert & NS_ANY_CA) return ca_ret;
+ /* Must be a valid CA. Should we really support the "I don't know"
+ value (2)? */
+ if(ca) return check_ca(x);
+ /* leaf certificate is checked in OCSP_verify() */
+ return 1;
+}
+
+static int check_purpose_timestamp_sign(const X509_PURPOSE *xp, const X509 *x,
+ int ca)
+{
+ int i_ext;
+
+ /* If ca is true we must return if this is a valid CA certificate. */
+ if (ca) return check_ca(x);
+
+ /*
+ * Check the optional key usage field:
+ * if Key Usage is present, it must be one of digitalSignature
+ * and/or nonRepudiation (other values are not consistent and shall
+ * be rejected).
+ */
+ if ((x->ex_flags & EXFLAG_KUSAGE)
+ && ((x->ex_kusage & ~(KU_NON_REPUDIATION | KU_DIGITAL_SIGNATURE)) ||
+ !(x->ex_kusage & (KU_NON_REPUDIATION | KU_DIGITAL_SIGNATURE))))
+ return 0;
+
+ /* Only time stamp key usage is permitted and it's required. */
+ if (!(x->ex_flags & EXFLAG_XKUSAGE) || x->ex_xkusage != XKU_TIMESTAMP)
+ return 0;
+
+ /* Extended Key Usage MUST be critical */
+ i_ext = X509_get_ext_by_NID((X509 *) x, NID_ext_key_usage, 0);
+ if (i_ext >= 0)
+ {
+ X509_EXTENSION *ext = X509_get_ext((X509 *) x, i_ext);
+ if (!X509_EXTENSION_get_critical(ext))
return 0;
}
- return 0;
- }
- /* leaf certificate is checked in OCSP_verify() */
+
return 1;
}
return X509_V_ERR_SUBJECT_ISSUER_MISMATCH;
x509v3_cache_extensions(issuer);
x509v3_cache_extensions(subject);
- if(subject->akid) {
- /* Check key ids (if present) */
- if(subject->akid->keyid && issuer->skid &&
- ASN1_OCTET_STRING_cmp(subject->akid->keyid, issuer->skid) )
+
+ if(subject->akid)
+ {
+ int ret = X509_check_akid(issuer, subject->akid);
+ if (ret != X509_V_OK)
+ return ret;
+ }
+
+ if(subject->ex_flags & EXFLAG_PROXY)
+ {
+ if(ku_reject(issuer, KU_DIGITAL_SIGNATURE))
+ return X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE;
+ }
+ else if(ku_reject(issuer, KU_KEY_CERT_SIGN))
+ return X509_V_ERR_KEYUSAGE_NO_CERTSIGN;
+ return X509_V_OK;
+}
+
+int X509_check_akid(X509 *issuer, AUTHORITY_KEYID *akid)
+ {
+
+ if(!akid)
+ return X509_V_OK;
+
+ /* Check key ids (if present) */
+ if(akid->keyid && issuer->skid &&
+ ASN1_OCTET_STRING_cmp(akid->keyid, issuer->skid) )
return X509_V_ERR_AKID_SKID_MISMATCH;
- /* Check serial number */
- if(subject->akid->serial &&
- ASN1_INTEGER_cmp(X509_get_serialNumber(issuer),
- subject->akid->serial))
+ /* Check serial number */
+ if(akid->serial &&
+ ASN1_INTEGER_cmp(X509_get_serialNumber(issuer), akid->serial))
return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH;
- /* Check issuer name */
- if(subject->akid->issuer) {
- /* Ugh, for some peculiar reason AKID includes
- * SEQUENCE OF GeneralName. So look for a DirName.
- * There may be more than one but we only take any
- * notice of the first.
- */
- GENERAL_NAMES *gens;
- GENERAL_NAME *gen;
- X509_NAME *nm = NULL;
- int i;
- gens = subject->akid->issuer;
- for(i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
- gen = sk_GENERAL_NAME_value(gens, i);
- if(gen->type == GEN_DIRNAME) {
- nm = gen->d.dirn;
- break;
+ /* Check issuer name */
+ if(akid->issuer)
+ {
+ /* Ugh, for some peculiar reason AKID includes
+ * SEQUENCE OF GeneralName. So look for a DirName.
+ * There may be more than one but we only take any
+ * notice of the first.
+ */
+ GENERAL_NAMES *gens;
+ GENERAL_NAME *gen;
+ X509_NAME *nm = NULL;
+ int i;
+ gens = akid->issuer;
+ for(i = 0; i < sk_GENERAL_NAME_num(gens); i++)
+ {
+ gen = sk_GENERAL_NAME_value(gens, i);
+ if(gen->type == GEN_DIRNAME)
+ {
+ nm = gen->d.dirn;
+ break;
}
}
- if(nm && X509_NAME_cmp(nm, X509_get_issuer_name(issuer)))
- return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH;
+ if(nm && X509_NAME_cmp(nm, X509_get_issuer_name(issuer)))
+ return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH;
}
- }
- if(ku_reject(issuer, KU_KEY_CERT_SIGN)) return X509_V_ERR_KEYUSAGE_NO_CERTSIGN;
return X509_V_OK;
-}
+ }