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