*BIG* verify code reorganisation.

The old code was painfully primitive and couldn't handle
distinct certificates using the same subject name.

The new code performs several tests on a candidate issuer
certificate based on certificate extensions.

It also adds several callbacks to X509_VERIFY_CTX so its
behaviour can be customised.

Unfortunately some hackery was needed to persuade X509_STORE
to tolerate this. This should go away when X509_STORE is
replaced, sometime...

This must have broken something though :-(
diff --git a/CHANGES b/CHANGES
index a8cc5f1..4fe8ae9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,7 +3,58 @@
  _______________
 
  Changes between 0.9.5a and 0.9.6  [xx XXX 2000]
+  
+   *) Phase 2 verify code reorganisation. The certificate
+      verify code now looks up an issuer certificate by a
+      number of criteria: subject name, authority key id
+      and key usage. It also verifies self signed certificates
+      by the same criteria. The main comparison function is
+      X509_check_issued() which performs these checks.
+ 
+      Lot of changes were necessary in order to support this
+      without completely rewriting the lookup code.
+ 
+      Authority and subject key identifier are now cached.
+ 
+      The LHASH 'certs' is X509_STORE has now been replaced
+      by a STACK_OF(X509_OBJECT). This is mainly because an
+      LHASH can't store or retrieve multiple objects with
+      the same hash value.
 
+      As a result various functions (which were all internal
+      use only) have changed to handle the new X509_STORE
+      structure. This will break anything that messed round
+      with X509_STORE internally.
+ 
+      The functions X509_STORE_add_cert() now checks for an
+      exact match, rather than just subject name.
+ 
+      The X509_STORE API doesn't directly support the retrieval
+      of multiple certificates matching a given criteria, however
+      this can be worked round by performing a lookup first
+      (which will fill the cache with candidate certificates)
+      and then examining the cache for matches. This is probably
+      the best we can do without throwing out X509_LOOKUP
+      entirely (maybe later...).
+ 
+      The X509_VERIFY_CTX structure has been enhanced considerably.
+ 
+      All certificate lookup operations now go via a get_issuer()
+      callback. Although this currently uses an X509_STORE it
+      can be replaced by custom lookups. This is a simple way
+      to bypass the X509_STORE hackery necessary to make this
+      work and makes it possible to use more efficient techniques
+      in future. A very simple version which uses a simple
+      STACK for its trusted certificate store is also provided
+      using X509_STORE_CTX_trusted_stack().
+ 
+      The verify_cb() and verify() callbacks now have equivalents
+      in the X509_STORE_CTX structure.
+ 
+      X509_STORE_CTX also has a 'flags' field which can be used
+      to customise the verify behaviour.
+      [Steve Henson]
+ 
   *) Add new PKCS#7 signing option PKCS7_NOSMIMECAP which 
      excludes S/MIME capabilities.
      [Steve Henson]
diff --git a/Configure b/Configure
index 49125c6..32b95d8 100755
--- a/Configure
+++ b/Configure
@@ -111,7 +111,7 @@
 "debug-rse","cc:-DTERMIOS -DL_ENDIAN -pipe -O -g -ggdb3 -Wall::(unknown)::BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:${x86_elf_asm}",
 "debug-bodo",	"gcc:-DL_ENDIAN -DBN_DEBUG -DREF_CHECK -DCONF_DEBUG -DBN_CTX_DEBUG -DCRYPTO_MDEBUG_ALL -DBIO_PAIR_DEBUG -g -m486 -pedantic -Wshadow -Wall::-D_REENTRANT::BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:${x86_elf_asm}",
 "debug-ulf",	"gcc:-DL_ENDIAN -DREF_CHECK -DCONF_DEBUG -DBN_DEBUG -DBN_CTX_DEBUG -DCRYPTO_MDEBUG_ALL -g -O2 -m486 -Wall -Werror -Wshadow -pipe::-D_REENTRANT::${x86_gcc_des} ${x86_gcc_opts}:${x86_elf_asm}",
-"debug-steve",	"gcc:-DL_ENDIAN -DREF_CHECK -DCONF_DEBUG -DBN_CTX_DEBUG -DCRYPTO_MDEBUG_ALL -DPEDANTIC -g -O2 -m486 -pedantic -Wall -Werror -Wshadow -pipe::-D_REENTRANT::${x86_gcc_des} ${x86_gcc_opts}:${x86_elf_asm}",
+"debug-steve",	"gcc:-DL_ENDIAN -DREF_CHECK -DCONF_DEBUG -DBN_CTX_DEBUG -DDEBUG_SAFESTACK -DCRYPTO_MDEBUG_ALL -DPEDANTIC -g -O2 -m486 -pedantic -Wall -Werror -Wshadow -pipe::-D_REENTRANT::${x86_gcc_des} ${x86_gcc_opts}:${x86_elf_asm}",
 "debug-levitte-linux-elf","gcc:-DUSE_ALLOCATING_PRINT -DRL_DEBUG -DREF_CHECK -DCONF_DEBUG -DBN_CTX_DEBUG -DCRYPTO_MDEBUG -DNO_ASM -DL_ENDIAN -DTERMIO -D_POSIX_SOURCE -ggdb -g3 -m486 -pedantic -ansi -Wall -Wshadow -Wid-clash-31 -pipe::-D_REENTRANT:-ldl:::::::::::dlfcn",
 "dist",		"cc:-O::(unknown):::::",
 
diff --git a/apps/verify.c b/apps/verify.c
index 8d211e0..f50eaae 100644
--- a/apps/verify.c
+++ b/apps/verify.c
@@ -70,7 +70,7 @@
 #define PROG	verify_main
 
 static int MS_CALLBACK cb(int ok, X509_STORE_CTX *ctx);
-static int check(X509_STORE *ctx,char *file, STACK_OF(X509)*other, int purpose);
+static int check(X509_STORE *ctx, char *file, STACK_OF(X509) *uchain, STACK_OF(X509) *tchain, int purpose);
 static STACK_OF(X509) *load_untrusted(char *file);
 static int v_verbose=0;
 
