STORE 'file' scheme loader: Add directory listing capability

This has it recognised when the given path is a directory.  In that
case, the file loader will give back a series of names, all as URI
formatted as possible given the incoming URI.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/3542)
This commit is contained in:
Richard Levitte 2017-02-07 16:19:40 +01:00
parent 7ad2ef366c
commit 970f467ac3
6 changed files with 238 additions and 73 deletions

View file

@ -88,6 +88,7 @@ static ERR_STRING_DATA ERR_str_functs[] = {
{ERR_PACK(0, SYS_F_OPEN, 0), "open"},
{ERR_PACK(0, SYS_F_CLOSE, 0), "close"},
{ERR_PACK(0, SYS_F_IOCTL, 0), "ioctl"},
{ERR_PACK(0, SYS_F_STAT, 0), "stat"},
{0, NULL},
};

View file

@ -727,6 +727,7 @@ 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_NAME_TO_URI:126:file_name_to_uri
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

View file

@ -8,6 +8,8 @@
*/
#include <string.h>
#include <sys/stat.h>
#include <assert.h>
#include <openssl/bio.h>
#include <openssl/dsa.h> /* For d2i_DSAPrivateKey */
@ -21,6 +23,8 @@
#include <openssl/ui.h>
#include <openssl/x509.h> /* For the PKCS8 stuff o.O */
#include "internal/asn1_int.h"
#include "internal/o_dir.h"
#include "internal/cryptlib.h"
#include "store_locl.h"
#include "e_os.h"
@ -555,23 +559,60 @@ static const FILE_HANDLER *file_handlers[] = {
*/
struct ossl_store_loader_ctx_st {
BIO *file;
int is_pem;
enum {
is_raw = 0,
is_pem,
is_dir
} type;
int errcnt;
union {
struct { /* Used with is_raw and is_pem */
BIO *file;
/* The following are used when the handler is marked as repeatable */
const FILE_HANDLER *last_handler;
void *last_handler_ctx;
/*
* The following are used when the handler is marked as
* repeatable
*/
const FILE_HANDLER *last_handler;
void *last_handler_ctx;
} file;
struct { /* Used with is_dir */
OPENSSL_DIR_CTX *ctx;
int end_reached;
char *uri;
/*
* The directory reading utility we have combines opening with
* reading the first name. To make sure we can detect the end
* at the right time, we read early and cache the name.
*/
const char *last_entry;
int last_errno;
} dir;
} _;
};
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->_.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);
}
static OSSL_STORE_LOADER_CTX *file_open(const OSSL_STORE_LOADER *loader,
const char *uri,
const UI_METHOD *ui_method,
void *ui_data)
{
BIO *buff = NULL;
char peekbuf[4096];
OSSL_STORE_LOADER_CTX *ctx = NULL;
struct stat st;
const char *path = NULL;
if (strncasecmp(uri, "file:", 5) == 0) {
@ -607,29 +648,64 @@ static OSSL_STORE_LOADER_CTX *file_open(const OSSL_STORE_LOADER *loader,
}
if (stat(path, &st) < 0) {
SYSerr(SYS_F_STAT, errno);
ERR_add_error_data(1, path);
return NULL;
}
ctx = OPENSSL_zalloc(sizeof(*ctx));
if (ctx == NULL) {
OSSL_STOREerr(OSSL_STORE_F_FILE_OPEN, ERR_R_MALLOC_FAILURE);
return NULL;
}
if ((buff = BIO_new(BIO_f_buffer())) == NULL)
goto err;
if ((ctx->file = BIO_new_file(path, "rb")) == NULL) {
goto err;
}
ctx->file = BIO_push(buff, ctx->file);
if (BIO_buffer_peek(ctx->file, peekbuf, sizeof(peekbuf)-1) > 0) {
peekbuf[sizeof(peekbuf)-1] = '\0';
if (strstr(peekbuf, "-----BEGIN ") != NULL)
ctx->is_pem = 1;
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);
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);
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;
}
}
return ctx;
err:
if (buff != NULL)
BIO_free(buff);
OPENSSL_free(ctx);
OSSL_STORE_LOADER_CTX_free(ctx);
return NULL;
}
@ -707,8 +783,8 @@ static OSSL_STORE_INFO *file_load_try_decode(OSSL_STORE_LOADER_CTX *ctx,
OSSL_STORE_INFO_free(result);
result = NULL;
} else {
ctx->last_handler = matching_handlers[0];
ctx->last_handler_ctx = handler_ctx;
ctx->_.file.last_handler = matching_handlers[0];
ctx->_.file.last_handler_ctx = handler_ctx;
}
}
@ -743,15 +819,16 @@ static OSSL_STORE_INFO *file_load_try_repeat(OSSL_STORE_LOADER_CTX *ctx,
{
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 (ctx->_.file.last_handler != NULL) {
result =
ctx->_.file.last_handler->try_decode(NULL, NULL, NULL, 0,
&ctx->_.file.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;
ctx->_.file.last_handler->destroy_ctx(&ctx->_.file.last_handler_ctx);
ctx->_.file.last_handler_ctx = NULL;
ctx->_.file.last_handler = NULL;
}
}
return result;
@ -801,55 +878,135 @@ static int file_read_asn1(BIO *bp, unsigned char **data, long *len)
return 1;
}
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 int file_name_to_uri(OSSL_STORE_LOADER_CTX *ctx, const char *name,
char **data)
{
assert(name != NULL);
assert(data != NULL);
{
const char *pathsep = ends_with_dirsep(ctx->_.dir.uri) ? "" : "/";
long calculated_length = strlen(ctx->_.dir.uri) + strlen(pathsep)
+ strlen(name) + 1 /* \0 */;
*data = OPENSSL_zalloc(calculated_length);
if (*data == NULL) {
OSSL_STOREerr(OSSL_STORE_F_FILE_NAME_TO_URI, ERR_R_MALLOC_FAILURE);
return 0;
}
OPENSSL_strlcat(*data, ctx->_.dir.uri, calculated_length);
OPENSSL_strlcat(*data, pathsep, calculated_length);
OPENSSL_strlcat(*data, name, calculated_length);
}
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 (ctx->type == is_dir) {
do {
char *newname = 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 */
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))
if (ctx->_.dir.last_entry == NULL) {
if (!ctx->_.dir.end_reached) {
char errbuf[256];
assert(ctx->_.dir.last_errno != 0);
errno = ctx->_.dir.last_errno;
ctx->errcnt++;
goto err;
openssl_strerror_r(errno, errbuf, sizeof(errbuf));
OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD, ERR_R_SYS_LIB);
ERR_add_error_data(1, errbuf);
}
return NULL;
}
} else {
if (!file_read_asn1(ctx->file, &data, &len)) {
if (!file_eof(ctx))
ctx->errcnt++;
goto err;
if (ctx->_.dir.last_entry[0] != '.'
&& !file_name_to_uri(ctx, ctx->_.dir.last_entry, &newname))
return NULL;
/*
* On the first call (with a NULL context), OPENSSL_DIR_read()
* cares about the second argument. On the following calls, it
* 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_errno = errno;
if (ctx->_.dir.last_entry == NULL && ctx->_.dir.last_errno == 0)
ctx->_.dir.end_reached = 1;
if (newname != NULL
&& (result = OSSL_STORE_INFO_new_NAME(newname)) == NULL) {
OPENSSL_free(newname);
OSSL_STOREerr(OSSL_STORE_F_FILE_LOAD, ERR_R_OSSL_STORE_LIB);
return NULL;
}
}
} while (result == NULL && !file_eof(ctx));
} else {
int matchcount = -1;
matchcount = -1;
result = file_load_try_decode(ctx, pem_name, pem_header, data, len,
ui_method, ui_data, &matchcount);
result = file_load_try_repeat(ctx, ui_method, ui_data);
if (result != NULL)
return result;
err:
OPENSSL_free(pem_name);
OPENSSL_free(pem_header);
OPENSSL_free(data);
} while (matchcount == 0 && !file_eof(ctx) && !file_error(ctx));
if (file_error(ctx))
return NULL;
/* We bail out on ambiguity */
if (matchcount > 1)
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 */
matchcount = -1;
if (ctx->type == is_pem) {
if (!file_read_pem(ctx->_.file.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.file, &data, &len)) {
if (!file_eof(ctx))
ctx->errcnt++;
goto err;
}
}
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);
OPENSSL_free(data);
} while (matchcount == 0 && !file_eof(ctx) && !file_error(ctx));
/* We bail out on ambiguity */
if (matchcount > 1)
return NULL;
}
return result;
}
@ -861,21 +1018,23 @@ static int file_error(OSSL_STORE_LOADER_CTX *ctx)
static int file_eof(OSSL_STORE_LOADER_CTX *ctx)
{
if (ctx->last_handler != NULL
&& !ctx->last_handler->eof(ctx->last_handler_ctx))
if (ctx->type == is_dir)
return ctx->_.dir.end_reached;
if (ctx->_.file.last_handler != NULL
&& !ctx->_.file.last_handler->eof(ctx->_.file.last_handler_ctx))
return 0;
return BIO_eof(ctx->file);
return BIO_eof(ctx->_.file.file);
}
static int file_close(OSSL_STORE_LOADER_CTX *ctx)
{
if (ctx->last_handler != NULL) {
ctx->last_handler->destroy_ctx(&ctx->last_handler_ctx);
ctx->last_handler_ctx = NULL;
ctx->last_handler = NULL;
if (ctx->type == is_dir) {
OPENSSL_DIR_end(&ctx->_.dir.ctx);
} else {
BIO_free_all(ctx->_.file.file);
}
BIO_free_all(ctx->file);
OPENSSL_free(ctx);
OSSL_STORE_LOADER_CTX_free(ctx);
return 1;
}

View file

@ -19,6 +19,8 @@ static const ERR_STRING_DATA OSSL_STORE_str_functs[] = {
{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_NAME_TO_URI, 0),
"file_name_to_uri"},
{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"},

View file

@ -164,6 +164,7 @@ typedef struct err_state_st {
# define SYS_F_OPEN 19
# define SYS_F_CLOSE 20
# define SYS_F_IOCTL 21
# define SYS_F_STAT 22
/* reasons */
# define ERR_R_SYS_LIB ERR_LIB_SYS/* 2 */

View file

@ -25,6 +25,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_NAME_TO_URI 126
# 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