Skip to content

Commit

Permalink
x509_vfy.c and x509_lu.c: refactor find_issuer(), X509_STORE_CTX_get1…
Browse files Browse the repository at this point in the history
…_issuer(), etc.
  • Loading branch information
DDvO committed Nov 9, 2024
1 parent 012353b commit 1ebcc6c
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 141 deletions.
152 changes: 34 additions & 118 deletions crypto/x509/x509_lu.c
Original file line number Diff line number Diff line change
Expand Up @@ -367,11 +367,13 @@ static int ossl_x509_store_ctx_get_by_subject(const X509_STORE_CTX *ctx,
if (tmp == NULL)
return 0;
}
if (!X509_OBJECT_up_ref_count(tmp))
return -1;

ret->type = tmp->type;
ret->data = tmp->data;
if (ret != NULL) {
if (!X509_OBJECT_up_ref_count(tmp))
return -1;
ret->type = tmp->type;
ret->data = tmp->data;
}
return 1;
}

Expand Down Expand Up @@ -647,9 +649,14 @@ STACK_OF(X509) *X509_STORE_get1_all_certs(X509_STORE *store)
return NULL;
}

/* Returns NULL on internal/fatal error, empty stack if not found */
STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx,
const X509_NAME *nm)
/*-
* Collect from |ctx->store| all certs with subject matching |nm|.
* Caller must free on ret == 1 the resulting list |*certs|.
* Caller must also free its entries if up_refs != 0.
* Returns NULL on internal/fatal error, empty stack if not found.
*/
STACK_OF(X509) *ossl_x509_store_ctx_get_certs(X509_STORE_CTX *ctx,
const X509_NAME *nm, int up_refs)
{
int i, idx, cnt;
STACK_OF(X509) *sk = NULL;
Expand All @@ -670,36 +677,28 @@ STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx,
* Nothing found in cache: do lookup to possibly add new objects to
* cache
*/
X509_OBJECT *xobj = X509_OBJECT_new();

X509_STORE_unlock(store);
if (xobj == NULL)
return NULL;
i = ossl_x509_store_ctx_get_by_subject(ctx, X509_LU_X509, nm, xobj);
if (i <= 0) {
X509_OBJECT_free(xobj);
i = ossl_x509_store_ctx_get_by_subject(ctx, X509_LU_X509, nm, NULL);
if (i <= 0)
return i < 0 ? NULL : sk_X509_new_null();
}
X509_OBJECT_free(xobj);
if (!X509_STORE_lock(store))
return NULL;
sk_X509_OBJECT_sort(store->objs);
idx = x509_object_idx_cnt(store->objs, X509_LU_X509, nm, &cnt);
if (idx < 0) {
sk = sk_X509_new_null();
goto end;
}
}

sk = sk_X509_new_null();
if (sk == NULL)
if (idx < 0 || sk == NULL)
goto end;
for (i = 0; i < cnt; i++, idx++) {
obj = sk_X509_OBJECT_value(store->objs, idx);
x = obj->data.x509;
if (!X509_add_cert(sk, x, X509_ADD_FLAG_UP_REF)) {
if (!X509_add_cert(sk, x, up_refs ? X509_ADD_FLAG_UP_REF : 0)) {
X509_STORE_unlock(store);
OSSL_STACK_OF_X509_free(sk);
if (up_refs)
OSSL_STACK_OF_X509_free(sk);
else
sk_X509_free(sk);
return NULL;
}
}
Expand All @@ -708,26 +707,28 @@ STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx,
return sk;
}

/* Returns NULL on internal/fatal error, empty stack if not found */
STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx,
const X509_NAME *nm)
{
return ossl_x509_store_ctx_get_certs(ctx, nm, 1 /* up_refs */);
}