@@ -81,8 +81,8 @@
 	int i,ret=1;
 	int purpose = -1;
 	char *CApath=NULL,*CAfile=NULL;
-	char *untfile = NULL;
-	STACK_OF(X509) *untrusted = NULL;
+	char *untfile = NULL, *trustfile = NULL;
+	STACK_OF(X509) *untrusted = NULL, *trusted = NULL;
 	X509_STORE *cert_ctx=NULL;
 	X509_LOOKUP *lookup=NULL;
 
@@ -132,6 +132,11 @@
 				if (argc-- < 1) goto end;
 				untfile= *(++argv);
 				}
+			else if (strcmp(*argv,"-trusted") == 0)
+				{
+				if (argc-- < 1) goto end;
+				trustfile= *(++argv);
+				}
 			else if (strcmp(*argv,"-help") == 0)
 				goto end;
 			else if (strcmp(*argv,"-verbose") == 0)
@@ -179,10 +184,18 @@
 		}
 	}
 
-	if (argc < 1) check(cert_ctx, NULL, untrusted, purpose);
+	if(trustfile) {
+		if(!(trusted = load_untrusted(trustfile))) {
+			BIO_printf(bio_err, "Error loading untrusted file %s\n", trustfile);
+			ERR_print_errors(bio_err);
+			goto end;
+		}
+	}
+
+	if (argc < 1) check(cert_ctx, NULL, untrusted, trusted, purpose);
 	else
 		for (i=0; i<argc; i++)
-			check(cert_ctx,argv[i], untrusted, purpose);
+			check(cert_ctx,argv[i], untrusted, trusted, purpose);
 	ret=0;
 end:
 	if (ret == 1) {
@@ -197,10 +210,11 @@
 	}
 	if (cert_ctx != NULL) X509_STORE_free(cert_ctx);
 	sk_X509_pop_free(untrusted, X509_free);
+	sk_X509_pop_free(trusted, X509_free);
 	EXIT(ret);
 	}
 
-static int check(X509_STORE *ctx, char *file, STACK_OF(X509) *uchain, int purpose)
+static int check(X509_STORE *ctx, char *file, STACK_OF(X509) *uchain, STACK_OF(X509) *tchain, int purpose)
 	{
 	X509 *x=NULL;
 	BIO *in=NULL;
@@ -242,6 +256,7 @@
 		goto end;
 		}
 	X509_STORE_CTX_init(csc,ctx,x,uchain);
+	if(tchain) X509_STORE_CTX_trusted_stack(csc, tchain);
 	if(purpose >= 0) X509_STORE_CTX_set_purpose(csc, purpose);
 	i=X509_verify_cert(csc);
 	X509_STORE_CTX_free(csc);
diff --git a/crypto/asn1/x_x509.c b/crypto/asn1/x_x509.c
index 36f0e47..61ba856 100644
--- a/crypto/asn1/x_x509.c
+++ b/crypto/asn1/x_x509.c
@@ -61,6 +61,7 @@
 #include <openssl/evp.h>
 #include <openssl/asn1_mac.h>
 #include <openssl/x509.h>
+#include <openssl/x509v3.h>
 
 static int x509_meth_num = 0;
 static STACK_OF(CRYPTO_EX_DATA_FUNCS) *x509_meth = NULL;
@@ -114,12 +115,14 @@
 	ASN1_CTX c;
 
 	M_ASN1_New_Malloc(ret,X509);
-	ret->references=1;
 	ret->valid=0;
+	ret->references=1;
+	ret->name = NULL;
 	ret->ex_flags = 0;
 	ret->ex_pathlen = -1;
-	ret->name=NULL;
-	ret->aux=NULL;
+	ret->skid = NULL;
+	ret->akid = NULL;
+	ret->aux = NULL;
 	M_ASN1_New(ret->cert_info,X509_CINF_new);
 	M_ASN1_New(ret->sig_alg,X509_ALGOR_new);
 	M_ASN1_New(ret->signature,M_ASN1_BIT_STRING_new);
@@ -152,6 +155,8 @@
 	X509_ALGOR_free(a->sig_alg);
 	M_ASN1_BIT_STRING_free(a->signature);
 	X509_CERT_AUX_free(a->aux);
+	ASN1_OCTET_STRING_free(a->skid);
+	AUTHORITY_KEYID_free(a->akid);
 
 	if (a->name != NULL) OPENSSL_free(a->name);
 	OPENSSL_free(a);
diff --git a/crypto/stack/safestack.h b/crypto/stack/safestack.h
index f22c1f9..9fa63e1 100644
--- a/crypto/stack/safestack.h
+++ b/crypto/stack/safestack.h
@@ -864,6 +864,26 @@
 #define sk_X509_NAME_ENTRY_pop(st) SKM_sk_pop(X509_NAME_ENTRY, (st))
 #define sk_X509_NAME_ENTRY_sort(st) SKM_sk_sort(X509_NAME_ENTRY, (st))
 
