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 check_revocation(X509_STORE_CTX *ctx);
+static int check_cert(X509_STORE_CTX *ctx);
static int internal_verify(X509_STORE_CTX *ctx);
const char *X509_version="X.509" OPENSSL_VERSION_PTEXT;
-static STACK_OF(CRYPTO_EX_DATA_FUNCS) *x509_store_ctx_method=NULL;
-static int x509_store_ctx_num=0;
-
static int null_callback(int ok, X509_STORE_CTX *e)
{
}
cb=ctx->verify_cb;
- if (cb == NULL) cb=null_callback;
/* first we make sure the chain we are going to build is
* present and that the first entry is in place */
/* We may as well copy down any DSA parameters that are required */
X509_get_pubkey_parameters(NULL,ctx->chain);
+ /* Check revocation status: we do this after copying parameters
+ * because they may be needed for CRL signature verification.
+ */
+
+ ok = ctx->check_revocation(ctx);
+ if(!ok) goto end;
+
/* At this point, we have a chain and just need to verify it */
if (ctx->verify != NULL)
ok=ctx->verify(ctx);
ctx->error = ret;
ctx->current_cert = x;
ctx->current_issuer = issuer;
- if (ctx->verify_cb)
- return ctx->verify_cb(0, ctx);
+ return ctx->verify_cb(0, ctx);
return 0;
}
static int check_chain_purpose(X509_STORE_CTX *ctx)
{
-#ifdef NO_CHAIN_VERIFY
+#ifdef OPENSSL_NO_CHAIN_VERIFY
return 1;
#else
int i, ok=0;
X509 *x;
int (*cb)();
cb=ctx->verify_cb;
- if (cb == NULL) cb=null_callback;
/* Check all untrusted certificates */
for (i = 0; i < ctx->last_untrusted; i++)
{
static int check_trust(X509_STORE_CTX *ctx)
{
-#ifdef NO_CHAIN_VERIFY
+#ifdef OPENSSL_NO_CHAIN_VERIFY
return 1;
#else
int i, ok;
X509 *x;
int (*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;
x = sk_X509_value(ctx->chain, i);
ok = X509_check_trust(x, ctx->trust, 0);
if (ok == X509_TRUST_TRUSTED)
return 1;
- ctx->error_depth = sk_X509_num(ctx->chain) - 1;
+ ctx->error_depth = i;
ctx->current_cert = x;
if (ok == X509_TRUST_REJECTED)
ctx->error = X509_V_ERR_CERT_REJECTED;
#endif
}
+static int check_revocation(X509_STORE_CTX *ctx)
+ {
+ int i, last, ok;
+ if (!(ctx->flags & X509_V_FLAG_CRL_CHECK))
+ return 1;
+ if (ctx->flags & X509_V_FLAG_CRL_CHECK_ALL)
+ last = 0;
+ else
+ last = sk_X509_num(ctx->chain) - 1;
+ for(i = 0; i <= last; i++)
+ {
+ ctx->error_depth = i;
+ ok = check_cert(ctx);
+ if (!ok) return ok;
+ }
+ return 1;
+ }
+
+static int check_cert(X509_STORE_CTX *ctx)
+ {
+ X509_CRL *crl = NULL;
+ X509 *x;
+ int ok, cnum;
+ cnum = ctx->error_depth;
+ x = sk_X509_value(ctx->chain, cnum);
+ ctx->current_cert = x;
+ /* Try to retrieve relevant CRL */
+ ok = ctx->get_crl(ctx, &crl, x);
+ /* If error looking up CRL, nothing we can do except
+ * notify callback
+ */
+ if(!ok)
+ {
+ ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL;
+ ok = ctx->verify_cb(0, ctx);
+ goto err;
+ }
+ ctx->current_crl = crl;
+ ok = ctx->check_crl(ctx, crl);
+ if (!ok) goto err;
+ ok = ctx->cert_crl(ctx, crl, x);
+ err:
+ ctx->current_crl = NULL;
+ X509_CRL_free(crl);
+ return ok;
+
+ }
+
+/* Retrieve CRL corresponding to certificate: currently just a
+ * subject lookup: maybe use AKID later...
+ * Also might look up any included CRLs too (e.g PKCS#7 signedData).
+ */
+static int get_crl(X509_STORE_CTX *ctx, X509_CRL **crl, X509 *x)
+ {
+ int ok;
+ X509_OBJECT xobj;
+ ok = X509_STORE_get_by_subject(ctx, X509_LU_CRL, X509_get_issuer_name(x), &xobj);
+ if (!ok) return 0;
+ *crl = xobj.data.crl;
+ return 1;
+ }
+
+/* Check CRL validity */
+static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl)
+ {
+ X509 *issuer = NULL;
+ EVP_PKEY *ikey = NULL;
+ int ok = 0, chnum, cnum, i;
+ time_t *ptime;
+ cnum = ctx->error_depth;
+ chnum = sk_X509_num(ctx->chain) - 1;
+ /* Find CRL issuer: if not last certificate then issuer
+ * is next certificate in chain.
+ */
+ if(cnum < chnum)
+ issuer = sk_X509_value(ctx->chain, cnum + 1);
+ else
+ {
+ issuer = sk_X509_value(ctx->chain, chnum);
+ /* If not self signed, can't check signature */
+ if(!ctx->check_issued(ctx, issuer, issuer))
+ {
+ ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER;
+ ok = ctx->verify_cb(0, ctx);
+ if(!ok) goto err;
+ }
+ }
+
+ if(issuer)
+ {
+
+ /* Attempt to get issuer certificate public key */
+ ikey = X509_get_pubkey(issuer);
+
+ if(!ikey)
+ {
+ ctx->error=X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY;
+ ok = ctx->verify_cb(0, ctx);
+ if (!ok) goto err;
+ }
+ else
+ {
+ /* Verify CRL signature */
+ if(X509_CRL_verify(crl, ikey) <= 0)
+ {
+ ctx->error=X509_V_ERR_CRL_SIGNATURE_FAILURE;
+ ok = ctx->verify_cb(0, ctx);
+ if (!ok) goto err;
+ }
+ }
+ }
+
+ /* OK, CRL signature valid check times */
+ if (ctx->flags & X509_V_FLAG_USE_CHECK_TIME)
+ ptime = &ctx->check_time;
+ else
+ ptime = NULL;
+
+ i=X509_cmp_time(X509_CRL_get_lastUpdate(crl), ptime);
+ if (i == 0)
+ {
+ ctx->error=X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD;
+ ok = ctx->verify_cb(0, ctx);
+ if (!ok) goto err;
+ }
+
+ if (i > 0)
+ {
+ ctx->error=X509_V_ERR_CRL_NOT_YET_VALID;
+ ok = ctx->verify_cb(0, ctx);
+ if (!ok) goto err;
+ }
+
+ if(X509_CRL_get_nextUpdate(crl))
+ {
+ i=X509_cmp_time(X509_CRL_get_nextUpdate(crl), ptime);
+
+ if (i == 0)
+ {
+ ctx->error=X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD;
+ ok = ctx->verify_cb(0, ctx);
+ if (!ok) goto err;
+ }
+
+ if (i < 0)
+ {
+ ctx->error=X509_V_ERR_CRL_HAS_EXPIRED;
+ ok = ctx->verify_cb(0, ctx);
+ if (!ok) goto err;
+ }
+ }
+
+ ok = 1;
+
+ err:
+ EVP_PKEY_free(ikey);
+ return ok;
+ }
+
+/* Check certificate against CRL */
+static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x)
+ {
+ int idx, ok;
+ X509_REVOKED rtmp;
+ /* Look for serial number of certificate in CRL */
+ rtmp.serialNumber = X509_get_serialNumber(x);
+ idx = sk_X509_REVOKED_find(crl->crl->revoked, &rtmp);
+ /* Not found: OK */
+ if(idx == -1) return 1;
+ /* Otherwise revoked: want something cleverer than
+ * this to handle entry extensions in V2 CRLs.
+ */
+ ctx->error = X509_V_ERR_CERT_REVOKED;
+ ok = ctx->verify_cb(0, ctx);
+ return ok;
+ }
+
static int internal_verify(X509_STORE_CTX *ctx)
{
int i,ok=0,n;
int (*cb)();
cb=ctx->verify_cb;
- if (cb == NULL) cb=null_callback;
n=sk_X509_num(ctx->chain);
ctx->error_depth=n-1;
CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func)
{
/* This function is (usually) called only once, by
- * SSL_get_ex_data_X509_STORE_CTX_idx (ssl/ssl_cert.c).
- * That function uses locking, so we don't (usually)
- * have to worry about locking here. For the whole cruel
- * truth, see crypto/ex_data.c */
- x509_store_ctx_num++;
- return CRYPTO_get_ex_new_index(x509_store_ctx_num-1,
- &x509_store_ctx_method,
- argl,argp,new_func,dup_func,free_func);
+ * SSL_get_ex_data_X509_STORE_CTX_idx (ssl/ssl_cert.c). */
+ return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_X509_STORE_CTX, argl, argp,
+ new_func, dup_func, free_func);
}
int X509_STORE_CTX_set_ex_data(X509_STORE_CTX *ctx, int idx, void *data)
}
}
- if (purpose) ctx->purpose = purpose;
- if (trust) ctx->trust = trust;
+ if (purpose && !ctx->purpose) ctx->purpose = purpose;
+ if (trust && !ctx->trust) ctx->trust = trust;
return 1;
}
{
X509_STORE_CTX *ctx;
ctx = (X509_STORE_CTX *)OPENSSL_malloc(sizeof(X509_STORE_CTX));
- if (ctx) memset(ctx, 0, sizeof(X509_STORE_CTX));
+ if (!ctx)
+ {
+ X509err(X509_F_X509_STORE_CTX_NEW,ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+ memset(ctx, 0, sizeof(X509_STORE_CTX));
return ctx;
}
OPENSSL_free(ctx);
}
-void X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509,
+int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509,
STACK_OF(X509) *chain)
{
ctx->ctx=store;
ctx->cert=x509;
ctx->untrusted=chain;
ctx->last_untrusted=0;
- ctx->purpose=0;
- ctx->trust=0;
+ ctx->purpose=store->purpose;
+ ctx->trust=store->trust;
ctx->check_time=0;
ctx->flags=0;
ctx->other_ctx=NULL;
ctx->error_depth=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 = 0;
- memset(&(ctx->ex_data),0,sizeof(CRYPTO_EX_DATA));
+
+ /* Inherit callbacks and flags from X509_STORE if not set
+ * use defaults.
+ */
+
+ ctx->flags = store->flags;
+
+ if (store->check_issued)
+ ctx->check_issued = store->check_issued;
+ else
+ ctx->check_issued = check_issued;
+
+ if (store->get_issuer)
+ ctx->get_issuer = store->get_issuer;
+ else
+ ctx->get_issuer = X509_STORE_CTX_get1_issuer;
+
+ if (store->verify_cb)
+ ctx->verify_cb = store->verify_cb;
+ else
+ ctx->verify_cb = null_callback;
+
+ if (store->verify)
+ ctx->verify = store->verify;
+ else
+ ctx->verify = internal_verify;
+
+ if (store->check_revocation)
+ ctx->check_revocation = store->check_revocation;
+ else
+ ctx->check_revocation = check_revocation;
+
+ if (store->get_crl)
+ ctx->get_crl = store->get_crl;
+ else
+ ctx->get_crl = get_crl;
+
+ if (store->check_crl)
+ ctx->check_crl = store->check_crl;
+ else
+ ctx->check_crl = check_crl;
+
+ if (store->cert_crl)
+ ctx->cert_crl = store->cert_crl;
+ else
+ ctx->cert_crl = cert_crl;
+
+ ctx->cleanup = store->cleanup;
+
+ /* This memset() can't make any sense anyway, so it's removed. As
+ * X509_STORE_CTX_cleanup does a proper "free" on the ex_data, we put a
+ * corresponding "new" here and remove this bogus initialisation. */
+ /* memset(&(ctx->ex_data),0,sizeof(CRYPTO_EX_DATA)); */
+ if(!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509_STORE_CTX, ctx,
+ &(ctx->ex_data)))
+ {
+ OPENSSL_free(ctx);
+ X509err(X509_F_X509_STORE_CTX_INIT,ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ return 1;
}
/* Set alternative lookup method: just a STACK of trusted certificates.
sk_X509_pop_free(ctx->chain,X509_free);
ctx->chain=NULL;
}
- CRYPTO_free_ex_data(x509_store_ctx_method,ctx,&(ctx->ex_data));
+ CRYPTO_free_ex_data(CRYPTO_EX_INDEX_X509_STORE_CTX, ctx, &(ctx->ex_data));
memset(&ctx->ex_data,0,sizeof(CRYPTO_EX_DATA));
}
ctx->flags |= X509_V_FLAG_USE_CHECK_TIME;
}
+void X509_STORE_CTX_set_verify_cb(X509_STORE_CTX *ctx,
+ int (*verify_cb)(int, X509_STORE_CTX *))
+ {
+ ctx->verify_cb=verify_cb;
+ }
+
IMPLEMENT_STACK_OF(X509)
IMPLEMENT_ASN1_SET_OF(X509)