OSSL_STORE: Treat URIs as files first (with exceptions), then as full URIs

To handle paths that contain devices (for example, C:/foo/bar.pem on
Windows), try to "open" the URI using the file scheme loader first,
and failing that, check if the device is really a scheme we know.

The "file" scheme does the same kind of thing to pick out the path
part of the URI.

An exception to this special treatment is if the URI has an authority
part (something that starts with "//" directly after what looks like a
scheme).  Such URIs will never be treated as plain file paths.

Reviewed-by: Andy Polyakov <appro@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/3907)
This commit is contained in:
Richard Levitte 2017-07-11 11:54:00 +02:00
parent ba476aa32c
commit ae9c39d83a
2 changed files with 75 additions and 27 deletions

View file

@ -744,26 +744,41 @@ static OSSL_STORE_LOADER_CTX *file_open(const OSSL_STORE_LOADER *loader,
{
OSSL_STORE_LOADER_CTX *ctx = NULL;
struct stat st;
const char *path = NULL;
const char *paths[2], *path;
size_t paths_n = 0, i;
/*
* First step, just take the URI as is.
*/
paths[paths_n++] = 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_UNSUPPORTED);
return NULL;
const char *p = &uri[5];
if (strncmp(&uri[5], "//", 2) == 0) {
paths_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;
}
}
/*
* If the scheme "file" was an explicit part of the URI, the path must
* be absolute. So says RFC 8089
*/
if (path[0] != '/') {
if (p[0] != '/') {
OSSL_STOREerr(OSSL_STORE_F_FILE_OPEN,
OSSL_STORE_R_PATH_MUST_BE_ABSOLUTE);
return NULL;
@ -771,20 +786,28 @@ static OSSL_STORE_LOADER_CTX *file_open(const OSSL_STORE_LOADER *loader,
#ifdef _WIN32
/* Windows file: URIs with a drive letter start with a / */
if (path[0] == '/' && path[2] == ':' && path[3] == '/')
path++;
if (p[0] == '/' && p[2] == ':' && p[3] == '/')
p++;
#endif
} else {
path = uri;
paths[paths_n++] = p;
}
if (stat(path, &st) < 0) {
SYSerr(SYS_F_STAT, errno);
ERR_add_error_data(1, path);
for (i = 0, path = NULL; path == NULL && i < paths_n; i++) {
if (stat(paths[i], &st) < 0) {
SYSerr(SYS_F_STAT, errno);
ERR_add_error_data(1, paths[i]);
} else {
path = paths[i];
}
}
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);

View file

@ -10,6 +10,8 @@
#include <stdlib.h>
#include <string.h>
#include "e_os.h"
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/store.h>
@ -31,22 +33,45 @@ OSSL_STORE_CTX *OSSL_STORE_open(const char *uri, const UI_METHOD *ui_method,
OSSL_STORE_post_process_info_fn post_process,
void *post_process_data)
{
const OSSL_STORE_LOADER *loader;
const OSSL_STORE_LOADER *loader = NULL;
OSSL_STORE_LOADER_CTX *loader_ctx = NULL;
OSSL_STORE_CTX *ctx = NULL;
char scheme_copy[256], *p;
char scheme_copy[256], *p, *schemes[2];
size_t schemes_n = 0;
size_t i;
/*
* Put the file scheme first. If the uri does represent an existing file,
* possible device name and all, then it should be loaded. Only a failed
* attempt at loading a local file should have us try something else.
*/
schemes[schemes_n++] = "file";
/*
* Now, check if we have something that looks like a scheme, and add it
* as a second scheme. However, also check if there's an authority start
* (://), because that will invalidate the previous file scheme. Also,
* check that this isn't actually the file scheme, as there's no point
* going through that one twice!
*/
OPENSSL_strlcpy(scheme_copy, uri, sizeof(scheme_copy));
if ((p = strchr(scheme_copy, ':')) != NULL) {
*p = '\0';
p = scheme_copy;
} else {
p = "file";
*p++ = '\0';
if (strcasecmp(scheme_copy, "file") != 0) {
if (strncmp(p, "//", 2) == 0)
schemes_n--; /* Invalidate the file scheme */
schemes[schemes_n++] = scheme_copy;
}
}
if ((loader = ossl_store_get0_loader_int(p)) == NULL
|| (loader_ctx = loader->open(loader, uri, ui_method, ui_data)) == NULL)
/* Try each scheme until we find one that could open the URI */
for (i = 0; loader_ctx == NULL && i < schemes_n; i++) {
if ((loader = ossl_store_get0_loader_int(schemes[i])) != NULL)
loader_ctx = loader->open(loader, uri, ui_method, ui_data);
}
if (loader_ctx == NULL)
goto done;
if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) {
OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_OPEN, ERR_R_MALLOC_FAILURE);
goto done;