/*
- * Copyright 2016-2017 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved.
*
- * Licensed under the OpenSSL license (the "License"). You may not use
+ * 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
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
+#include "e_os.h"
#include <string.h>
#include <sys/stat.h>
+#include <ctype.h>
#include <assert.h>
#include <openssl/bio.h>
#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>
#include <openssl/store.h>
#include <openssl/ui.h>
#include <openssl/x509.h> /* For the PKCS8 stuff o.O */
-#include "internal/asn1_int.h"
+#include "crypto/asn1.h"
+#include "crypto/ctype.h"
#include "internal/o_dir.h"
#include "internal/cryptlib.h"
-#include "internal/store_int.h"
-#include "store_locl.h"
+#include "crypto/store.h"
+#include "crypto/evp.h"
+#include "store_local.h"
-#include "e_os.h"
+DEFINE_STACK_OF(X509)
+
+#ifdef _WIN32
+# define stat _stat
+#endif
+
+#ifndef S_ISDIR
+# define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)
+#endif
/*-
* Password prompting
*/
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;
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,
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;
}
{
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);
}
* or any other interactive data.
* ui_data: Application data to be passed to ui_method when
* it's called.
+ * libctx: The library context to be used if applicable
+ * propq: The property query string for any algorithm fetches
* Output:
* a OSSL_STORE_INFO
*/
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);
/*
* The eof function should return 1 if there's no more data to be found
* with the handler_ctx, otherwise 0. This is only used when the handler is
typedef int (*file_eof_fn)(void *handler_ctx);
/*
* The destroy_ctx function is used to destroy the handler_ctx that was
- * intiated by a repeatable try_decode fuction. This is only used when
+ * initiated by a repeatable try_decode function. This is only used when
* the handler is marked repeatable.
*/
typedef void (*file_destroy_ctx_fn)(void **handler_ctx);
size_t len, void **pctx,
int *matchcount,
const UI_METHOD *ui_method,
- void *ui_data)
+ void *ui_data, const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
{
OSSL_STORE_INFO *store_info = NULL;
STACK_OF(OSSL_STORE_INFO) *ctx = *pctx;
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);
}
if (PKCS12_parse(p12, pass, &pkey, &cert, &chain)) {
- OSSL_STORE_INFO *si_pkey = NULL;
- OSSL_STORE_INFO *si_cert = NULL;
- OSSL_STORE_INFO *si_ca = NULL;
+ OSSL_STORE_INFO *osi_pkey = NULL;
+ OSSL_STORE_INFO *osi_cert = NULL;
+ OSSL_STORE_INFO *osi_ca = NULL;
if ((ctx = sk_OSSL_STORE_INFO_new_null()) != NULL
- && (si_pkey = OSSL_STORE_INFO_new_PKEY(pkey)) != NULL
- && sk_OSSL_STORE_INFO_push(ctx, si_pkey) != 0
- && (si_cert = OSSL_STORE_INFO_new_CERT(cert)) != NULL
- && sk_OSSL_STORE_INFO_push(ctx, si_cert) != 0) {
+ && (osi_pkey = OSSL_STORE_INFO_new_PKEY(pkey)) != NULL
+ && sk_OSSL_STORE_INFO_push(ctx, osi_pkey) != 0
+ && (osi_cert = OSSL_STORE_INFO_new_CERT(cert)) != NULL
+ && sk_OSSL_STORE_INFO_push(ctx, osi_cert) != 0) {
ok = 1;
- si_pkey = NULL;
- si_cert = NULL;
+ osi_pkey = NULL;
+ osi_cert = NULL;
while(sk_X509_num(chain) > 0) {
X509 *ca = sk_X509_value(chain, 0);
- if ((si_ca = OSSL_STORE_INFO_new_CERT(ca)) == NULL
- || sk_OSSL_STORE_INFO_push(ctx, si_ca) == 0) {
+ if ((osi_ca = OSSL_STORE_INFO_new_CERT(ca)) == NULL
+ || sk_OSSL_STORE_INFO_push(ctx, osi_ca) == 0) {
ok = 0;
break;
}
- si_ca = NULL;
+ osi_ca = NULL;
(void)sk_X509_shift(chain);
}
}
if (!ok) {
- OSSL_STORE_INFO_free(si_ca);
- OSSL_STORE_INFO_free(si_cert);
- OSSL_STORE_INFO_free(si_pkey);
+ OSSL_STORE_INFO_free(osi_ca);
+ OSSL_STORE_INFO_free(osi_cert);
+ OSSL_STORE_INFO_free(osi_pkey);
sk_OSSL_STORE_INFO_pop_free(ctx, OSSL_STORE_INFO_free);
EVP_PKEY_free(pkey);
X509_free(cert);
size_t len, void **pctx,
int *matchcount,
const UI_METHOD *ui_method,
- void *ui_data)
+ void *ui_data,
+ const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
{
X509_SIG *p8 = NULL;
char kbuf[PEM_BUFSIZE];
}
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;
size_t len, void **pctx,
int *matchcount,
const UI_METHOD *ui_method,
- void *ui_data)
+ void *ui_data, const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
{
OSSL_STORE_INFO *store_info = NULL;
EVP_PKEY *pkey = NULL;
*matchcount = 1;
if (p8inf != NULL)
- pkey = EVP_PKCS82PKEY(p8inf);
+ pkey = evp_pkcs82pkey_int(p8inf, libctx, propq);
PKCS8_PRIV_KEY_INFO_free(p8inf);
} else {
int slen;
&& (ameth = EVP_PKEY_asn1_find_str(NULL, pem_name,
slen)) != NULL) {
*matchcount = 1;
- pkey = d2i_PrivateKey(ameth->pkey_id, NULL, &blob, len);
+ pkey = d2i_PrivateKey_ex(ameth->pkey_id, NULL, &blob, len,
+ libctx, propq);
}
}
} else {
if (ameth->pkey_flags & ASN1_PKEY_ALIAS)
continue;
- tmp_pkey = d2i_PrivateKey(ameth->pkey_id, NULL, &tmp_blob, len);
+ tmp_pkey = d2i_PrivateKey_ex(ameth->pkey_id, NULL, &tmp_blob, len,
+ libctx, propq);
if (tmp_pkey != NULL) {
if (pkey != NULL)
EVP_PKEY_free(tmp_pkey);
};
/*
- * Public key decoder. Only supports SubjectPublicKeyInfo formated keys.
+ * Public key decoder. Only supports SubjectPublicKeyInfo formatted keys.
*/
static OSSL_STORE_INFO *try_decode_PUBKEY(const char *pem_name,
const char *pem_header,
size_t len, void **pctx,
int *matchcount,
const UI_METHOD *ui_method,
- void *ui_data)
+ void *ui_data, const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
{
OSSL_STORE_INFO *store_info = NULL;
EVP_PKEY *pkey = NULL;
size_t len, void **pctx,
int *matchcount,
const UI_METHOD *ui_method,
- void *ui_data)
+ void *ui_data, const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
{
OSSL_STORE_INFO *store_info = NULL;
int slen = 0;
size_t len, void **pctx,
int *matchcount,
const UI_METHOD *ui_method,
- void *ui_data)
+ void *ui_data,
+ const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
{
OSSL_STORE_INFO *store_info = NULL;
X509 *cert = NULL;
size_t len, void **pctx,
int *matchcount,
const UI_METHOD *ui_method,
- void *ui_data)
+ void *ui_data, const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
{
OSSL_STORE_INFO *store_info = NULL;
X509_CRL *crl = NULL;
*/
struct ossl_store_loader_ctx_st {
+ char *uri; /* The URI we currently try to load */
enum {
is_raw = 0,
is_pem,
} type;
int errcnt;
#define FILE_FLAG_SECMEM (1<<0)
+#define FILE_FLAG_ATTACHED (1<<1)
unsigned int flags;
union {
struct { /* Used with is_raw and is_pem */
struct { /* Used with is_dir */
OPENSSL_DIR_CTX *ctx;
int end_reached;
- char *uri;
+
+ /*
+ * When a search expression is given, these are filled in.
+ * |search_name| contains the file basename to look for.
+ * The string is exactly 8 characters long.
+ */
+ char search_name[9];
/*
* The directory reading utility we have combines opening with
int last_errno;
} dir;
} _;
+
+ /* Expected object type. May be unspecified */
+ int expected_type;
+
+ OPENSSL_CTX *libctx;
+ char *propq;
};
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;
ctx->_.file.last_handler = NULL;
}
}
+ OPENSSL_free(ctx->propq);
OPENSSL_free(ctx);
}
+static int file_find_type(OSSL_STORE_LOADER_CTX *ctx)
+{
+ BIO *buff = NULL;
+ char peekbuf[4096] = { 0, };
+
+ if ((buff = BIO_new(BIO_f_buffer())) == NULL)
+ return 0;
+
+ ctx->_.file.file = BIO_push(buff, ctx->_.file.file);
+ if (BIO_buffer_peek(ctx->_.file.file, peekbuf, sizeof(peekbuf) - 1) > 0) {
+ peekbuf[sizeof(peekbuf) - 1] = '\0';
+ if (strstr(peekbuf, "-----BEGIN ") != NULL)
+ ctx->type = is_pem;
+ }
+ return 1;
+}
+
static OSSL_STORE_LOADER_CTX *file_open(const OSSL_STORE_LOADER *loader,
const char *uri,
const UI_METHOD *ui_method,
{
OSSL_STORE_LOADER_CTX *ctx = NULL;
struct stat st;
- const char *path = NULL;
+ struct {
+ const char *path;
+ unsigned int check_absolute:1;
+ } path_data[2];
+ size_t path_data_n = 0, i;
+ const char *path;
+
+ /*
+ * First step, just take the URI as is.
+ */
+ path_data[path_data_n].check_absolute = 0;
+ path_data[path_data_n++].path = uri;
+ /*
+ * Second step, if the URI appears to start with the 'file' scheme,
+ * extract the path and make that the second path to check.
+ * There's a special case if the URI also contains an authority, then
+ * the full URI shouldn't be used as a path anywhere.
+ */
if (strncasecmp(uri, "file:", 5) == 0) {
- if (strncasecmp(&uri[5], "//localhost/", 12) == 0) {
- path = &uri[16];
- } else if (strncmp(&uri[5], "///", 3) == 0) {
- path = &uri[7];
- } else if (strncmp(&uri[5], "//", 2) != 0) {
- path = &uri[5];
- } else {
- OSSL_STOREerr(OSSL_STORE_F_FILE_OPEN,
- OSSL_STORE_R_URI_AUTHORITY_UNSUPPORED);
- return NULL;
+ const char *p = &uri[5];
+
+ if (strncmp(&uri[5], "//", 2) == 0) {
+ path_data_n--; /* Invalidate using the full URI */
+ if (strncasecmp(&uri[7], "localhost/", 10) == 0) {
+ p = &uri[16];
+ } else if (uri[7] == '/') {
+ p = &uri[7];
+ } else {
+ OSSL_STOREerr(OSSL_STORE_F_FILE_OPEN,
+ OSSL_STORE_R_URI_AUTHORITY_UNSUPPORTED);
+ return NULL;
+ }
}
+ path_data[path_data_n].check_absolute = 1;
+#ifdef _WIN32
+ /* Windows file: URIs with a drive letter start with a / */
+ if (p[0] == '/' && p[2] == ':' && p[3] == '/') {
+ char c = ossl_tolower(p[1]);
+
+ if (c >= 'a' && c <= 'z') {
+ p++;
+ /* We know it's absolute, so no need to check */
+ path_data[path_data_n].check_absolute = 0;
+ }
+ }
+#endif
+ path_data[path_data_n++].path = p;
+ }
+
+
+ for (i = 0, path = NULL; path == NULL && i < path_data_n; i++) {
/*
* If the scheme "file" was an explicit part of the URI, the path must
* be absolute. So says RFC 8089
*/
- if (path[0] != '/') {
+ if (path_data[i].check_absolute && path_data[i].path[0] != '/') {
OSSL_STOREerr(OSSL_STORE_F_FILE_OPEN,
OSSL_STORE_R_PATH_MUST_BE_ABSOLUTE);
+ ERR_add_error_data(1, path_data[i].path);
return NULL;
}
-#ifdef _WIN32
- /* Windows file: URIs with a drive letter start with a / */
- if (path[0] == '/' && path[2] == ':' && path[3] == '/')
- path++;
-#endif
- } else {
- path = uri;
+ if (stat(path_data[i].path, &st) < 0) {
+ ERR_raise_data(ERR_LIB_SYS, errno,
+ "calling stat(%s)",
+ path_data[i].path);
+ } else {
+ path = path_data[i].path;
+ }
}
-
-
- if (stat(path, &st) < 0) {
- SYSerr(SYS_F_STAT, errno);
- ERR_add_error_data(1, path);
+ if (path == NULL) {
return NULL;
}
+ /* Successfully found a working path, clear possible collected errors */
+ ERR_clear_error();
+
ctx = OPENSSL_zalloc(sizeof(*ctx));
if (ctx == NULL) {
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 ((st.st_mode & S_IFDIR) == S_IFDIR) {
- /*
- * 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);
+ if (S_ISDIR(st.st_mode)) {
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) {
if (ctx->_.dir.last_errno != 0) {
char errbuf[256];
- errno = ctx->_.dir.last_errno;
- openssl_strerror_r(errno, errbuf, sizeof(errbuf));
OSSL_STOREerr(OSSL_STORE_F_FILE_OPEN, ERR_R_SYS_LIB);
- ERR_add_error_data(1, errbuf);
+ errno = ctx->_.dir.last_errno;
+ if (openssl_strerror_r(errno, errbuf, sizeof(errbuf)))
+ ERR_add_error_data(1, errbuf);
goto err;
}
ctx->_.dir.end_reached = 1;
}
- } else {
- BIO *buff = NULL;
- char peekbuf[4096];
-
- if ((buff = BIO_new(BIO_f_buffer())) == NULL
- || (ctx->_.file.file = BIO_new_file(path, "rb")) == NULL) {
- BIO_free_all(buff);
- goto err;
- }
-
- ctx->_.file.file = BIO_push(buff, ctx->_.file.file);
- if (BIO_buffer_peek(ctx->_.file.file, peekbuf, sizeof(peekbuf)-1) > 0) {
- peekbuf[sizeof(peekbuf)-1] = '\0';
- if (strstr(peekbuf, "-----BEGIN ") != NULL)
- ctx->type = is_pem;
- }
+ } else if ((ctx->_.file.file = BIO_new_file(path, "rb")) == NULL
+ || !file_find_type(ctx)) {
+ BIO_free_all(ctx->_.file.file);
+ goto err;
}
return ctx;
return NULL;
}
+static OSSL_STORE_LOADER_CTX *file_attach(const OSSL_STORE_LOADER *loader,
+ BIO *bp, OPENSSL_CTX *libctx,
+ const char *propq,
+ const UI_METHOD *ui_method,
+ void *ui_data)
+{
+ OSSL_STORE_LOADER_CTX *ctx;
+
+ if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL
+ || (propq != NULL && (ctx->propq = OPENSSL_strdup(propq)) == NULL)) {
+ OSSL_STOREerr(OSSL_STORE_F_FILE_ATTACH, ERR_R_MALLOC_FAILURE);
+ OSSL_STORE_LOADER_CTX_free(ctx);
+ return NULL;
+ }
+
+ ctx->libctx = libctx;
+ ctx->flags |= FILE_FLAG_ATTACHED;
+ ctx->_.file.file = bp;
+ if (!file_find_type(ctx)) {
+ /* Safety measure */
+ ctx->_.file.file = NULL;
+ OSSL_STORE_LOADER_CTX_free(ctx);
+ ctx = NULL;
+ }
+
+ return ctx;
+}
+
static int file_ctrl(OSSL_STORE_LOADER_CTX *ctx, int cmd, va_list args)
{
int ret = 1;
return ret;
}
-/* Internal function to decode an already opened PEM file */
-OSSL_STORE_LOADER_CTX *ossl_store_file_attach_pem_bio_int(BIO *bp)
+static int file_expect(OSSL_STORE_LOADER_CTX *ctx, int expected)
{
- OSSL_STORE_LOADER_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+ ctx->expected_type = expected;
+ return 1;
+}
- if (ctx == NULL) {
- OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_FILE_ATTACH_PEM_BIO_INT,
- ERR_R_MALLOC_FAILURE);
- return NULL;
- }
+static int file_find(OSSL_STORE_LOADER_CTX *ctx,
+ const OSSL_STORE_SEARCH *search)
+{
+ /*
+ * If ctx == NULL, the library is looking to know if this loader supports
+ * the given search type.
+ */
- ctx->_.file.file = bp;
- ctx->type = is_pem;
+ if (OSSL_STORE_SEARCH_get_type(search) == OSSL_STORE_SEARCH_BY_NAME) {
+ unsigned long hash = 0;
- return ctx;
+ if (ctx == NULL)
+ return 1;
+
+ if (ctx->type != is_dir) {
+ OSSL_STOREerr(OSSL_STORE_F_FILE_FIND,
+ OSSL_STORE_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES);
+ return 0;
+ }
+
+ hash = X509_NAME_hash(OSSL_STORE_SEARCH_get0_name(search));
+ BIO_snprintf(ctx->_.dir.search_name, sizeof(ctx->_.dir.search_name),
+ "%08lx", hash);
+ return 1;
+ }
+
+ if (ctx != NULL)
+ OSSL_STOREerr(OSSL_STORE_F_FILE_FIND,
+ OSSL_STORE_R_UNSUPPORTED_SEARCH_TYPE);
+ return 0;
}
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);
+ ui_method, ui_data, ctx->uri,
+ ctx->libctx, ctx->propq);
if (try_matchcount > 0) {
- if (matching_handlers)
- matching_handlers[*matchcount] = handler;
+
+ matching_handlers[*matchcount] = handler;
if (handler_ctx)
handler->destroy_ctx(&handler_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) {
ctx->_.file.last_handler->destroy_ctx(&ctx->_.file.last_handler_ctx);
return result;
}
-static void pem_free_flag(void *pem_data, int secure)
+static void pem_free_flag(void *pem_data, int secure, size_t num)
{
if (secure)
- OPENSSL_secure_free(pem_data);
+ OPENSSL_secure_clear_free(pem_data, num);
else
OPENSSL_free(pem_data);
}
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,
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;
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;
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);
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);
}
return 1;
}
+static int file_name_check(OSSL_STORE_LOADER_CTX *ctx, const char *name)
+{
+ const char *p = NULL;
+
+ /* If there are no search criteria, all names are accepted */
+ if (ctx->_.dir.search_name[0] == '\0')
+ return 1;
+
+ /* If the expected type isn't supported, no name is accepted */
+ if (ctx->expected_type != 0
+ && ctx->expected_type != OSSL_STORE_INFO_CERT
+ && ctx->expected_type != OSSL_STORE_INFO_CRL)
+ return 0;
+
+ /*
+ * First, check the basename
+ */
+ if (strncasecmp(name, ctx->_.dir.search_name,
+ sizeof(ctx->_.dir.search_name) - 1) != 0
+ || name[sizeof(ctx->_.dir.search_name) - 1] != '.')
+ return 0;
+ p = &name[sizeof(ctx->_.dir.search_name)];
+
+ /*
+ * Then, if the expected type is a CRL, check that the extension starts
+ * with 'r'
+ */
+ if (*p == 'r') {
+ p++;
+ if (ctx->expected_type != 0
+ && ctx->expected_type != OSSL_STORE_INFO_CRL)
+ return 0;
+ } else if (ctx->expected_type == OSSL_STORE_INFO_CRL) {
+ return 0;
+ }
+
+ /*
+ * Last, check that the rest of the extension is a decimal number, at
+ * least one digit long.
+ */
+ if (!ossl_isdigit(*p))
+ return 0;
+ while (ossl_isdigit(*p))
+ p++;
+
+# ifdef __VMS
+ /*
+ * One extra step here, check for a possible generation number.
+ */
+ if (*p == ';')
+ for (p++; *p != '\0'; p++)
+ if (!ossl_isdigit(*p))
+ break;
+# endif
+
+ /*
+ * If we've reached the end of the string at this point, we've successfully
+ * found a fitting file name.
+ */
+ return *p == '\0';
+}
+
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,
if (!ctx->_.dir.end_reached) {
char errbuf[256];
assert(ctx->_.dir.last_errno != 0);
+ OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD, ERR_R_SYS_LIB);
errno = ctx->_.dir.last_errno;
ctx->errcnt++;
- openssl_strerror_r(errno, errbuf, sizeof(errbuf));
- OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD, ERR_R_SYS_LIB);
- ERR_add_error_data(1, errbuf);
+ if (openssl_strerror_r(errno, errbuf, sizeof(errbuf)))
+ ERR_add_error_data(1, errbuf);
}
return NULL;
}
if (ctx->_.dir.last_entry[0] != '.'
+ && file_name_check(ctx, ctx->_.dir.last_entry)
&& !file_name_to_uri(ctx, ctx->_.dir.last_entry, &newname))
return NULL;
* 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;
} else {
int matchcount = -1;
+ again:
result = file_load_try_repeat(ctx, ui_method, ui_data);
if (result != NULL)
return result;
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;
ctx->errcnt++;
endloop:
- pem_free_flag(pem_name, (ctx->flags & FILE_FLAG_SECMEM) != 0);
- pem_free_flag(pem_header, (ctx->flags & FILE_FLAG_SECMEM) != 0);
- pem_free_flag(data, (ctx->flags & FILE_FLAG_SECMEM) != 0);
+ pem_free_flag(pem_name, (ctx->flags & FILE_FLAG_SECMEM) != 0, 0);
+ pem_free_flag(pem_header, (ctx->flags & FILE_FLAG_SECMEM) != 0, 0);
+ pem_free_flag(data, (ctx->flags & FILE_FLAG_SECMEM) != 0, len);
} while (matchcount == 0 && !file_eof(ctx) && !file_error(ctx));
/* We bail out on ambiguity */
if (matchcount > 1)
return NULL;
+
+ if (result != NULL
+ && ctx->expected_type != 0
+ && ctx->expected_type != OSSL_STORE_INFO_get_type(result)) {
+ OSSL_STORE_INFO_free(result);
+ goto again;
+ }
}
return result;
static int file_close(OSSL_STORE_LOADER_CTX *ctx)
{
- if (ctx->type == is_dir) {
- OPENSSL_DIR_end(&ctx->_.dir.ctx);
+ if ((ctx->flags & FILE_FLAG_ATTACHED) == 0) {
+ if (ctx->type == is_dir)
+ OPENSSL_DIR_end(&ctx->_.dir.ctx);
+ else
+ BIO_free_all(ctx->_.file.file);
} else {
- BIO_free_all(ctx->_.file.file);
- }
- OSSL_STORE_LOADER_CTX_free(ctx);
- return 1;
-}
+ /*
+ * Because file_attach() called file_find_type(), we know that a
+ * BIO_f_buffer() has been pushed on top of the regular BIO.
+ */
+ BIO *buff = ctx->_.file.file;
-int ossl_store_file_detach_pem_bio_int(OSSL_STORE_LOADER_CTX *ctx)
-{
+ /* Detach buff */
+ (void)BIO_pop(ctx->_.file.file);
+ /* Safety measure */
+ ctx->_.file.file = NULL;
+
+ BIO_free(buff);
+ }
OSSL_STORE_LOADER_CTX_free(ctx);
return 1;
}
"file",
NULL,
file_open,
+ file_attach,
file_ctrl,
+ file_expect,
+ file_find,
file_load,
file_eof,
file_error,