From 1aabc2445b126f951707b287db9a9145b003f2ca Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Sat, 11 Feb 2017 01:17:50 +0100 Subject: [PATCH] STORE 'file' scheme loader: refactor file_load to support decoding restart Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/3542) --- crypto/err/openssl.txt | 1 + crypto/store/loader_file.c | 288 +++++++++++++++++++++++-------------- crypto/store/store_err.c | 2 + include/openssl/storeerr.h | 1 + 4 files changed, 181 insertions(+), 111 deletions(-) diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 5efa37e7b1..d66ab4f2ed 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -726,6 +726,7 @@ OCSP_F_OCSP_RESPONSE_GET1_BASIC:111:OCSP_response_get1_basic OCSP_F_PARSE_HTTP_LINE1:118:parse_http_line1 OSSL_STORE_F_FILE_GET_PASS:118:file_get_pass OSSL_STORE_F_FILE_LOAD:119:file_load +OSSL_STORE_F_FILE_LOAD_TRY_DECODE:124:file_load_try_decode OSSL_STORE_F_FILE_OPEN:120:file_open OSSL_STORE_F_OSSL_STORE_GET0_LOADER_INT:100:ossl_store_get0_loader_int OSSL_STORE_F_OSSL_STORE_INFO_GET1_CERT:101:OSSL_STORE_INFO_get1_CERT diff --git a/crypto/store/loader_file.c b/crypto/store/loader_file.c index 558980a591..472e8197f8 100644 --- a/crypto/store/loader_file.c +++ b/crypto/store/loader_file.c @@ -559,26 +559,185 @@ static OSSL_STORE_LOADER_CTX *file_open(const OSSL_STORE_LOADER *loader, return NULL; } -static int file_eof(OSSL_STORE_LOADER_CTX *ctx); -static int file_error(OSSL_STORE_LOADER_CTX *ctx); -static OSSL_STORE_INFO *file_load(OSSL_STORE_LOADER_CTX *ctx, - const UI_METHOD *ui_method, void *ui_data) +static OSSL_STORE_INFO *file_load_try_decode(OSSL_STORE_LOADER_CTX *ctx, + const char *pem_name, + const char *pem_header, + unsigned char *data, size_t len, + const UI_METHOD *ui_method, + void *ui_data, int *matchcount) +{ + OSSL_STORE_INFO *result = NULL; + BUF_MEM *new_mem = NULL; + char *new_pem_name = NULL; + int t = 0; + + again: + { + size_t i = 0; + void *handler_ctx = NULL; + const FILE_HANDLER **matching_handlers = + OPENSSL_zalloc(sizeof(*matching_handlers) + * OSSL_NELEM(file_handlers)); + + if (matching_handlers == NULL) { + OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD_TRY_DECODE, + ERR_R_MALLOC_FAILURE); + goto err; + } + + *matchcount = 0; + for (i = 0; i < OSSL_NELEM(file_handlers); i++) { + const FILE_HANDLER *handler = file_handlers[i]; + void *tmp_handler_ctx = NULL; + OSSL_STORE_INFO *tmp_result = + handler->try_decode(pem_name, pem_header, data, len, + &tmp_handler_ctx, ui_method, ui_data); + + if (tmp_result == NULL) { + OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD_TRY_DECODE, + OSSL_STORE_R_IS_NOT_A); + ERR_add_error_data(1, handler->name); + } else { + if (matching_handlers) + matching_handlers[*matchcount] = handler; + + if (handler_ctx) + handler->destroy_ctx(&handler_ctx); + handler_ctx = tmp_handler_ctx; + + if (++*matchcount == 1) { + result = tmp_result; + tmp_result = NULL; + } else { + /* more than one match => ambiguous, kill any result */ + OSSL_STORE_INFO_free(result); + OSSL_STORE_INFO_free(tmp_result); + if (handler->destroy_ctx != NULL) + handler->destroy_ctx(&handler_ctx); + handler_ctx = NULL; + result = NULL; + } + } + } + + if (*matchcount > 1) + OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD_TRY_DECODE, + OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE); + if (*matchcount == 0) + OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD_TRY_DECODE, + OSSL_STORE_R_UNSUPPORTED_CONTENT_TYPE); + else if (matching_handlers[0]->repeatable) { + if (ctx == NULL) { + OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD_TRY_DECODE, + OSSL_STORE_R_UNSUPPORTED_CONTENT_TYPE); + OSSL_STORE_INFO_free(result); + result = NULL; + } else { + ctx->last_handler = matching_handlers[0]; + ctx->last_handler_ctx = handler_ctx; + } + } + + OPENSSL_free(matching_handlers); + } + + err: + OPENSSL_free(new_pem_name); + BUF_MEM_free(new_mem); + + if (result != NULL + && (t = OSSL_STORE_INFO_get_type(result)) == OSSL_STORE_INFO_EMBEDDED) { + pem_name = new_pem_name = + ossl_store_info_get0_EMBEDDED_pem_name(result); + new_mem = ossl_store_info_get0_EMBEDDED_buffer(result); + data = (unsigned char *)new_mem->data; + len = new_mem->length; + OPENSSL_free(result); + result = NULL; + goto again; + } + + if (result != NULL) + ERR_clear_error(); + + return result; +} + +static OSSL_STORE_INFO *file_load_try_repeat(OSSL_STORE_LOADER_CTX *ctx, + const UI_METHOD *ui_method, + void *ui_data) { OSSL_STORE_INFO *result = NULL; - int matchcount = -1; if (ctx->last_handler != NULL) { result = ctx->last_handler->try_decode(NULL, NULL, NULL, 0, &ctx->last_handler_ctx, ui_method, ui_data); - if (result != NULL) - return result; + if (result == NULL) { + ctx->last_handler->destroy_ctx(&ctx->last_handler_ctx); + ctx->last_handler_ctx = NULL; + ctx->last_handler = NULL; + } + } + return result; +} - ctx->last_handler->destroy_ctx(&ctx->last_handler_ctx); - ctx->last_handler_ctx = NULL; - ctx->last_handler = NULL; +static int file_read_pem(BIO *bp, char **pem_name, char **pem_header, + unsigned char **data, long *len, + const UI_METHOD *ui_method, + void *ui_data) +{ + int i = PEM_read_bio(bp, pem_name, pem_header, data, len); + + if (i <= 0) + return 0; + + /* + * 10 is the number of characters in "Proc-Type:", which + * PEM_get_EVP_CIPHER_INFO() requires to be present. + * If the PEM header has less characters than that, it's + * not worth spending cycles on it. + */ + if (strlen(*pem_header) > 10) { + EVP_CIPHER_INFO cipher; + struct pem_pass_data pass_data; + + if (!PEM_get_EVP_CIPHER_INFO(*pem_header, &cipher) + || !file_fill_pem_pass_data(&pass_data, "PEM", ui_method, ui_data) + || !PEM_do_header(&cipher, *data, len, file_get_pem_pass, + &pass_data)) { + return 0; + } } + return 1; +} + +static int file_read_asn1(BIO *bp, unsigned char **data, long *len) +{ + BUF_MEM *mem = NULL; + + if (asn1_d2i_read_bio(bp, &mem) < 0) + return 0; + + *data = (unsigned char *)mem->data; + *len = (long)mem->length; + OPENSSL_free(mem); + + return 1; +} + +static int file_eof(OSSL_STORE_LOADER_CTX *ctx); +static int file_error(OSSL_STORE_LOADER_CTX *ctx); +static OSSL_STORE_INFO *file_load(OSSL_STORE_LOADER_CTX *ctx, + const UI_METHOD *ui_method, void *ui_data) +{ + OSSL_STORE_INFO *result = NULL; + int matchcount = -1; + + result = file_load_try_repeat(ctx, ui_method, ui_data); + if (result != NULL) + return result; if (file_error(ctx)) return NULL; @@ -587,130 +746,37 @@ static OSSL_STORE_INFO *file_load(OSSL_STORE_LOADER_CTX *ctx, char *pem_name = NULL; /* PEM record name */ char *pem_header = NULL; /* PEM record header */ unsigned char *data = NULL; /* DER encoded data */ - BUF_MEM *mem = NULL; long len = 0; /* DER encoded data length */ - int r = 0; - matchcount = -1; if (ctx->is_pem) { - r = PEM_read_bio(ctx->file, &pem_name, &pem_header, &data, &len); - if (r <= 0) { + if (!file_read_pem(ctx->file, &pem_name, &pem_header, &data, &len, + ui_method, ui_data)) { if (!file_eof(ctx)) ctx->errcnt++; - goto end; - } - - /* - * 10 is the number of characters in "Proc-Type:", which - * PEM_get_EVP_CIPHER_INFO() requires to be present. - * If the PEM header has less characters than that, it's - * not worth spending cycles on it. - */ - if (strlen(pem_header) > 10) { - EVP_CIPHER_INFO cipher; - struct pem_pass_data pass_data; - - if (!PEM_get_EVP_CIPHER_INFO(pem_header, &cipher) - || !file_fill_pem_pass_data(&pass_data, "PEM", ui_method, - ui_data) - || !PEM_do_header(&cipher, data, &len, file_get_pem_pass, - &pass_data)) { - ctx->errcnt++; - goto err; - } + goto err; } } else { - if ((len = asn1_d2i_read_bio(ctx->file, &mem)) < 0) { + if (!file_read_asn1(ctx->file, &data, &len)) { if (!file_eof(ctx)) ctx->errcnt++; goto err; } - - data = (unsigned char *)mem->data; - len = (long)mem->length; - } - - result = NULL; - - { - size_t i = 0; - void *handler_ctx = NULL; - const FILE_HANDLER **matching_handlers = - OPENSSL_zalloc(sizeof(*matching_handlers) - * OSSL_NELEM(file_handlers)); - - if (matching_handlers == NULL) { - OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD, ERR_R_MALLOC_FAILURE); - goto err; - } - - matchcount = 0; - for (i = 0; i < OSSL_NELEM(file_handlers); i++) { - const FILE_HANDLER *handler = file_handlers[i]; - void *tmp_handler_ctx = NULL; - OSSL_STORE_INFO *tmp_result = - handler->try_decode(pem_name, pem_header, data, len, - &tmp_handler_ctx, ui_method, ui_data); - - if (tmp_result == NULL) { - OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD, OSSL_STORE_R_IS_NOT_A); - ERR_add_error_data(1, handler->name); - } else { - if (matching_handlers) - matching_handlers[matchcount] = handler; - - if (handler_ctx) - handler->destroy_ctx(&handler_ctx); - handler_ctx = tmp_handler_ctx; - - if (++matchcount == 1) { - result = tmp_result; - tmp_result = NULL; - } else { - /* more than one match => ambiguous, kill any result */ - OSSL_STORE_INFO_free(result); - OSSL_STORE_INFO_free(tmp_result); - if (handler->destroy_ctx != NULL) - handler->destroy_ctx(&handler_ctx); - handler_ctx = NULL; - result = NULL; - } - } - } - - if (matchcount > 1) - OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD, - OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE); - if (matchcount == 0) - OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD, - OSSL_STORE_R_UNSUPPORTED_CONTENT_TYPE); - else if (matching_handlers[0]->repeatable) { - ctx->last_handler = matching_handlers[0]; - ctx->last_handler_ctx = handler_ctx; - mem = NULL; - data = NULL; - } - - OPENSSL_free(matching_handlers); } - if (result) - ERR_clear_error(); + matchcount = -1; + result = file_load_try_decode(ctx, pem_name, pem_header, data, len, + ui_method, ui_data, &matchcount); err: OPENSSL_free(pem_name); OPENSSL_free(pem_header); - if (mem == NULL) - OPENSSL_free(data); - else - BUF_MEM_free(mem); + OPENSSL_free(data); } while (matchcount == 0 && !file_eof(ctx) && !file_error(ctx)); /* We bail out on ambiguity */ if (matchcount > 1) return NULL; - end: return result; } diff --git a/crypto/store/store_err.c b/crypto/store/store_err.c index 0f3a6a142e..57627ebdec 100644 --- a/crypto/store/store_err.c +++ b/crypto/store/store_err.c @@ -17,6 +17,8 @@ static const ERR_STRING_DATA OSSL_STORE_str_functs[] = { {ERR_PACK(ERR_LIB_OSSL_STORE, OSSL_STORE_F_FILE_GET_PASS, 0), "file_get_pass"}, {ERR_PACK(ERR_LIB_OSSL_STORE, OSSL_STORE_F_FILE_LOAD, 0), "file_load"}, + {ERR_PACK(ERR_LIB_OSSL_STORE, OSSL_STORE_F_FILE_LOAD_TRY_DECODE, 0), + "file_load_try_decode"}, {ERR_PACK(ERR_LIB_OSSL_STORE, OSSL_STORE_F_FILE_OPEN, 0), "file_open"}, {ERR_PACK(ERR_LIB_OSSL_STORE, OSSL_STORE_F_OSSL_STORE_GET0_LOADER_INT, 0), "ossl_store_get0_loader_int"}, diff --git a/include/openssl/storeerr.h b/include/openssl/storeerr.h index 1d8695592b..c37e4170a9 100644 --- a/include/openssl/storeerr.h +++ b/include/openssl/storeerr.h @@ -24,6 +24,7 @@ int ERR_load_OSSL_STORE_strings(void); */ # define OSSL_STORE_F_FILE_GET_PASS 118 # define OSSL_STORE_F_FILE_LOAD 119 +# define OSSL_STORE_F_FILE_LOAD_TRY_DECODE 124 # define OSSL_STORE_F_FILE_OPEN 120 # define OSSL_STORE_F_OSSL_STORE_GET0_LOADER_INT 100 # define OSSL_STORE_F_OSSL_STORE_INFO_GET1_CERT 101 -- 2.34.1