• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
sanitize_file_name(char ** const sanitized,const char * file_name,int flags)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 */
truncate_dryrun(const char * path,const size_t truncate_pos)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)
msdosify(char ** const sanitized,const char * file_name,int flags)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 */
rename_if_reserved_dos_device_name(char ** const sanitized,const char * file_name,int flags)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  */
__crt0_glob_function(char * arg)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 
FindWin32CACert(struct OperationConfig * config,const char * bundle_file)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     DWORD buf_tchar_size = PATH_MAX + 1;
650     DWORD buf_bytes_size = sizeof(TCHAR) * buf_tchar_size;
651     char *ptr = NULL;
652 
653     char *buf = malloc(buf_bytes_size);
654     if(!buf)
655       return CURLE_OUT_OF_MEMORY;
656     buf[0] = '\0';
657 
658     res_len = SearchPathA(NULL, bundle_file, NULL, buf_tchar_size, buf, &ptr);
659     if(res_len > 0) {
660       Curl_safefree(config->cacert);
661       config->cacert = strdup(buf);
662       if(!config->cacert)
663         result = CURLE_OUT_OF_MEMORY;
664     }
665 
666     Curl_safefree(buf);
667   }
668 
669   return result;
670 }
671 
672 #endif /* WIN32 */
673 
674 #endif /* MSDOS || WIN32 */
675