Initial OCSP certificate verify. Not complete,
it just supports a "trusted OCSP global root CA".
diff --git a/CHANGES b/CHANGES
index c111153..d0e2699 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,6 +3,16 @@
 
  Changes between 0.9.6 and 0.9.7  [xx XXX 2000]
 
+  *) Initial OCSP certificate verification added to OCSP_basic_verify()
+     and related routines. This uses the standard OpenSSL certificate
+     verify routines to perform initial checks (just CA validity) and
+     to obtain the certificate chain. Then additional checks will be
+     performed on the chain. Currently the root CA is checked to see
+     if it is explicitly trusted for OCSP signing. This is used to set
+     a root CA as a global signing root: that is any certificate that
+     chains to that CA is an acceptable OCSP signing certificate.
+     [Steve Henson]
+
   *) New '-extfile ...' option to 'openssl ca' for reading X.509v3
      extensions from a separate configuration file.
      As when reading extensions from the main configuration file,
diff --git a/apps/apps.c b/apps/apps.c
index ca3f557..bdd8c71 100644
--- a/apps/apps.c
+++ b/apps/apps.c
@@ -837,3 +837,32 @@
 	}
 }
 
+X509_STORE *setup_verify(BIO *bp, char *CAfile, char *CApath)
+{
+	X509_STORE *store;
+	X509_LOOKUP *lookup;
+	if(!(store = X509_STORE_new())) goto end;
+	lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
+	if (lookup == NULL) goto end;
+	if (CAfile) {
+		if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) {
+			BIO_printf(bp, "Error loading file %s\n", CAfile);
+			goto end;
+		}
+	} else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
+		
+	lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
+	if (lookup == NULL) goto end;
+	if (CApath) {
+		if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) {
+			BIO_printf(bp, "Error loading directory %s\n", CApath);
+			goto end;
+		}
+	} else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
+
+	ERR_clear_error();
+	return store;
+	end:
+	X509_STORE_free(store);
+	return NULL;
+}
diff --git a/apps/apps.h b/apps/apps.h
index 11133cb..2da89e2 100644
--- a/apps/apps.h
+++ b/apps/apps.h
@@ -158,6 +158,7 @@
 EVP_PKEY *load_key(BIO *err, char *file, int format, char *pass, ENGINE *e);
 EVP_PKEY *load_pubkey(BIO *err, char *file, int format, ENGINE *e);
 STACK_OF(X509) *load_certs(BIO *err, char *file, int format);
+X509_STORE *setup_verify(BIO *bp, char *CAfile, char *CApath);
 
 #define FORMAT_UNDEF    0
 #define FORMAT_ASN1     1
diff --git a/apps/ocsp.c b/apps/ocsp.c
index cfd4f18..3125583 100644
--- a/apps/ocsp.c
+++ b/apps/ocsp.c
@@ -82,14 +82,18 @@
 	int add_nonce = 1;
 	OCSP_REQUEST *req = NULL;
 	OCSP_RESPONSE *resp = NULL;
+	OCSP_BASICRESP *bs = NULL;
 	X509 *issuer = NULL, *cert = NULL;
 	X509 *signer = NULL;
 	EVP_PKEY *key = NULL;
 	BIO *cbio = NULL, *derbio = NULL;
 	BIO *out = NULL;
 	int req_text = 0, resp_text = 0;
+	char *CAfile = NULL, *CApath = NULL;
+	X509_STORE *store = NULL;
 	int ret = 1;
 	int badarg = 0;
+	int i;
 	if (bio_err == NULL) bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
 	ERR_load_crypto_strings();
 	args = argv + 1;
@@ -153,6 +157,24 @@
 				}
 			else badarg = 1;
 			}
