DECODER: use property definitions instead of getting implementation parameters
[openssl.git] / crypto / encode_decode / decoder_lib.c
index 9c01dc894b37b9502bb614d1d4954e95af39c1e7..bf9b17f754aed9811891bd2e433d1893075a14ea 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
@@ -16,8 +16,8 @@
 #include <openssl/pkcs12err.h>
 #include <openssl/x509err.h>
 #include <openssl/trace.h>
-#include "internal/passphrase.h"
 #include "internal/bio.h"
+#include "internal/provider.h"
 #include "crypto/decoder.h"
 #include "encoder_local.h"
 #include "e_os.h"
@@ -32,6 +32,12 @@ struct decoder_process_data_st {
     size_t current_decoder_inst_index;
     /* For tracing, count recursion level */
     size_t recursion;
+
+    /*-
+     * Flags
+     */
+    unsigned int flag_next_level_called : 1;
+    unsigned int flag_construct_called : 1;
 };
 
 static int decoder_process(const OSSL_PARAM params[], void *arg);
@@ -42,6 +48,14 @@ int OSSL_DECODER_from_bio(OSSL_DECODER_CTX *ctx, BIO *in)
     int ok = 0;
     BIO *new_bio = NULL;
 
+    if (OSSL_DECODER_CTX_get_num_decoders(ctx) == 0) {
+        ERR_raise_data(ERR_LIB_OSSL_DECODER, OSSL_DECODER_R_DECODER_NOT_FOUND,
+                       "No decoders were found. For standard decoders you need "
+                       "at least one of the default or base providers "
+                       "available. Did you forget to load them?");
+        return 0;
+    }
+
     if (BIO_tell(in) < 0) {
         new_bio = BIO_new(BIO_f_readbuffer());
         if (new_bio == NULL)
@@ -57,6 +71,31 @@ int OSSL_DECODER_from_bio(OSSL_DECODER_CTX *ctx, BIO *in)
 
     ok = decoder_process(NULL, &data);
 
+    if (!data.flag_construct_called) {
+        const char *spaces
+            = ctx->start_input_type != NULL && ctx->input_structure != NULL
+            ? " " : "";
+        const char *input_type_label
+            = ctx->start_input_type != NULL ? "Input type: " : "";
+        const char *input_structure_label
+            = ctx->input_structure != NULL ? "Input structure: " : "";
+        const char *comma
+            = ctx->start_input_type != NULL && ctx->input_structure != NULL
+            ? ", " : "";
+        const char *input_type
+            = ctx->start_input_type != NULL ? ctx->start_input_type : "";
+        const char *input_structure
+            = ctx->input_structure != NULL ? ctx->input_structure : "";
+
+        if (BIO_eof(in) == 0 || ERR_peek_error() == 0)
+            /* Prevent spurious decoding error */
+            ERR_raise_data(ERR_LIB_OSSL_DECODER, ERR_R_UNSUPPORTED,
+                           "No supported data to decode. %s%s%s%s%s%s",
+                           spaces, input_type_label, input_type, comma,
+                           input_structure_label, input_structure);
+        ok = 0;
+    }
+
     /* Clear any internally cached passphrase */
     (void)ossl_pw_clear_passphrase_cache(&ctx->pwdata);
 
@@ -154,8 +193,8 @@ int OSSL_DECODER_CTX_set_input_structure(OSSL_DECODER_CTX *ctx,
     }
 
     /*
-     * NULL is a valid starting input type, and means that the caller leaves
-     * it to code to discover what the starting input type is.
+     * NULL is a valid starting input structure, and means that the caller
+     * leaves it to code to discover what the starting input structure is.
      */
     ctx->input_structure = input_structure;
     return 1;
@@ -165,19 +204,16 @@ OSSL_DECODER_INSTANCE *ossl_decoder_instance_new(OSSL_DECODER *decoder,
                                                  void *decoderctx)
 {
     OSSL_DECODER_INSTANCE *decoder_inst = NULL;
-    OSSL_PARAM params[3];
+    const OSSL_PROVIDER *prov;
+    OSSL_LIB_CTX *libctx;
+    const OSSL_PROPERTY_LIST *props;
+    const OSSL_PROPERTY_DEFINITION *prop;
 
     if (!ossl_assert(decoder != NULL)) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
     }
 
-    if (decoder->get_params == NULL) {
-        ERR_raise(ERR_LIB_OSSL_DECODER,
-                  OSSL_DECODER_R_MISSING_GET_PARAMS);
-        return 0;
-    }
-
     if ((decoder_inst = OPENSSL_zalloc(sizeof(*decoder_inst))) == NULL) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_MALLOC_FAILURE);
         return 0;