+#define sk_X509_OBJECT_new(st) SKM_sk_new(X509_OBJECT, (st))
+#define sk_X509_OBJECT_new_null() SKM_sk_new_null(X509_OBJECT)
+#define sk_X509_OBJECT_free(st) SKM_sk_free(X509_OBJECT, (st))
+#define sk_X509_OBJECT_num(st) SKM_sk_num(X509_OBJECT, (st))
+#define sk_X509_OBJECT_value(st, i) SKM_sk_value(X509_OBJECT, (st), (i))
+#define sk_X509_OBJECT_set(st, i, val) SKM_sk_set(X509_OBJECT, (st), (i), (val))
+#define sk_X509_OBJECT_zero(st) SKM_sk_zero(X509_OBJECT, (st))
+#define sk_X509_OBJECT_push(st, val) SKM_sk_push(X509_OBJECT, (st), (val))
+#define sk_X509_OBJECT_unshift(st, val) SKM_sk_unshift(X509_OBJECT, (st), (val))
+#define sk_X509_OBJECT_find(st, val) SKM_sk_find(X509_OBJECT, (st), (val))
+#define sk_X509_OBJECT_delete(st, i) SKM_sk_delete(X509_OBJECT, (st), (i))
+#define sk_X509_OBJECT_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_OBJECT, (st), (ptr))
+#define sk_X509_OBJECT_insert(st, val, i) SKM_sk_insert(X509_OBJECT, (st), (val), (i))
+#define sk_X509_OBJECT_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_OBJECT, (st), (cmp))
+#define sk_X509_OBJECT_dup(st) SKM_sk_dup(X509_OBJECT, st)
+#define sk_X509_OBJECT_pop_free(st, free_func) SKM_sk_pop_free(X509_OBJECT, (st), (free_func))
+#define sk_X509_OBJECT_shift(st) SKM_sk_shift(X509_OBJECT, (st))
+#define sk_X509_OBJECT_pop(st) SKM_sk_pop(X509_OBJECT, (st))
+#define sk_X509_OBJECT_sort(st) SKM_sk_sort(X509_OBJECT, (st))
+
 #define sk_X509_PURPOSE_new(st) SKM_sk_new(X509_PURPOSE, (st))
 #define sk_X509_PURPOSE_new_null() SKM_sk_new_null(X509_PURPOSE)
 #define sk_X509_PURPOSE_free(st) SKM_sk_free(X509_PURPOSE, (st))
diff --git a/crypto/x509/by_dir.c b/crypto/x509/by_dir.c
index c5920cc..cac64a6 100644
--- a/crypto/x509/by_dir.c
+++ b/crypto/x509/by_dir.c
@@ -326,7 +326,9 @@
 		/* we have added it to the cache so now pull
 		 * it out again */
 		CRYPTO_r_lock(CRYPTO_LOCK_X509_STORE);
-		tmp=(X509_OBJECT *)lh_retrieve(xl->store_ctx->certs,&stmp);
+		j = sk_X509_OBJECT_find(xl->store_ctx->objs,&stmp);
+		if(j != -1) tmp=sk_X509_OBJECT_value(xl->store_ctx->objs,i);
+		else tmp = NULL;
 		CRYPTO_r_unlock(CRYPTO_LOCK_X509_STORE);
 
 		if (tmp != NULL)
diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h
index 9768754..db80eda 100644
--- a/crypto/x509/x509.h
+++ b/crypto/x509/x509.h
@@ -275,6 +275,8 @@
 	unsigned long ex_kusage;
 	unsigned long ex_xkusage;
 	unsigned long ex_nscert;
+	ASN1_OCTET_STRING *skid;
+	struct AUTHORITY_KEYID_st *akid;
 #ifndef NO_SHA
 	unsigned char sha1_hash[SHA_DIGEST_LENGTH];
 #endif
diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c
index 68f26f1..8dfd755 100644
--- a/crypto/x509/x509_lu.c
+++ b/crypto/x509/x509_lu.c
@@ -62,7 +62,6 @@
 #include <openssl/x509.h>
 
 static STACK_OF(CRYPTO_EX_DATA_FUNCS) *x509_store_meth=NULL;
-static STACK_OF(CRYPTO_EX_DATA_FUNCS) *x509_store_ctx_meth=NULL;
 
 X509_LOOKUP *X509_LOOKUP_new(X509_LOOKUP_METHOD *method)
 	{
@@ -155,39 +154,21 @@
 	return(ctx->method->get_by_alias(ctx,type,str,len,ret));
 	}
 
