OSSL_STORE: Make the 'file' scheme loader handle MSBLOB and PVK files
[openssl.git] / crypto / store / loader_file.c
index b87e1657059e6a44f88363420728fc2dc1b1c749..9f6158ff793f4dfe7e87f4888ce3a4723bbc26d3 100644 (file)
@@ -18,6 +18,7 @@
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/pem.h>
+#include "internal/pem_int.h"
 #include <openssl/pkcs12.h>      /* For the PKCS8 stuff o.O */
 #include <openssl/rsa.h>         /* For d2i_RSAPrivateKey */
 #include <openssl/safestack.h>
@@ -48,7 +49,8 @@ DEFINE_STACK_OF(X509)
  */
 
 static char *file_get_pass(const UI_METHOD *ui_method, char *pass,
-                           size_t maxsize, const char *prompt_info, void *data)
+                           size_t maxsize, const char *desc, const char *info,
+                           void *data)
 {
     UI *ui = UI_new();
     char *prompt = NULL;
@@ -62,8 +64,7 @@ static char *file_get_pass(const UI_METHOD *ui_method, char *pass,
         UI_set_method(ui, ui_method);
     UI_add_user_data(ui, data);
 
-    if ((prompt = UI_construct_prompt(ui, "pass phrase",
-                                      prompt_info)) == NULL) {
+    if ((prompt = UI_construct_prompt(ui, desc, info)) == NULL) {
         OSSL_STOREerr(OSSL_STORE_F_FILE_GET_PASS, ERR_R_MALLOC_FAILURE);
         pass = NULL;
     } else if (!UI_add_input_string(ui, prompt, UI_INPUT_FLAG_DEFAULT_PWD,
@@ -94,18 +95,20 @@ static char *file_get_pass(const UI_METHOD *ui_method, char *pass,
 struct pem_pass_data {
     const UI_METHOD *ui_method;
     void *data;
+    const char *prompt_desc;
     const char *prompt_info;
 };
 
 static int file_fill_pem_pass_data(struct pem_pass_data *pass_data,
-                                   const char *prompt_info,
+                                   const char *desc, const char *info,
                                    const UI_METHOD *ui_method, void *ui_data)
 {
     if (pass_data == NULL)
         return 0;
     pass_data->ui_method = ui_method;
     pass_data->data = ui_data;
-    pass_data->prompt_info = prompt_info;
+    pass_data->prompt_desc = desc;
+    pass_data->prompt_info = info;
     return 1;
 }
 
@@ -114,7 +117,8 @@ static int file_get_pem_pass(char *buf, int num, int w, void *data)
 {
     struct pem_pass_data *pass_data = data;
     char *pass = file_get_pass(pass_data->ui_method, buf, num,
-                               pass_data->prompt_info, pass_data->data);
+                               pass_data->prompt_desc, pass_data->prompt_info,
+                               pass_data->data);
 
     return pass == NULL ? 0 : strlen(pass);
 }
@@ -168,7 +172,7 @@ typedef OSSL_STORE_INFO *(*file_try_decode_fn)(const char *pem_name,
                                                size_t len, void **handler_ctx,
                                                int *matchcount,
                                                const UI_METHOD *ui_method,
-                                               void *ui_data,
+                                               void *ui_data, const char *uri,
                                                OPENSSL_CTX *libctx,
                                                const char *propq);
 /*
@@ -205,7 +209,8 @@ static OSSL_STORE_INFO *try_decode_PKCS12(const char *pem_name,
                                           size_t len, void **pctx,
                                           int *matchcount,
                                           const UI_METHOD *ui_method,
-                                          void *ui_data, OPENSSL_CTX *libctx,
+                                          void *ui_data, const char *uri,
+                                          OPENSSL_CTX *libctx,
                                           const char *propq)
 {
     OSSL_STORE_INFO *store_info = NULL;
@@ -234,7 +239,7 @@ static OSSL_STORE_INFO *try_decode_PKCS12(const char *pem_name,
                 pass = "";
             } else {
                 if ((pass = file_get_pass(ui_method, tpass, PEM_BUFSIZE,
-                                          "PKCS12 import password",
+                                          "PKCS12 import pass phrase", uri,
                                           ui_data)) == NULL) {
                     OSSL_STOREerr(OSSL_STORE_F_TRY_DECODE_PKCS12,
                                   OSSL_STORE_R_PASSPHRASE_CALLBACK_ERROR);
@@ -335,6 +340,7 @@ static OSSL_STORE_INFO *try_decode_PKCS8Encrypted(const char *pem_name,
                                                   int *matchcount,
                                                   const UI_METHOD *ui_method,
                                                   void *ui_data,
+                                                  const char *uri,
                                                   OPENSSL_CTX *libctx,
                                                   const char *propq)
 {
@@ -366,7 +372,8 @@ static OSSL_STORE_INFO *try_decode_PKCS8Encrypted(const char *pem_name,
     }
 
     if ((pass = file_get_pass(ui_method, kbuf, PEM_BUFSIZE,
-                              "PKCS8 decrypt password", ui_data)) == NULL) {
+                              "PKCS8 decrypt pass phrase", uri,
+                              ui_data)) == NULL) {
         OSSL_STOREerr(OSSL_STORE_F_TRY_DECODE_PKCS8ENCRYPTED,
                       OSSL_STORE_R_BAD_PASSWORD_READ);
         goto nop8;
@@ -412,7 +419,8 @@ static OSSL_STORE_INFO *try_decode_PrivateKey(const char *pem_name,
                                               size_t len, void **pctx,
                                               int *matchcount,
                                               const UI_METHOD *ui_method,
-                                              void *ui_data, OPENSSL_CTX *libctx,
+                                              void *ui_data, const char *uri,
+                                              OPENSSL_CTX *libctx,
                                               const char *propq)
 {
     OSSL_STORE_INFO *store_info = NULL;
@@ -491,7 +499,8 @@ static OSSL_STORE_INFO *try_decode_PUBKEY(const char *pem_name,
                                           size_t len, void **pctx,
                                           int *matchcount,
                                           const UI_METHOD *ui_method,
-                                          void *ui_data, OPENSSL_CTX *libctx,
+                                          void *ui_data, const char *uri,
+                                          OPENSSL_CTX *libctx,
                                           const char *propq)
 {
     OSSL_STORE_INFO *store_info = NULL;
@@ -526,7 +535,8 @@ static OSSL_STORE_INFO *try_decode_params(const char *pem_name,
                                           size_t len, void **pctx,
                                           int *matchcount,
                                           const UI_METHOD *ui_method,
-                                          void *ui_data, OPENSSL_CTX *libctx,
+                                          void *ui_data, const char *uri,
+                                          OPENSSL_CTX *libctx,
                                           const char *propq)
 {
     OSSL_STORE_INFO *store_info = NULL;
@@ -611,6 +621,7 @@ static OSSL_STORE_INFO *try_decode_X509Certificate(const char *pem_name,
                                                    int *matchcount,
                                                    const UI_METHOD *ui_method,
                                                    void *ui_data,
+                                                   const char *uri,
                                                    OPENSSL_CTX *libctx,
                                                    const char *propq)
 {
@@ -662,7 +673,8 @@ static OSSL_STORE_INFO *try_decode_X509CRL(const char *pem_name,
                                            size_t len, void **pctx,
                                            int *matchcount,
                                            const UI_METHOD *ui_method,
-                                           void *ui_data, OPENSSL_CTX *libctx,
+                                           void *ui_data, const char *uri,
+                                           OPENSSL_CTX *libctx,
                                            const char *propq)
 {
     OSSL_STORE_INFO *store_info = NULL;
@@ -711,6 +723,7 @@ static const FILE_HANDLER *file_handlers[] = {
  */
 
 struct ossl_store_loader_ctx_st {
+    char *uri;                   /* The URI we currently try to load */
     enum {
         is_raw = 0,
         is_pem,
@@ -734,7 +747,6 @@ struct ossl_store_loader_ctx_st {
         struct {                 /* Used with is_dir */
             OPENSSL_DIR_CTX *ctx;
             int end_reached;
-            char *uri;
 
             /*
              * When a search expression is given, these are filled in.
@@ -762,9 +774,11 @@ struct ossl_store_loader_ctx_st {
 
 static void OSSL_STORE_LOADER_CTX_free(OSSL_STORE_LOADER_CTX *ctx)
 {
-    if (ctx->type == is_dir) {
-        OPENSSL_free(ctx->_.dir.uri);
-    } else {
+    if (ctx == NULL)
+        return;
+
+    OPENSSL_free(ctx->uri);
+    if (ctx->type != is_dir) {
         if (ctx->_.file.last_handler != NULL) {
             ctx->_.file.last_handler->destroy_ctx(&ctx->_.file.last_handler_ctx);
             ctx->_.file.last_handler_ctx = NULL;
@@ -883,19 +897,14 @@ static OSSL_STORE_LOADER_CTX *file_open(const OSSL_STORE_LOADER *loader,
         OSSL_STOREerr(OSSL_STORE_F_FILE_OPEN, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
+    ctx->uri = OPENSSL_strdup(uri);
+    if (ctx->uri == NULL) {
+        OSSL_STOREerr(OSSL_STORE_F_FILE_OPEN, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
 
     if (S_ISDIR(st.st_mode)) {
-        /*
-         * Try to copy everything, even if we know that some of them must be
-         * NULL for the moment.  This prevents errors in the future, when more
-         * components may be used.
-         */
-        ctx->_.dir.uri = OPENSSL_strdup(uri);
         ctx->type = is_dir;
-
-        if (ctx->_.dir.uri == NULL)
-            goto err;
-
         ctx->_.dir.last_entry = OPENSSL_DIR_read(&ctx->_.dir.ctx, path);
         ctx->_.dir.last_errno = errno;
         if (ctx->_.dir.last_entry == NULL) {
@@ -1052,7 +1061,8 @@ static OSSL_STORE_INFO *file_load_try_decode(OSSL_STORE_LOADER_CTX *ctx,
             OSSL_STORE_INFO *tmp_result =
                 handler->try_decode(pem_name, pem_header, data, len,
                                     &tmp_handler_ctx, &try_matchcount,
-                                    ui_method, ui_data, ctx->libctx, ctx->propq);
+                                    ui_method, ui_data, ctx->uri,
+                                    ctx->libctx, ctx->propq);
 
             if (try_matchcount > 0) {
 
@@ -1119,7 +1129,7 @@ static OSSL_STORE_INFO *file_load_try_repeat(OSSL_STORE_LOADER_CTX *ctx,
             ctx->_.file.last_handler->try_decode(NULL, NULL, NULL, 0,
                                                  &ctx->_.file.last_handler_ctx,
                                                  &try_matchcount,
-                                                 ui_method, ui_data,
+                                                 ui_method, ui_data, ctx->uri,
                                                  ctx->libctx, ctx->propq);
 
         if (result == NULL) {
@@ -1140,8 +1150,8 @@ static void pem_free_flag(void *pem_data, int secure, size_t num)
 }
 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 secure)
+                         const UI_METHOD *ui_method, void *ui_data,
+                         const char *uri, int secure)
 {
     int i = secure
         ? PEM_read_bio_ex(bp, pem_name, pem_header, data, len,
@@ -1162,7 +1172,8 @@ static int file_read_pem(BIO *bp, char **pem_name, char **pem_header,
         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)
+            || !file_fill_pem_pass_data(&pass_data, "PEM pass phrase", uri,
+                                        ui_method, ui_data)
             || !PEM_do_header(&cipher, *data, len, file_get_pem_pass,
                               &pass_data)) {
             return 0;
@@ -1171,6 +1182,84 @@ static int file_read_pem(BIO *bp, char **pem_name, char **pem_header,
     return 1;
 }
 
+static OSSL_STORE_INFO *file_try_read_msblob(BIO *bp, int *matchcount)
+{
+#ifdef OPENSSL_NO_DSA
+    return NULL;
+#else
+    OSSL_STORE_INFO *result = NULL;
+    int ispub = -1;
+
+    {
+        unsigned int magic = 0, bitlen = 0;
+        int isdss = 0;
+        unsigned char peekbuf[16] = { 0, };
+        const unsigned char *p = peekbuf;
+
+        if (BIO_buffer_peek(bp, peekbuf, sizeof(peekbuf)) <= 0)
+            return 0;
+        if (!ossl_do_blob_header(&p, sizeof(peekbuf), &magic, &bitlen,
+                                 &isdss, &ispub))
+            return 0;
+    }
+
+    (*matchcount)++;
+
+    {
+        EVP_PKEY *tmp = ispub
+            ? b2i_PublicKey_bio(bp)
+            : b2i_PrivateKey_bio(bp);
+
+        if (tmp == NULL
+            || (result = OSSL_STORE_INFO_new_PKEY(tmp)) == NULL) {
+            EVP_PKEY_free(tmp);
+            return 0;
+        }
+    }
+
+    return result;
+#endif
+}
+
+static OSSL_STORE_INFO *file_try_read_PVK(BIO *bp, const UI_METHOD *ui_method,
+                                          void *ui_data, const char *uri,
+                                          int *matchcount)
+{
+#if defined(OPENSSL_NO_DSA) || defined(OPENSSL_NO_RC4)
+    return NULL;
+#else
+    OSSL_STORE_INFO *result = NULL;
+
+    {
+        unsigned int saltlen = 0, keylen = 0;
+        unsigned char peekbuf[24] = { 0, };
+        const unsigned char *p = peekbuf;
+
+        if (BIO_buffer_peek(bp, peekbuf, sizeof(peekbuf)) <= 0)
+            return 0;
+        if (!ossl_do_PVK_header(&p, sizeof(peekbuf), 0, &saltlen, &keylen))
+            return 0;
+    }
+
+    (*matchcount)++;
+
+    {
+        EVP_PKEY *tmp = NULL;
+        struct pem_pass_data pass_data;
+
+        if (!file_fill_pem_pass_data(&pass_data, "PVK pass phrase", uri,
+                                     ui_method, ui_data)
+            || (tmp = b2i_PVK_bio(bp, file_get_pem_pass, &pass_data)) == NULL
+            || (result = OSSL_STORE_INFO_new_PKEY(tmp)) == NULL) {
+            EVP_PKEY_free(tmp);
+            return 0;
+        }
+    }
+
+    return result;
+#endif
+}
+
 static int file_read_asn1(BIO *bp, unsigned char **data, long *len)
 {
     BUF_MEM *mem = NULL;
@@ -1205,8 +1294,8 @@ static int file_name_to_uri(OSSL_STORE_LOADER_CTX *ctx, const char *name,
     assert(name != NULL);
     assert(data != NULL);
     {
-        const char *pathsep = ends_with_dirsep(ctx->_.dir.uri) ? "" : "/";
-        long calculated_length = strlen(ctx->_.dir.uri) + strlen(pathsep)
+        const char *pathsep = ends_with_dirsep(ctx->uri) ? "" : "/";
+        long calculated_length = strlen(ctx->uri) + strlen(pathsep)
             + strlen(name) + 1 /* \0 */;
 
         *data = OPENSSL_zalloc(calculated_length);
@@ -1215,7 +1304,7 @@ static int file_name_to_uri(OSSL_STORE_LOADER_CTX *ctx, const char *name,
             return 0;
         }
 
-        OPENSSL_strlcat(*data, ctx->_.dir.uri, calculated_length);
+        OPENSSL_strlcat(*data, ctx->uri, calculated_length);
         OPENSSL_strlcat(*data, pathsep, calculated_length);
         OPENSSL_strlcat(*data, name, calculated_length);
     }
@@ -1322,8 +1411,7 @@ static OSSL_STORE_INFO *file_load(OSSL_STORE_LOADER_CTX *ctx,
              * only cares that it isn't NULL.  Therefore, we can safely give
              * it our URI here.
              */
-            ctx->_.dir.last_entry = OPENSSL_DIR_read(&ctx->_.dir.ctx,
-                                                     ctx->_.dir.uri);
+            ctx->_.dir.last_entry = OPENSSL_DIR_read(&ctx->_.dir.ctx, ctx->uri);
             ctx->_.dir.last_errno = errno;
             if (ctx->_.dir.last_entry == NULL && ctx->_.dir.last_errno == 0)
                 ctx->_.dir.end_reached = 1;
@@ -1355,12 +1443,19 @@ static OSSL_STORE_INFO *file_load(OSSL_STORE_LOADER_CTX *ctx,
             matchcount = -1;
             if (ctx->type == is_pem) {
                 if (!file_read_pem(ctx->_.file.file, &pem_name, &pem_header,
-                                   &data, &len, ui_method, ui_data,
+                                   &data, &len, ui_method, ui_data, ctx->uri,
                                    (ctx->flags & FILE_FLAG_SECMEM) != 0)) {
                     ctx->errcnt++;
                     goto endloop;
                 }
             } else {
+                if ((result = file_try_read_msblob(ctx->_.file.file,
+                                                   &matchcount)) != NULL
+                    || (result = file_try_read_PVK(ctx->_.file.file,
+                                                   ui_method, ui_data, ctx->uri,
+                                                   &matchcount)) != NULL)
+                    goto endloop;
+
                 if (!file_read_asn1(ctx->_.file.file, &data, &len)) {
                     ctx->errcnt++;
                     goto endloop;