Make the -inform option to be respected if possible
[openssl.git] / providers / implementations / storemgmt / file_store.c
index 5b0616e26a2315c2400356d3c44b4dce34dab15f..b9bb3b36c032b035511c7db2a6ceeda02f614a61 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
@@ -8,29 +8,26 @@
  */
 
 #include "e_os.h"                /* To get strncasecmp() on Windows */
+
 #include <string.h>
 #include <sys/stat.h>
-#include <ctype.h>
+#include <ctype.h>  /* isdigit */
 #include <assert.h>
 
-#include <openssl/core.h>
 #include <openssl/core_dispatch.h>
 #include <openssl/core_names.h>
 #include <openssl/core_object.h>
-#include <openssl/crypto.h>
 #include <openssl/bio.h>
 #include <openssl/err.h>
-#include <openssl/buffer.h>
 #include <openssl/params.h>
 #include <openssl/decoder.h>
+#include <openssl/proverr.h>
 #include <openssl/store.h>       /* The OSSL_STORE_INFO type numbers */
+#include "internal/cryptlib.h"
 #include "internal/o_dir.h"
-#include "internal/pem.h"        /* For PVK and "blob" PEM headers */
 #include "crypto/decoder.h"
 #include "prov/implementations.h"
 #include "prov/bio.h"
-#include "prov/provider_ctx.h"
-#include "prov/providercommonerr.h"
 #include "file_store_local.h"
 
 DEFINE_STACK_OF(OSSL_STORE_INFO)
@@ -58,7 +55,7 @@ static OSSL_FUNC_store_close_fn file_close;
  * internal OpenSSL functions, thereby bypassing the need for a surrounding
  * provider.  This is ok, since this is a local decoder, not meant for
  * public consumption.  It also uses the libcrypto internal decoder
- * setup function ossl_decoder_ctx_setup_for_EVP_PKEY(), to allow the
+ * setup function ossl_decoder_ctx_setup_for_pkey(), to allow the
  * last resort decoder to be added first (and thereby be executed last).
  * Finally, it sets up its own construct and cleanup functions.
  *
@@ -73,10 +70,6 @@ struct file_ctx_st {
         IS_DIR                   /* Pass directory entry names */
     } type;
 