-static unsigned long x509_object_hash(X509_OBJECT *a)
-	{
-	unsigned long h;
+  
+static int x509_object_cmp(const X509_OBJECT * const *a, const X509_OBJECT * const *b)
+  	{
+ 	int ret;
 
-	switch (a->type)
-		{
-	case X509_LU_X509:
-		h=X509_NAME_hash(a->data.x509->cert_info->subject);
-		break;
-	case X509_LU_CRL:
-		h=X509_NAME_hash(a->data.crl->crl->issuer);
-		break;
-	default:
-		/* abort(); */
-		return 0;
-		}
-	return(h);
-	}
-
-static int x509_object_cmp(X509_OBJECT *a, X509_OBJECT *b)
-	{
-	int ret;
-
-	ret=(a->type - b->type);
-	if (ret) return(ret);
-	switch (a->type)
-		{
-	case X509_LU_X509:
-		ret=X509_subject_name_cmp(a->data.x509,b->data.x509);
-		break;
-	case X509_LU_CRL:
-		ret=X509_CRL_cmp(a->data.crl,b->data.crl);
-		break;
+ 	ret=((*a)->type - (*b)->type);
+ 	if (ret) return(ret);
+ 	switch ((*a)->type)
+ 		{
+ 	case X509_LU_X509:
+ 		ret=X509_subject_name_cmp((*a)->data.x509,(*b)->data.x509);
+ 		break;
+ 	case X509_LU_CRL:
+ 		ret=X509_CRL_cmp((*a)->data.crl,(*b)->data.crl);
+ 		break;
 	default:
 		/* abort(); */
 		return 0;
@@ -201,7 +182,7 @@
 
 	if ((ret=(X509_STORE *)OPENSSL_malloc(sizeof(X509_STORE))) == NULL)
 		return(NULL);
-	ret->certs=lh_new(x509_object_hash,x509_object_cmp);
+	ret->objs = sk_X509_OBJECT_new(x509_object_cmp);
 	ret->cache=1;
 	ret->get_cert_methods=sk_X509_LOOKUP_new_null();
 	ret->verify=NULL;
@@ -247,10 +228,9 @@
 		X509_LOOKUP_free(lu);
 		}
 	sk_X509_LOOKUP_free(sk);
+	sk_X509_OBJECT_pop_free(vfy->objs, cleanup);
 
 	CRYPTO_free_ex_data(x509_store_meth,vfy,&vfy->ex_data);
-	lh_doall(vfy->certs,cleanup);
-	lh_free(vfy->certs);
 	OPENSSL_free(vfy);
 	}
 
@@ -294,7 +274,7 @@
 	X509_OBJECT stmp,*tmp;
 	int i,j;
 
-	tmp=X509_OBJECT_retrieve_by_subject(ctx->certs,type,name);
+	tmp=X509_OBJECT_retrieve_by_subject(ctx->objs,type,name);
 
 	if (tmp == NULL)
 		{
@@ -329,6 +309,73 @@
 	return(1);
 	}
 
+int X509_STORE_add_cert(X509_STORE *ctx, X509 *x)
+	{
+	X509_OBJECT *obj;
+	int ret=1;
+
+	if (x == NULL) return(0);
+	obj=(X509_OBJECT *)OPENSSL_malloc(sizeof(X509_OBJECT));
+	if (obj == NULL)
+		{
+		X509err(X509_F_X509_STORE_ADD_CERT,ERR_R_MALLOC_FAILURE);
+		return(0);
+		}
+	obj->type=X509_LU_X509;
+	obj->data.x509=x;
+
+	CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+
+	X509_OBJECT_up_ref_count(obj);
+
+
+	if (X509_OBJECT_retrieve_match(ctx->objs, obj))
+		{
+		X509_OBJECT_free_contents(obj);
+		OPENSSL_free(obj);
+		X509err(X509_F_X509_STORE_ADD_CERT,X509_R_CERT_ALREADY_IN_HASH_TABLE);
+		ret=0;
+		} 
+	else sk_X509_OBJECT_push(ctx->objs, obj);
+
+	CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+
+	return(ret);	
+	}
+
+int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x)
+	{
+	X509_OBJECT *obj;
+	int ret=1;
+
+	if (x == NULL) return(0);
+	obj=(X509_OBJECT *)OPENSSL_malloc(sizeof(X509_OBJECT));
+	if (obj == NULL)
+		{
+		X509err(X509_F_X509_STORE_ADD_CRL,ERR_R_MALLOC_FAILURE);
+		return(0);
+		}
+	obj->type=X509_LU_CRL;
+	obj->data.crl=x;
+
+	CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+
+	X509_OBJECT_up_ref_count(obj);
+
+	if (X509_OBJECT_retrieve_match(ctx->objs, obj))
+		{
+		X509_OBJECT_free_contents(obj);
+		OPENSSL_free(obj);
+		X509err(X509_F_X509_STORE_ADD_CRL,X509_R_CERT_ALREADY_IN_HASH_TABLE);
+		ret=0;
+		}
+	else sk_X509_OBJECT_push(ctx->objs, obj);
+
+	CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+
+	return(ret);	
+	}
+
 void X509_OBJECT_up_ref_count(X509_OBJECT *a)
 	{
 	switch (a->type)
@@ -355,10 +402,10 @@
 		}
 	}
 
-X509_OBJECT *X509_OBJECT_retrieve_by_subject(LHASH *h, int type,
+int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h, int type,
 	     X509_NAME *name)
 	{
-	X509_OBJECT stmp,*tmp;
+	X509_OBJECT stmp;
 	X509 x509_s;
 	X509_CINF cinf_s;
 	X509_CRL crl_s;
@@ -379,54 +426,98 @@
 		break;
 	default:
 		/* abort(); */
-		return NULL;
+		return -1;
 		}
 
-	tmp=(X509_OBJECT *)lh_retrieve(h,&stmp);
-	return(tmp);
+	return sk_X509_OBJECT_find(h,&stmp);
 	}
 
-X509_STORE_CTX *X509_STORE_CTX_new(void)
+X509_OBJECT *X509_OBJECT_retrieve_by_subject(STACK_OF(X509_OBJECT) *h, int type,
+	     X509_NAME *name)
 {
-	X509_STORE_CTX *ctx;
-	ctx = (X509_STORE_CTX *)OPENSSL_malloc(sizeof(X509_STORE_CTX));
-	if(ctx) memset(ctx, 0, sizeof(X509_STORE_CTX));
-	return ctx;
+	int idx;
+	idx = X509_OBJECT_idx_by_subject(h, type, name);
+	if(idx==-1) return NULL;
+	return sk_X509_OBJECT_value(h, idx);
 }
 
-void X509_STORE_CTX_free(X509_STORE_CTX *ctx)
+X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h, X509_OBJECT *x)
 {
-	X509_STORE_CTX_cleanup(ctx);
-	OPENSSL_free(ctx);
-}
-
-void X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509,
-	     STACK_OF(X509) *chain)
-	{
-	ctx->ctx=store;
-	ctx->current_method=0;
-	ctx->cert=x509;
-	ctx->untrusted=chain;
-	ctx->last_untrusted=0;
-	ctx->purpose=0;
-	ctx->trust=0;
-	ctx->valid=0;
-	ctx->chain=NULL;
-	ctx->depth=9;
-	ctx->error=0;
-	ctx->current_cert=NULL;
-	memset(&(ctx->ex_data),0,sizeof(CRYPTO_EX_DATA));
+	int idx, i;
+	X509_OBJECT *obj;
+	idx = sk_X509_OBJECT_find(h, x);
+	if(idx == -1) return NULL;
+	if(x->type != X509_LU_X509) return sk_X509_OBJECT_value(h, idx);
+	for(i = idx; i < sk_X509_OBJECT_num(h); i++) {
+		obj = sk_X509_OBJECT_value(h, i);
+		if(x509_object_cmp((const X509_OBJECT **)&obj, (const X509_OBJECT **)&x)) return NULL;
+		if((x->type != X509_LU_X509) || !X509_cmp(obj->data.x509, x->data.x509)) return obj;
 	}
+	return NULL;
+}
 
