Commit 1aabc244 authored by Richard Levitte's avatar Richard Levitte
Browse files

STORE 'file' scheme loader: refactor file_load to support decoding restart

parent 50ecedda
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+177 −111
Original line number Diff line number Diff line
@@ -559,79 +559,19 @@ 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;
    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;

        ctx->last_handler->destroy_ctx(&ctx->last_handler_ctx);
        ctx->last_handler_ctx = NULL;
        ctx->last_handler = NULL;
    }

    if (file_error(ctx))
        return NULL;

    do {
        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_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;
                }
            }
        } else {
            if ((len = asn1_d2i_read_bio(ctx->file, &mem)) < 0) {
                if (!file_eof(ctx))
                    ctx->errcnt++;
                goto err;
            }

            data = (unsigned char *)mem->data;
            len = (long)mem->length;
        }

        result = NULL;
    BUF_MEM *new_mem = NULL;
    char *new_pem_name = NULL;
    int t = 0;

 again:
    {
        size_t i = 0;
        void *handler_ctx = NULL;
@@ -640,11 +580,12 @@ static OSSL_STORE_INFO *file_load(OSSL_STORE_LOADER_CTX *ctx,
                           * OSSL_NELEM(file_handlers));

        if (matching_handlers == NULL) {
                OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD, ERR_R_MALLOC_FAILURE);
            OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD_TRY_DECODE,
                          ERR_R_MALLOC_FAILURE);
            goto err;
        }

            matchcount = 0;
        *matchcount = 0;
        for (i = 0; i < OSSL_NELEM(file_handlers); i++) {
            const FILE_HANDLER *handler = file_handlers[i];
            void *tmp_handler_ctx = NULL;
@@ -653,17 +594,18 @@ static OSSL_STORE_INFO *file_load(OSSL_STORE_LOADER_CTX *ctx,
                                    &tmp_handler_ctx, ui_method, ui_data);

            if (tmp_result == NULL) {
                    OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD, OSSL_STORE_R_IS_NOT_A);
                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;
                    matching_handlers[*matchcount] = handler;

                if (handler_ctx)
                    handler->destroy_ctx(&handler_ctx);
                handler_ctx = tmp_handler_ctx;

                    if (++matchcount == 1) {
                if (++*matchcount == 1) {
                    result = tmp_result;
                    tmp_result = NULL;
                } else {
@@ -678,39 +620,163 @@ static OSSL_STORE_INFO *file_load(OSSL_STORE_LOADER_CTX *ctx,
            }
        }

            if (matchcount > 1)
                OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD,
        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,
        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;
                mem = NULL;
                data = NULL;
            }
        }

        OPENSSL_free(matching_handlers);
    }

        if (result)
 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;

    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) {
            ctx->last_handler->destroy_ctx(&ctx->last_handler_ctx);
            ctx->last_handler_ctx = NULL;
            ctx->last_handler = NULL;
        }
    }
    return result;
}

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;

    do {
        char *pem_name = NULL;      /* PEM record name */
        char *pem_header = NULL;    /* PEM record header */
        unsigned char *data = NULL; /* DER encoded data */
        long len = 0;               /* DER encoded data length */

        if (ctx->is_pem) {
            if (!file_read_pem(ctx->file, &pem_name, &pem_header, &data, &len,
                               ui_method, ui_data)) {
                if (!file_eof(ctx))
                    ctx->errcnt++;
                goto err;
            }
        } else {
            if (!file_read_asn1(ctx->file, &data, &len)) {
                if (!file_eof(ctx))
                    ctx->errcnt++;
                goto err;
            }
        }

        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);
    } while (matchcount == 0 && !file_eof(ctx) && !file_error(ctx));

    /* We bail out on ambiguity */
    if (matchcount > 1)
        return NULL;

 end:
    return result;
}

+2 −0
Original line number Diff line number Diff line
@@ -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"},
+1 −0
Original line number Diff line number Diff line
@@ -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