From: Dr. Stephen Henson Date: Wed, 13 Oct 1999 01:11:56 +0000 (+0000) Subject: Initial support for certificate purpose checking: this will X-Git-Tag: OpenSSL_0_9_5beta1~477 X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff_plain;h=673b102c5b265bd6c517ac40ab76e1606a243c08 Initial support for certificate purpose checking: this will ultimately lead to certificate chain verification. It is VERY EXPERIMENTAL at present though. --- diff --git a/CHANGES b/CHANGES index 12abb95840..3843a9ea75 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,14 @@ Changes between 0.9.4 and 0.9.5 [xx XXX 1999] + *) Add various functions that can check a certificate's extensions + to see if it usable for various purposes such as SSL client, + server or S/MIME and CAs of these types. This is currently + VERY EXPERIMENTAL but will ultimately be used for certificate chain + verification. Also added a -purpose flag to x509 utility to + print out all the purposes. + [Steve Henson] + *) Add a CRYPTO_EX_DATA to X509 certificate structure and associated functions. [Steve Henson] diff --git a/apps/x509.c b/apps/x509.c index aa6e0573d6..70bd1391a7 100644 --- a/apps/x509.c +++ b/apps/x509.c @@ -97,6 +97,7 @@ static char *x509_usage[]={ " -issuer - print issuer DN\n", " -startdate - notBefore field\n", " -enddate - notAfter field\n", +" -purpose - print out certificate purposes\n", " -dates - both Before and After dates\n", " -modulus - print the RSA key modulus\n", " -fingerprint - print the certificate fingerprint\n", @@ -127,8 +128,14 @@ static int sign (X509 *x, EVP_PKEY *pkey,int days,const EVP_MD *digest, static int x509_certify (X509_STORE *ctx,char *CAfile,const EVP_MD *digest, X509 *x,X509 *xca,EVP_PKEY *pkey,char *serial, int create,int days, LHASH *conf, char *section); +static int efunc(X509_PURPOSE *pt, void *arg); static int reqfile=0; +typedef struct { +BIO *bio; +X509 *cert; +} X509_PPRINT; + int MAIN(int argc, char **argv) { int ret=1; @@ -145,6 +152,7 @@ int MAIN(int argc, char **argv) int noout=0,sign_flag=0,CA_flag=0,CA_createserial=0; int C=0; int x509req=0,days=DEF_DAYS,modulus=0; + int pprint = 0; char **pp; X509_STORE *ctx=NULL; X509_REQ *rq=NULL; @@ -279,6 +287,8 @@ int MAIN(int argc, char **argv) startdate= ++num; enddate= ++num; } + else if (strcmp(*argv,"-purpose") == 0) + pprint= ++num; else if (strcmp(*argv,"-startdate") == 0) startdate= ++num; else if (strcmp(*argv,"-enddate") == 0) @@ -312,6 +322,7 @@ bad: ERR_load_crypto_strings(); X509V3_add_standard_extensions(); + X509_PURPOSE_add_standard(); if (!X509_STORE_set_default_paths(ctx)) { @@ -500,6 +511,14 @@ bad: { BIO_printf(STDout,"%08lx\n",X509_subject_name_hash(x)); } + else if (pprint == i) + { + X509_PPRINT ptmp; + ptmp.bio = STDout; + ptmp.cert = x; + BIO_printf(STDout, "Certificate purposes:\n"); + X509_PURPOSE_enum(efunc, &ptmp); + } else if (modulus == i) { @@ -1090,3 +1109,24 @@ err: ERR_print_errors(bio_err); return(0); } + +static int efunc(X509_PURPOSE *pt, void *arg) +{ + X509_PPRINT *ptmp; + int id, i, idret; + char *pname; + ptmp = arg; + id = X509_PURPOSE_get_id(pt); + pname = X509_PURPOSE_get_name(pt); + for(i = 0; i < 2; i++) { + idret = X509_check_purpose(ptmp->cert, id, i); + BIO_printf(ptmp->bio, "%s%s : ", pname, i ? " CA" : ""); + if(idret == 1) BIO_printf(ptmp->bio, "Yes\n"); + else if (idret == 0) BIO_printf(ptmp->bio, "No\n"); + else BIO_printf(ptmp->bio, "Yes (WARNING code=%d)\n", idret); + } + return 1; +} + + + diff --git a/crypto/asn1/x_x509.c b/crypto/asn1/x_x509.c index c428997c00..5326debe00 100644 --- a/crypto/asn1/x_x509.c +++ b/crypto/asn1/x_x509.c @@ -116,6 +116,7 @@ X509 *X509_new(void) M_ASN1_New_Malloc(ret,X509); ret->references=1; ret->valid=0; + ret->ex_flags = 0; ret->name=NULL; M_ASN1_New(ret->cert_info,X509_CINF_new); M_ASN1_New(ret->sig_alg,X509_ALGOR_new); diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h index 36772b57ea..64b07e4786 100644 --- a/crypto/x509/x509.h +++ b/crypto/x509/x509.h @@ -239,6 +239,12 @@ typedef struct x509_st int references; char *name; CRYPTO_EX_DATA ex_data; + /* These contain copies of various extension values */ + long ex_pathlen; + unsigned long ex_flags; + unsigned long ex_kusage; + unsigned long ex_xkusage; + unsigned long ex_nscert; } X509; DECLARE_STACK_OF(X509) diff --git a/crypto/x509v3/Makefile.ssl b/crypto/x509v3/Makefile.ssl index 57006e6875..bb22bee92f 100644 --- a/crypto/x509v3/Makefile.ssl +++ b/crypto/x509v3/Makefile.ssl @@ -24,10 +24,10 @@ APPS= LIB=$(TOP)/libcrypto.a LIBSRC= v3_bcons.c v3_bitst.c v3_conf.c v3_extku.c v3_ia5.c \ v3_lib.c v3_prn.c v3_utl.c v3err.c v3_genn.c v3_alt.c v3_skey.c v3_akey.c \ -v3_pku.c v3_int.c v3_enum.c v3_sxnet.c v3_cpols.c v3_crld.c +v3_pku.c v3_int.c v3_enum.c v3_sxnet.c v3_cpols.c v3_crld.c v3_purp.c LIBOBJ= v3_bcons.o v3_bitst.o v3_conf.o v3_extku.o v3_ia5.o v3_lib.o \ v3_prn.o v3_utl.o v3err.o v3_genn.o v3_alt.o v3_skey.o v3_akey.o v3_pku.o \ -v3_int.o v3_enum.o v3_sxnet.o v3_cpols.o v3_crld.o +v3_int.o v3_enum.o v3_sxnet.o v3_cpols.o v3_crld.o v3_purp.o SRC= $(LIBSRC) diff --git a/crypto/x509v3/v3_purp.c b/crypto/x509v3/v3_purp.c new file mode 100644 index 0000000000..9c4706e715 --- /dev/null +++ b/crypto/x509v3/v3_purp.c @@ -0,0 +1,366 @@ +/* v3_purp.c */ +/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL + * project 1999. + */ +/* ==================================================================== + * Copyright (c) 1999 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 + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#include +#include "cryptlib.h" +#include + + +static int x509_purpose_get_idx(int id); +void x509v3_cache_extensions(X509 *x); + +static int ca_check(X509 *x); +static int check_purpose_ssl_client(X509_PURPOSE *xp, X509 *x, int ca); +static int check_purpose_ssl_server(X509_PURPOSE *xp, X509 *x, int ca); +static int check_purpose_ns_ssl_server(X509_PURPOSE *xp, X509 *x, int ca); +static int purpose_smime(X509 *x, int ca); +static int check_purpose_smime_sign(X509_PURPOSE *xp, X509 *x, int ca); +static int check_purpose_smime_encrypt(X509_PURPOSE *xp, X509 *x, int ca); +static int check_purpose_crl_sign(X509_PURPOSE *xp, X509 *x, int ca); + +static int xp_cmp(X509_PURPOSE **a, X509_PURPOSE **b); + +static X509_PURPOSE xstandard[] = { +{1, check_purpose_ssl_client, "SSL client", NULL}, +{2, check_purpose_ssl_server, "SSL server", NULL}, +{3, check_purpose_ns_ssl_server, "Netscape SSL server", NULL}, +{4, check_purpose_smime_sign, "S/MIME signing", NULL}, +{5, check_purpose_smime_encrypt, "S/MIME encryption", NULL}, +{6, check_purpose_crl_sign, "CRL signing", NULL}, +{-1, NULL, NULL, NULL} +}; + +IMPLEMENT_STACK_OF(X509_PURPOSE) + +static STACK_OF(X509_PURPOSE) *xptable; + +static int xp_cmp(X509_PURPOSE **a, X509_PURPOSE **b) +{ + return (*a)->purpose_id - (*b)->purpose_id; +} + +int X509_check_purpose(X509 *x, int id, int ca) +{ + int idx; + X509_PURPOSE *pt; + if(!(x->ex_flags & EXFLAG_SET)) { + CRYPTO_w_lock(CRYPTO_LOCK_X509); + x509v3_cache_extensions(x); + CRYPTO_w_unlock(CRYPTO_LOCK_X509); + } + idx = x509_purpose_get_idx(id); + if(idx == -1) return -1; + pt = sk_X509_PURPOSE_value(xptable, idx); + return pt->check_purpose(pt, x,ca); +} + + + + +static int x509_purpose_get_idx(int id) +{ + X509_PURPOSE tmp; + tmp.purpose_id = id; + if(!xptable) return -1; + return sk_X509_PURPOSE_find(xptable, &tmp); +} + +int X509_PURPOSE_add(X509_PURPOSE *xp) +{ + int idx; + if(!xptable) xptable = sk_X509_PURPOSE_new(xp_cmp); + idx = x509_purpose_get_idx(xp->purpose_id); + if(idx != -1) sk_X509_PURPOSE_set(xptable, idx, xp); + else sk_X509_PURPOSE_push(xptable, xp); + return 1; +} + +void X509_PURPOSE_add_standard(void) +{ + X509_PURPOSE *xp; + for(xp = xstandard; xp->purpose_name; xp++) + X509_PURPOSE_add(xp); +} + +int X509_PURPOSE_enum(int (*efunc)(X509_PURPOSE *, void *), void *usr) +{ + int i; + X509_PURPOSE *xp; + if(!xptable) return 0; + for(i = 0; i < sk_X509_PURPOSE_num(xptable); i++) { + xp = sk_X509_PURPOSE_value(xptable, i); + if(!efunc(xp, usr)) return i; + } + return i; +} + + +int X509_PURPOSE_get_id(X509_PURPOSE *xp) +{ + return xp->purpose_id; +} + +char *X509_PURPOSE_get_name(X509_PURPOSE *xp) +{ + return xp->purpose_name; +} + +void x509v3_cache_extensions(X509 *x) +{ + BASIC_CONSTRAINTS *bs; + ASN1_BIT_STRING *usage; + ASN1_BIT_STRING *ns; + STACK_OF(ASN1_OBJECT) *extusage; + int i; + if(x->ex_flags & EXFLAG_SET) return; + /* 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 */ + if((bs=X509V3_X509_get_d2i(x, NID_basic_constraints, NULL, NULL))) { + if(bs->ca) x->ex_flags |= EXFLAG_CA; + if(bs->pathlen) { + if((bs->pathlen->type == V_ASN1_NEG_INTEGER) + || !bs->ca) { + x->ex_flags |= EXFLAG_INVALID; + x->ex_pathlen = 0; + } else x->ex_pathlen = ASN1_INTEGER_get(bs->pathlen); + } else x->ex_pathlen = -1; + BASIC_CONSTRAINTS_free(bs); + x->ex_flags |= EXFLAG_BCONS; + } + /* Handle key usage */ + if((usage=X509V3_X509_get_d2i(x, NID_key_usage, NULL, NULL))) { + if(usage->length > 0) { + x->ex_kusage = usage->data[0]; + if(usage->length > 1) + x->ex_kusage |= usage->data[1] << 8; + } else x->ex_kusage = 0; + x->ex_flags |= EXFLAG_KUSAGE; + ASN1_BIT_STRING_free(usage); + } + x->ex_xkusage = 0; + if((extusage=X509V3_X509_get_d2i(x, NID_ext_key_usage, NULL, NULL))) { + x->ex_flags |= EXFLAG_XKUSAGE; + for(i = 0; i < sk_ASN1_OBJECT_num(extusage); i++) { + switch(OBJ_obj2nid(sk_ASN1_OBJECT_value(extusage,i))) { + case NID_server_auth: + x->ex_xkusage |= XKU_SSL_SERVER; + break; + + case NID_client_auth: + x->ex_xkusage |= XKU_SSL_CLIENT; + break; + + case NID_email_protect: + x->ex_xkusage |= XKU_SMIME; + break; + + case NID_code_sign: + x->ex_xkusage |= XKU_CODE_SIGN; + break; + + case NID_ms_sgc: + case NID_ns_sgc: + x->ex_xkusage |= XKU_SGC; + } + } + sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free); + } + + if((ns=X509V3_X509_get_d2i(x, NID_netscape_cert_type, NULL, NULL))) { + if(ns->length > 0) x->ex_nscert = ns->data[0]; + else x->ex_nscert = 0; + x->ex_flags |= EXFLAG_NSCERT; + ASN1_BIT_STRING_free(ns); + } + x->ex_flags |= EXFLAG_SET; +} + +/* CA checks common to all purposes + * return codes: + * 0 not a CA + * 1 is a CA + * 2 basicConstraints absent so "maybe" a CA + * 3 basicConstraints absent but self signed V1. + */ + +#define V1_ROOT (EXFLAG_V1|EXFLAG_SS) +#define ku_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage))) +#define xku_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_XKUSAGE) && !((x)->ex_xkusage & (usage))) +#define ns_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_NSCERT) && !((x)->ex_nscert & (usage))) + +static int ca_check(X509 *x) +{ + /* keyUsage if present should allow cert signing */ + if(ku_reject(x, KU_KEY_CERT_SIGN)) return 0; + if(x->ex_flags & EXFLAG_BCONS) { + if(x->ex_flags & EXFLAG_CA) return 1; + /* If basicConstraints says not a CA then say so */ + else return 0; + } else { + if((x->ex_flags & V1_ROOT) == V1_ROOT) return 3; + else return 2; + } +} + + +static int check_purpose_ssl_client(X509_PURPOSE *xp, X509 *x, int ca) +{ + if(xku_reject(x,XKU_SSL_CLIENT)) return 0; + if(ca) { + int ca_ret; + ca_ret = ca_check(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; + else return 0; + } + /* We need to do digital signatures with it */ + if(ku_reject(x,KU_DIGITAL_SIGNATURE)) return 0; + /* nsCertType if present should allow SSL client use */ + if(ns_reject(x, NS_SSL_CLIENT)) return 0; + return 1; +} + +static int check_purpose_ssl_server(X509_PURPOSE *xp, X509 *x, int ca) +{ + if(xku_reject(x,XKU_SSL_SERVER|XKU_SGC)) return 0; + /* Otherwise same as SSL client for a CA */ + if(ca) return check_purpose_ssl_client(xp, x, 1); + + 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; + + return 1; + +} + +static int check_purpose_ns_ssl_server(X509_PURPOSE *xp, X509 *x, int ca) +{ + int ret; + ret = check_purpose_ssl_server(xp, x, ca); + if(!ret || ca) return ret; + /* We need to encipher or Netscape complains */ + if(ku_reject(x, KU_KEY_ENCIPHERMENT)) return 0; + return ret; +} + +/* common S/MIME checks */ +static int purpose_smime(X509 *x, int ca) +{ + if(xku_reject(x,XKU_SMIME)) return 0; + if(ca) { + int ca_ret; + ca_ret = ca_check(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; + else return 0; + } + if(x->ex_flags & EXFLAG_NSCERT) { + if(x->ex_nscert & NS_SMIME) return 1; + /* Workaround for some buggy certificates */ + if(x->ex_nscert & NS_SSL_CLIENT) return 2; + return 0; + } + return 1; +} + +static int check_purpose_smime_sign(X509_PURPOSE *xp, X509 *x, int ca) +{ + int ret; + ret = purpose_smime(x, ca); + if(!ret || ca) return ret; + if(ku_reject(x, KU_DIGITAL_SIGNATURE)) return 0; + return ret; +} + +static int check_purpose_smime_encrypt(X509_PURPOSE *xp, X509 *x, int ca) +{ + int ret; + ret = purpose_smime(x, ca); + if(!ret || ca) return ret; + if(ku_reject(x, KU_KEY_ENCIPHERMENT)) return 0; + return ret; +} + +static int check_purpose_crl_sign(X509_PURPOSE *xp, X509 *x, int ca) +{ + if(ca) { + int ca_ret; + if((ca_ret = ca_check(x)) != 2) return ca_ret; + else return 0; + } + if(ku_reject(x, KU_CRL_SIGN)) return 0; + return 1; +} diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h index 229146ac7c..c44ea05965 100644 --- a/crypto/x509v3/x509v3.h +++ b/crypto/x509v3/x509v3.h @@ -279,6 +279,56 @@ DECLARE_ASN1_SET_OF(POLICYINFO) NULL, NULL, NULL, NULL, \ NULL} + +/* X509_PURPOSE stuff */ + +#define EXFLAG_BCONS 0x1 +#define EXFLAG_KUSAGE 0x2 +#define EXFLAG_XKUSAGE 0x4 +#define EXFLAG_NSCERT 0x8 + +#define EXFLAG_CA 0x10 +#define EXFLAG_SS 0x20 +#define EXFLAG_V1 0x40 +#define EXFLAG_INVALID 0x80 +#define EXFLAG_SET 0x100 + +#define KU_DIGITAL_SIGNATURE 0x0080 +#define KU_NON_REPUDIATION 0x0040 +#define KU_KEY_ENCIPHERMENT 0x0020 +#define KU_DATA_ENCIPHERMENT 0x0010 +#define KU_KEY_AGREEMENT 0x0008 +#define KU_KEY_CERT_SIGN 0x0004 +#define KU_CRL_SIGN 0x0002 +#define KU_ENCIPHER_ONLY 0x0001 +#define KU_DECIPHER_ONLY 0x8000 + +#define NS_SSL_CLIENT 0x80 +#define NS_SSL_SERVER 0x40 +#define NS_SMIME 0x20 +#define NS_OBJSIGN 0x10 +#define NS_SSL_CA 0x04 +#define NS_SMIME_CA 0x02 +#define NS_OBJSIGN_CA 0x01 + +#define XKU_SSL_SERVER 0x1 +#define XKU_SSL_CLIENT 0x2 +#define XKU_SMIME 0x4 +#define XKU_CODE_SIGN 0x8 +#define XKU_SGC 0x10 + +typedef struct x509_purpose_st { +int purpose_id; +int (*check_purpose)(struct x509_purpose_st *, X509 *, int); +char *purpose_name; +void *usr_data; +} X509_PURPOSE; + +DECLARE_STACK_OF(X509_PURPOSE) + + + + void ERR_load_X509V3_strings(void); int i2d_BASIC_CONSTRAINTS(BASIC_CONSTRAINTS *a, unsigned char **pp); BASIC_CONSTRAINTS *d2i_BASIC_CONSTRAINTS(BASIC_CONSTRAINTS **a, unsigned char **pp, long length); @@ -440,6 +490,13 @@ void X509V3_EXT_val_prn(BIO *out, STACK_OF(CONF_VALUE) *val, int indent, int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, int flag, int indent); int X509V3_EXT_print_fp(FILE *out, X509_EXTENSION *ext, int flag, int indent); +int X509_check_purpose(X509 *x, int id, int ca); +int X509_PURPOSE_add(X509_PURPOSE *xp); +void X509_PURPOSE_add_standard(void); +int X509_PURPOSE_enum(int (*efunc)(X509_PURPOSE *, void *), void *usr); +int X509_PURPOSE_get_id(X509_PURPOSE *); +char * X509_PURPOSE_get_name(X509_PURPOSE *); + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run.