-void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx)
-	{
-	if (ctx->chain != NULL)
+
+/* Try to get issuer certificate from store. Due to limitations
+ * of the API this can only retrieve a single certificate matching
+ * a given subject name. However it will fill the cache with all
+ * matching certificates, so we can examine the cache for all 
+ * matches.
+ *
+ * Return values are:
+ *  1 lookup successful.
+ *  0 certificate not found.
+ * -1 some other error.
+ */
+
+
+int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
+{
+	X509_NAME *xn;
+	X509_OBJECT obj, *pobj;
+	int i, ok, idx;
+	xn=X509_get_issuer_name(x);
+	ok=X509_STORE_get_by_subject(ctx,X509_LU_X509,xn,&obj);
+	if (ok != X509_LU_X509)
 		{
-		sk_X509_pop_free(ctx->chain,X509_free);
-		ctx->chain=NULL;
+		if (ok == X509_LU_RETRY)
+			{
+			X509_OBJECT_free_contents(&obj);
+			X509err(X509_F_X509_VERIFY_CERT,X509_R_SHOULD_RETRY);
+			return -1;
+			}
+		else if (ok != X509_LU_FAIL)
+			{
+			X509_OBJECT_free_contents(&obj);
+			/* not good :-(, break anyway */
+			return -1;
+			}
+		return 0;
 		}
-	CRYPTO_free_ex_data(x509_store_ctx_meth,ctx,&(ctx->ex_data));
-	memset(&ctx->ex_data,0,sizeof(CRYPTO_EX_DATA));
+	/* If certificate matches all OK */
+	if(ctx->check_issued(ctx, x, obj.data.x509)) {
+			*issuer = obj.data.x509;
+			return 1;
 	}
+	X509_OBJECT_free_contents(&obj);
+	/* Else find index of first matching cert */
+	idx = X509_OBJECT_idx_by_subject(ctx->ctx->objs, X509_LU_X509, xn);
+	/* This shouldn't normally happen since we already have one match */
+	if(idx == -1) return 0;
+
+	/* Look through all matching certificates for a suitable issuer */
+	for(i = idx; i < sk_X509_OBJECT_num(ctx->ctx->objs); i++) {
+		pobj = sk_X509_OBJECT_value(ctx->ctx->objs, i);
+		/* See if we've ran out of matches */
+		if(pobj->type != X509_LU_X509) return 0;
+		if(X509_NAME_cmp(xn, X509_get_subject_name(pobj->data.x509))) return 0;
+		if(ctx->check_issued(ctx, x, pobj->data.x509)) {
+			*issuer = pobj->data.x509;
+			X509_OBJECT_up_ref_count(pobj);
+			return 1;
+		}
+	}
+	return 0;
+}
 
 IMPLEMENT_STACK_OF(X509_LOOKUP)
+IMPLEMENT_STACK_OF(X509_OBJECT)
diff --git a/crypto/x509/x509_txt.c b/crypto/x509/x509_txt.c
index 209cf53..cfb478d 100644
--- a/crypto/x509/x509_txt.c
+++ b/crypto/x509/x509_txt.c
@@ -132,6 +132,15 @@
 		return ("certificate rejected");
 	case X509_V_ERR_APPLICATION_VERIFICATION:
 		return("application verification failure");
+	case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+		return("subject issuer mismatch");
+	case X509_V_ERR_AKID_SKID_MISMATCH:
+		return("authority and subject key identifier mismatch");
+	case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+		return("authority and issuer serial number mismatch");
+	case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+		return("key usage does not include certificate signing");
+
 	default:
 		sprintf(buf,"error number %ld",n);
 		return(buf);
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index ccc0313..07a8bd4 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -71,6 +71,8 @@
 #include <openssl/objects.h>
 
 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_purpose(X509_STORE_CTX *ctx);
 static int check_trust(X509_STORE_CTX *ctx);
 static int internal_verify(X509_STORE_CTX *ctx);
@@ -99,7 +101,6 @@
 	{
 	X509 *x,*xtmp,*chain_ss=NULL;
 	X509_NAME *xn;
-	X509_OBJECT obj;
 	int depth,i,ok=0;
 	int num;
 	int (*cb)();
@@ -111,7 +112,7 @@
 		return(-1);
 		}
 
-	cb=ctx->ctx->verify_cb;
+	cb=ctx->verify_cb;
 	if (cb == NULL) cb=null_callback;
 
 	/* first we make sure the chain we are going to build is
@@ -152,13 +153,12 @@
 
 		/* If we are self signed, we break */
 		xn=X509_get_issuer_name(x);
-		if (X509_NAME_cmp(X509_get_subject_name(x),xn) == 0)
-			break;
+		if (ctx->check_issued(ctx, x,x)) break;
 
 		/* If we were passed a cert chain, use it first */
 		if (ctx->untrusted != NULL)
 			{
-			xtmp=X509_find_by_subject(sktmp,xn);
+			xtmp=find_issuer(ctx, sktmp,x);
 			if (xtmp != NULL)
 				{
 				if (!sk_X509_push(ctx->chain,xtmp))
@@ -183,11 +183,14 @@
 	 * certificates.  We now need to add at least one trusted one,
 	 * if possible, otherwise we complain. */
 
+	/* Examine last certificate in chain and see if it
+ 	 * is self signed.
+ 	 */
+
 	i=sk_X509_num(ctx->chain);
 	x=sk_X509_value(ctx->chain,i-1);
 	xn = X509_get_subject_name(x);
-	if (X509_NAME_cmp(xn,X509_get_issuer_name(x))
-		== 0)
+	if (ctx->check_issued(ctx, x, x))
 		{
 		/* we have a self signed certificate */
 		if (sk_X509_num(ctx->chain) == 1)
@@ -196,13 +199,13 @@
 			 * we can find it in the store. We must have an exact
 			 * match to avoid possible impersonation.
 			 */
-			ok=X509_STORE_get_by_subject(ctx,X509_LU_X509,xn,&obj);
-			if ((ok != X509_LU_X509) || X509_cmp(x, obj.data.x509)) 
+			ok = ctx->get_issuer(&xtmp, ctx, x);
+			if ((ok <= 0) || X509_cmp(x, xtmp)) 
 				{
 				ctx->error=X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT;
 				ctx->current_cert=x;
 				ctx->error_depth=i-1;
-				if(ok == X509_LU_X509) X509_OBJECT_free_contents(&obj);
+				if(ok == 1) X509_free(xtmp);
 				ok=cb(0,ctx);
 				if (!ok) goto end;
 				}
@@ -212,14 +215,14 @@
 				 * so we get any trust settings.
 				 */
 				X509_free(x);
-				x = obj.data.x509;
+				x = xtmp;
 				sk_X509_set(ctx->chain, i - 1, x);
 				ctx->last_untrusted=0;
 				}
 			}
 		else
 			{
-			/* worry more about this one elsewhere */
+			/* extract and save self signed certificate for later use */
 			chain_ss=sk_X509_pop(ctx->chain);
 			ctx->last_untrusted--;
 			num--;
@@ -235,30 +238,17 @@
 
 		/* If we are self signed, we break */
 		xn=X509_get_issuer_name(x);
-		if (X509_NAME_cmp(X509_get_subject_name(x),xn) == 0)
-			break;
+		if (ctx->check_issued(ctx,x,x)) break;
 
-		ok=X509_STORE_get_by_subject(ctx,X509_LU_X509,xn,&obj);
-		if (ok != X509_LU_X509)
+		ok = ctx->get_issuer(&xtmp, ctx, x);
+
+		if (ok < 0) return ok;
+		if(ok == 0) break;
+
+		x = xtmp;
+		if (!sk_X509_push(ctx->chain,x))
 			{
-			if (ok == X509_LU_RETRY)
-				{
-				X509_OBJECT_free_contents(&obj);
-				X509err(X509_F_X509_VERIFY_CERT,X509_R_SHOULD_RETRY);
-				return(ok);
-				}
-			else if (ok != X509_LU_FAIL)
-				{
-				X509_OBJECT_free_contents(&obj);
-				/* not good :-(, break anyway */
-				return(ok);
-				}
-			break;
-			}
-		x=obj.data.x509;
-		if (!sk_X509_push(ctx->chain,obj.data.x509))
-			{
-			X509_OBJECT_free_contents(&obj);
+			X509_free(xtmp);
 			X509err(X509_F_X509_VERIFY_CERT,ERR_R_MALLOC_FAILURE);
 			return(0);
 			}
@@ -267,9 +257,11 @@
 
 	/* we now have our chain, lets check it... */
 	xn=X509_get_issuer_name(x);
-	if (X509_NAME_cmp(X509_get_subject_name(x),xn) != 0)
+
+	/* Is last certificate looked up self signed? */
+	if (!ctx->check_issued(ctx,x,x))
 		{
-		if ((chain_ss == NULL) || (X509_NAME_cmp(X509_get_subject_name(chain_ss),xn) != 0))
+		if ((chain_ss == NULL) || !ctx->check_issued(ctx, x, chain_ss))
 			{
 			if (ctx->last_untrusted >= num)
 				ctx->error=X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY;
@@ -308,8 +300,8 @@
 	X509_get_pubkey_parameters(NULL,ctx->chain);
 
 	/* At this point, we have a chain and just need to verify it */
-	if (ctx->ctx->verify != NULL)
-		ok=ctx->ctx->verify(ctx);
+	if (ctx->verify != NULL)
+		ok=ctx->verify(ctx);
 	else
 		ok=internal_verify(ctx);
 	if (0)
@@ -322,6 +314,51 @@
 	return(ok);
 	}
 
+
+/* Given a STACK_OF(X509) find the issuer of cert (if any)
+ */
+
+static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x)
+{
+	int i;
+	X509 *issuer;
+	for(i = 0; i < sk_X509_num(sk); i++) {
+		issuer = sk_X509_value(sk, i);
+		if(ctx->check_issued(ctx, x, issuer)) return issuer;
+	}
+	return NULL;
+}
+
+/* Given a possible certificate and issuer check them */
+
+static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer)
+{
+	int ret;
+	ret = X509_check_issued(issuer, x);
+	if(ret == X509_V_OK) return 1;
+	else {
+			ctx->error = ret;
+			ctx->current_cert = x;
+			ctx->current_issuer = issuer;
+			if(ctx->flags &	X509_V_FLAG_CB_ISSUER_CHECK)
+				return ctx->verify_cb(0, ctx);
+			else return 0;
+	}
+	return 0;
+}
+
+/* Alternative lookup method: look from a STACK stored in other_ctx */
+
+static int get_issuer_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
+{
+	*issuer = find_issuer(ctx, ctx->other_ctx, x);
+	if(*issuer) {
+		CRYPTO_add(&(*issuer)->references,1,CRYPTO_LOCK_X509);
+		return 1;
+	} else return 0;
+}
+	
+
 /* Check a certificate chains extensions for consistency
  * with the supplied purpose
  */
@@ -334,7 +371,7 @@
 	int i, ok=0;
 	X509 *x;
 	int (*cb)();
-	cb=ctx->ctx->verify_cb;
+	cb=ctx->verify_cb;
 	if (cb == NULL) cb=null_callback;
 	/* Check all untrusted certificates */
 	for(i = 0; i < ctx->last_untrusted; i++) {
@@ -371,7 +408,7 @@
 	int i, ok;
 	X509 *x;
 	int (*cb)();
-	cb=ctx->ctx->verify_cb;
+	cb=ctx->verify_cb;
 	if (cb == NULL) cb=null_callback;
 /* For now just check the last certificate in the chain */
 	i = sk_X509_num(ctx->chain) - 1;
@@ -394,7 +431,7 @@
 	EVP_PKEY *pkey=NULL;
 	int (*cb)();
 
-	cb=ctx->ctx->verify_cb;
+	cb=ctx->verify_cb;
 	if (cb == NULL) cb=null_callback;
 
 	n=sk_X509_num(ctx->chain);
@@ -629,76 +666,6 @@
 	return(1);
 	}
 
-int X509_STORE_add_cert(X509_STORE *ctx, X509 *x)
-	{
-	X509_OBJECT *obj,*r;
-	int ret=1;
-
-	if (x == NULL) return(0);
-	obj=(X509_OBJECT *)OPENSSL_malloc(sizeof(X509_OBJECT));
-	if (obj == NULL)
-		{
-		X509err(X509_F_X509_STORE_ADD_CERT,ERR_R_MALLOC_FAILURE);
-		return(0);
-		}
-	obj->type=X509_LU_X509;
-	obj->data.x509=x;
-
-	CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
-
-	X509_OBJECT_up_ref_count(obj);
-
-	r=(X509_OBJECT *)lh_insert(ctx->certs,obj);
-	if (r != NULL)
-		{ /* oops, put it back */
-		lh_delete(ctx->certs,obj);
-		X509_OBJECT_free_contents(obj);
-		OPENSSL_free(obj);
-		lh_insert(ctx->certs,r);
-		X509err(X509_F_X509_STORE_ADD_CERT,X509_R_CERT_ALREADY_IN_HASH_TABLE);
-		ret=0;
-		}
-
-	CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
-
-	return(ret);	
-	}
-
-int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x)
-	{
-	X509_OBJECT *obj,*r;
-	int ret=1;
-
-	if (x == NULL) return(0);
-	obj=(X509_OBJECT *)OPENSSL_malloc(sizeof(X509_OBJECT));
-	if (obj == NULL)
-		{
-		X509err(X509_F_X509_STORE_ADD_CRL,ERR_R_MALLOC_FAILURE);
-		return(0);
-		}
-	obj->type=X509_LU_CRL;
-	obj->data.crl=x;
-
-	CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
-
-	X509_OBJECT_up_ref_count(obj);
-
-	r=(X509_OBJECT *)lh_insert(ctx->certs,obj);
-	if (r != NULL)
-		{ /* oops, put it back */
-		lh_delete(ctx->certs,obj);
-		X509_OBJECT_free_contents(obj);
-		OPENSSL_free(obj);
-		lh_insert(ctx->certs,r);
-		X509err(X509_F_X509_STORE_ADD_CRL,X509_R_CERT_ALREADY_IN_HASH_TABLE);
-		ret=0;
-		}
-
-	CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
-
-	return(ret);	
-	}
-
 int X509_STORE_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
 	     CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func)
         {
@@ -828,6 +795,65 @@
 	return 1;
 }
 
