diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 36145bc4b4..034462b384 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -88,8 +88,11 @@ SANITIZE_ALLOW_PATH: Allow path separators and colons. Without this flag path separators and colons are sanitized. SANITIZE_ALLOW_RESERVED: Allow reserved device names. -Without this flag a reserved device name is renamed (COM1 => _COM1) unless it -is in a UNC prefixed path. +Without this flag a reserved device name is renamed (COM1 => _COM1). + +To fully block reserved device names requires not passing either flag. Some +less common path styles are allowed to use reserved device names. For example, +a "\\" prefixed path may use reserved device names if paths are allowed. Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. @@ -110,15 +113,13 @@ SANITIZEcode sanitize_file_name(char ** const sanitized, const char *file_name, if(!file_name) return SANITIZE_ERR_BAD_ARGUMENT; - if(flags & SANITIZE_ALLOW_PATH) { -#ifndef MSDOS - if(file_name[0] == '\\' && file_name[1] == '\\') - /* UNC prefixed path \\ (eg \\?\C:\foo) */ - max_sanitized_len = 32767 - 1; - else + if(flags & SANITIZE_ALLOW_PATH) +#ifdef MSDOS + max_sanitized_len = PATH_MAX - 1; +#else + /* Windows extended-length path max */ + max_sanitized_len = 32767 - 1; #endif - max_sanitized_len = PATH_MAX - 1; - } else /* The maximum length of a filename. FILENAME_MAX is often the same as PATH_MAX, in other words it is 260 and does not discount the path @@ -135,7 +136,7 @@ SANITIZEcode sanitize_file_name(char ** const sanitized, const char *file_name, #ifndef MSDOS if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4)) - /* Skip the literal path prefix \\?\ */ + /* Skip the literal-path prefix \\?\ */ p = target + 4; else #endif @@ -438,20 +439,18 @@ static SANITIZEcode rename_if_reserved_dos(char ** const sanitized, /* We could have a file whose name is a device on MS-DOS. Trying to * retrieve such a file would fail at best and wedge us at worst. We need * to rename such files. */ - char *p, *base; - char fname[PATH_MAX]; + char *p, *base, *buffer; #ifdef MSDOS struct_stat st_buf; #endif - size_t len; + size_t len, bufsize; if(!sanitized || !file_name) return SANITIZE_ERR_BAD_ARGUMENT; *sanitized = NULL; - len = strlen(file_name); - /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */ + /* Ignore "\\" prefixed paths, they are allowed to use reserved names. */ #ifndef MSDOS if((flags & SANITIZE_ALLOW_PATH) && file_name[0] == '\\' && file_name[1] == '\\') { @@ -462,19 +461,25 @@ static SANITIZEcode rename_if_reserved_dos(char ** const sanitized, } #endif - if(len > PATH_MAX - 1) - return SANITIZE_ERR_INVALID_PATH; + /* The buffer contains two extra bytes to allow for path expansion that + occurs if reserved name(s) need an underscore prepended. */ + len = strlen(file_name); + bufsize = len + 2 + 1; - memcpy(fname, file_name, len); - fname[len] = '\0'; - base = basename(fname); + buffer = curlx_malloc(bufsize); + if(!buffer) + return SANITIZE_ERR_OUT_OF_MEMORY; + + memcpy(buffer, file_name, len + 1); + + base = basename(buffer); /* Rename reserved device names that are known to be accessible without \\.\ Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS https://web.archive.org/web/20160314141551/support.microsoft.com/en-us/kb/74496 https://learn.microsoft.com/windows/win32/fileio/naming-a-file */ - for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) { + for(p = buffer; p; p = (p == buffer && buffer != base ? base : NULL)) { size_t p_len; int x = (curl_strnequal(p, "CON", 3) || curl_strnequal(p, "PRN", 3) || @@ -511,15 +516,18 @@ static SANITIZEcode rename_if_reserved_dos(char ** const sanitized, p_len = strlen(p); /* Prepend a '_' */ - if(strlen(fname) == PATH_MAX - 1) + if(len == bufsize - 1) { + curlx_free(buffer); return SANITIZE_ERR_INVALID_PATH; + } memmove(p + 1, p, p_len + 1); p[0] = '_'; ++p_len; + ++len; - /* if fname was just modified then the basename pointer must be updated */ - if(p == fname) - base = basename(fname); + /* the basename pointer must be updated since the path has expanded */ + if(p == buffer) + base = basename(buffer); } /* This is the legacy portion from rename_if_dos_device_name that checks for @@ -534,16 +542,19 @@ static SANITIZEcode rename_if_reserved_dos(char ** const sanitized, /* Prepend a '_' */ size_t blen = strlen(base); if(blen) { - if(strlen(fname) >= PATH_MAX - 1) + if(len == bufsize - 1) { + curlx_free(buffer); return SANITIZE_ERR_INVALID_PATH; + } memmove(base + 1, base, blen + 1); base[0] = '_'; + ++len; } } #endif - *sanitized = curlx_strdup(fname); - return *sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY; + *sanitized = buffer; + return SANITIZE_ERR_OK; } #ifdef __DJGPP__ diff --git a/tests/tunit/tool1604.c b/tests/tunit/tool1604.c index dcfee4cbac..de665bbc5e 100644 --- a/tests/tunit/tool1604.c +++ b/tests/tunit/tool1604.c @@ -178,9 +178,6 @@ static CURLcode test_tool1604(const char *arg) { "COM56", 0, "COM56", SANITIZE_ERR_OK }, - /* At the moment we expect a maximum path length of 259. I assume MS-DOS - has variable max path lengths depending on compiler that are shorter - so currently these "good" truncate tests will not run on MS-DOS */ { "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"