[352] | 1 | /***************************************************************************
|
---|
| 2 | * _ _ ____ _
|
---|
| 3 | * Project ___| | | | _ \| |
|
---|
| 4 | * / __| | | | |_) | |
|
---|
| 5 | * | (__| |_| | _ <| |___
|
---|
| 6 | * \___|\___/|_| \_\_____|
|
---|
| 7 | *
|
---|
| 8 | * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
|
---|
| 9 | *
|
---|
| 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.
|
---|
| 13 | *
|
---|
| 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.
|
---|
| 17 | *
|
---|
| 18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
---|
| 19 | * KIND, either express or implied.
|
---|
| 20 | *
|
---|
| 21 | ***************************************************************************/
|
---|
| 22 | #include "tool_setup.h"
|
---|
| 23 |
|
---|
| 24 | #if defined(MSDOS) || defined(WIN32)
|
---|
| 25 |
|
---|
| 26 | #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
|
---|
| 27 | # include <libgen.h>
|
---|
| 28 | #endif
|
---|
| 29 |
|
---|
| 30 | #ifdef WIN32
|
---|
| 31 | # include "tool_cfgable.h"
|
---|
| 32 | # include "tool_libinfo.h"
|
---|
| 33 | #endif
|
---|
| 34 |
|
---|
| 35 | #include "tool_bname.h"
|
---|
| 36 | #include "tool_doswin.h"
|
---|
| 37 |
|
---|
| 38 | #include "memdebug.h" /* keep this as LAST include */
|
---|
| 39 |
|
---|
| 40 | /*
|
---|
| 41 | * Macros ALWAYS_TRUE and ALWAYS_FALSE are used to avoid compiler warnings.
|
---|
| 42 | */
|
---|
| 43 |
|
---|
| 44 | #define ALWAYS_TRUE (1)
|
---|
| 45 | #define ALWAYS_FALSE (0)
|
---|
| 46 |
|
---|
| 47 | #if defined(_MSC_VER) && !defined(__POCC__)
|
---|
| 48 | # undef ALWAYS_TRUE
|
---|
| 49 | # undef ALWAYS_FALSE
|
---|
| 50 | # if (_MSC_VER < 1500)
|
---|
| 51 | # define ALWAYS_TRUE (0, 1)
|
---|
| 52 | # define ALWAYS_FALSE (1, 0)
|
---|
| 53 | # else
|
---|
| 54 | # define ALWAYS_TRUE \
|
---|
| 55 | __pragma(warning(push)) \
|
---|
| 56 | __pragma(warning(disable:4127)) \
|
---|
| 57 | (1) \
|
---|
| 58 | __pragma(warning(pop))
|
---|
| 59 | # define ALWAYS_FALSE \
|
---|
| 60 | __pragma(warning(push)) \
|
---|
| 61 | __pragma(warning(disable:4127)) \
|
---|
| 62 | (0) \
|
---|
| 63 | __pragma(warning(pop))
|
---|
| 64 | # endif
|
---|
| 65 | #endif
|
---|
| 66 |
|
---|
| 67 | #ifdef WIN32
|
---|
| 68 | # undef PATH_MAX
|
---|
| 69 | # define PATH_MAX MAX_PATH
|
---|
| 70 | #endif
|
---|
| 71 |
|
---|
| 72 | #ifndef S_ISCHR
|
---|
| 73 | # ifdef S_IFCHR
|
---|
| 74 | # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
---|
| 75 | # else
|
---|
| 76 | # define S_ISCHR(m) (0) /* cannot tell if file is a device */
|
---|
| 77 | # endif
|
---|
| 78 | #endif
|
---|
| 79 |
|
---|
| 80 | #ifdef WIN32
|
---|
| 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 */
|
---|
| 86 | #endif
|
---|
| 87 |
|
---|
| 88 | #ifndef UNITTESTS
|
---|
| 89 | static SANITIZEcode truncate_dryrun(const char *path,
|
---|
| 90 | const size_t truncate_pos);
|
---|
| 91 | #ifdef MSDOS
|
---|
| 92 | static SANITIZEcode msdosify(char **const sanitized, const char *file_name,
|
---|
| 93 | int flags);
|
---|
| 94 | #endif
|
---|
| 95 | static SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
|
---|
| 96 | const char *file_name,
|
---|
| 97 | int flags);
|
---|
| 98 | #endif /* !UNITTESTS (static declarations used if no unit tests) */
|
---|
| 99 |
|
---|
| 100 |
|
---|
| 101 | /*
|
---|
| 102 | Sanitize a file or path name.
|
---|
| 103 |
|
---|
| 104 | All banned characters are replaced by underscores, for example:
|
---|
| 105 | f?*foo => f__foo
|
---|
| 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)
|
---|
| 109 |
|
---|
| 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
|
---|
| 113 |
|
---|
| 114 | Flags
|
---|
| 115 | -----
|
---|
| 116 | SANITIZE_ALLOW_COLONS: Allow colons.
|
---|
| 117 | Without this flag colons are sanitized.
|
---|
| 118 |
|
---|
| 119 | SANITIZE_ALLOW_PATH: Allow path separators and colons.
|
---|
| 120 | Without this flag path separators and colons are sanitized.
|
---|
| 121 |
|
---|
| 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.
|
---|
| 125 |
|
---|
| 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.
|
---|
| 131 |
|
---|
| 132 | Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
|
---|
| 133 | Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
|
---|
| 134 | */
|
---|
| 135 | SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name,
|
---|
| 136 | int flags)
|
---|
| 137 | {
|
---|
| 138 | char *p, *target;
|
---|
| 139 | size_t len;
|
---|
| 140 | SANITIZEcode sc;
|
---|
| 141 | size_t max_sanitized_len;
|
---|
| 142 |
|
---|
| 143 | if(!sanitized)
|
---|
| 144 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
| 145 |
|
---|
| 146 | *sanitized = NULL;
|
---|
| 147 |
|
---|
| 148 | if(!file_name)
|
---|
| 149 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
| 150 |
|
---|
| 151 | if((flags & SANITIZE_ALLOW_PATH)) {
|
---|
| 152 | #ifndef MSDOS
|
---|
| 153 | if(file_name[0] == '\\' && file_name[1] == '\\')
|
---|
| 154 | /* UNC prefixed path \\ (eg \\?\C:\foo) */
|
---|
| 155 | max_sanitized_len = 32767-1;
|
---|
| 156 | else
|
---|
| 157 | #endif
|
---|
| 158 | max_sanitized_len = PATH_MAX-1;
|
---|
| 159 | }
|
---|
| 160 | else
|
---|
| 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;
|
---|
| 165 |
|
---|
| 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;
|
---|
| 171 |
|
---|
| 172 | len = max_sanitized_len;
|
---|
| 173 | }
|
---|
| 174 |
|
---|
| 175 | target = malloc(len + 1);
|
---|
| 176 | if(!target)
|
---|
| 177 | return SANITIZE_ERR_OUT_OF_MEMORY;
|
---|
| 178 |
|
---|
| 179 | strncpy(target, file_name, len);
|
---|
| 180 | target[len] = '\0';
|
---|
| 181 |
|
---|
| 182 | #ifndef MSDOS
|
---|
| 183 | if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4))
|
---|
| 184 | /* Skip the literal path prefix \\?\ */
|
---|
| 185 | p = target + 4;
|
---|
| 186 | else
|
---|
| 187 | #endif
|
---|
| 188 | p = target;
|
---|
| 189 |
|
---|
| 190 | /* replace control characters and other banned characters */
|
---|
| 191 | for(; *p; ++p) {
|
---|
| 192 | const char *banned;
|
---|
| 193 |
|
---|
| 194 | if((1 <= *p && *p <= 31) ||
|
---|
| 195 | (!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *p == ':') ||
|
---|
| 196 | (!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) {
|
---|
| 197 | *p = '_';
|
---|
| 198 | continue;
|
---|
| 199 | }
|
---|
| 200 |
|
---|
| 201 | for(banned = "|<>\"?*"; *banned; ++banned) {
|
---|
| 202 | if(*p == *banned) {
|
---|
| 203 | *p = '_';
|
---|
| 204 | break;
|
---|
| 205 | }
|
---|
| 206 | }
|
---|
| 207 | }
|
---|
| 208 |
|
---|
| 209 | /* remove trailing spaces and periods if not allowing paths */
|
---|
| 210 | if(!(flags & SANITIZE_ALLOW_PATH) && len) {
|
---|
| 211 | char *clip = NULL;
|
---|
| 212 |
|
---|
| 213 | p = &target[len];
|
---|
| 214 | do {
|
---|
| 215 | --p;
|
---|
| 216 | if(*p != ' ' && *p != '.')
|
---|
| 217 | break;
|
---|
| 218 | clip = p;
|
---|
| 219 | } while(p != target);
|
---|
| 220 |
|
---|
| 221 | if(clip) {
|
---|
| 222 | *clip = '\0';
|
---|
| 223 | len = clip - target;
|
---|
| 224 | }
|
---|
| 225 | }
|
---|
| 226 |
|
---|
| 227 | #ifdef MSDOS
|
---|
| 228 | sc = msdosify(&p, target, flags);
|
---|
| 229 | free(target);
|
---|
| 230 | if(sc)
|
---|
| 231 | return sc;
|
---|
| 232 | target = p;
|
---|
| 233 | len = strlen(target);
|
---|
| 234 |
|
---|
| 235 | if(len > max_sanitized_len) {
|
---|
| 236 | free(target);
|
---|
| 237 | return SANITIZE_ERR_INVALID_PATH;
|
---|
| 238 | }
|
---|
| 239 | #endif
|
---|
| 240 |
|
---|
| 241 | if(!(flags & SANITIZE_ALLOW_RESERVED)) {
|
---|
| 242 | sc = rename_if_reserved_dos_device_name(&p, target, flags);
|
---|
| 243 | free(target);
|
---|
| 244 | if(sc)
|
---|
| 245 | return sc;
|
---|
| 246 | target = p;
|
---|
| 247 | len = strlen(target);
|
---|
| 248 |
|
---|
| 249 | if(len > max_sanitized_len) {
|
---|
| 250 | free(target);
|
---|
| 251 | return SANITIZE_ERR_INVALID_PATH;
|
---|
| 252 | }
|
---|
| 253 | }
|
---|
| 254 |
|
---|
| 255 | *sanitized = target;
|
---|
| 256 | return SANITIZE_ERR_OK;
|
---|
| 257 | }
|
---|
| 258 |
|
---|
| 259 |
|
---|
| 260 | /*
|
---|
| 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.
|
---|
| 264 |
|
---|
| 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
|
---|
| 276 |
|
---|
| 277 | * C:foo is ambiguous, C could end up being a drive or file therefore something
|
---|
| 278 | like C:superlongfilename can't be truncated.
|
---|
| 279 |
|
---|
| 280 | Returns
|
---|
| 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
|
---|
| 284 | */
|
---|
| 285 | SANITIZEcode truncate_dryrun(const char *path, const size_t truncate_pos)
|
---|
| 286 | {
|
---|
| 287 | size_t len;
|
---|
| 288 |
|
---|
| 289 | if(!path)
|
---|
| 290 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
| 291 |
|
---|
| 292 | len = strlen(path);
|
---|
| 293 |
|
---|
| 294 | if(truncate_pos > len)
|
---|
| 295 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
| 296 |
|
---|
| 297 | if(!len || !truncate_pos)
|
---|
| 298 | return SANITIZE_ERR_INVALID_PATH;
|
---|
| 299 |
|
---|
| 300 | if(strpbrk(&path[truncate_pos - 1], "\\/:"))
|
---|
| 301 | return SANITIZE_ERR_INVALID_PATH;
|
---|
| 302 |
|
---|
| 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];
|
---|
| 306 | do {
|
---|
| 307 | --p;
|
---|
| 308 | if(*p == ':')
|
---|
| 309 | return SANITIZE_ERR_INVALID_PATH;
|
---|
| 310 | } while(p != path && *p != '\\' && *p != '/');
|
---|
| 311 | }
|
---|
| 312 |
|
---|
| 313 | return SANITIZE_ERR_OK;
|
---|
| 314 | }
|
---|
| 315 |
|
---|
| 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.
|
---|
| 319 | */
|
---|
| 320 |
|
---|
| 321 | /*
|
---|
| 322 | Extra sanitization MSDOS for file_name.
|
---|
| 323 |
|
---|
| 324 | This is a supporting function for sanitize_file_name.
|
---|
| 325 |
|
---|
| 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
|
---|
| 329 | sanitize_file_name.
|
---|
| 330 |
|
---|
| 331 | Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
|
---|
| 332 | Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
|
---|
| 333 | */
|
---|
| 334 | #if defined(MSDOS) || defined(UNITTESTS)
|
---|
| 335 | SANITIZEcode msdosify(char **const sanitized, const char *file_name,
|
---|
| 336 | int flags)
|
---|
| 337 | {
|
---|
| 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];
|
---|
| 342 | int idx, dot_idx;
|
---|
| 343 | const char *s = file_name;
|
---|
| 344 | char *d = dos_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;
|
---|
| 348 |
|
---|
| 349 | if(!sanitized)
|
---|
| 350 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
| 351 |
|
---|
| 352 | *sanitized = NULL;
|
---|
| 353 |
|
---|
| 354 | if(!file_name)
|
---|
| 355 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
| 356 |
|
---|
| 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;
|
---|
| 361 |
|
---|
| 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);
|
---|
| 366 | }
|
---|
| 367 |
|
---|
| 368 | /* Get past the drive letter, if any. */
|
---|
| 369 | if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
|
---|
| 370 | *d++ = *s++;
|
---|
| 371 | *d = ((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) ? ':' : '_';
|
---|
| 372 | ++d, ++s;
|
---|
| 373 | }
|
---|
| 374 |
|
---|
| 375 | for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) {
|
---|
| 376 | if(memchr(illegal_aliens, *s, len)) {
|
---|
| 377 |
|
---|
| 378 | if((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *s == ':')
|
---|
| 379 | *d = ':';
|
---|
| 380 | else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\'))
|
---|
| 381 | *d = *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
|
---|
| 386 | sh.lex-c. */
|
---|
| 387 | else if(*s == '.') {
|
---|
| 388 | if((flags & SANITIZE_ALLOW_PATH) && idx == 0 &&
|
---|
| 389 | (s[1] == '/' || s[1] == '\\' ||
|
---|
| 390 | (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) {
|
---|
| 391 | /* Copy "./" and "../" verbatim. */
|
---|
| 392 | *d++ = *s++;
|
---|
| 393 | if(d == dlimit)
|
---|
| 394 | break;
|
---|
| 395 | if(*s == '.') {
|
---|
| 396 | *d++ = *s++;
|
---|
| 397 | if(d == dlimit)
|
---|
| 398 | break;
|
---|
| 399 | }
|
---|
| 400 | *d = *s;
|
---|
| 401 | }
|
---|
| 402 | else if(idx == 0)
|
---|
| 403 | *d = '_';
|
---|
| 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 */
|
---|
| 407 | *d = '.';
|
---|
| 408 | }
|
---|
| 409 | else
|
---|
| 410 | *d = '-';
|
---|
| 411 | }
|
---|
| 412 | else
|
---|
| 413 | *d = '.';
|
---|
| 414 |
|
---|
| 415 | if(*s == '.')
|
---|
| 416 | dot_idx = idx;
|
---|
| 417 | }
|
---|
| 418 | else if(*s == '+' && s[1] == '+') {
|
---|
| 419 | if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */
|
---|
| 420 | *d++ = 'x';
|
---|
| 421 | if(d == dlimit)
|
---|
| 422 | break;
|
---|
| 423 | *d = 'x';
|
---|
| 424 | }
|
---|
| 425 | else {
|
---|
| 426 | /* libg++ etc. */
|
---|
| 427 | if(dlimit - d < 4) {
|
---|
| 428 | *d++ = 'x';
|
---|
| 429 | if(d == dlimit)
|
---|
| 430 | break;
|
---|
| 431 | *d = 'x';
|
---|
| 432 | }
|
---|
| 433 | else {
|
---|
| 434 | memcpy(d, "plus", 4);
|
---|
| 435 | d += 3;
|
---|
| 436 | }
|
---|
| 437 | }
|
---|
| 438 | s++;
|
---|
| 439 | idx++;
|
---|
| 440 | }
|
---|
| 441 | else
|
---|
| 442 | *d = '_';
|
---|
| 443 | }
|
---|
| 444 | else
|
---|
| 445 | *d = *s;
|
---|
| 446 | if(*s == '/' || *s == '\\') {
|
---|
| 447 | idx = 0;
|
---|
| 448 | dot_idx = -1;
|
---|
| 449 | }
|
---|
| 450 | else
|
---|
| 451 | idx++;
|
---|
| 452 | }
|
---|
| 453 | *d = '\0';
|
---|
| 454 |
|
---|
| 455 | if(*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;
|
---|
| 462 | }
|
---|
| 463 |
|
---|
| 464 | *sanitized = strdup(dos_name);
|
---|
| 465 | return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
|
---|
| 466 | }
|
---|
| 467 | #endif /* MSDOS || UNITTESTS */
|
---|
| 468 |
|
---|
| 469 | /*
|
---|
| 470 | Rename file_name if it's a reserved dos device name.
|
---|
| 471 |
|
---|
| 472 | This is a supporting function for sanitize_file_name.
|
---|
| 473 |
|
---|
| 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
|
---|
| 477 | sanitize_file_name.
|
---|
| 478 |
|
---|
| 479 | Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
|
---|
| 480 | Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
|
---|
| 481 | */
|
---|
| 482 | SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
|
---|
| 483 | const char *file_name,
|
---|
| 484 | int flags)
|
---|
| 485 | {
|
---|
| 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. */
|
---|
| 489 | char *p, *base;
|
---|
| 490 | char fname[PATH_MAX];
|
---|
| 491 | #ifdef MSDOS
|
---|
| 492 | struct_stat st_buf;
|
---|
| 493 | #endif
|
---|
| 494 |
|
---|
| 495 | if(!sanitized)
|
---|
| 496 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
| 497 |
|
---|
| 498 | *sanitized = NULL;
|
---|
| 499 |
|
---|
| 500 | if(!file_name)
|
---|
| 501 | return SANITIZE_ERR_BAD_ARGUMENT;
|
---|
| 502 |
|
---|
| 503 | /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */
|
---|
| 504 | #ifndef MSDOS
|
---|
| 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);
|
---|
| 509 | if(!*sanitized)
|
---|
| 510 | return SANITIZE_ERR_OUT_OF_MEMORY;
|
---|
| 511 | strncpy(*sanitized, file_name, len + 1);
|
---|
| 512 | return SANITIZE_ERR_OK;
|
---|
| 513 | }
|
---|
| 514 | #endif
|
---|
| 515 |
|
---|
| 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;
|
---|
| 520 |
|
---|
| 521 | strncpy(fname, file_name, PATH_MAX-1);
|
---|
| 522 | fname[PATH_MAX-1] = '\0';
|
---|
| 523 | base = basename(fname);
|
---|
| 524 |
|
---|
| 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
|
---|
| 529 | */
|
---|
| 530 | for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) {
|
---|
| 531 | size_t p_len;
|
---|
| 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;
|
---|
| 539 |
|
---|
| 540 | if(!x)
|
---|
| 541 | continue;
|
---|
| 542 |
|
---|
| 543 | /* the devices may be accessible with an extension or ADS, for
|
---|
| 544 | example CON.AIR and 'CON . AIR' and CON:AIR access console */
|
---|
| 545 |
|
---|
| 546 | for(; p[x] == ' '; ++x)
|
---|
| 547 | ;
|
---|
| 548 |
|
---|
| 549 | if(p[x] == '.') {
|
---|
| 550 | p[x] = '_';
|
---|
| 551 | continue;
|
---|
| 552 | }
|
---|
| 553 | else if(p[x] == ':') {
|
---|
| 554 | if(!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) {
|
---|
| 555 | p[x] = '_';
|
---|
| 556 | continue;
|
---|
| 557 | }
|
---|
| 558 | ++x;
|
---|
| 559 | }
|
---|
| 560 | else if(p[x]) /* no match */
|
---|
| 561 | continue;
|
---|
| 562 |
|
---|
| 563 | /* p points to 'CON' or 'CON ' or 'CON:', etc */
|
---|
| 564 | p_len = strlen(p);
|
---|
| 565 |
|
---|
| 566 | /* Prepend a '_' */
|
---|
| 567 | if(strlen(fname) == PATH_MAX-1) {
|
---|
| 568 | --p_len;
|
---|
| 569 | if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(p, p_len))
|
---|
| 570 | return SANITIZE_ERR_INVALID_PATH;
|
---|
| 571 | p[p_len] = '\0';
|
---|
| 572 | }
|
---|
| 573 | memmove(p + 1, p, p_len + 1);
|
---|
| 574 | p[0] = '_';
|
---|
| 575 | ++p_len;
|
---|
| 576 |
|
---|
| 577 | /* if fname was just modified then the basename pointer must be updated */
|
---|
| 578 | if(p == fname)
|
---|
| 579 | base = basename(fname);
|
---|
| 580 | }
|
---|
| 581 |
|
---|
| 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. */
|
---|
| 588 | #ifdef MSDOS
|
---|
| 589 | if(base && ((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
|
---|
| 590 | /* Prepend a '_' */
|
---|
| 591 | size_t blen = strlen(base);
|
---|
| 592 | if(blen) {
|
---|
| 593 | if(strlen(fname) == PATH_MAX-1) {
|
---|
| 594 | --blen;
|
---|
| 595 | if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(base, blen))
|
---|
| 596 | return SANITIZE_ERR_INVALID_PATH;
|
---|
| 597 | base[blen] = '\0';
|
---|
| 598 | }
|
---|
| 599 | memmove(base + 1, base, blen + 1);
|
---|
| 600 | base[0] = '_';
|
---|
| 601 | ++blen;
|
---|
| 602 | }
|
---|
| 603 | }
|
---|
| 604 | #endif
|
---|
| 605 |
|
---|
| 606 | *sanitized = strdup(fname);
|
---|
| 607 | return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
|
---|
| 608 | }
|
---|
| 609 |
|
---|
| 610 | #if defined(MSDOS) && (defined(__DJGPP__) || defined(__GO32__))
|
---|
| 611 |
|
---|
| 612 | /*
|
---|
| 613 | * Disable program default argument globbing. We do it on our own.
|
---|
| 614 | */
|
---|
| 615 | char **__crt0_glob_function(char *arg)
|
---|
| 616 | {
|
---|
| 617 | (void)arg;
|
---|
| 618 | return (char **)0;
|
---|
| 619 | }
|
---|
| 620 |
|
---|
| 621 | #endif /* MSDOS && (__DJGPP__ || __GO32__) */
|
---|
| 622 |
|
---|
| 623 | #ifdef WIN32
|
---|
| 624 |
|
---|
| 625 | /*
|
---|
| 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%
|
---|
| 635 | *
|
---|
| 636 | * For WinXP and later search order actually depends on registry value:
|
---|
| 637 | * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode
|
---|
| 638 | */
|
---|
| 639 |
|
---|
| 640 | CURLcode FindWin32CACert(struct OperationConfig *config,
|
---|
| 641 | const char *bundle_file)
|
---|
| 642 | {
|
---|
| 643 | CURLcode result = CURLE_OK;
|
---|
| 644 |
|
---|
| 645 | /* search and set cert file only if libcurl supports SSL */
|
---|
| 646 | if(curlinfo->features & CURL_VERSION_SSL) {
|
---|
| 647 |
|
---|
| 648 | DWORD res_len;
|
---|
| 649 | char buf[PATH_MAX];
|
---|
| 650 | char *ptr = NULL;
|
---|
| 651 |
|
---|
| 652 | buf[0] = '\0';
|
---|
| 653 |
|
---|
| 654 | res_len = SearchPathA(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr);
|
---|
| 655 | if(res_len > 0) {
|
---|
| 656 | Curl_safefree(config->cacert);
|
---|
| 657 | config->cacert = strdup(buf);
|
---|
| 658 | if(!config->cacert)
|
---|
| 659 | result = CURLE_OUT_OF_MEMORY;
|
---|
| 660 | }
|
---|
| 661 | }
|
---|
| 662 |
|
---|
| 663 | return result;
|
---|
| 664 | }
|
---|
| 665 |
|
---|
| 666 | #endif /* WIN32 */
|
---|
| 667 |
|
---|
| 668 | #endif /* MSDOS || WIN32 */
|
---|