-    /* Flag bits */
-    unsigned int flag_attached:1;
-    unsigned int flag_buffered:1;
-
     union {
         /* Used with |IS_FILE| */
         struct {
@@ -156,15 +149,11 @@ static OSSL_DECODER_CLEANUP file_load_cleanup;
  *
  */
 static struct file_ctx_st *file_open_stream(BIO *source, const char *uri,
-                                            const char *input_type,
                                             void *provctx)
 {
     struct file_ctx_st *ctx;
 
-    if ((ctx = new_file_ctx(IS_FILE, uri, provctx)) == NULL
-        || (input_type != NULL
-            && (ctx->_.file.input_type =
-                OPENSSL_strdup(input_type)) == NULL)) {
+    if ((ctx = new_file_ctx(IS_FILE, uri, provctx)) == NULL) {
         ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
         goto err;
     }
@@ -292,145 +281,24 @@ static void *file_open(void *provctx, const char *uri)
     if (S_ISDIR(st.st_mode))
         ctx = file_open_dir(path, uri, provctx);
     else if ((bio = BIO_new_file(path, "rb")) == NULL
-             || (ctx = file_open_stream(bio, uri, NULL, provctx)) == NULL)
+             || (ctx = file_open_stream(bio, uri, provctx)) == NULL)
         BIO_free_all(bio);
 
     return ctx;
 }
 
-/*
- * Attached input streams must be treated very very carefully to avoid
- * nasty surprises.
- *
- * This implementation tries to support input streams that can't be reset,
- * such as standard input.  However, OSSL_DECODER assumes resettable streams,
- * and because the PEM decoder may read quite a bit of the input file to skip
- * past any non-PEM text that precedes the PEM block, we may need to detect
- * if the input stream is a PEM file early.
- *
- * If the input stream supports BIO_tell(), we assume that it also supports
- * BIO_seek(), making it a resettable stream and therefore safe to fully
- * unleash OSSL_DECODER.
- *
- * If the input stream doesn't support BIO_tell(), we must assume that we
- * have a non-resettable stream, and must tread carefully.  We do so by
- * trying to detect if the input is PEM, MSBLOB or PVK, and if not, we
- * assume that it's DER.
- *
- * To detect if an input stream is PEM, MSBLOB or PVK, we use the buffer BIO
- * filter, which allows us a 4KiB resettable read-ahead.  We *hope* that 4KiB
- * will be enough to find the start of the PEM block.
- *
- * It should be possible to use this same technique to detect other file
- * types as well.
- *
- * An alternative technique would be to have an endlessly caching BIO filter.
- * That would take away the need for all the detection here, and simply leave
- * it for OSSL_DECODER to find out on its own while supporting its demand for
- * resettable input streams.
- * That's a possible future development.
- */
-
-# define INPUT_TYPE_ANY         NULL
-# define INPUT_TYPE_DER         "DER"
-# define INPUT_TYPE_PEM         "PEM"
-# define INPUT_TYPE_MSBLOB      "MSBLOB"
-# define INPUT_TYPE_PVK         "PVK"
-
 void *file_attach(void *provctx, OSSL_CORE_BIO *cin)
 {
-    BIO *new_bio = bio_new_from_core_bio(provctx, cin);
-    BIO *new_bio_tmp = NULL;
-    BIO *buff = NULL;
-    char peekbuf[4096] = { 0, };
-    int loc;
-    const char *input_type = NULL;
-    unsigned int flag_attached = 1;
-    unsigned int flag_buffered = 0;
-    struct file_ctx_st *ctx = NULL;
+    struct file_ctx_st *ctx;
+    BIO *new_bio = ossl_bio_new_from_core_bio(provctx, cin);
 
     if (new_bio == NULL)
-        return 0;
-
-    /* Try to get the current position */
-    loc = BIO_tell(new_bio);
-
-    if ((buff = BIO_new(BIO_f_buffer())) == NULL
-        || (new_bio_tmp = BIO_push(buff, new_bio)) == NULL)
-        goto err;
-
-    /* Assumption, if we can't detect PEM */
-    input_type = INPUT_TYPE_DER;
-    flag_buffered = 1;
-    new_bio = new_bio_tmp;
-
-    if (BIO_buffer_peek(new_bio, peekbuf, sizeof(peekbuf) - 1) > 0) {
-#ifndef OPENSSL_NO_DSA
-        const unsigned char *p = NULL;
-        unsigned int magic = 0, bitlen = 0;
-        int isdss = 0, ispub = -1;
-# ifndef OPENSSL_NO_RC4
-        unsigned int saltlen = 0, keylen = 0;
-# endif
-#endif
-
-        peekbuf[sizeof(peekbuf) - 1] = '\0';
-        if (strstr(peekbuf, "-----BEGIN ") != NULL)
-            input_type = INPUT_TYPE_PEM;
-#ifndef OPENSSL_NO_DSA
-        else if (p = (unsigned char *)peekbuf,
-                 ossl_do_blob_header(&p, sizeof(peekbuf), &magic, &bitlen,
-                                     &isdss, &ispub))
-            input_type = INPUT_TYPE_MSBLOB;
-# ifndef OPENSSL_NO_RC4
-        else if (p = (unsigned char *)peekbuf,
-                 ossl_do_PVK_header(&p, sizeof(peekbuf), 0, &saltlen, &keylen))
-            input_type = INPUT_TYPE_PVK;
-# endif
-#endif
-    }
-
-    /*
-     * After peeking, we know that the underlying source BIO has moved ahead
-     * from its earlier position and that if it supports BIO_tell(), that
-     * should be a number that differs from |loc|.  Otherwise, we will get
-     * the same value, which may one of:
-     *
-     * -   zero (the source BIO doesn't support BIO_tell() / BIO_seek() /
-     *     BIO_reset())
-     * -   -1 (the underlying operating system / C library routines do not
-     *     support BIO_tell() / BIO_seek() / BIO_reset())
-     *
-     * If it turns out that the source BIO does support BIO_tell(), we pop
-     * the buffer BIO filter and mark this input as |INPUT_TYPE_ANY|, which
-     * fully unleashes OSSL_DECODER to do its thing.
-     */
-    if (BIO_tell(new_bio) != loc) {
-        /* In this case, anything goes */
-        input_type = INPUT_TYPE_ANY;
-
-        /* Restore the source BIO like it was when entering this function */
-        new_bio = BIO_pop(buff);
-        BIO_free(buff);
-        (void)BIO_seek(new_bio, loc);
-
-        flag_buffered = 0;
-    }
-
-    if ((ctx = file_open_stream(new_bio, NULL, input_type, provctx)) == NULL)
-        goto err;
-
-    ctx->flag_attached = flag_attached;
-    ctx->flag_buffered = flag_buffered;
+        return NULL;
 
+    ctx = file_open_stream(new_bio, NULL, provctx);
+    if (ctx == NULL)
+        BIO_free(new_bio);
     return ctx;
- err:
-    if (flag_buffered) {
-        new_bio = BIO_pop(buff);
-        BIO_free(buff);
-    }
-    BIO_free(new_bio);           /* Removes the provider BIO filter */
-    return NULL;
 }
 
 /*-
@@ -444,6 +312,7 @@ static const OSSL_PARAM *file_settable_ctx_params(void *provctx)
         OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_PROPERTIES, NULL, 0),
         OSSL_PARAM_int(OSSL_STORE_PARAM_EXPECT, NULL),
         OSSL_PARAM_octet_string(OSSL_STORE_PARAM_SUBJECT, NULL, 0),
+        OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_INPUT_TYPE, NULL, 0),
         OSSL_PARAM_END
     };
     return known_settable_ctx_params;
@@ -454,12 +323,25 @@ static int file_set_ctx_params(void *loaderctx, const OSSL_PARAM params[])
     struct file_ctx_st *ctx = loaderctx;
     const OSSL_PARAM *p;
 
-    p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_PROPERTIES);
-    if (p != NULL) {
-        OPENSSL_free(ctx->_.file.propq);
-        ctx->_.file.propq = NULL;
-        if (!OSSL_PARAM_get_utf8_string(p, &ctx->_.file.propq, 0))
-            return 0;
+    if (params == NULL)
+        return 1;
+
+    if (ctx->type != IS_DIR) {
+        /* these parameters are ignored for directories */
+        p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_PROPERTIES);
+        if (p != NULL) {
+            OPENSSL_free(ctx->_.file.propq);
+            ctx->_.file.propq = NULL;
+            if (!OSSL_PARAM_get_utf8_string(p, &ctx->_.file.propq, 0))
+                return 0;
+        }
+        p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_INPUT_TYPE);
+        if (p != NULL) {
+            OPENSSL_free(ctx->_.file.input_type);
+            ctx->_.file.input_type = NULL;
+            if (!OSSL_PARAM_get_utf8_string(p, &ctx->_.file.input_type, 0))
+                return 0;
+        }
     }
     p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_EXPECT);
     if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->expected_type))
