*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 :-(
This commit is contained in:
Dr. Stephen Henson 2000-09-05 17:53:58 +00:00
parent 29eb7d9ce0
commit 2f043896d1
13 changed files with 514 additions and 208 deletions

51
CHANGES
View File

@ -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]

View File

@ -111,7 +111,7 @@ my %table=(
"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):::::",

View File

@ -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 MAIN(int argc, char **argv)
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 @@ int MAIN(int argc, char **argv)
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 @@ int MAIN(int argc, char **argv)
}
}
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 @@ end:
}
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 @@ static int check(X509_STORE *ctx, char *file, STACK_OF(X509) *uchain, int purpos
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);

View File

@ -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 @@ X509 *X509_new(void)
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 @@ void X509_free(X509 *a)
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);

View File

@ -864,6 +864,26 @@ STACK_OF(type) \
#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))

View File

@ -326,7 +326,9 @@ static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
/* 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)

View File

@ -275,6 +275,8 @@ typedef struct x509_st
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

View File

@ -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 @@ int X509_LOOKUP_by_alias(X509_LOOKUP *ctx, int type, char *str, int len,
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 @@ X509_STORE *X509_STORE_new(void)
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 @@ void X509_STORE_free(X509_STORE *vfy)
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 @@ int X509_STORE_get_by_subject(X509_STORE_CTX *vs, int type, X509_NAME *name,
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 @@ int X509_STORE_get_by_subject(X509_STORE_CTX *vs, int type, X509_NAME *name,
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 @@ void X509_OBJECT_free_contents(X509_OBJECT *a)
}
}
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 @@ X509_OBJECT *X509_OBJECT_retrieve_by_subject(LHASH *h, int type,
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)

View File

@ -132,6 +132,15 @@ const char *X509_verify_cert_error_string(long n)
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);

View File

@ -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 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
{
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 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
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 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
/* 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 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
* 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 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
* 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 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
* 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 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
/* 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 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
/* 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 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
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 @@ end:
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 @@ static int check_chain_purpose(X509_STORE_CTX *ctx)
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 @@ static int check_trust(X509_STORE_CTX *ctx)
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 @@ static int internal_verify(X509_STORE_CTX *ctx)
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 @@ int X509_get_pubkey_parameters(EVP_PKEY *pkey, STACK_OF(X509) *chain)
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 @@ int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose,
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)

View File

@ -131,6 +131,7 @@ typedef struct x509_object_st
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 @@ typedef struct x509_store_st
{
/* 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 @@ struct x509_lookup_st
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 @@ struct x509_store_state_st /* X509_STORE_CTX */
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 @@ struct x509_store_state_st /* X509_STORE_CTX */
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 @@ struct x509_store_state_st /* X509_STORE_CTX */
#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 @@ struct x509_store_state_st /* X509_STORE_CTX */
#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);

View File

@ -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 @@ int X509_PURPOSE_get_trust(X509_PURPOSE *xp)
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 @@ static void x509v3_cache_extensions(X509 *x)
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 @@ static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca)
{
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;
}

View File

@ -532,6 +532,7 @@ 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_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);