+		else if (!strcmp (*args, "-CAfile"))
+			{
+			if (args[1])
+				{
+				args++;
+				CAfile = *args;
+				}
+			else badarg = 1;
+			}
+		else if (!strcmp (*args, "-CApath"))
+			{
+			if (args[1])
+				{
+				args++;
+				CApath = *args;
+				}
+			else badarg = 1;
+			}
 		 else if (!strcmp(*args, "-signkey"))
 			{
 			if (args[1])
@@ -386,11 +408,25 @@
 
 	if (resp_text) OCSP_RESPONSE_print(out, resp, 0);
 
+	store = setup_verify(bio_err, CAfile, CApath);
+	if(!store) goto end;
+
+	bs = OCSP_response_get1_basic(resp);
+
+	i = OCSP_basic_verify(bs, NULL, store, 0);
+
+	if(i <= 0)
+		{
+		BIO_printf(bio_err, "Response verify error (%d)\n", i);
+		ERR_print_errors(bio_err);
+		}
+
 	ret = 0;
 
 end:
 	ERR_print_errors(bio_err);
 	X509_free(signer);
+	X509_STORE_free(store);
 	EVP_PKEY_free(key);
 	X509_free(issuer);
 	X509_free(cert);
@@ -398,6 +434,7 @@
 	BIO_free(out);
 	OCSP_REQUEST_free(req);
 	OCSP_RESPONSE_free(resp);
+	OCSP_BASICRESP_free(bs);
 
 	EXIT(ret);
 }
diff --git a/apps/smime.c b/apps/smime.c
index 0a16bbc..e0d31b2 100644
--- a/apps/smime.c
+++ b/apps/smime.c
@@ -68,7 +68,6 @@
 
 #undef PROG
 #define PROG smime_main
-static X509_STORE *setup_verify(char *CAfile, char *CApath);
 static int save_certs(char *signerfile, STACK_OF(X509) *signers);
 
 #define SMIME_OP	0x10
@@ -431,7 +430,7 @@
 	}
 
 	if(operation == SMIME_VERIFY) {
-		if(!(store = setup_verify(CAfile, CApath))) goto end;
+		if(!(store = setup_verify(bio_err, CAfile, CApath))) goto end;
 	}
 
 	ret = 3;
@@ -530,36 +529,6 @@
 	return (ret);
 }
 