@@ -187,22 +223,35 @@ OSSL_DECODER_INSTANCE *ossl_decoder_instance_new(OSSL_DECODER *decoder,
         goto err;
     }
 
-    /* Cache the input type for this decoder */
-    params[0] =
-        OSSL_PARAM_construct_utf8_ptr(OSSL_DECODER_PARAM_INPUT_TYPE,
-                                      (char **)&decoder_inst->input_type, 0);
-    params[1] =
-        OSSL_PARAM_construct_utf8_ptr(OSSL_DECODER_PARAM_INPUT_STRUCTURE,
-                                      (char **)&decoder_inst->input_structure,
-                                      0);
-    params[2] = OSSL_PARAM_construct_end();
-
-    if (!decoder->get_params(params)
-        || !OSSL_PARAM_modified(&params[0]))
+    prov = OSSL_DECODER_get0_provider(decoder);
+    libctx = ossl_provider_libctx(prov);
+    props = ossl_decoder_parsed_properties(decoder);
+    if (props == NULL) {
+        ERR_raise_data(ERR_LIB_OSSL_DECODER, ERR_R_INVALID_PROPERTY_DEFINITION,
+                       "there are no property definitions with decoder %s",
+                       OSSL_DECODER_get0_name(decoder));
         goto err;
+    }
+
+    /* The "input" property is mandatory */
+    prop = ossl_property_find_property(props, libctx, "input");
+    decoder_inst->input_type = ossl_property_get_string_value(libctx, prop);
+    if (decoder_inst->input_type == NULL) {
+        ERR_raise_data(ERR_LIB_OSSL_DECODER, ERR_R_INVALID_PROPERTY_DEFINITION,
+                       "the mandatory 'input' property is missing "
+                       "for decoder %s (properties: %s)",
+                       OSSL_DECODER_get0_name(decoder),
+                       OSSL_DECODER_get0_properties(decoder));
+        goto err;
+    }
+
+    /* The "structure" property is optional */
+    prop = ossl_property_find_property(props, libctx, "structure");
+    if (prop != NULL) {
+        decoder_inst->input_structure
+            = ossl_property_get_string_value(libctx, prop);
+    }
 
-    decoder_inst->flag_input_structure_was_set =
-        OSSL_PARAM_modified(&params[1]);
     decoder_inst->decoder = decoder;
     decoder_inst->decoderctx = decoderctx;
     return decoder_inst;
@@ -261,7 +310,7 @@ int OSSL_DECODER_CTX_add_decoder(OSSL_DECODER_CTX *ctx, OSSL_DECODER *decoder)
         return 0;
     }
 
