From ae9c39d83aa0f993e382cf913cb4fd84fc25d03d Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Tue, 11 Jul 2017 11:54:00 +0200 Subject: [PATCH] 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 (Merged from https://github.com/openssl/openssl/pull/3907) --- crypto/store/loader_file.c | 61 ++++++++++++++++++++++++++------------ crypto/store/store_lib.c | 43 +++++++++++++++++++++------ 2 files changed, 76 insertions(+), 28 deletions(-) diff --git a/crypto/store/loader_file.c b/crypto/store/loader_file.c index 85ab2b4206..0a9191d2d6 100644 --- a/crypto/store/loader_file.c +++ b/crypto/store/loader_file.c @@ -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); diff --git a/crypto/store/store_lib.c b/crypto/store/store_lib.c index 91faae20c5..9dc3a70a41 100644 --- a/crypto/store/store_lib.c +++ b/crypto/store/store_lib.c @@ -10,6 +10,8 @@ #include #include +#include "e_os.h" + #include #include #include @@ -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; -- 2.25.1