+X509_STORE_CTX *X509_STORE_CTX_new(void)
+{
+	X509_STORE_CTX *ctx;
+	ctx = (X509_STORE_CTX *)OPENSSL_malloc(sizeof(X509_STORE_CTX));
+	if(ctx) memset(ctx, 0, sizeof(X509_STORE_CTX));
+	return ctx;
+}
+
+void X509_STORE_CTX_free(X509_STORE_CTX *ctx)
+{
+	X509_STORE_CTX_cleanup(ctx);
+	OPENSSL_free(ctx);
+}
+
+void X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509,
+	     STACK_OF(X509) *chain)
+	{
+	ctx->ctx=store;
+	ctx->current_method=0;
+	ctx->cert=x509;
+	ctx->untrusted=chain;
+	ctx->last_untrusted=0;
+	ctx->purpose=0;
+	ctx->trust=0;
+	ctx->valid=0;
+	ctx->chain=NULL;
+	ctx->depth=9;
+	ctx->error=0;
+	ctx->current_cert=NULL;
+	ctx->current_issuer=NULL;
+	ctx->check_issued = check_issued;
+	ctx->get_issuer = X509_STORE_CTX_get1_issuer;
+	ctx->verify_cb = store->verify_cb;
+	ctx->verify = store->verify;
+	ctx->cleanup = NULL;
+	memset(&(ctx->ex_data),0,sizeof(CRYPTO_EX_DATA));
+	}
+
+/* Set alternative lookup method: just a STACK of trusted certificates.
+ * This avoids X509_STORE nastiness where it isn't needed.
+ */
+
+void X509_STORE_CTX_trusted_stack(X509_STORE_CTX *ctx, STACK_OF(X509) *sk)
+{
+	ctx->other_ctx = sk;
+	ctx->get_issuer = get_issuer_sk;
+}
+
+void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx)
+	{
+	if(ctx->cleanup) ctx->cleanup(ctx);
+	if (ctx->chain != NULL)
+		{
+		sk_X509_pop_free(ctx->chain,X509_free);
+		ctx->chain=NULL;
+		}
+	CRYPTO_free_ex_data(x509_store_ctx_method,ctx,&(ctx->ex_data));
+	memset(&ctx->ex_data,0,sizeof(CRYPTO_EX_DATA));
+	}
 
 IMPLEMENT_STACK_OF(X509)
 IMPLEMENT_ASN1_SET_OF(X509)