/* Returns NULL on internal/fatal error, empty stack if not found */
STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(const X509_STORE_CTX *ctx,
const X509_NAME *nm)
{
int i = 1, idx, cnt;
STACK_OF(X509_CRL) *sk = sk_X509_CRL_new_null();
STACK_OF(X509_CRL) *sk;
X509_CRL *x;
X509_OBJECT *obj, *xobj = X509_OBJECT_new();
X509_OBJECT *obj;
X509_STORE *store = ctx->store;

/* Always do lookup to possibly add new CRLs to cache */
if (sk == NULL
|| xobj == NULL
|| (i = ossl_x509_store_ctx_get_by_subject(ctx, X509_LU_CRL,
nm, xobj)) < 0) {
X509_OBJECT_free(xobj);
sk_X509_CRL_free(sk);
i = ossl_x509_store_ctx_get_by_subject(ctx, X509_LU_CRL, nm, NULL);
if (i < 0)
return NULL;
}
X509_OBJECT_free(xobj);
sk = sk_X509_CRL_new_null();
if (i == 0)
return sk;
if (!X509_STORE_lock(store)) {
Expand Down Expand Up @@ -789,91 +790,6 @@ X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h,
return NULL;
}

/*-
* Try to get issuer cert from |ctx->store| matching the subject name of |x|.
* Prefer the first non-expired one, else take the most recently expired one.
*
* 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)
{
const X509_NAME *xn;
X509_OBJECT *obj = X509_OBJECT_new(), *pobj = NULL;
X509_STORE *store = ctx->store;
int i, ok, idx, ret, nmatch = 0;

if (obj == NULL)
return -1;
*issuer = NULL;
xn = X509_get_issuer_name(x);
ok = ossl_x509_store_ctx_get_by_subject(ctx, X509_LU_X509, xn, obj);
if (ok != 1) {
X509_OBJECT_free(obj);
return ok;
}
/* If certificate matches and is currently valid all OK */
if (ctx->check_issued(ctx, x, obj->data.x509)) {
if (ossl_x509_check_cert_time(ctx, obj->data.x509, -1)) {
*issuer = obj->data.x509;
/* |*issuer| has taken over the cert reference from |obj| */
obj->type = X509_LU_NONE;
X509_OBJECT_free(obj);
return 1;
}
}
X509_OBJECT_free(obj);

/*
* Due to limitations of the API this can only retrieve a single cert.
* However it will fill the cache with all matching certificates,
* so we can examine the cache for all matches.
*/
if (store == NULL)
return 0;

/* Find index of first currently valid cert accepted by 'check_issued' */
ret = 0;
if (!X509_STORE_lock(store))
return 0;

sk_X509_OBJECT_sort(store->objs);
idx = x509_object_idx_cnt(store->objs, X509_LU_X509, xn, &nmatch);
if (idx != -1) { /* should be true as we've had at least one match */
/* Look through all matching certs for suitable issuer */
for (i = idx; i < idx + nmatch; i++) {
pobj = sk_X509_OBJECT_value(store->objs, i);
/* See if we've run past the matches */
if (pobj->type != X509_LU_X509)
break;
if (ctx->check_issued(ctx, x, pobj->data.x509)) {
ret = 1;
/* If times check fine, exit with match, else keep looking. */
if (ossl_x509_check_cert_time(ctx, pobj->data.x509, -1)) {
*issuer = pobj->data.x509;
break;
}
/*
* Leave the so far most recently expired match in *issuer
* so we return nearest match if no certificate time is OK.
*/
if (*issuer == NULL
|| ASN1_TIME_compare(X509_get0_notAfter(pobj->data.x509),
X509_get0_notAfter(*issuer)) > 0)
*issuer = pobj->data.x509;
}
}
}
if (*issuer != NULL && !X509_up_ref(*issuer)) {
*issuer = NULL;
ret = -1;
}
X509_STORE_unlock(store);
return ret;
}

int X509_STORE_set_flags(X509_STORE *xs, unsigned long flags)
{
return X509_VERIFY_PARAM_set_flags(xs->param, flags);
Expand Down
79 changes: 56 additions & 23 deletions crypto/x509/x509_vfy.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,13 @@ static int dane_verify(X509_STORE_CTX *ctx);
static int dane_verify_rpk(X509_STORE_CTX *ctx);
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_extensions(X509_STORE_CTX *ctx);
static int check_name_constraints(X509_STORE_CTX *ctx);
static int check_id(X509_STORE_CTX *ctx);
static int check_trust(X509_STORE_CTX *ctx, int num_untrusted);
static int check_revocation(X509_STORE_CTX *ctx);
static int check_cert(X509_STORE_CTX *ctx);
static int check_policy(X509_STORE_CTX *ctx);
static int get_issuer_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x);
static int check_dane_issuer(X509_STORE_CTX *ctx, int depth);
static int check_cert_key_level(X509_STORE_CTX *ctx, X509 *cert);
static int check_key_level(X509_STORE_CTX *ctx, EVP_PKEY *pkey);
Expand Down Expand Up @@ -377,30 +375,64 @@ static int sk_X509_contains(STACK_OF(X509) *sk, X509 *cert)
return 0;
}

