1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
22 #include "tool_setup.h"
24 #if defined(MSDOS) || defined(WIN32)
26 #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
31 # include "tool_cfgable.h"
32 # include "tool_libinfo.h"
35 #include "tool_bname.h"
36 #include "tool_doswin.h"
38 #include "memdebug.h" /* keep this as LAST include */
41 * Macros ALWAYS_TRUE and ALWAYS_FALSE are used to avoid compiler warnings.
44 #define ALWAYS_TRUE (1)
45 #define ALWAYS_FALSE (0)
47 #if defined(_MSC_VER) && !defined(__POCC__)
50 # if (_MSC_VER < 1500)
51 # define ALWAYS_TRUE (0, 1)
52 # define ALWAYS_FALSE (1, 0)
54 # define ALWAYS_TRUE \
55 __pragma(warning(push)) \
56 __pragma(warning(disable:4127)) \
58 __pragma(warning(pop))
59 # define ALWAYS_FALSE \
60 __pragma(warning(push)) \
61 __pragma(warning(disable:4127)) \
63 __pragma(warning(pop))
69 # define PATH_MAX MAX_PATH
74 # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
76 # define S_ISCHR(m) (0) /* cannot tell if file is a device */
81 # define _use_lfn(f) ALWAYS_TRUE /* long file names always available */
82 #elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */
83 # define _use_lfn(f) ALWAYS_FALSE /* long file names never available */
84 #elif defined(__DJGPP__)
85 # include <fcntl.h> /* _use_lfn(f) prototype */
89 static SANITIZEcode truncate_dryrun(const char *path,
90 const size_t truncate_pos);
92 static SANITIZEcode msdosify(char **const sanitized, const char *file_name,
95 static SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
96 const char *file_name,
98 #endif /* !UNITTESTS (static declarations used if no unit tests) */
102 Sanitize a file or path name.
104 All banned characters are replaced by underscores, for example:
106 f:foo::$DATA => f_foo__$DATA
107 f:\foo:bar => f__foo_bar
108 f:\foo:bar => f:\foo:bar (flag SANITIZE_ALLOW_PATH)
110 This function was implemented according to the guidelines in 'Naming Files,
111 Paths, and Namespaces' section 'Naming Conventions'.
112 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
116 SANITIZE_ALLOW_COLONS: Allow colons.
117 Without this flag colons are sanitized.
119 SANITIZE_ALLOW_PATH: Allow path separators and colons.
120 Without this flag path separators and colons are sanitized.
122 SANITIZE_ALLOW_RESERVED: Allow reserved device names.
123 Without this flag a reserved device name is renamed (COM1 => _COM1) unless it's
124 in a UNC prefixed path.
126 SANITIZE_ALLOW_TRUNCATE: Allow truncating a long filename.
127 Without this flag if the sanitized filename or path will be too long an error
128 occurs. With this flag the filename --and not any other parts of the path-- may
129 be truncated to at least a single character. A filename followed by an
130 alternate data stream (ADS) cannot be truncated in any case.
132 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
133 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
135 SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name,
141 size_t max_sanitized_len;
144 return SANITIZE_ERR_BAD_ARGUMENT;
149 return SANITIZE_ERR_BAD_ARGUMENT;
151 if((flags & SANITIZE_ALLOW_PATH)) {
153 if(file_name[0] == '\\' && file_name[1] == '\\')
154 /* UNC prefixed path \\ (eg \\?\C:\foo) */
155 max_sanitized_len = 32767-1;
158 max_sanitized_len = PATH_MAX-1;
161 /* The maximum length of a filename.
162 FILENAME_MAX is often the same as PATH_MAX, in other words it is 260 and
163 does not discount the path information therefore we shouldn't use it. */
164 max_sanitized_len = (PATH_MAX-1 > 255) ? 255 : PATH_MAX-1;
166 len = strlen(file_name);
167 if(len > max_sanitized_len) {
168 if(!(flags & SANITIZE_ALLOW_TRUNCATE) ||
169 truncate_dryrun(file_name, max_sanitized_len))
170 return SANITIZE_ERR_INVALID_PATH;
172 len = max_sanitized_len;
175 target = malloc(len + 1);
177 return SANITIZE_ERR_OUT_OF_MEMORY;
179 strncpy(target, file_name, len);
183 if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4))
184 /* Skip the literal path prefix \\?\ */
190 /* replace control characters and other banned characters */
194 if((1 <= *p && *p <= 31) ||
195 (!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *p == ':') ||
196 (!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) {
201 for(banned = "|<>\"?*"; *banned; ++banned) {
209 /* remove trailing spaces and periods if not allowing paths */
210 if(!(flags & SANITIZE_ALLOW_PATH) && len) {
216 if(*p != ' ' && *p != '.')
219 } while(p != target);
228 sc = msdosify(&p, target, flags);
233 len = strlen(target);
235 if(len > max_sanitized_len) {
237 return SANITIZE_ERR_INVALID_PATH;
241 if(!(flags & SANITIZE_ALLOW_RESERVED)) {
242 sc = rename_if_reserved_dos_device_name(&p, target, flags);
247 len = strlen(target);
249 if(len > max_sanitized_len) {
251 return SANITIZE_ERR_INVALID_PATH;
256 return SANITIZE_ERR_OK;
261 Test if truncating a path to a file will leave at least a single character in
262 the filename. Filenames suffixed by an alternate data stream can't be
263 truncated. This performs a dry run, nothing is modified.
265 Good truncate_pos 9: C:\foo\bar => C:\foo\ba
266 Good truncate_pos 6: C:\foo => C:\foo
267 Good truncate_pos 5: C:\foo => C:\fo
268 Bad* truncate_pos 5: C:foo => C:foo
269 Bad truncate_pos 5: C:\foo:ads => C:\fo
270 Bad truncate_pos 9: C:\foo:ads => C:\foo:ad
271 Bad truncate_pos 5: C:\foo\bar => C:\fo
272 Bad truncate_pos 5: C:\foo\ => C:\fo
273 Bad truncate_pos 7: C:\foo\ => C:\foo\
274 Error truncate_pos 7: C:\foo => (pos out of range)
275 Bad truncate_pos 1: C:\foo\ => C
277 * C:foo is ambiguous, C could end up being a drive or file therefore something
278 like C:superlongfilename can't be truncated.
281 SANITIZE_ERR_OK: Good -- 'path' can be truncated
282 SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated
283 != SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error
285 SANITIZEcode truncate_dryrun(const char *path, const size_t truncate_pos)
290 return SANITIZE_ERR_BAD_ARGUMENT;
294 if(truncate_pos > len)
295 return SANITIZE_ERR_BAD_ARGUMENT;
297 if(!len || !truncate_pos)
298 return SANITIZE_ERR_INVALID_PATH;
300 if(strpbrk(&path[truncate_pos - 1], "\\/:"))
301 return SANITIZE_ERR_INVALID_PATH;
303 /* C:\foo can be truncated but C:\foo:ads can't */
304 if(truncate_pos > 1) {
305 const char *p = &path[truncate_pos - 1];
309 return SANITIZE_ERR_INVALID_PATH;
310 } while(p != path && *p != '\\' && *p != '/');
313 return SANITIZE_ERR_OK;
316 /* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function
317 * were taken with modification from the DJGPP port of tar 1.12. They use
318 * algorithms originally from DJTAR.
322 Extra sanitization MSDOS for file_name.
324 This is a supporting function for sanitize_file_name.
326 Warning: This is an MSDOS legacy function and was purposely written in a way
327 that some path information may pass through. For example drive letter names
328 (C:, D:, etc) are allowed to pass through. For sanitizing a filename use
331 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
332 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
334 #if defined(MSDOS) || defined(UNITTESTS)
335 SANITIZEcode msdosify(char **const sanitized, const char *file_name,
338 char dos_name[PATH_MAX];
339 static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */
340 "|<>/\\\":?*"; /* illegal in DOS & W95 */
341 static const char *illegal_chars_w95 = &illegal_chars_dos[8];
343 const char *s = file_name;
345 const char *const dlimit = dos_name + sizeof(dos_name) - 1;
346 const char *illegal_aliens = illegal_chars_dos;
347 size_t len = sizeof(illegal_chars_dos) - 1;
350 return SANITIZE_ERR_BAD_ARGUMENT;
355 return SANITIZE_ERR_BAD_ARGUMENT;
357 if(strlen(file_name) > PATH_MAX-1 &&
358 (!(flags & SANITIZE_ALLOW_TRUNCATE) ||
359 truncate_dryrun(file_name, PATH_MAX-1)))
360 return SANITIZE_ERR_INVALID_PATH;
362 /* Support for Windows 9X VFAT systems, when available. */
363 if(_use_lfn(file_name)) {
364 illegal_aliens = illegal_chars_w95;
365 len -= (illegal_chars_w95 - illegal_chars_dos);
368 /* Get past the drive letter, if any. */
369 if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
371 *d = ((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) ? ':' : '_';
375 for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) {
376 if(memchr(illegal_aliens, *s, len)) {
378 if((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *s == ':')
380 else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\'))
382 /* Dots are special: DOS doesn't allow them as the leading character,
383 and a file name cannot have more than a single dot. We leave the
384 first non-leading dot alone, unless it comes too close to the
385 beginning of the name: we want sh.lex.c to become sh_lex.c, not
388 if((flags & SANITIZE_ALLOW_PATH) && idx == 0 &&
389 (s[1] == '/' || s[1] == '\\' ||
390 (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) {
391 /* Copy "./" and "../" verbatim. */
404 else if(dot_idx >= 0) {
405 if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */
406 d[dot_idx - idx] = '_'; /* replace previous dot */
418 else if(*s == '+' && s[1] == '+') {
419 if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */
434 memcpy(d, "plus", 4);
446 if(*s == '/' || *s == '\\') {
456 /* dos_name is truncated, check that truncation requirements are met,
457 specifically truncating a filename suffixed by an alternate data stream
458 or truncating the entire filename is not allowed. */
459 if(!(flags & SANITIZE_ALLOW_TRUNCATE) || strpbrk(s, "\\/:") ||
460 truncate_dryrun(dos_name, d - dos_name))
461 return SANITIZE_ERR_INVALID_PATH;
464 *sanitized = strdup(dos_name);
465 return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
467 #endif /* MSDOS || UNITTESTS */
470 Rename file_name if it's a reserved dos device name.
472 This is a supporting function for sanitize_file_name.
474 Warning: This is an MSDOS legacy function and was purposely written in a way
475 that some path information may pass through. For example drive letter names
476 (C:, D:, etc) are allowed to pass through. For sanitizing a filename use
479 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
480 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
482 SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
483 const char *file_name,
486 /* We could have a file whose name is a device on MS-DOS. Trying to
487 * retrieve such a file would fail at best and wedge us at worst. We need
488 * to rename such files. */
490 char fname[PATH_MAX];
496 return SANITIZE_ERR_BAD_ARGUMENT;
501 return SANITIZE_ERR_BAD_ARGUMENT;
503 /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */
505 if((flags & SANITIZE_ALLOW_PATH) &&
506 file_name[0] == '\\' && file_name[1] == '\\') {
507 size_t len = strlen(file_name);
508 *sanitized = malloc(len + 1);
510 return SANITIZE_ERR_OUT_OF_MEMORY;
511 strncpy(*sanitized, file_name, len + 1);
512 return SANITIZE_ERR_OK;
516 if(strlen(file_name) > PATH_MAX-1 &&
517 (!(flags & SANITIZE_ALLOW_TRUNCATE) ||
518 truncate_dryrun(file_name, PATH_MAX-1)))
519 return SANITIZE_ERR_INVALID_PATH;
521 strncpy(fname, file_name, PATH_MAX-1);
522 fname[PATH_MAX-1] = '\0';
523 base = basename(fname);
525 /* Rename reserved device names that are known to be accessible without \\.\
526 Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS
527 https://support.microsoft.com/en-us/kb/74496
528 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
530 for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) {
532 int x = (curl_strnequal(p, "CON", 3) ||
533 curl_strnequal(p, "PRN", 3) ||
534 curl_strnequal(p, "AUX", 3) ||
535 curl_strnequal(p, "NUL", 3)) ? 3 :
536 (curl_strnequal(p, "CLOCK$", 6)) ? 6 :
537 (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ?
538 (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0;
543 /* the devices may be accessible with an extension or ADS, for
544 example CON.AIR and 'CON . AIR' and CON:AIR access console */
546 for(; p[x] == ' '; ++x)
553 else if(p[x] == ':') {
554 if(!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) {
560 else if(p[x]) /* no match */
563 /* p points to 'CON' or 'CON ' or 'CON:', etc */
567 if(strlen(fname) == PATH_MAX-1) {
569 if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(p, p_len))
570 return SANITIZE_ERR_INVALID_PATH;
573 memmove(p + 1, p, p_len + 1);
577 /* if fname was just modified then the basename pointer must be updated */
579 base = basename(fname);
582 /* This is the legacy portion from rename_if_dos_device_name that checks for
583 reserved device names. It only works on MSDOS. On Windows XP the stat
584 check errors with EINVAL if the device name is reserved. On Windows
585 Vista/7/8 it sets mode S_IFREG (regular file or device). According to MSDN
586 stat doc the latter behavior is correct, but that doesn't help us identify
587 whether it's a reserved device name and not a regular file name. */
589 if(base && ((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
591 size_t blen = strlen(base);
593 if(strlen(fname) == PATH_MAX-1) {
595 if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(base, blen))
596 return SANITIZE_ERR_INVALID_PATH;
599 memmove(base + 1, base, blen + 1);
606 *sanitized = strdup(fname);
607 return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
610 #if defined(MSDOS) && (defined(__DJGPP__) || defined(__GO32__))
613 * Disable program default argument globbing. We do it on our own.
615 char **__crt0_glob_function(char *arg)
621 #endif /* MSDOS && (__DJGPP__ || __GO32__) */
626 * Function to find CACert bundle on a Win32 platform using SearchPath.
627 * (SearchPath is already declared via inclusions done in setup header file)
628 * (Use the ASCII version instead of the unicode one!)
629 * The order of the directories it searches is:
630 * 1. application's directory
631 * 2. current working directory
632 * 3. Windows System directory (e.g. C:\windows\system32)
633 * 4. Windows Directory (e.g. C:\windows)
634 * 5. all directories along %PATH%
636 * For WinXP and later search order actually depends on registry value:
637 * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode
640 CURLcode FindWin32CACert(struct OperationConfig *config,
641 const char *bundle_file)
643 CURLcode result = CURLE_OK;
645 /* search and set cert file only if libcurl supports SSL */
646 if(curlinfo->features & CURL_VERSION_SSL) {
654 res_len = SearchPathA(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr);
656 Curl_safefree(config->cacert);
657 config->cacert = strdup(buf);
659 result = CURLE_OUT_OF_MEMORY;
668 #endif /* MSDOS || WIN32 */