diff --git a/crypto/x509/x509_vfy.h b/crypto/x509/x509_vfy.h
index cc0bd3d..71d56bb 100644
--- a/crypto/x509/x509_vfy.h
+++ b/crypto/x509/x509_vfy.h
@@ -131,6 +131,7 @@
 typedef struct x509_lookup_st X509_LOOKUP;
 
 DECLARE_STACK_OF(X509_LOOKUP)
+DECLARE_STACK_OF(X509_OBJECT)
 
 /* This is a static that defines the function interface */
 typedef struct x509_lookup_method_st
@@ -162,11 +163,7 @@
 	{
 	/* The following is a cache of trusted certs */
 	int cache; 	/* if true, stash any hits */
-#ifndef NO_LHASH
-	LHASH *certs;	/* cached certs; */ 
-#else
-	char *certs;
-#endif
+	STACK_OF(X509_OBJECT) *objs;	/* Cache of all objects */
 
 	/* These are external lookup methods */
 	STACK_OF(X509_LOOKUP) *get_cert_methods;
@@ -194,7 +191,7 @@
 	X509_STORE *store_ctx;	/* who owns us */
 	};
 
-/* This is a temporary used when processing cert chains.  Since the
+/* This is a used when verifying cert chains.  Since the
  * gathering of the cert chain can take some time (and have to be
  * 'retried', this needs to be kept and passed around. */
 struct x509_store_state_st      /* X509_STORE_CTX */
