From 8d52e2b6b74fb5e81b4c6a74f61a9c5b30aaec01 Mon Sep 17 00:00:00 2001 From: Sergey Markelov Date: Thu, 8 Jul 2021 11:24:14 -0700 Subject: [PATCH] urlapi: UNC paths in file: URLs on Windows - file://host.name/path/file.txt is a valid UNC path \\host.name\path\files.txt to a non-local file transformed into URI (RFC 8089 Appendix E.3) - UNC paths on other OSs must be smb: URLs --- lib/urlapi.c | 40 ++++++++++++++++++++++++++++++++++------ tests/data/test2080 | Bin 20673 -> 20675 bytes tests/libtest/lib1560.c | 4 ++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 905c499d995829..8976e29b35b2fb 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -760,6 +760,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) { char *path; bool path_alloced = FALSE; + bool uncpath = FALSE; char *hostname; char *query = NULL; char *fragment = NULL; @@ -799,7 +800,6 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) /* path has been allocated large enough to hold this */ strcpy(path, &url[5]); - hostname = NULL; /* no host for file: URLs */ u->scheme = strdup("file"); if(!u->scheme) return CURLUE_OUT_OF_MEMORY; @@ -821,10 +821,13 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) * * o the hostname matches "localhost" (case-insensitively), or * - * o the hostname is a FQDN that resolves to this machine. + * o the hostname is a FQDN that resolves to this machine, or + * + * o it is an UNC String transformed to an URI (Windows only, RFC 8089 + * Appendix E.3). * * For brevity, we only consider URLs with empty, "localhost", or - * "127.0.0.1" hostnames as local. + * "127.0.0.1" hostnames as local, otherwise as an UNC String. * * Additionally, there is an exception for URLs with a Windows drive * letter in the authority (which was accidentally omitted from RFC 8089 @@ -833,18 +836,43 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { /* the URL includes a host name, it must match "localhost" or "127.0.0.1" to be valid */ - if(!checkprefix("localhost/", ptr) && - !checkprefix("127.0.0.1/", ptr)) { + if(checkprefix("localhost/", ptr) || + checkprefix("127.0.0.1/", ptr)) { + ptr += 9; /* now points to the slash after the host */ + } + else { +#if defined(WIN32) + size_t len; + + /* the host name, NetBIOS computer name, can not contain disallowed + chars, and the delimiting slash character must be appended to the + host name */ + path = strpbrk(ptr, "/\\:*?\"<>|"); + if(!path || *path != '/') + return CURLUE_MALFORMED_INPUT; + + len = path - ptr; + if(len) { + memcpy(hostname, ptr, len); + hostname[len] = 0; + uncpath = TRUE; + } + + ptr -= 2; /* now points to the // before the host in UNC */ +#else /* Invalid file://hostname/, expected localhost or 127.0.0.1 or none */ return CURLUE_MALFORMED_INPUT; +#endif } - ptr += 9; /* now points to the slash after the host */ } path = ptr; } + if(!uncpath) + hostname = NULL; /* no host for file: URLs by default */ + #if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__) /* Don't allow Windows drive letters when not in Windows. * This catches both "file:/c:" and "file:c:" */ diff --git a/tests/data/test2080 b/tests/data/test2080 index 9a337031d694d2d224afc63ceaedaf800ed811e0..9c8d538fc896b486e19301f84ffabbeb539bc9f3 100644 GIT binary patch delta 15 XcmX@Okn!+B#tEO9V(d14KIabrJMIUm delta 12 UcmX@Skn!L`#tEM{em&<804u5o4*&oF diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index b822004ad6f28e..bcf57d86c2a479 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -187,6 +187,10 @@ static struct testcase get_parts_list[] ={ {"file:///C:\\programs\\foo", "file | [11] | [12] | [13] | [14] | [15] | C:\\programs\\foo | [16] | [17]", CURLU_DEFAULT_SCHEME, 0, CURLUE_OK}, + {"file://host.example.com/Share/path/to/file.txt", + "file | [11] | [12] | [13] | host.example.com | [15] | " + "//host.example.com/Share/path/to/file.txt | [16] | [17]", + CURLU_DEFAULT_SCHEME, 0, CURLUE_OK}, #endif {"https://example.com/color/#green?no-red", "https | [11] | [12] | [13] | example.com | [15] | /color/ | [16] | "