From 88e5d4476be93cdd38d69b681fe8cc963fa2a4cd Mon Sep 17 00:00:00 2001 From: beck Date: Thu, 4 Nov 2021 23:52:34 +0000 Subject: [PATCH] Cache sha512 hash and parsed not_before and not_after with X509 cert. Replace sha1 hash use with sha512 for certificate comparisons internal to the library. use the cached sha512 for the validator's verification cache. Reduces our recomputation of hashes, and heavy use of time1 time conversion functions noticed bu claudio@ in rpki client. ok jsing@ tb@ --- lib/libcrypto/ts/ts_rsp_sign.c | 10 +- lib/libcrypto/ts/ts_rsp_verify.c | 11 +- lib/libcrypto/x509/x509_cmp.c | 4 +- lib/libcrypto/x509/x509_internal.h | 6 +- lib/libcrypto/x509/x509_lcl.h | 12 +- lib/libcrypto/x509/x509_purp.c | 10 +- lib/libcrypto/x509/x509_verify.c | 172 +++++++++++++---------------- lib/libcrypto/x509/x509_verify.h | 3 +- lib/libcrypto/x509/x509_vfy.c | 56 ++++++---- 9 files changed, 149 insertions(+), 135 deletions(-) diff --git a/lib/libcrypto/ts/ts_rsp_sign.c b/lib/libcrypto/ts/ts_rsp_sign.c index 828407aa4b8..cbd7039abe2 100644 --- a/lib/libcrypto/ts/ts_rsp_sign.c +++ b/lib/libcrypto/ts/ts_rsp_sign.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ts_rsp_sign.c,v 1.24 2021/11/01 20:53:08 tb Exp $ */ +/* $OpenBSD: ts_rsp_sign.c,v 1.25 2021/11/04 23:52:34 beck Exp $ */ /* Written by Zoltan Glozik (zglozik@stones.com) for the OpenSSL * project 2002. */ @@ -849,14 +849,18 @@ ESS_CERT_ID_new_init(X509 *cert, int issuer_needed) { ESS_CERT_ID *cid = NULL; GENERAL_NAME *name = NULL; + unsigned char cert_hash[TS_HASH_LEN]; /* Recompute SHA1 hash of certificate if necessary (side effect). */ X509_check_purpose(cert, -1, 0); if (!(cid = ESS_CERT_ID_new())) goto err; - if (!ASN1_OCTET_STRING_set(cid->hash, cert->sha1_hash, - sizeof(cert->sha1_hash))) + + if (!X509_digest(cert, TS_HASH_EVP, cert_hash, NULL)) + goto err; + + if (!ASN1_OCTET_STRING_set(cid->hash, cert_hash, sizeof(cert_hash))) goto err; /* Setting the issuer/serial if requested. */ diff --git a/lib/libcrypto/ts/ts_rsp_verify.c b/lib/libcrypto/ts/ts_rsp_verify.c index 2e72ff47aef..07578945cb3 100644 --- a/lib/libcrypto/ts/ts_rsp_verify.c +++ b/lib/libcrypto/ts/ts_rsp_verify.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ts_rsp_verify.c,v 1.22 2021/11/01 20:53:08 tb Exp $ */ +/* $OpenBSD: ts_rsp_verify.c,v 1.23 2021/11/04 23:52:34 beck Exp $ */ /* Written by Zoltan Glozik (zglozik@stones.com) for the OpenSSL * project 2002. */ @@ -325,10 +325,14 @@ static int TS_find_cert(STACK_OF(ESS_CERT_ID) *cert_ids, X509 *cert) { int i; + unsigned char cert_hash[TS_HASH_LEN]; if (!cert_ids || !cert) return -1; + if (!X509_digest(cert, TS_HASH_EVP, cert_hash, NULL)) + return -1; + /* Recompute SHA1 hash of certificate if necessary (side effect). */ X509_check_purpose(cert, -1, 0); @@ -337,9 +341,8 @@ TS_find_cert(STACK_OF(ESS_CERT_ID) *cert_ids, X509 *cert) ESS_CERT_ID *cid = sk_ESS_CERT_ID_value(cert_ids, i); /* Check the SHA-1 hash first. */ - if (cid->hash->length == sizeof(cert->sha1_hash) && - !memcmp(cid->hash->data, cert->sha1_hash, - sizeof(cert->sha1_hash))) { + if (cid->hash->length == TS_HASH_LEN && !memcmp(cid->hash->data, + cert_hash, TS_HASH_LEN)) { /* Check the issuer/serial as well if specified. */ ESS_ISSUER_SERIAL *is = cid->issuer_serial; if (!is || !TS_issuer_serial_cmp(is, cert->cert_info)) diff --git a/lib/libcrypto/x509/x509_cmp.c b/lib/libcrypto/x509/x509_cmp.c index 134a0827302..bc944b71d78 100644 --- a/lib/libcrypto/x509/x509_cmp.c +++ b/lib/libcrypto/x509/x509_cmp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: x509_cmp.c,v 1.36 2021/11/01 20:53:08 tb Exp $ */ +/* $OpenBSD: x509_cmp.c,v 1.37 2021/11/04 23:52:34 beck Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -215,7 +215,7 @@ X509_cmp(const X509 *a, const X509 *b) X509_check_purpose((X509 *)a, -1, 0); X509_check_purpose((X509 *)b, -1, 0); - return memcmp(a->sha1_hash, b->sha1_hash, SHA_DIGEST_LENGTH); + return memcmp(a->hash, b->hash, X509_CERT_HASH_LEN); } #endif diff --git a/lib/libcrypto/x509/x509_internal.h b/lib/libcrypto/x509/x509_internal.h index 7ca67a853b6..a9b584b13e1 100644 --- a/lib/libcrypto/x509/x509_internal.h +++ b/lib/libcrypto/x509/x509_internal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: x509_internal.h,v 1.14 2021/11/01 20:53:08 tb Exp $ */ +/* $OpenBSD: x509_internal.h,v 1.15 2021/11/04 23:52:34 beck Exp $ */ /* * Copyright (c) 2020 Bob Beck * @@ -96,8 +96,7 @@ int x509_vfy_callback_indicate_success(X509_STORE_CTX *ctx); void x509v3_cache_extensions(X509 *x); X509 *x509_vfy_lookup_cert_match(X509_STORE_CTX *ctx, X509 *x); -int x509_verify_asn1_time_to_tm(const ASN1_TIME *atime, struct tm *tm, - int notafter); +time_t x509_verify_asn1_time_to_time_t(const ASN1_TIME *atime, int notafter); struct x509_verify_ctx *x509_verify_ctx_new_from_xsc(X509_STORE_CTX *xsc); @@ -132,6 +131,7 @@ int x509_constraints_check(struct x509_constraints_names *names, struct x509_constraints_names *excluded, int *error); int x509_constraints_chain(STACK_OF(X509) *chain, int *error, int *depth); +void x509_verify_cert_info_populate(X509 *cert); __END_HIDDEN_DECLS diff --git a/lib/libcrypto/x509/x509_lcl.h b/lib/libcrypto/x509/x509_lcl.h index 1b352aee787..804fff48fc3 100644 --- a/lib/libcrypto/x509/x509_lcl.h +++ b/lib/libcrypto/x509/x509_lcl.h @@ -61,6 +61,12 @@ __BEGIN_HIDDEN_DECLS +#define TS_HASH_EVP EVP_sha1() +#define TS_HASH_LEN SHA_DIGEST_LENGTH + +#define X509_CERT_HASH_EVP EVP_sha512() +#define X509_CERT_HASH_LEN SHA512_DIGEST_LENGTH + struct X509_pubkey_st { X509_ALGOR *algor; ASN1_BIT_STRING *public_key; @@ -177,9 +183,9 @@ struct x509_st { STACK_OF(IPAddressFamily) *rfc3779_addr; struct ASIdentifiers_st *rfc3779_asid; #endif -#ifndef OPENSSL_NO_SHA - unsigned char sha1_hash[SHA_DIGEST_LENGTH]; -#endif + unsigned char hash[X509_CERT_HASH_LEN]; + time_t not_before; + time_t not_after; X509_CERT_AUX *aux; } /* X509 */; diff --git a/lib/libcrypto/x509/x509_purp.c b/lib/libcrypto/x509/x509_purp.c index 67a7b51fb15..a05c0388ac2 100644 --- a/lib/libcrypto/x509/x509_purp.c +++ b/lib/libcrypto/x509/x509_purp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: x509_purp.c,v 1.12 2021/11/01 20:53:08 tb Exp $ */ +/* $OpenBSD: x509_purp.c,v 1.13 2021/11/04 23:52:34 beck Exp $ */ /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL * project 2001. */ @@ -65,6 +65,7 @@ #include #include +#include "x509_internal.h" #include "x509_lcl.h" #define V1_ROOT (EXFLAG_V1|EXFLAG_SS) @@ -449,9 +450,7 @@ x509v3_cache_extensions(X509 *x) if (x->ex_flags & EXFLAG_SET) return; -#ifndef OPENSSL_NO_SHA - X509_digest(x, EVP_sha1(), x->sha1_hash, NULL); -#endif + X509_digest(x, X509_CERT_HASH_EVP, x->hash, NULL); /* V1 should mean no extensions ... */ if (!X509_get_version(x)) @@ -618,6 +617,9 @@ x509v3_cache_extensions(X509 *x) break; } } + + x509_verify_cert_info_populate(x); + x->ex_flags |= EXFLAG_SET; } diff --git a/lib/libcrypto/x509/x509_verify.c b/lib/libcrypto/x509/x509_verify.c index 8bcc6471492..b9ba2bee3cb 100644 --- a/lib/libcrypto/x509/x509_verify.c +++ b/lib/libcrypto/x509/x509_verify.c @@ -1,4 +1,4 @@ -/* $OpenBSD: x509_verify.c,v 1.50 2021/10/26 15:14:18 job Exp $ */ +/* $OpenBSD: x509_verify.c,v 1.51 2021/11/04 23:52:34 beck Exp $ */ /* * Copyright (c) 2020-2021 Bob Beck * @@ -38,7 +38,58 @@ static int x509_verify_cert_error(struct x509_verify_ctx *ctx, X509 *cert, size_t depth, int error, int ok); static void x509_verify_chain_free(struct x509_verify_chain *chain); -#define X509_VERIFY_CERT_HASH (EVP_sha512()) +/* + * Parse an asn1 to a representable time_t as per RFC 5280 rules. + * Returns -1 if that can't be done for any reason. + */ +time_t +x509_verify_asn1_time_to_time_t(const ASN1_TIME *atime, int notAfter) +{ + struct tm tm = { 0 }; + int type; + + type = ASN1_time_parse(atime->data, atime->length, &tm, atime->type); + if (type == -1) + return -1; + + /* RFC 5280 section 4.1.2.5 */ + if (tm.tm_year < 150 && type != V_ASN1_UTCTIME) + return -1; + if (tm.tm_year >= 150 && type != V_ASN1_GENERALIZEDTIME) + return -1; + + if (notAfter) { + /* + * If we are a completely broken operating system with a + * 32 bit time_t, and we have been told this is a notAfter + * date, limit the date to a 32 bit representable value. + */ + if (!ASN1_time_tm_clamp_notafter(&tm)) + return -1; + } + + /* + * Defensively fail if the time string is not representable as + * a time_t. A time_t must be sane if you care about times after + * Jan 19 2038. + */ + return timegm(&tm); +} + +/* + * Cache certificate hash, and values parsed out of an X509. + * called from cache_extensions() + */ +void +x509_verify_cert_info_populate(X509 *cert) +{ + /* + * Parse and save the cert times, or remember that they + * are unacceptable/unparsable. + */ + cert->not_before = x509_verify_asn1_time_to_time_t(X509_get_notBefore(cert), 0); + cert->not_after = x509_verify_asn1_time_to_time_t(X509_get_notAfter(cert), 1); +} struct x509_verify_chain * x509_verify_chain_new(void) @@ -194,6 +245,7 @@ x509_verify_cert_cache_extensions(X509 *cert) { } if (cert->ex_flags & EXFLAG_INVALID) return 0; + return (cert->ex_flags & EXFLAG_SET); } @@ -455,22 +507,15 @@ x509_verify_potential_parent(struct x509_verify_ctx *ctx, X509 *parent, } static int -x509_verify_parent_signature(X509 *parent, X509 *child, - unsigned char *child_md, int *error) +x509_verify_parent_signature(X509 *parent, X509 *child, int *error) { - unsigned char parent_md[EVP_MAX_MD_SIZE] = { 0 }; EVP_PKEY *pkey; int cached; int ret = 0; /* Use cached value if we have it */ - if (child_md != NULL) { - if (!X509_digest(parent, X509_VERIFY_CERT_HASH, parent_md, - NULL)) - return 0; - if ((cached = x509_issuer_cache_find(parent_md, child_md)) >= 0) - return cached; - } + if ((cached = x509_issuer_cache_find(parent->hash, child->hash)) >= 0) + return cached; /* Check signature. Did parent sign child? */ if ((pkey = X509_get_pubkey(parent)) == NULL) { @@ -483,8 +528,7 @@ x509_verify_parent_signature(X509 *parent, X509 *child, ret = 1; /* Add result to cache */ - if (child_md != NULL) - x509_issuer_cache_add(parent_md, child_md, ret); + x509_issuer_cache_add(parent->hash, child->hash, ret); EVP_PKEY_free(pkey); @@ -493,8 +537,8 @@ x509_verify_parent_signature(X509 *parent, X509 *child, static int x509_verify_consider_candidate(struct x509_verify_ctx *ctx, X509 *cert, - unsigned char *cert_md, int is_root_cert, X509 *candidate, - struct x509_verify_chain *current_chain, int full_chain) + int is_root_cert, X509 *candidate, struct x509_verify_chain *current_chain, + int full_chain) { int depth = sk_X509_num(current_chain->certs); struct x509_verify_chain *new_chain; @@ -514,8 +558,7 @@ x509_verify_consider_candidate(struct x509_verify_ctx *ctx, X509 *cert, return 0; } - if (!x509_verify_parent_signature(candidate, cert, cert_md, - &ctx->error)) { + if (!x509_verify_parent_signature(candidate, cert, &ctx->error)) { if (!x509_verify_cert_error(ctx, candidate, depth, ctx->error, 0)) return 0; @@ -579,7 +622,6 @@ static void x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert, struct x509_verify_chain *current_chain, int full_chain) { - unsigned char cert_md[EVP_MAX_MD_SIZE] = { 0 }; X509 *candidate; int i, depth, count, ret, is_root; @@ -600,11 +642,6 @@ x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert, X509_V_ERR_CERT_CHAIN_TOO_LONG, 0)) return; - if (!X509_digest(cert, X509_VERIFY_CERT_HASH, cert_md, NULL) && - !x509_verify_cert_error(ctx, cert, depth, - X509_V_ERR_UNSPECIFIED, 0)) - return; - count = ctx->chains_count; ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; @@ -640,7 +677,7 @@ x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert, is_root = !full_chain || x509_verify_cert_self_signed(candidate); x509_verify_consider_candidate(ctx, cert, - cert_md, is_root, candidate, current_chain, + is_root, candidate, current_chain, full_chain); } X509_free(candidate); @@ -653,7 +690,7 @@ x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert, is_root = !full_chain || x509_verify_cert_self_signed(candidate); x509_verify_consider_candidate(ctx, cert, - cert_md, is_root, candidate, current_chain, + is_root, candidate, current_chain, full_chain); } } @@ -665,7 +702,7 @@ x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert, candidate = sk_X509_value(ctx->intermediates, i); if (x509_verify_potential_parent(ctx, candidate, cert)) { x509_verify_consider_candidate(ctx, cert, - cert_md, 0, candidate, current_chain, + 0, candidate, current_chain, full_chain); } } @@ -748,47 +785,9 @@ x509_verify_set_check_time(struct x509_verify_ctx *ctx) { return 1; } -int -x509_verify_asn1_time_to_tm(const ASN1_TIME *atime, struct tm *tm, int notafter) -{ - int type; - - type = ASN1_time_parse(atime->data, atime->length, tm, atime->type); - if (type == -1) - return 0; - - /* RFC 5280 section 4.1.2.5 */ - if (tm->tm_year < 150 && type != V_ASN1_UTCTIME) - return 0; - if (tm->tm_year >= 150 && type != V_ASN1_GENERALIZEDTIME) - return 0; - - if (notafter) { - /* - * If we are a completely broken operating system with a - * 32 bit time_t, and we have been told this is a notafter - * date, limit the date to a 32 bit representable value. - */ - if (!ASN1_time_tm_clamp_notafter(tm)) - return 0; - } - - /* - * Defensively fail if the time string is not representable as - * a time_t. A time_t must be sane if you care about times after - * Jan 19 2038. - */ - if (timegm(tm) == -1) - return 0; - - return 1; -} - static int -x509_verify_cert_time(int is_notafter, const ASN1_TIME *cert_asn1, - time_t *cmp_time, int *error) +x509_verify_cert_times(X509 *cert, time_t *cmp_time, int *error) { - struct tm cert_tm, when_tm; time_t when; if (cmp_time == NULL) @@ -796,29 +795,21 @@ x509_verify_cert_time(int is_notafter, const ASN1_TIME *cert_asn1, else when = *cmp_time; - if (!x509_verify_asn1_time_to_tm(cert_asn1, &cert_tm, - is_notafter)) { - *error = is_notafter ? - X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD : - X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD; + if (cert->not_before == -1) { + *error = X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD; return 0; } - - if (gmtime_r(&when, &when_tm) == NULL) { - *error = X509_V_ERR_UNSPECIFIED; + if (when < cert->not_before) { + *error = X509_V_ERR_CERT_NOT_YET_VALID; return 0; } - - if (is_notafter) { - if (ASN1_time_tm_cmp(&cert_tm, &when_tm) == -1) { - *error = X509_V_ERR_CERT_HAS_EXPIRED; - return 0; - } - } else { - if (ASN1_time_tm_cmp(&cert_tm, &when_tm) == 1) { - *error = X509_V_ERR_CERT_NOT_YET_VALID; - return 0; - } + if (cert->not_after == -1) { + *error = X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD; + return 0; + } + if (when > cert->not_after) { + *error = X509_V_ERR_CERT_HAS_EXPIRED; + return 0; } return 1; @@ -924,15 +915,8 @@ x509_verify_cert_valid(struct x509_verify_ctx *ctx, X509 *cert, } if (x509_verify_set_check_time(ctx)) { - if (!x509_verify_cert_time(0, X509_get_notBefore(cert), - ctx->check_time, &ctx->error)) { - if (!x509_verify_cert_error(ctx, cert, depth, - ctx->error, 0)) - return 0; - } - - if (!x509_verify_cert_time(1, X509_get_notAfter(cert), - ctx->check_time, &ctx->error)) { + if (!x509_verify_cert_times(cert, ctx->check_time, + &ctx->error)) { if (!x509_verify_cert_error(ctx, cert, depth, ctx->error, 0)) return 0; diff --git a/lib/libcrypto/x509/x509_verify.h b/lib/libcrypto/x509/x509_verify.h index a097404f2e8..d8d2cb0b5f2 100644 --- a/lib/libcrypto/x509/x509_verify.h +++ b/lib/libcrypto/x509/x509_verify.h @@ -1,4 +1,4 @@ -/* $OpenBSD: x509_verify.h,v 1.1 2020/09/13 15:06:17 beck Exp $ */ +/* $OpenBSD: x509_verify.h,v 1.2 2021/11/04 23:52:34 beck Exp $ */ /* * Copyright (c) 2020 Bob Beck * @@ -19,6 +19,7 @@ #ifdef LIBRESSL_INTERNAL struct x509_verify_ctx; +struct x509_verify_cert_info; typedef struct x509_verify_ctx X509_VERIFY_CTX; X509_VERIFY_CTX *x509_verify_ctx_new(STACK_OF(X509) *roots); diff --git a/lib/libcrypto/x509/x509_vfy.c b/lib/libcrypto/x509/x509_vfy.c index 664474139cd..3b0d6dfa354 100644 --- a/lib/libcrypto/x509/x509_vfy.c +++ b/lib/libcrypto/x509/x509_vfy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: x509_vfy.c,v 1.93 2021/11/01 20:53:08 tb Exp $ */ +/* $OpenBSD: x509_vfy.c,v 1.94 2021/11/04 23:52:34 beck Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -1843,6 +1843,18 @@ verify_cb_cert(X509_STORE_CTX *ctx, X509 *x, int depth, int err) return ctx->verify_cb(0, ctx); } + +/* Mimic OpenSSL '0 for failure' ick */ +static int +time_t_bogocmp(time_t a, time_t b) +{ + if (a == -1 || b == -1) + return 0; + if (a <= b) + return -1; + return 1; +} + /* * Check certificate validity times. * @@ -1854,17 +1866,21 @@ verify_cb_cert(X509_STORE_CTX *ctx, X509 *x, int depth, int err) int x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth) { - time_t *ptime; + time_t ptime; int i; if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) - ptime = &ctx->param->check_time; + ptime = ctx->param->check_time; else if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) return 1; else - ptime = NULL; + ptime = time(NULL); + + if (x->ex_flags & EXFLAG_SET) + i = time_t_bogocmp(x->not_before, ptime); + else + i = X509_cmp_time(X509_get_notBefore(x), &ptime); - i = X509_cmp_time(X509_get_notBefore(x), ptime); if (i >= 0 && depth < 0) return 0; if (i == 0 && !verify_cb_cert(ctx, x, depth, @@ -1874,7 +1890,11 @@ x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth) X509_V_ERR_CERT_NOT_YET_VALID)) return 0; - i = X509_cmp_time_internal(X509_get_notAfter(x), ptime, 1); + if (x->ex_flags & EXFLAG_SET) + i = time_t_bogocmp(x->not_after, ptime); + else + i = X509_cmp_time_internal(X509_get_notAfter(x), &ptime, 1); + if (i <= 0 && depth < 0) return 0; if (i == 0 && !verify_cb_cert(ctx, x, depth, @@ -1883,6 +1903,7 @@ x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth) if (i < 0 && !verify_cb_cert(ctx, x, depth, X509_V_ERR_CERT_HAS_EXPIRED)) return 0; + return 1; } @@ -1994,30 +2015,23 @@ X509_cmp_current_time(const ASN1_TIME *ctm) * 0 on error. */ static int -X509_cmp_time_internal(const ASN1_TIME *ctm, time_t *cmp_time, int clamp_notafter) +X509_cmp_time_internal(const ASN1_TIME *ctm, time_t *cmp_time, int is_notafter) { - time_t compare; - struct tm tm1, tm2; - int ret = 0; + time_t compare, cert_time; if (cmp_time == NULL) compare = time(NULL); else compare = *cmp_time; - memset(&tm1, 0, sizeof(tm1)); + if ((cert_time = x509_verify_asn1_time_to_time_t(ctm, is_notafter)) == + -1) + return 0; /* invalid time */ - if (!x509_verify_asn1_time_to_tm(ctm, &tm1, clamp_notafter)) - goto out; /* invalid time */ + if (cert_time <= compare) + return -1; /* 0 is used for error, so map same to less than */ - if (gmtime_r(&compare, &tm2) == NULL) - goto out; - - ret = ASN1_time_tm_cmp(&tm1, &tm2); - if (ret == 0) - ret = -1; /* 0 is used for error, so map same to less than */ - out: - return (ret); + return 1; } int -- 2.20.1