@@ -470,6 +352,7 @@ static int file_set_ctx_params(void *loaderctx, const OSSL_PARAM params[])
         size_t der_len = 0;
         X509_NAME *x509_name;
         unsigned long hash;
+        int ok;
 
         if (ctx->type != IS_DIR) {
             ERR_raise(ERR_LIB_PROV,
@@ -480,10 +363,14 @@ static int file_set_ctx_params(void *loaderctx, const OSSL_PARAM params[])
         if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&der, &der_len)
             || (x509_name = d2i_X509_NAME(NULL, &der, der_len)) == NULL)
             return 0;
-        hash = X509_NAME_hash(x509_name);
+        hash = X509_NAME_hash_ex(x509_name,
+                                 ossl_prov_ctx_get0_libctx(ctx->provctx), NULL,
+                                 &ok);
         BIO_snprintf(ctx->_.dir.search_name, sizeof(ctx->_.dir.search_name),
                      "%08lx", hash);
         X509_NAME_free(x509_name);
+        if (ok == 0)
+            return 0;
     }
     return 1;
 }
@@ -529,13 +416,13 @@ void file_load_cleanup(void *construct_data)
 
 static int file_setup_decoders(struct file_ctx_st *ctx)
 {
-    EVP_PKEY *dummy; /* for OSSL_DECODER_CTX_new_by_EVP_PKEY() */
-    OPENSSL_CTX *libctx = ossl_prov_ctx_get0_library_context(ctx->provctx);
+    EVP_PKEY *dummy; /* for ossl_decoder_ctx_setup_for_pkey() */
+    OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(ctx->provctx);
     OSSL_DECODER *to_obj = NULL; /* Last resort decoder */
     OSSL_DECODER_INSTANCE *to_obj_inst = NULL;
     OSSL_DECODER_CLEANUP *old_cleanup = NULL;
     void *old_construct_data = NULL;
-    int ok = 0;
+    int ok = 0, expect_evp_pkey = 0;
 
     /* Setup for this session, so only if not already done */
     if (ctx->_.file.decoderctx == NULL) {
@@ -544,6 +431,11 @@ static int file_setup_decoders(struct file_ctx_st *ctx)
             goto err;
         }
 
+        expect_evp_pkey = (ctx->expected_type == 0
+                           || ctx->expected_type == OSSL_STORE_INFO_PARAMS
+                           || ctx->expected_type == OSSL_STORE_INFO_PUBKEY
+                           || ctx->expected_type == OSSL_STORE_INFO_PKEY);
+
         /* Make sure the input type is set */
         if (!OSSL_DECODER_CTX_set_input_type(ctx->_.file.decoderctx,
                                              ctx->_.file.input_type)) {
@@ -557,7 +449,8 @@ static int file_setup_decoders(struct file_ctx_st *ctx)
          * The decoder doesn't need any identification or to be attached to
          * any provider, since it's only used locally.
          */
-        to_obj = ossl_decoder_from_dispatch(0, &der_to_obj_algorithm, NULL);
+        to_obj = ossl_decoder_from_algorithm(0, &ossl_der_to_obj_algorithm,
+                                             NULL);
         if (to_obj == NULL)
             goto err;
         to_obj_inst = ossl_decoder_instance_new(to_obj, ctx->provctx);
@@ -581,8 +474,10 @@ static int file_setup_decoders(struct file_ctx_st *ctx)
          * Since we're setting up our own constructor, we don't need to care
          * more than that...
          */
-        if (!ossl_decoder_ctx_setup_for_EVP_PKEY(ctx->_.file.decoderctx, &dummy,
-                                                 libctx, ctx->_.file.propq)
+        if ((expect_evp_pkey
+             && !ossl_decoder_ctx_setup_for_pkey(ctx->_.file.decoderctx,
+                                                 &dummy, NULL,
+                                                 libctx, ctx->_.file.propq))
             || !OSSL_DECODER_CTX_add_extra(ctx->_.file.decoderctx,
                                            libctx, ctx->_.file.propq)) {
             ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
@@ -645,27 +540,13 @@ static int file_load_file(struct file_ctx_st *ctx,
  *  --------------------------------------
  */
 
-static int ends_with_dirsep(const char *uri)
-{
-    if (*uri != '\0')
-        uri += strlen(uri) - 1;
-#if defined(__VMS)
-    if (*uri == ']' || *uri == '>' || *uri == ':')
-        return 1;
-#elif defined(_WIN32)
-    if (*uri == '\\')
-        return 1;
-#endif
-    return *uri == '/';
-}
-
 static char *file_name_to_uri(struct file_ctx_st *ctx, const char *name)
 {
     char *data = NULL;
 
     assert(name != NULL);
     {
-        const char *pathsep = ends_with_dirsep(ctx->uri) ? "" : "/";
+        const char *pathsep = ossl_ends_with_dirsep(ctx->uri) ? "" : "/";
         long calculated_length = strlen(ctx->uri) + strlen(pathsep)
             + strlen(name) + 1 /* \0 */;
 
@@ -860,30 +741,11 @@ static int file_close_dir(struct file_ctx_st *ctx)
 
 static int file_close_stream(struct file_ctx_st *ctx)
 {
-    if (ctx->flag_buffered) {
-        /*
-         * file_attach() pushed a BIO_f_buffer() on top of the regular BIO.
-         * Drop it.
-         */
-        BIO *buff = ctx->_.file.file;
-
-        /* Detach buff */
-        ctx->_.file.file = BIO_pop(ctx->_.file.file);
-
-        BIO_free(buff);
-    }
-
     /*
-     * If it was attached, we only free the top, as that's the provider BIO
-     * filter.  Otherwise, it was entirely allocated by this implementation,
-     * and can safely be completely freed.
+     * This frees either the provider BIO filter (for file_attach()) OR
+     * the allocated file BIO (for file_open()).
      */
-    if (ctx->flag_attached)
-        BIO_free(ctx->_.file.file);
-    else
-        BIO_free_all(ctx->_.file.file);
-
-    /* To avoid double free */
+    BIO_free(ctx->_.file.file);
     ctx->_.file.file = NULL;
 
     free_file_ctx(ctx);