-    prov = OSSL_DECODER_provider(decoder);
+    prov = OSSL_DECODER_get0_provider(decoder);
     provctx = OSSL_PROVIDER_get0_provider_ctx(prov);
 
     if ((decoderctx = decoder->newctx(provctx)) == NULL
@@ -525,12 +574,18 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
     BIO *bio = data->bio;
     long loc;
     size_t i;
-    int err, lib, reason, ok = 0;
+    int ok = 0;
     /* For recursions */
     struct decoder_process_data_st new_data;
     const char *data_type = NULL;
     const char *data_structure = NULL;
 
+    /*
+     * This is an indicator up the call stack that something was indeed
+     * decoded, leading to a recursive call of this function.
+     */
+    data->flag_next_level_called = 1;
+
     memset(&new_data, 0, sizeof(new_data));
     new_data.ctx = data->ctx;
     new_data.recursion = data->recursion + 1;
@@ -562,10 +617,14 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
                                            data->current_decoder_inst_index);
         decoder = OSSL_DECODER_INSTANCE_get_decoder(decoder_inst);
 
-        if (ctx->construct != NULL
-            && ctx->construct(decoder_inst, params, ctx->construct_data)) {
-            ok = 1;
-            goto end;
+        data->flag_construct_called = 0;
+        if (ctx->construct != NULL) {
+            int rv = ctx->construct(decoder_inst, params, ctx->construct_data);
+
+            data->flag_construct_called = 1;
+            ok = (rv > 0);
+            if (ok)
+                goto end;
         }
 
         /* The constructor didn't return success */
@@ -608,7 +667,7 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
          * too special knowledge.
          */
         trace_data_structure = data_structure;
-        if (data_type != NULL
+        if (data_type != NULL && data_structure != NULL
             && strcasecmp(data_structure, "type-specific") == 0)
             data_structure = NULL;
 
@@ -746,6 +805,12 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
                        (void *)new_decoder_inst);
         } OSSL_TRACE_END(DECODER);
 
+        /*
+         * We only care about errors reported from decoder implementations
+         * if it returns false (i.e. there was a fatal error).
+         */
+        ERR_set_mark();
+
         new_data.current_decoder_inst_index = i;
         ok = new_decoder->decode(new_decoderctx, cbio,
                                  new_data.ctx->selection,
@@ -755,31 +820,29 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
 
         OSSL_TRACE_BEGIN(DECODER) {
             BIO_printf(trc_out,
-                       "(ctx %p) %s [%u] Running decoder instance %p => %d\n",
+                       "(ctx %p) %s [%u] Running decoder instance %p => %d"
+                       " (recursed further: %s, construct called: %s)\n",
                        (void *)new_data.ctx, LEVEL, (unsigned int)i,
-                       (void *)new_decoder_inst, ok);
+                       (void *)new_decoder_inst, ok,
+                       new_data.flag_next_level_called ? "yes" : "no",
+                       new_data.flag_construct_called ? "yes" : "no");
         } OSSL_TRACE_END(DECODER);
 
-        if (ok)
+        data->flag_construct_called = new_data.flag_construct_called;
+
+        /* Break on error or if we tried to construct an object already */
+        if (!ok || data->flag_construct_called) {
+            ERR_clear_last_mark();
             break;
+        }
+        ERR_pop_to_mark();
 
         /*
-         * These errors are assumed to come from ossl_store_handle_load_result()
-         * in crypto/store/store_result.c.  They are currently considered fatal
-         * errors, so we preserve them in the error queue and stop.
+         * Break if the decoder implementation that we called recursed, since
+         * that indicates that it successfully decoded something.
          */
-        err = ERR_peek_last_error();
-        lib = ERR_GET_LIB(err);
-        reason = ERR_GET_REASON(err);
-        if ((lib == ERR_LIB_EVP
-             && reason == EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM)
-#ifndef OPENSSL_NO_EC
-            || (lib == ERR_LIB_EC && reason == EC_R_UNKNOWN_GROUP)
-#endif
-            || (lib == ERR_LIB_X509 && reason == X509_R_UNSUPPORTED_ALGORITHM)
-            || (lib == ERR_LIB_PKCS12
-                && reason == PKCS12_R_PKCS12_CIPHERFINAL_ERROR))
-            goto end;
+        if (new_data.flag_next_level_called)
+            break;
     }
 
  end: