1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2019, 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 <tlhelp32.h>
32 #  include "tool_cfgable.h"
33 #  include "tool_libinfo.h"
34 #endif
35 
36 #include "tool_bname.h"
37 #include "tool_doswin.h"
38 
39 #include "memdebug.h" /* keep this as LAST include */
40 
41 /*
42  * Macros ALWAYS_TRUE and ALWAYS_FALSE are used to avoid compiler warnings.
43  */
44 
45 #define ALWAYS_TRUE   (1)
46 #define ALWAYS_FALSE  (0)
47 
48 #if defined(_MSC_VER) && !defined(__POCC__)
49 #  undef ALWAYS_TRUE
50 #  undef ALWAYS_FALSE
51 #  if (_MSC_VER < 1500)
52 #    define ALWAYS_TRUE   (0, 1)
53 #    define ALWAYS_FALSE  (1, 0)
54 #  else
55 #    define ALWAYS_TRUE \
56 __pragma(warning(push)) \
57 __pragma(warning(disable:4127)) \
58 (1) \
59 __pragma(warning(pop))
60 #    define ALWAYS_FALSE \
61 __pragma(warning(push)) \
62 __pragma(warning(disable:4127)) \
63 (0) \
64 __pragma(warning(pop))
65 #  endif
66 #endif
67 
68 #ifdef WIN32
69 #  undef  PATH_MAX
70 #  define PATH_MAX MAX_PATH
71 #endif
72 
73 #ifndef S_ISCHR
74 #  ifdef S_IFCHR
75 #    define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
76 #  else
77 #    define S_ISCHR(m) (0) /* cannot tell if file is a device */
78 #  endif
79 #endif
80 
81 #ifdef WIN32
82 #  define _use_lfn(f) ALWAYS_TRUE   /* long file names always available */
83 #elif !defined(__DJGPP__) || (__DJGPP__ < 2)  /* DJGPP 2.0 has _use_lfn() */
84 #  define _use_lfn(f) ALWAYS_FALSE  /* long file names never available */
85 #elif defined(__DJGPP__)
86 #  include <fcntl.h>                /* _use_lfn(f) prototype */
87 #endif
88 
89 #ifndef UNITTESTS
90 static SANITIZEcode truncate_dryrun(const char *path,
91                                     const size_t truncate_pos);
92 #ifdef MSDOS
93 static SANITIZEcode msdosify(char **const sanitized, const char *file_name,
94                              int flags);
95 #endif
96 static SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
97                                                        const char *file_name,
98                                                        int flags);
99 #endif /* !UNITTESTS (static declarations used if no unit tests) */
100 
101 
102 /*
103 Sanitize a file or path name.
104 
105 All banned characters are replaced by underscores, for example:
106 f?*foo => f__foo
107 f:foo::$DATA => f_foo__$DATA
108 f:\foo:bar => f__foo_bar
109 f:\foo:bar => f:\foo:bar   (flag SANITIZE_ALLOW_PATH)
110 
111 This function was implemented according to the guidelines in 'Naming Files,
112 Paths, and Namespaces' section 'Naming Conventions'.
113 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
114 
115 Flags
116 -----
117 SANITIZE_ALLOW_COLONS:     Allow colons.
118 Without this flag colons are sanitized.
119 
120 SANITIZE_ALLOW_PATH:       Allow path separators and colons.
121 Without this flag path separators and colons are sanitized.
122 
123 SANITIZE_ALLOW_RESERVED:   Allow reserved device names.
124 Without this flag a reserved device name is renamed (COM1 => _COM1) unless it's
125 in a UNC prefixed path.
126 
127 SANITIZE_ALLOW_TRUNCATE:   Allow truncating a long filename.
128 Without this flag if the sanitized filename or path will be too long an error
129 occurs. With this flag the filename --and not any other parts of the path-- may
130 be truncated to at least a single character. A filename followed by an
131 alternate data stream (ADS) cannot be truncated in any case.
132 
133 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
134 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
135 */
sanitize_file_name(char ** const sanitized,const char * file_name,int flags)136 SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name,
137                                 int flags)
138 {
139   char *p, *target;
140   size_t len;
141   SANITIZEcode sc;
142   size_t max_sanitized_len;
143 
144   if(!sanitized)
145     return SANITIZE_ERR_BAD_ARGUMENT;
146 
147   *sanitized = NULL;
148 
149   if(!file_name)
150     return SANITIZE_ERR_BAD_ARGUMENT;
151 
152   if((flags & SANITIZE_ALLOW_PATH)) {
153 #ifndef MSDOS
154     if(file_name[0] == '\\' && file_name[1] == '\\')
155       /* UNC prefixed path \\ (eg \\?\C:\foo) */
156       max_sanitized_len = 32767-1;
157     else
158 #endif
159       max_sanitized_len = PATH_MAX-1;
160   }
161   else
162     /* The maximum length of a filename.
163        FILENAME_MAX is often the same as PATH_MAX, in other words it is 260 and
164        does not discount the path information therefore we shouldn't use it. */
165     max_sanitized_len = (PATH_MAX-1 > 255) ? 255 : PATH_MAX-1;
166 
167   len = strlen(file_name);
168   if(len > max_sanitized_len) {
169     if(!(flags & SANITIZE_ALLOW_TRUNCATE) ||
170        truncate_dryrun(file_name, max_sanitized_len))
171       return SANITIZE_ERR_INVALID_PATH;
172 
173     len = max_sanitized_len;
174   }
175 
176   target = malloc(len + 1);
177   if(!target)
178     return SANITIZE_ERR_OUT_OF_MEMORY;
179 
180   strncpy(target, file_name, len);
181   target[len] = '\0';
182 
183 #ifndef MSDOS
184   if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4))
185     /* Skip the literal path prefix \\?\ */
186     p = target + 4;
187   else
188 #endif
189     p = target;
190 
191   /* replace control characters and other banned characters */
192   for(; *p; ++p) {
193     const char *banned;
194 
195     if((1 <= *p && *p <= 31) ||
196        (!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *p == ':') ||
197        (!(flags & SANITIZE_ALLOW_PATH) && (*p == '/' || *p == '\\'))) {
198       *p = '_';
199       continue;
200     }
201 
202     for(banned = "|<>\"?*"; *banned; ++banned) {
203       if(*p == *banned) {
204         *p = '_';
205         break;
206       }
207     }
208   }
209 
210   /* remove trailing spaces and periods if not allowing paths */
211   if(!(flags & SANITIZE_ALLOW_PATH) && len) {
212     char *clip = NULL;
213 
214     p = &target[len];
215     do {
216       --p;
217       if(*p != ' ' && *p != '.')
218         break;
219       clip = p;
220     } while(p != target);
221 
222     if(clip) {
223       *clip = '\0';
224       len = clip - target;
225     }
226   }
227 
228 #ifdef MSDOS
229   sc = msdosify(&p, target, flags);
230   free(target);
231   if(sc)
232     return sc;
233   target = p;
234   len = strlen(target);
235 
236   if(len > max_sanitized_len) {
237     free(target);
238     return SANITIZE_ERR_INVALID_PATH;
239   }
240 #endif
241 
242   if(!(flags & SANITIZE_ALLOW_RESERVED)) {
243     sc = rename_if_reserved_dos_device_name(&p, target, flags);
244     free(target);
245     if(sc)
246       return sc;
247     target = p;
248     len = strlen(target);
249 
250     if(len > max_sanitized_len) {
251       free(target);
252       return SANITIZE_ERR_INVALID_PATH;
253     }
254   }
255 
256   *sanitized = target;
257   return SANITIZE_ERR_OK;
258 }
259 
260 
261 /*
262 Test if truncating a path to a file will leave at least a single character in
263 the filename. Filenames suffixed by an alternate data stream can't be
264 truncated. This performs a dry run, nothing is modified.
265 
266 Good truncate_pos 9:    C:\foo\bar  =>  C:\foo\ba
267 Good truncate_pos 6:    C:\foo      =>  C:\foo
268 Good truncate_pos 5:    C:\foo      =>  C:\fo
269 Bad* truncate_pos 5:    C:foo       =>  C:foo
270 Bad truncate_pos 5:     C:\foo:ads  =>  C:\fo
271 Bad truncate_pos 9:     C:\foo:ads  =>  C:\foo:ad
272 Bad truncate_pos 5:     C:\foo\bar  =>  C:\fo
273 Bad truncate_pos 5:     C:\foo\     =>  C:\fo
274 Bad truncate_pos 7:     C:\foo\     =>  C:\foo\
275 Error truncate_pos 7:   C:\foo      =>  (pos out of range)
276 Bad truncate_pos 1:     C:\foo\     =>  C
277 
278 * C:foo is ambiguous, C could end up being a drive or file therefore something
279   like C:superlongfilename can't be truncated.
280 
281 Returns
282 SANITIZE_ERR_OK: Good -- 'path' can be truncated
283 SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated
284 != SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error
285 */
truncate_dryrun(const char * path,const size_t truncate_pos)286 SANITIZEcode truncate_dryrun(const char *path, const size_t truncate_pos)
287 {
288   size_t len;
289 
290   if(!path)
291     return SANITIZE_ERR_BAD_ARGUMENT;
292 
293   len = strlen(path);
294 
295   if(truncate_pos > len)
296     return SANITIZE_ERR_BAD_ARGUMENT;
297 
298   if(!len || !truncate_pos)
299     return SANITIZE_ERR_INVALID_PATH;
300 
301   if(strpbrk(&path[truncate_pos - 1], "\\/:"))
302     return SANITIZE_ERR_INVALID_PATH;
303 
304   /* C:\foo can be truncated but C:\foo:ads can't */
305   if(truncate_pos > 1) {
306     const char *p = &path[truncate_pos - 1];
307     do {
308       --p;
309       if(*p == ':')
310         return SANITIZE_ERR_INVALID_PATH;
311     } while(p != path && *p != '\\' && *p != '/');
312   }
313 
314   return SANITIZE_ERR_OK;
315 }
316 
317 /* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function
318  * were taken with modification from the DJGPP port of tar 1.12. They use
319  * algorithms originally from DJTAR.
320  */
321 
322 /*
323 Extra sanitization MSDOS for file_name.
324 
325 This is a supporting function for sanitize_file_name.
326 
327 Warning: This is an MSDOS legacy function and was purposely written in a way
328 that some path information may pass through. For example drive letter names
329 (C:, D:, etc) are allowed to pass through. For sanitizing a filename use
330 sanitize_file_name.
331 
332 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
333 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
334 */
335 #if defined(MSDOS) || defined(UNITTESTS)
msdosify(char ** const sanitized,const char * file_name,int flags)336 SANITIZEcode msdosify(char **const sanitized, const char *file_name,
337                       int flags)
338 {
339   char dos_name[PATH_MAX];
340   static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */
341     "|<>/\\\":?*"; /* illegal in DOS & W95 */
342   static const char *illegal_chars_w95 = &illegal_chars_dos[8];
343   int idx, dot_idx;
344   const char *s = file_name;
345   char *d = dos_name;
346   const char *const dlimit = dos_name + sizeof(dos_name) - 1;
347   const char *illegal_aliens = illegal_chars_dos;
348   size_t len = sizeof(illegal_chars_dos) - 1;
349 
350   if(!sanitized)
351     return SANITIZE_ERR_BAD_ARGUMENT;
352 
353   *sanitized = NULL;
354 
355   if(!file_name)
356     return SANITIZE_ERR_BAD_ARGUMENT;
357 
358   if(strlen(file_name) > PATH_MAX-1 &&
359      (!(flags & SANITIZE_ALLOW_TRUNCATE) ||
360       truncate_dryrun(file_name, PATH_MAX-1)))
361     return SANITIZE_ERR_INVALID_PATH;
362 
363   /* Support for Windows 9X VFAT systems, when available. */
364   if(_use_lfn(file_name)) {
365     illegal_aliens = illegal_chars_w95;
366     len -= (illegal_chars_w95 - illegal_chars_dos);
367   }
368 
369   /* Get past the drive letter, if any. */
370   if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') {
371     *d++ = *s++;
372     *d = ((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) ? ':' : '_';
373     ++d, ++s;
374   }
375 
376   for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) {
377     if(memchr(illegal_aliens, *s, len)) {
378 
379       if((flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH)) && *s == ':')
380         *d = ':';
381       else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\'))
382         *d = *s;
383       /* Dots are special: DOS doesn't allow them as the leading character,
384          and a file name cannot have more than a single dot.  We leave the
385          first non-leading dot alone, unless it comes too close to the
386          beginning of the name: we want sh.lex.c to become sh_lex.c, not
387          sh.lex-c.  */
388       else if(*s == '.') {
389         if((flags & SANITIZE_ALLOW_PATH) && idx == 0 &&
390            (s[1] == '/' || s[1] == '\\' ||
391             (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) {
392           /* Copy "./" and "../" verbatim.  */
393           *d++ = *s++;
394           if(d == dlimit)
395             break;
396           if(*s == '.') {
397             *d++ = *s++;
398             if(d == dlimit)
399               break;
400           }
401           *d = *s;
402         }
403         else if(idx == 0)
404           *d = '_';
405         else if(dot_idx >= 0) {
406           if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */
407             d[dot_idx - idx] = '_'; /* replace previous dot */
408             *d = '.';
409           }
410           else
411             *d = '-';
412         }
413         else
414           *d = '.';
415 
416         if(*s == '.')
417           dot_idx = idx;
418       }
419       else if(*s == '+' && s[1] == '+') {
420         if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */
421           *d++ = 'x';
422           if(d == dlimit)
423             break;
424           *d   = 'x';
425         }
426         else {
427           /* libg++ etc.  */
428           if(dlimit - d < 4) {
429             *d++ = 'x';
430             if(d == dlimit)
431               break;
432             *d   = 'x';
433           }
434           else {
435             memcpy(d, "plus", 4);
436             d += 3;
437           }
438         }
439         s++;
440         idx++;
441       }
442       else
443         *d = '_';
444     }
445     else
446       *d = *s;
447     if(*s == '/' || *s == '\\') {
448       idx = 0;
449       dot_idx = -1;
450     }
451     else
452       idx++;
453   }
454   *d = '\0';
455 
456   if(*s) {
457     /* dos_name is truncated, check that truncation requirements are met,
458        specifically truncating a filename suffixed by an alternate data stream
459        or truncating the entire filename is not allowed. */
460     if(!(flags & SANITIZE_ALLOW_TRUNCATE) || strpbrk(s, "\\/:") ||
461        truncate_dryrun(dos_name, d - dos_name))
462       return SANITIZE_ERR_INVALID_PATH;
463   }
464 
465   *sanitized = strdup(dos_name);
466   return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
467 }
468 #endif /* MSDOS || UNITTESTS */
469 
470 /*
471 Rename file_name if it's a reserved dos device name.
472 
473 This is a supporting function for sanitize_file_name.
474 
475 Warning: This is an MSDOS legacy function and was purposely written in a way
476 that some path information may pass through. For example drive letter names
477 (C:, D:, etc) are allowed to pass through. For sanitizing a filename use
478 sanitize_file_name.
479 
480 Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name.
481 Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL.
482 */
rename_if_reserved_dos_device_name(char ** const sanitized,const char * file_name,int flags)483 SANITIZEcode rename_if_reserved_dos_device_name(char **const sanitized,
484                                                 const char *file_name,
485                                                 int flags)
486 {
487   /* We could have a file whose name is a device on MS-DOS.  Trying to
488    * retrieve such a file would fail at best and wedge us at worst.  We need
489    * to rename such files. */
490   char *p, *base;
491   char fname[PATH_MAX];
492 #ifdef MSDOS
493   struct_stat st_buf;
494 #endif
495 
496   if(!sanitized)
497     return SANITIZE_ERR_BAD_ARGUMENT;
498 
499   *sanitized = NULL;
500 
501   if(!file_name)
502     return SANITIZE_ERR_BAD_ARGUMENT;
503 
504   /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */
505 #ifndef MSDOS
506   if((flags & SANITIZE_ALLOW_PATH) &&
507      file_name[0] == '\\' && file_name[1] == '\\') {
508     size_t len = strlen(file_name);
509     *sanitized = malloc(len + 1);
510     if(!*sanitized)
511       return SANITIZE_ERR_OUT_OF_MEMORY;
512     strncpy(*sanitized, file_name, len + 1);
513     return SANITIZE_ERR_OK;
514   }
515 #endif
516 
517   if(strlen(file_name) > PATH_MAX-1 &&
518      (!(flags & SANITIZE_ALLOW_TRUNCATE) ||
519       truncate_dryrun(file_name, PATH_MAX-1)))
520     return SANITIZE_ERR_INVALID_PATH;
521 
522   strncpy(fname, file_name, PATH_MAX-1);
523   fname[PATH_MAX-1] = '\0';
524   base = basename(fname);
525 
526   /* Rename reserved device names that are known to be accessible without \\.\
527      Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS
528      https://support.microsoft.com/en-us/kb/74496
529      https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
530      */
531   for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) {
532     size_t p_len;
533     int x = (curl_strnequal(p, "CON", 3) ||
534              curl_strnequal(p, "PRN", 3) ||
535              curl_strnequal(p, "AUX", 3) ||
536              curl_strnequal(p, "NUL", 3)) ? 3 :
537             (curl_strnequal(p, "CLOCK$", 6)) ? 6 :
538             (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ?
539               (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0;
540 
541     if(!x)
542       continue;
543 
544     /* the devices may be accessible with an extension or ADS, for
545        example CON.AIR and 'CON . AIR' and CON:AIR access console */
546 
547     for(; p[x] == ' '; ++x)
548       ;
549 
550     if(p[x] == '.') {
551       p[x] = '_';
552       continue;
553     }
554     else if(p[x] == ':') {
555       if(!(flags & (SANITIZE_ALLOW_COLONS|SANITIZE_ALLOW_PATH))) {
556         p[x] = '_';
557         continue;
558       }
559       ++x;
560     }
561     else if(p[x]) /* no match */
562       continue;
563 
564     /* p points to 'CON' or 'CON ' or 'CON:', etc */
565     p_len = strlen(p);
566 
567     /* Prepend a '_' */
568     if(strlen(fname) == PATH_MAX-1) {
569       --p_len;
570       if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(p, p_len))
571         return SANITIZE_ERR_INVALID_PATH;
572       p[p_len] = '\0';
573     }
574     memmove(p + 1, p, p_len + 1);
575     p[0] = '_';
576     ++p_len;
577 
578     /* if fname was just modified then the basename pointer must be updated */
579     if(p == fname)
580       base = basename(fname);
581   }
582 
583   /* This is the legacy portion from rename_if_dos_device_name that checks for
584      reserved device names. It only works on MSDOS. On Windows XP the stat
585      check errors with EINVAL if the device name is reserved. On Windows
586      Vista/7/8 it sets mode S_IFREG (regular file or device). According to MSDN
587      stat doc the latter behavior is correct, but that doesn't help us identify
588      whether it's a reserved device name and not a regular file name. */
589 #ifdef MSDOS
590   if(base && ((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) {
591     /* Prepend a '_' */
592     size_t blen = strlen(base);
593     if(blen) {
594       if(strlen(fname) == PATH_MAX-1) {
595         --blen;
596         if(!(flags & SANITIZE_ALLOW_TRUNCATE) || truncate_dryrun(base, blen))
597           return SANITIZE_ERR_INVALID_PATH;
598         base[blen] = '\0';
599       }
600       memmove(base + 1, base, blen + 1);
601       base[0] = '_';
602       ++blen;
603     }
604   }
605 #endif
606 
607   *sanitized = strdup(fname);
608   return (*sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY);
609 }
610 
611 #if defined(MSDOS) && (defined(__DJGPP__) || defined(__GO32__))
612 
613 /*
614  * Disable program default argument globbing. We do it on our own.
615  */
__crt0_glob_function(char * arg)616 char **__crt0_glob_function(char *arg)
617 {
618   (void)arg;
619   return (char **)0;
620 }
621 
622 #endif /* MSDOS && (__DJGPP__ || __GO32__) */
623 
624 #ifdef WIN32
625 
626 /*
627  * Function to find CACert bundle on a Win32 platform using SearchPath.
628  * (SearchPath is already declared via inclusions done in setup header file)
629  * (Use the ASCII version instead of the unicode one!)
630  * The order of the directories it searches is:
631  *  1. application's directory
632  *  2. current working directory
633  *  3. Windows System directory (e.g. C:\windows\system32)
634  *  4. Windows Directory (e.g. C:\windows)
635  *  5. all directories along %PATH%
636  *
637  * For WinXP and later search order actually depends on registry value:
638  * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode
639  */
640 
FindWin32CACert(struct OperationConfig * config,curl_sslbackend backend,const char * bundle_file)641 CURLcode FindWin32CACert(struct OperationConfig *config,
642                          curl_sslbackend backend,
643                          const char *bundle_file)
644 {
645   CURLcode result = CURLE_OK;
646 
647   /* Search and set cert file only if libcurl supports SSL.
648    *
649    * If Schannel is the selected SSL backend then these locations are
650    * ignored. We allow setting CA location for schannel only when explicitly
651    * specified by the user via CURLOPT_CAINFO / --cacert.
652    */
653   if((curlinfo->features & CURL_VERSION_SSL) &&
654      backend != CURLSSLBACKEND_SCHANNEL) {
655 
656     DWORD res_len;
657     char buf[PATH_MAX];
658     char *ptr = NULL;
659 
660     buf[0] = '\0';
661 
662     res_len = SearchPathA(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr);
663     if(res_len > 0) {
664       Curl_safefree(config->cacert);
665       config->cacert = strdup(buf);
666       if(!config->cacert)
667         result = CURLE_OUT_OF_MEMORY;
668     }
669   }
670 
671   return result;
672 }
673 
674 
675 /* Get a list of all loaded modules with full paths.
676  * Returns slist on success or NULL on error.
677  */
GetLoadedModulePaths(void)678 struct curl_slist *GetLoadedModulePaths(void)
679 {
680   HANDLE hnd = INVALID_HANDLE_VALUE;
681   MODULEENTRY32 mod = {0};
682   struct curl_slist *slist = NULL;
683 
684   mod.dwSize = sizeof(MODULEENTRY32);
685 
686   do {
687     hnd = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
688   } while(hnd == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH);
689 
690   if(hnd == INVALID_HANDLE_VALUE)
691     goto error;
692 
693   if(!Module32First(hnd, &mod))
694     goto error;
695 
696   do {
697     char *path; /* points to stack allocated buffer */
698     struct curl_slist *temp;
699 
700 #ifdef UNICODE
701     /* sizeof(mod.szExePath) is the max total bytes of wchars. the max total
702        bytes of multibyte chars won't be more than twice that. */
703     char buffer[sizeof(mod.szExePath) * 2];
704     if(!WideCharToMultiByte(CP_ACP, 0, mod.szExePath, -1,
705                             buffer, sizeof(buffer), NULL, NULL))
706       goto error;
707     path = buffer;
708 #else
709     path = mod.szExePath;
710 #endif
711     temp = curl_slist_append(slist, path);
712     if(!temp)
713       goto error;
714     slist = temp;
715   } while(Module32Next(hnd, &mod));
716 
717   goto cleanup;
718 
719 error:
720   curl_slist_free_all(slist);
721   slist = NULL;
722 cleanup:
723   if(hnd != INVALID_HANDLE_VALUE)
724     CloseHandle(hnd);
725   return slist;
726 }
727 
728 #endif /* WIN32 */
729 
730 #endif /* MSDOS || WIN32 */
731