@@ -207,6 +204,16 @@
 	STACK_OF(X509) *untrusted;	/* chain of X509s - untrusted - passed in */
 	int purpose;		/* purpose to check untrusted certificates */
 	int trust;		/* trust setting to check */
+	time_t	check_time;	/* time to make verify at */
+	unsigned long flags;	/* Various verify flags */
+	void *other_ctx;	/* Other info for use with get_issuer() */
+
+	/* Callbacks for various operations */
+	int (*verify)(X509_STORE_CTX *ctx);	/* called to verify a certificate */
+	int (*verify_cb)(int ok,X509_STORE_CTX *ctx);		/* error callback */
+	int (*get_issuer)(X509 **issuer, X509_STORE_CTX *ctx, X509 *x);	/* get issuers cert from ctx */
+	int (*check_issued)(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); /* check issued */
+	int (*cleanup)(X509_STORE_CTX *ctx);
 
 	/* The following is built up */
 	int depth;		/* how far to go looking up certs */
@@ -218,6 +225,7 @@
 	int error_depth;
 	int error;
 	X509 *current_cert;
+	X509 *current_issuer;	/* cert currently being tested as valid issuer */
 
 	CRYPTO_EX_DATA ex_data;
 	};
@@ -268,10 +276,20 @@
 #define		X509_V_ERR_INVALID_PURPOSE			26
 #define		X509_V_ERR_CERT_UNTRUSTED			27
 #define		X509_V_ERR_CERT_REJECTED			28
+/* These are 'informational' when looking for issuer cert */
+#define		X509_V_ERR_SUBJECT_ISSUER_MISMATCH		29
+#define		X509_V_ERR_AKID_SKID_MISMATCH			30
+#define		X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH		31
+#define		X509_V_ERR_KEYUSAGE_NO_CERTSIGN			32
 
 /* The application is not happy */
 #define		X509_V_ERR_APPLICATION_VERIFICATION		50
 
+/* Certificate verify flags */
+
+#define	X509_V_FLAG_CB_ISSUER_CHECK		0x1	/* Send issuer+subject checks to verify_cb */
+#define	X509_V_FLAG_USE_CHECK_TIME		0x2	/* Use check time instead of current time */
+
 		  /* These functions are being redefined in another directory,
 		     and clash when the linker is case-insensitive, so let's
 		     hide them a little, by giving them an extra 'o' at the
@@ -287,18 +305,23 @@
 #define X509v3_add_standard_extensions oX509v3_add_standard_extensions
 #endif
 
-#ifndef NO_LHASH
-X509_OBJECT *X509_OBJECT_retrieve_by_subject(LHASH *h,int type,X509_NAME *name);
-#endif
+int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h, int type,
+	     X509_NAME *name);
+X509_OBJECT *X509_OBJECT_retrieve_by_subject(STACK_OF(X509_OBJECT) *h,int type,X509_NAME *name);
+X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h, X509_OBJECT *x);
 void X509_OBJECT_up_ref_count(X509_OBJECT *a);
 void X509_OBJECT_free_contents(X509_OBJECT *a);
 X509_STORE *X509_STORE_new(void );
 void X509_STORE_free(X509_STORE *v);
 
 X509_STORE_CTX *X509_STORE_CTX_new(void);
+
+int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x);
+
 void X509_STORE_CTX_free(X509_STORE_CTX *ctx);
 void X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store,
 			 X509 *x509, STACK_OF(X509) *chain);
+void X509_STORE_CTX_trusted_stack(X509_STORE_CTX *ctx, STACK_OF(X509) *sk);
 void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx);
 
 X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m);
diff --git a/crypto/x509v3/v3_purp.c b/crypto/x509v3/v3_purp.c
index d84d013..867699b 100644
--- a/crypto/x509v3/v3_purp.c
+++ b/crypto/x509v3/v3_purp.c
@@ -59,6 +59,7 @@
 #include <stdio.h>
 #include "cryptlib.h"
 #include <openssl/x509v3.h>
+#include <openssl/x509_vfy.h>
 
 
 static void x509v3_cache_extensions(X509 *x);
@@ -255,16 +256,18 @@
 	return xp->trust;
 }
 
-#ifndef NO_SHA
 static 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;
+#ifndef 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;
@@ -328,9 +331,10 @@
 		x->ex_flags |= EXFLAG_NSCERT;
 		ASN1_BIT_STRING_free(ns);
 	}
+	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->ex_flags |= EXFLAG_SET;
 }
-#endif
 
 /* CA checks common to all purposes
  * return codes:
@@ -470,3 +474,60 @@
 {
 	return 1;
 }
+
+/* Various checks to see if one certificate issued the second.
+ * This can be used to prune a set of possible issuer certificates
+ * which have been looked up using some simple method such as by
+ * subject name.
+ * These are:
+ * 1. Check issuer_name(subject) == subject_name(issuer)
+ * 2. If akid(subject) exists check it matches issuer
+ * 3. If key_usage(issuer) exists check it supports certificate signing
+ * returns 0 for OK, positive for reason for mismatch, reasons match
+ * codes for X509_verify_cert()
+ */
+
+int X509_check_issued(X509 *issuer, X509 *subject)
+{
+	if(X509_NAME_cmp(X509_get_subject_name(issuer),
+			X509_get_issuer_name(subject)))
+				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) )
+				return X509_V_ERR_AKID_SKID_MISMATCH;
+		/* Check serial number */
+		if(subject->akid->serial &&
+			ASN1_INTEGER_cmp(X509_get_serialNumber(issuer),
+						subject->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.
+			 */
+			STACK_OF(GENERAL_NAME) *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;
+				}
+			}
+			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;
+}
+
diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h
index 5f00ed5..0453b12 100644
--- a/crypto/x509v3/x509v3.h
+++ b/crypto/x509v3/x509v3.h
@@ -532,6 +532,7 @@
 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_check_issued(X509 *issuer, X509 *subject);
 int X509_PURPOSE_get_count(void);
 X509_PURPOSE * X509_PURPOSE_get0(int idx);
 int X509_PURPOSE_get_by_sname(char *sname);