-static X509_STORE *setup_verify(char *CAfile, char *CApath)
-{
-	X509_STORE *store;
-	X509_LOOKUP *lookup;
-	if(!(store = X509_STORE_new())) goto end;
-	lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
-	if (lookup == NULL) goto end;
-	if (CAfile) {
-		if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) {
-			BIO_printf(bio_err, "Error loading file %s\n", CAfile);
-			goto end;
-		}
-	} else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
-		
-	lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
-	if (lookup == NULL) goto end;
-	if (CApath) {
-		if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) {
-			BIO_printf(bio_err, "Error loading directory %s\n", CApath);
-			goto end;
-		}
-	} else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
-
-	ERR_clear_error();
-	return store;
-	end:
-	X509_STORE_free(store);
-	return NULL;
-}
-
 static int save_certs(char *signerfile, STACK_OF(X509) *signers)
 {
 	int i;
diff --git a/crypto/ocsp/ocsp.h b/crypto/ocsp/ocsp.h
index 60b843a..1cb6ead 100644
--- a/crypto/ocsp/ocsp.h
+++ b/crypto/ocsp/ocsp.h
@@ -79,6 +79,12 @@
 #define OCSP_NOCERTS			0x1
 #define OCSP_NOINTERN			0x2
 #define OCSP_NOSIGS			0x4
+#define OCSP_NOCHAIN			0x8
+#define OCSP_NOVERIFY			0x10
+#define OCSP_NOEXPLICIT			0x20
+#define OCSP_NOCASIGN			0x40
+#define OCSP_NODELEGATED		0x80
+#define OCSP_NOCHECKS			0x100
 
 /*   CertID ::= SEQUENCE {
  *       hashAlgorithm            AlgorithmIdentifier,
@@ -434,6 +440,7 @@
 
 int OCSP_request_verify(OCSP_REQUEST *req, EVP_PKEY *pkey);
 
+int OCSP_id_issuer_cmp(OCSP_CERTID *a, OCSP_CERTID *b);
 int OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b);
 
 OCSP_BASICRESP *OCSP_basic_response_new(int tag,
@@ -565,6 +572,7 @@
 /* Reason codes. */
 #define OCSP_R_BAD_DATA					 108
 #define OCSP_R_BAD_TAG					 100
+#define OCSP_R_CERTIFICATE_VERIFY_ERROR			 126
 #define OCSP_R_DIGEST_ERR				 101
 #define OCSP_R_FAILED_TO_OPEN				 109
 #define OCSP_R_FAILED_TO_READ				 110
@@ -579,6 +587,7 @@
 #define OCSP_R_NO_RESPONSE_DATA				 104
 #define OCSP_R_NO_SIGNATURE				 105
 #define OCSP_R_REVOKED_NO_TIME				 106
+#define OCSP_R_ROOT_CA_NOT_TRUSTED			 127
 #define OCSP_R_SERVER_READ_ERROR			 116
 #define OCSP_R_SERVER_RESPONSE_ERROR			 117
 #define OCSP_R_SERVER_RESPONSE_PARSE_ERROR		 118
diff --git a/crypto/ocsp/ocsp_err.c b/crypto/ocsp/ocsp_err.c
index 70a2756..f4335d2 100644
--- a/crypto/ocsp/ocsp_err.c
+++ b/crypto/ocsp/ocsp_err.c
@@ -87,6 +87,7 @@
 	{
 {OCSP_R_BAD_DATA                         ,"bad data"},
 {OCSP_R_BAD_TAG                          ,"bad tag"},
+{OCSP_R_CERTIFICATE_VERIFY_ERROR         ,"certificate verify error"},
 {OCSP_R_DIGEST_ERR                       ,"digest err"},
 {OCSP_R_FAILED_TO_OPEN                   ,"failed to open"},
 {OCSP_R_FAILED_TO_READ                   ,"failed to read"},
@@ -101,6 +102,7 @@
 {OCSP_R_NO_RESPONSE_DATA                 ,"no response data"},
 {OCSP_R_NO_SIGNATURE                     ,"no signature"},
 {OCSP_R_REVOKED_NO_TIME                  ,"revoked no time"},
+{OCSP_R_ROOT_CA_NOT_TRUSTED              ,"root ca not trusted"},
 {OCSP_R_SERVER_READ_ERROR                ,"server read error"},
 {OCSP_R_SERVER_RESPONSE_ERROR            ,"server response error"},
 {OCSP_R_SERVER_RESPONSE_PARSE_ERROR      ,"server response parse error"},
diff --git a/crypto/ocsp/ocsp_lib.c b/crypto/ocsp/ocsp_lib.c
index f9d2978..bdd4cfc 100644
--- a/crypto/ocsp/ocsp_lib.c
+++ b/crypto/ocsp/ocsp_lib.c
@@ -163,14 +163,21 @@
 	return NULL;
 	}
 
-int OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b)
+
+int OCSP_id_issuer_cmp(OCSP_CERTID *a, OCSP_CERTID *b)
 	{
 	int ret;
 	ret = OBJ_cmp(a->hashAlgorithm->algorithm, b->hashAlgorithm->algorithm);
 	if (ret) return ret;
 	ret = ASN1_OCTET_STRING_cmp(a->issuerNameHash, b->issuerNameHash);
 	if (ret) return ret;
-	ret = ASN1_OCTET_STRING_cmp(a->issuerKeyHash, b->issuerKeyHash);
+	return ASN1_OCTET_STRING_cmp(a->issuerKeyHash, b->issuerKeyHash);
+	}
+
+int OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b)
+	{
+	int ret;
+	ret = OCSP_id_issuer_cmp(a, b);
 	if (ret) return ret;
 	return ASN1_INTEGER_cmp(a->serialNumber, b->serialNumber);
 	}
diff --git a/crypto/ocsp/ocsp_vfy.c b/crypto/ocsp/ocsp_vfy.c
index 2ea3f4a..6110825 100644
--- a/crypto/ocsp/ocsp_vfy.c
+++ b/crypto/ocsp/ocsp_vfy.c
@@ -68,13 +68,15 @@
 int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs,
 				X509_STORE *st, unsigned long flags)
 	{
-	X509 *signer;
-	int ret;
+	X509 *signer, *x;
+	STACK_OF(X509) *chain = NULL;
+	X509_STORE_CTX ctx;
+	int i, ret = 0;
 	signer = ocsp_find_signer(bs, certs, st, flags);
 	if (!signer)
 		{
 		OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND);
-		return 0;
+		goto end;
 		}
 	if(!(flags & OCSP_NOSIGS))
 		{
@@ -85,9 +87,62 @@
 		if(ret <= 0)
 			{
 			OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, OCSP_R_SIGNATURE_FAILURE);
-			return 0;
+			goto end;
 			}
 		}
