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