Initial support for certificate purpose checking: this will
authorDr. Stephen Henson <steve@openssl.org>
Wed, 13 Oct 1999 01:11:56 +0000 (01:11 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Wed, 13 Oct 1999 01:11:56 +0000 (01:11 +0000)
ultimately lead to certificate chain verification. It is
VERY EXPERIMENTAL at present though.

CHANGES
apps/x509.c
crypto/asn1/x_x509.c
crypto/x509/x509.h
crypto/x509v3/Makefile.ssl
crypto/x509v3/v3_purp.c [new file with mode: 0644]
crypto/x509v3/x509v3.h

diff --git a/CHANGES b/CHANGES
index 12abb95..3843a9e 100644 (file)
--- 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]
index aa6e057..70bd139 100644 (file)
@@ -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;
+}
+
+
+
index c428997..5326deb 100644 (file)
@@ -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);
index 36772b5..64b07e4 100644 (file)
@@ -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)
index 57006e6..bb22bee 100644 (file)
@@ -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 (file)
index 0000000..9c4706e
--- /dev/null
@@ -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 <stdio.h>
+#include "cryptlib.h"
+#include <openssl/x509v3.h>
+
+
+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;
+}
index 229146a..c44ea05 100644 (file)
@@ -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.