+	if(!(flags & OCSP_NOVERIFY))
+		{
+		if(flags & OCSP_NOCHAIN)
+			X509_STORE_CTX_init(&ctx, st, signer, NULL);
+		else
+			X509_STORE_CTX_init(&ctx, st, signer, bs->certs);
+
+		X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_OCSP_HELPER);
+		ret = X509_verify_cert(&ctx);
+		chain = X509_STORE_CTX_get1_chain(&ctx);
+		X509_STORE_CTX_cleanup(&ctx);
+                if (ret <= 0)
+			{
+			i = X509_STORE_CTX_get_error(&ctx);	
+			OCSPerr(OCSP_F_OCSP_BASIC_VERIFY,OCSP_R_CERTIFICATE_VERIFY_ERROR);
+			ERR_add_error_data(2, "Verify error:",
+					X509_verify_cert_error_string(i));
+                        goto end;
+                	}
+		if(flags & OCSP_NOCHECKS)
+			{
+			ret = 1;
+			goto end;
+			}
+		/* At this point we have a valid certificate chain
+		 * need to verify it against the OCSP criteria.
+		 */
+#if 0
+		if(ocsp_check_issuer(bs, chain, flags))
+			{
+			ret = 1;
+			goto end;
+			}
+#endif
+
+		/* Easy case: explicitly trusted. Get root CA and
+		 * check for explicit trust
+		 */
+		if(flags & OCSP_NOEXPLICIT) goto end;
+
+		x = sk_X509_value(chain, sk_X509_num(chain) - 1);
+		if(X509_check_trust(x, NID_OCSP_sign, 0) != X509_TRUST_TRUSTED)
+			{
+			OCSPerr(OCSP_F_OCSP_BASIC_VERIFY,OCSP_R_ROOT_CA_NOT_TRUSTED);
+			goto end;
+			}
+		ret = 1;
+		}
+		
+
+
+	end:
+	if(chain) sk_X509_pop_free(chain, X509_free);
 	return 1;
 	}
 
diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h
index 332cad5..4b19572 100644
--- a/crypto/x509/x509.h
+++ b/crypto/x509/x509.h
@@ -300,10 +300,11 @@
 #define X509_TRUST_SSL_SERVER	3
 #define X509_TRUST_EMAIL	4
 #define X509_TRUST_OBJECT_SIGN	5
+#define X509_TRUST_OCSP_SIGN	6
 
 /* Keep these up to date! */
 #define X509_TRUST_MIN		1
-#define X509_TRUST_MAX		5
+#define X509_TRUST_MAX		6
 
 
 /* trust_flags values */
diff --git a/crypto/x509/x509_trs.c b/crypto/x509/x509_trs.c
index a7b1543..0de252c 100644
--- a/crypto/x509/x509_trs.c
+++ b/crypto/x509/x509_trs.c
@@ -66,6 +66,7 @@
 static void trtable_free(X509_TRUST *p);
 
 static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags);
+static int trust_1oid(X509_TRUST *trust, X509 *x, int flags);
 static int trust_compat(X509_TRUST *trust, X509 *x, int flags);
 
 static int obj_trust(int id, X509 *x, int flags);
@@ -81,6 +82,7 @@
 {X509_TRUST_SSL_CLIENT, 0, trust_1oidany, "SSL Client", NID_client_auth, NULL},
 {X509_TRUST_SSL_SERVER, 0, trust_1oidany, "SSL Client", NID_server_auth, NULL},
 {X509_TRUST_EMAIL, 0, trust_1oidany, "S/MIME email", NID_email_protect, NULL},
+{X509_TRUST_OCSP_SIGN, 0, trust_1oid, "OCSP responder", NID_OCSP_sign, NULL}
 };
 
 #define X509_TRUST_COUNT	(sizeof(trstandard)/sizeof(X509_TRUST))