/*
* Find in given STACK_OF(X509) |sk| an issuer cert (if any) of given cert |x|.
* The issuer must not yet be in |ctx->chain|, yet allowing the exception that
* |x| is self-issued and |ctx->chain| has just one element.
* Prefer the first non-expired one, else take the most recently expired one.
/*-
* Find in |sk| an issuer cert of cert |x| accepted by |ctx->check_issued|.
* If no_dup, the issuer must not yet be in |ctx->chain|, yet allowing the
* exception that |x| is self-issued and |ctx->chain| has just one element.
* Prefer the first match with suitable validity period or latest expiration.
*/
static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x)
static X509 *get0_best_issuer_sk(X509_STORE_CTX *ctx, int trusted,
int no_dup, STACK_OF(X509) *sk, X509 *x)
{
int i;
X509 *issuer, *rv = NULL;
X509 *candidate, *issuer = NULL;

for (i = 0; i < sk_X509_num(sk); i++) {
issuer = sk_X509_value(sk, i);
if (ctx->check_issued(ctx, x, issuer)
&& (((x->ex_flags & EXFLAG_SI) != 0 && sk_X509_num(ctx->chain) == 1)
|| !sk_X509_contains(ctx->chain, issuer))) {
if (ossl_x509_check_cert_time(ctx, issuer, -1))
return issuer;
if (rv == NULL || ASN1_TIME_compare(X509_get0_notAfter(issuer),
X509_get0_notAfter(rv)) > 0)
rv = issuer;
candidate = sk_X509_value(sk, i);
if (no_dup
&& !((x->ex_flags & EXFLAG_SI) != 0 && sk_X509_num(ctx->chain) == 1)
&& sk_X509_contains(ctx->chain, candidate))
continue;
if (ctx->check_issued(ctx, x, candidate)) {
if (!trusted) { /* do not check key usage for trust anchors */
if (ossl_x509_signing_allowed(candidate, x) != X509_V_OK)
continue;
}
if (ossl_x509_check_cert_time(ctx, candidate, -1))
return candidate;
/*
* Leave in *issuer the first match that has the latest expiration
* date so we return nearest match if no certificate time is OK.
*/
if (issuer == NULL
|| ASN1_TIME_compare(X509_get0_notAfter(candidate),
X509_get0_notAfter(issuer)) > 0)
issuer = candidate;
}
}
return rv;
return issuer;
}

/*-
* Try to get issuer cert from |ctx->store| accepted by |ctx->check_issued|.
*
* 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)
{
const X509_NAME *xn = X509_get_issuer_name(x);
STACK_OF(X509) *certs = ossl_x509_store_ctx_get_certs(ctx, xn, 0);

if (certs == NULL)
return -1;
*issuer = get0_best_issuer_sk(ctx, 1 /* trusted */, 0, certs, x);
sk_X509_free(certs);
if (*issuer == NULL)
return 0;
return X509_up_ref(*issuer) ? 1 : -1;
}

/* Check that the given certificate |x| is issued by the certificate |issuer| */
Expand All @@ -421,9 +453,10 @@ static int check_issued(ossl_unused X509_STORE_CTX *ctx, X509 *x, X509 *issuer)
* Alternative get_issuer method: look up from a STACK_OF(X509) in other_ctx.
* Returns -1 on internal error.
*/
static int get_issuer_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
static int get1_best_issuer_other_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
{
*issuer = find_issuer(ctx, ctx->other_ctx, x);
*issuer = get0_best_issuer_sk(ctx, 1 /* trusted */, 1 /* no_dup */,
ctx->other_ctx, x);
if (*issuer == NULL)
return 0;
return X509_up_ref(*issuer) ? 1 : -1;
Expand Down Expand Up @@ -2570,7 +2603,7 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509,
void X509_STORE_CTX_set0_trusted_stack(X509_STORE_CTX *ctx, STACK_OF(X509) *sk)
{
ctx->other_ctx = sk;
ctx->get_issuer = get_issuer_sk;
ctx->get_issuer = get1_best_issuer_other_sk;
ctx->lookup_certs = lookup_certs_sk;
}

Expand Down Expand Up @@ -3458,7 +3491,7 @@ static int build_chain(X509_STORE_CTX *ctx)
goto int_err;
curr = sk_X509_value(ctx->chain, num - 1);
issuer = (X509_self_signed(curr, 0) > 0 || num > max_depth) ?
NULL : find_issuer(ctx, sk_untrusted, curr);
NULL : get0_best_issuer_sk(ctx, 0, 1, sk_untrusted, curr);
if (issuer == NULL) {
/*
* Once we have reached a self-signed cert or num > max_depth
Expand Down
2 changes: 2 additions & 0 deletions include/crypto/x509.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ int ossl_a2i_ipadd(unsigned char *ipout, const char *ipasc);
int ossl_x509_set1_time(int *modified, ASN1_TIME **ptm, const ASN1_TIME *tm);
int ossl_x509_print_ex_brief(BIO *bio, X509 *cert, unsigned long neg_cflags);
int ossl_x509v3_cache_extensions(X509 *x);
STACK_OF(X509) *ossl_x509_store_ctx_get_certs(X509_STORE_CTX *ctx,
const X509_NAME *nm, int up_refs);
int ossl_x509_init_sig_info(X509 *x);

int ossl_x509_set0_libctx(X509 *x, OSSL_LIB_CTX *libctx, const char *propq);
Expand Down

0 comments on commit 1ebcc6c

Please sign in to comment.