@@ -235,6 +237,12 @@
 	return trust_compat(trust, x, flags);
 }
 
+static int trust_1oid(X509_TRUST *trust, X509 *x, int flags)
+{
+	if(x->aux) return obj_trust(trust->arg1, x, flags);
+	return X509_TRUST_UNTRUSTED;
+}
+
 static int trust_compat(X509_TRUST *trust, X509 *x, int flags)
 {
 	X509_check_purpose(x, -1, 0);
diff --git a/crypto/x509v3/v3_purp.c b/crypto/x509v3/v3_purp.c
index 717e23d..9d67bd9 100644
--- a/crypto/x509v3/v3_purp.c
+++ b/crypto/x509v3/v3_purp.c
@@ -61,7 +61,6 @@
 #include <openssl/x509v3.h>
 #include <openssl/x509_vfy.h>
 
-
 static void x509v3_cache_extensions(X509 *x);
 
 static int ca_check(const X509 *x);
@@ -74,6 +73,7 @@
 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 no_check(const X509_PURPOSE *xp, const X509 *x, int ca);
+static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca);
 
 static int xp_cmp(const X509_PURPOSE * const *a,
 		const X509_PURPOSE * const *b);
@@ -87,6 +87,7 @@
 	{X509_PURPOSE_SMIME_ENCRYPT, X509_TRUST_EMAIL, 0, check_purpose_smime_encrypt, "S/MIME encryption", "smimeencrypt", NULL},
 	{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},
 };
 
 #define X509_PURPOSE_COUNT (sizeof(xstandard)/sizeof(X509_PURPOSE))
@@ -144,7 +145,6 @@
 	return -1;
 }
 
-
 int X509_PURPOSE_get_by_id(int purpose)
 {
 	X509_PURPOSE tmp;
@@ -320,6 +320,15 @@
 				case NID_ms_sgc:
 				case NID_ns_sgc:
 				x->ex_xkusage |= XKU_SGC;
+				break;
+
+				case NID_OCSP_sign:
+				x->ex_xkusage |= XKU_OCSP_SIGN;
+				break;
+
+				case NID_time_stamp:
+				x->ex_xkusage |= XKU_TIMESTAMP;
+				break;
 			}
 		}
 		sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free);
@@ -470,6 +479,27 @@
 	return 1;
 }
 
+/* OCSP helper: this is *not* a full OCSP check. It just checks that
+ * each CA is valid. Additional checks must be made on the chain.
+ */
+
+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;
+			return 0;
+		}
+		return 0;
+	}
+	/* leaf certificate is checked in OCSP_verify() */
+	return 1;
+}
+
 static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca)
 {
 	return 1;
diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h
index 8bf67bc..29421b7 100644
--- a/crypto/x509v3/x509v3.h
+++ b/crypto/x509v3/x509v3.h
@@ -342,12 +342,15 @@
 #define NS_SSL_CA		0x04
 #define NS_SMIME_CA		0x02
 #define NS_OBJSIGN_CA		0x01
+#define NS_ANY_CA		(NS_SSL_CA|NS_SMIME_CA|NS_OBJSIGN_CA)
 
 #define XKU_SSL_SERVER		0x1	
 #define XKU_SSL_CLIENT		0x2
 #define XKU_SMIME		0x4
 #define XKU_CODE_SIGN		0x8
 #define XKU_SGC			0x10
+#define XKU_OCSP_SIGN		0x20
+#define XKU_TIMESTAMP		0x40
 
 #define X509_PURPOSE_DYNAMIC	0x1
 #define X509_PURPOSE_DYNAMIC_NAME	0x2
@@ -370,9 +373,10 @@
 #define X509_PURPOSE_SMIME_ENCRYPT	5
 #define X509_PURPOSE_CRL_SIGN		6
 #define X509_PURPOSE_ANY		7
+#define X509_PURPOSE_OCSP_HELPER	8
 
 #define X509_PURPOSE_MIN		1
-#define X509_PURPOSE_MAX		7
+#define X509_PURPOSE_MAX		8
 
 /* Flags for X509V3_EXT_print() */