Line data Source code
1 :
2 : /***********************************************************************
3 : * Copyright (c) 2009-2020, Secure Endpoints Inc.
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : *
10 : * - Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : *
13 : * - Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in
15 : * the documentation and/or other materials provided with the
16 : * distribution.
17 : *
18 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 : * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 : * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 : * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 : * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 : * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 : * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 : * OF THE POSSIBILITY OF SUCH DAMAGE.
30 : *
31 : **********************************************************************/
32 :
33 : #include "baselocl.h"
34 :
35 : #include <stdarg.h>
36 :
37 : typedef int PTYPE;
38 :
39 : #ifdef _WIN32
40 : #include <shlobj.h>
41 : #include <sddl.h>
42 :
43 : /*
44 : * Expand a %{TEMP} token
45 : *
46 : * The %{TEMP} token expands to the temporary path for the current
47 : * user as returned by GetTempPath().
48 : *
49 : * @note: Since the GetTempPath() function relies on the TMP or TEMP
50 : * environment variables, this function will failover to the system
51 : * temporary directory until the user profile is loaded. In addition,
52 : * the returned path may or may not exist.
53 : */
54 : static heim_error_code
55 : expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
56 : const char *arg, char **ret)
57 : {
58 : TCHAR tpath[MAX_PATH];
59 : size_t len;
60 :
61 : if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
62 : heim_set_error_message(context, EINVAL,
63 : "Failed to get temporary path (GLE=%d)",
64 : GetLastError());
65 : return EINVAL;
66 : }
67 :
68 : len = strlen(tpath);
69 :
70 : if (len > 0 && tpath[len - 1] == '\\')
71 : tpath[len - 1] = '\0';
72 :
73 : *ret = strdup(tpath);
74 :
75 : if (*ret == NULL)
76 : return heim_enomem(context);
77 :
78 : return 0;
79 : }
80 :
81 : EXTERN_C IMAGE_DOS_HEADER __ImageBase;
82 :
83 : /*
84 : * Expand a %{BINDIR} token
85 : *
86 : * This is also used to expand a few other tokens on Windows, since
87 : * most of the executable binaries end up in the same directory. The
88 : * "bin" directory is considered to be the directory in which the
89 : * containing DLL is located.
90 : */
91 : static heim_error_code
92 : expand_bin_dir(heim_context context, PTYPE param, const char *postfix,
93 : const char *arg, char **ret)
94 : {
95 : TCHAR path[MAX_PATH];
96 : TCHAR *lastSlash;
97 : DWORD nc;
98 :
99 : nc = GetModuleFileName((HINSTANCE)&__ImageBase, path,
100 : sizeof(path)/sizeof(path[0]));
101 : if (nc == 0 ||
102 : nc == sizeof(path)/sizeof(path[0])) {
103 : return EINVAL;
104 : }
105 :
106 : lastSlash = strrchr(path, '\\');
107 : if (lastSlash != NULL) {
108 : TCHAR *fslash = strrchr(lastSlash, '/');
109 :
110 : if (fslash != NULL)
111 : lastSlash = fslash;
112 :
113 : *lastSlash = '\0';
114 : }
115 :
116 : if (postfix) {
117 : if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
118 : return EINVAL;
119 : }
120 :
121 : *ret = strdup(path);
122 : if (*ret == NULL)
123 : return heim_enomem(context);
124 :
125 : return 0;
126 : }
127 :
128 : /*
129 : * Expand a %{USERID} token
130 : *
131 : * The %{USERID} token expands to the string representation of the
132 : * user's SID. The user account that will be used is the account
133 : * corresponding to the current thread's security token. This means
134 : * that:
135 : *
136 : * - If the current thread token has the anonymous impersonation
137 : * level, the call will fail.
138 : *
139 : * - If the current thread is impersonating a token at
140 : * SecurityIdentification level the call will fail.
141 : *
142 : */
143 : static heim_error_code
144 : expand_userid(heim_context context, PTYPE param, const char *postfix,
145 : const char *arg, char **ret)
146 : {
147 : int rv = EINVAL;
148 : HANDLE hThread = NULL;
149 : HANDLE hToken = NULL;
150 : PTOKEN_OWNER pOwner = NULL;
151 : DWORD len = 0;
152 : LPTSTR strSid = NULL;
153 :
154 : hThread = GetCurrentThread();
155 :
156 : if (!OpenThreadToken(hThread, TOKEN_QUERY,
157 : FALSE, /* Open the thread token as the
158 : current thread user. */
159 : &hToken)) {
160 :
161 : DWORD le = GetLastError();
162 :
163 : if (le == ERROR_NO_TOKEN) {
164 : HANDLE hProcess = GetCurrentProcess();
165 :
166 : le = 0;
167 : if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
168 : le = GetLastError();
169 : }
170 :
171 : if (le != 0) {
172 : heim_set_error_message(context, rv,
173 : "Can't open thread token (GLE=%d)", le);
174 : goto _exit;
175 : }
176 : }
177 :
178 : if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
179 : if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
180 : heim_set_error_message(context, rv,
181 : "Unexpected error reading token information (GLE=%d)",
182 : GetLastError());
183 : goto _exit;
184 : }
185 :
186 : if (len == 0) {
187 : heim_set_error_message(context, rv,
188 : "GetTokenInformation() returned truncated buffer");
189 : goto _exit;
190 : }
191 :
192 : pOwner = malloc(len);
193 : if (pOwner == NULL) {
194 : heim_set_error_message(context, rv, "Out of memory");
195 : goto _exit;
196 : }
197 : } else {
198 : heim_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
199 : goto _exit;
200 : }
201 :
202 : if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
203 : heim_set_error_message(context, rv,
204 : "GetTokenInformation() failed. GLE=%d",
205 : GetLastError());
206 : goto _exit;
207 : }
208 :
209 : if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
210 : heim_set_error_message(context, rv,
211 : "Can't convert SID to string. GLE=%d",
212 : GetLastError());
213 : goto _exit;
214 : }
215 :
216 : *ret = strdup(strSid);
217 : if (*ret == NULL)
218 : heim_set_error_message(context, rv, "Out of memory");
219 :
220 : rv = 0;
221 :
222 : _exit:
223 : if (hToken != NULL)
224 : CloseHandle(hToken);
225 :
226 : if (pOwner != NULL)
227 : free (pOwner);
228 :
229 : if (strSid != NULL)
230 : LocalFree(strSid);
231 :
232 : return rv;
233 : }
234 :
235 : /*
236 : * Expand a folder identified by a CSIDL
237 : */
238 :
239 : static heim_error_code
240 : expand_csidl(heim_context context, PTYPE folder, const char *postfix,
241 : const char *arg, char **ret)
242 : {
243 : TCHAR path[MAX_PATH];
244 : size_t len;
245 :
246 : if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
247 : heim_set_error_message(context, EINVAL, "Unable to determine folder path");
248 : return EINVAL;
249 : }
250 :
251 : len = strlen(path);
252 :
253 : if (len > 0 && path[len - 1] == '\\')
254 : path[len - 1] = '\0';
255 :
256 : if (postfix &&
257 : strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
258 : return heim_enomem(context);
259 :
260 : *ret = strdup(path);
261 : if (*ret == NULL)
262 : return heim_enomem(context);
263 : return 0;
264 : }
265 :
266 : #else
267 :
268 : static heim_error_code
269 0 : expand_path(heim_context context, PTYPE param, const char *postfix,
270 : const char *arg, char **ret)
271 : {
272 0 : *ret = strdup(postfix);
273 0 : if (*ret == NULL)
274 0 : return heim_enomem(context);
275 0 : return 0;
276 : }
277 :
278 : static heim_error_code
279 172 : expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
280 : const char *arg, char **ret)
281 : {
282 172 : const char *p = NULL;
283 :
284 172 : p = secure_getenv("TEMP");
285 :
286 172 : if (p)
287 0 : *ret = strdup(p);
288 : else
289 172 : *ret = strdup("/tmp");
290 172 : if (*ret == NULL)
291 0 : return heim_enomem(context);
292 172 : return 0;
293 : }
294 :
295 : static heim_error_code
296 0 : expand_userid(heim_context context, PTYPE param, const char *postfix,
297 : const char *arg, char **str)
298 : {
299 0 : int ret = asprintf(str, "%ld", (unsigned long)getuid());
300 0 : if (ret < 0 || *str == NULL)
301 0 : return heim_enomem(context);
302 0 : return 0;
303 : }
304 :
305 : static heim_error_code
306 0 : expand_euid(heim_context context, PTYPE param, const char *postfix,
307 : const char *arg, char **str)
308 : {
309 0 : int ret = asprintf(str, "%ld", (unsigned long)geteuid());
310 0 : if (ret < 0 || *str == NULL)
311 0 : return heim_enomem(context);
312 0 : return 0;
313 : }
314 : #endif /* _WIN32 */
315 :
316 : static heim_error_code
317 792122 : expand_home(heim_context context, PTYPE param, const char *postfix,
318 : const char *arg, char **str)
319 : {
320 19522 : char homedir[MAX_PATH];
321 19522 : int ret;
322 :
323 792122 : if (roken_get_homedir(homedir, sizeof(homedir)))
324 792122 : ret = asprintf(str, "%s", homedir);
325 : else
326 0 : ret = asprintf(str, "/unknown");
327 792122 : if (ret < 0 || *str == NULL)
328 0 : return heim_enomem(context);
329 772600 : return 0;
330 : }
331 :
332 : static heim_error_code
333 0 : expand_username(heim_context context, PTYPE param, const char *postfix,
334 : const char *arg, char **str)
335 : {
336 0 : char user[128];
337 0 : const char *username = roken_get_username(user, sizeof(user));
338 :
339 0 : if (username == NULL) {
340 0 : heim_set_error_message(context, ENOTTY,
341 0 : N_("unable to figure out current principal",
342 : ""));
343 0 : return ENOTTY; /* XXX */
344 : }
345 :
346 0 : *str = strdup(username);
347 0 : if (*str == NULL)
348 0 : return heim_enomem(context);
349 :
350 0 : return 0;
351 : }
352 :
353 : static heim_error_code
354 0 : expand_loginname(heim_context context, PTYPE param, const char *postfix,
355 : const char *arg, char **str)
356 : {
357 0 : char user[128];
358 0 : const char *username = roken_get_loginname(user, sizeof(user));
359 :
360 0 : if (username == NULL) {
361 0 : heim_set_error_message(context, ENOTTY,
362 0 : N_("unable to figure out current principal",
363 : ""));
364 0 : return ENOTTY; /* XXX */
365 : }
366 :
367 0 : *str = strdup(username);
368 0 : if (*str == NULL)
369 0 : return heim_enomem(context);
370 :
371 0 : return 0;
372 : }
373 :
374 : static heim_error_code
375 0 : expand_strftime(heim_context context, PTYPE param, const char *postfix,
376 : const char *arg, char **ret)
377 : {
378 0 : size_t len;
379 0 : time_t t;
380 0 : char buf[1024];
381 :
382 0 : t = time(NULL);
383 0 : len = strftime(buf, sizeof(buf), arg, localtime(&t));
384 0 : if (len == 0 || len >= sizeof(buf))
385 0 : return heim_enomem(context);
386 0 : *ret = strdup(buf);
387 0 : return 0;
388 : }
389 :
390 : /**
391 : * Expand an extra token
392 : */
393 :
394 : static heim_error_code
395 0 : expand_extra_token(heim_context context, const char *value, char **ret)
396 : {
397 0 : *ret = strdup(value);
398 0 : if (*ret == NULL)
399 0 : return heim_enomem(context);
400 0 : return 0;
401 : }
402 :
403 : /**
404 : * Expand a %{null} token
405 : *
406 : * The expansion of a %{null} token is always the empty string.
407 : */
408 :
409 : static heim_error_code
410 0 : expand_null(heim_context context, PTYPE param, const char *postfix,
411 : const char *arg, char **ret)
412 : {
413 0 : *ret = strdup("");
414 0 : if (*ret == NULL)
415 0 : return heim_enomem(context);
416 0 : return 0;
417 : }
418 :
419 :
420 : static const struct {
421 : const char * tok;
422 : int ftype;
423 : #define FTYPE_CSIDL 0
424 : #define FTYPE_SPECIAL 1
425 :
426 : PTYPE param;
427 : const char * postfix;
428 :
429 : int (*exp_func)(heim_context, PTYPE, const char *, const char *, char **);
430 :
431 : #define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
432 : #define SPECIAL(f) SPECIALP(f, NULL)
433 :
434 : } tokens[] = {
435 : #ifdef _WIN32
436 : #define CSIDLP(C,P) FTYPE_CSIDL, C, P, expand_csidl
437 : #define CSIDL(C) CSIDLP(C, NULL)
438 :
439 : {"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */
440 : {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */
441 : {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */
442 : {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */
443 : {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */
444 : {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */
445 : {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */
446 : {"LIBDIR", SPECIAL(expand_bin_dir)},
447 : {"BINDIR", SPECIAL(expand_bin_dir)},
448 : {"LIBEXEC", SPECIAL(expand_bin_dir)},
449 : {"SBINDIR", SPECIAL(expand_bin_dir)},
450 : #else
451 : {"LOCALSTATEDIR", FTYPE_SPECIAL, 0, LOCALSTATEDIR, expand_path},
452 : {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, expand_path},
453 : {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, expand_path},
454 : {"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, expand_path},
455 : {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, expand_path},
456 : {"USERCONFIG", SPECIAL(expand_home)}, /* same as %{HOME} on not-Windows */
457 : {"euid", SPECIAL(expand_euid)},
458 : {"ruid", SPECIAL(expand_userid)},
459 : {"loginname", SPECIAL(expand_loginname)},
460 : #endif
461 : {"username", SPECIAL(expand_username)},
462 : {"TEMP", SPECIAL(expand_temp_folder)},
463 : {"USERID", SPECIAL(expand_userid)},
464 : {"uid", SPECIAL(expand_userid)},
465 : {"null", SPECIAL(expand_null)},
466 : {"strftime", SPECIAL(expand_strftime)},
467 : {"HOME", SPECIAL(expand_home)},
468 : };
469 :
470 : static heim_error_code
471 792294 : expand_token(heim_context context,
472 : const char *token,
473 : const char *token_end,
474 : char **extra_tokens,
475 : char **ret)
476 : {
477 19522 : heim_error_code errcode;
478 19522 : size_t i;
479 19522 : char **p;
480 19522 : const char *colon;
481 :
482 792294 : *ret = NULL;
483 :
484 792294 : if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
485 792294 : token_end - token <= 2) {
486 0 : heim_set_error_message(context, EINVAL,"Invalid token.");
487 0 : return EINVAL;
488 : }
489 :
490 792294 : for (p = extra_tokens; p && p[0]; p += 2) {
491 0 : if (strncmp(token+2, p[0], (token_end - token) - 2) == 0)
492 0 : return expand_extra_token(context, p[1], ret);
493 : }
494 :
495 8714202 : for (colon=token+2; colon < token_end; colon++)
496 7921908 : if (*colon == ':')
497 0 : break;
498 :
499 4754624 : for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++)
500 4754624 : if (!strncmp(token+2, tokens[i].tok, (colon - token) - 2)) {
501 792294 : char *arg = NULL;
502 :
503 792294 : errcode = 0;
504 792294 : if (*colon == ':') {
505 0 : int asprintf_ret = asprintf(&arg, "%.*s",
506 0 : (int)(token_end - colon - 1),
507 : colon + 1);
508 0 : if (asprintf_ret < 0 || !arg)
509 0 : errcode = ENOMEM;
510 : }
511 772772 : if (!errcode)
512 792294 : errcode = tokens[i].exp_func(context, tokens[i].param,
513 792294 : tokens[i].postfix, arg, ret);
514 792294 : free(arg);
515 792294 : return errcode;
516 : }
517 :
518 0 : heim_set_error_message(context, EINVAL, "Invalid token.");
519 0 : return EINVAL;
520 : }
521 :
522 : /**
523 : * Internal function to expand tokens in paths.
524 : *
525 : * Params:
526 : *
527 : * @context A heim_context
528 : * @path_in The path to expand tokens from
529 : * @filepath True if this is a filesystem path (converts slashes to
530 : * backslashes on Windows)
531 : * @ppath_out The expanded path
532 : * @... Variable number of pairs of strings, the first of each
533 : * being a token (e.g., "luser") and the second a string to
534 : * replace it with. The list is terminated by a NULL.
535 : */
536 : heim_error_code
537 3317074 : heim_expand_path_tokens(heim_context context,
538 : const char *path_in,
539 : int filepath,
540 : char **ppath_out,
541 : ...)
542 : {
543 77626 : heim_error_code ret;
544 77626 : va_list ap;
545 :
546 3317074 : va_start(ap, ppath_out);
547 3317074 : ret = heim_expand_path_tokensv(context, path_in, filepath, ppath_out, ap);
548 3317074 : va_end(ap);
549 :
550 3317074 : return ret;
551 : }
552 :
553 : static void
554 3317074 : free_extra_tokens(char **extra_tokens)
555 : {
556 77626 : char **p;
557 :
558 3317074 : for (p = extra_tokens; p && *p; p++)
559 0 : free(*p);
560 3317074 : free(extra_tokens);
561 3317074 : }
562 :
563 : /**
564 : * Internal function to expand tokens in paths.
565 : *
566 : * Inputs:
567 : *
568 : * @context A heim_context
569 : * @path_in The path to expand tokens from
570 : * @filepath True if this is a filesystem path (converts slashes to
571 : * backslashes on Windows)
572 : * @ppath_out The expanded path
573 : * @ap A NULL-terminated va_list of pairs of strings, the first of each
574 : * being a token (e.g., "luser") and the second a string to replace
575 : * it with.
576 : *
577 : * Outputs:
578 : *
579 : * @ppath_out Path with expanded tokens (caller must free() this)
580 : */
581 : heim_error_code
582 3317074 : heim_expand_path_tokensv(heim_context context,
583 : const char *path_in,
584 : int filepath,
585 : char **ppath_out, va_list ap)
586 : {
587 77626 : char *tok_begin, *tok_end, *append;
588 3317074 : char **extra_tokens = NULL;
589 77626 : const char *path_left;
590 3317074 : size_t nargs = 0;
591 3317074 : size_t len = 0;
592 77626 : va_list ap2;
593 :
594 3317074 : if (path_in == NULL || *path_in == '\0') {
595 0 : *ppath_out = strdup("");
596 0 : return 0;
597 : }
598 :
599 3317074 : *ppath_out = NULL;
600 :
601 : #if defined(_MSC_VER)
602 : ap2 = ap; /* Come on! See SO #558223 */
603 : #else
604 3317074 : va_copy(ap2, ap);
605 : #endif
606 3317074 : while (va_arg(ap2, const char *)) {
607 0 : nargs++;
608 0 : va_arg(ap2, const char *);
609 : }
610 3317074 : va_end(ap2);
611 3317074 : nargs *= 2;
612 :
613 : /* Get extra tokens */
614 3317074 : if (nargs) {
615 0 : size_t i;
616 :
617 0 : extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens));
618 0 : if (extra_tokens == NULL)
619 0 : return heim_enomem(context);
620 0 : for (i = 0; i < nargs; i++) {
621 0 : const char *s = va_arg(ap, const char *); /* token key */
622 0 : if (s == NULL)
623 0 : break;
624 0 : extra_tokens[i] = strdup(s);
625 0 : if (extra_tokens[i++] == NULL) {
626 0 : free_extra_tokens(extra_tokens);
627 0 : return heim_enomem(context);
628 : }
629 0 : s = va_arg(ap, const char *); /* token value */
630 0 : if (s == NULL)
631 0 : s = "";
632 0 : extra_tokens[i] = strdup(s);
633 0 : if (extra_tokens[i] == NULL) {
634 0 : free_extra_tokens(extra_tokens);
635 0 : return heim_enomem(context);
636 : }
637 : }
638 : }
639 :
640 7426442 : for (path_left = path_in; path_left && *path_left; ) {
641 :
642 4109368 : tok_begin = strstr(path_left, "%{");
643 :
644 4109368 : if (tok_begin && tok_begin != path_left) {
645 :
646 0 : append = malloc((tok_begin - path_left) + 1);
647 0 : if (append) {
648 0 : memcpy(append, path_left, tok_begin - path_left);
649 0 : append[tok_begin - path_left] = '\0';
650 : }
651 0 : path_left = tok_begin;
652 :
653 4109368 : } else if (tok_begin) {
654 :
655 792294 : tok_end = strchr(tok_begin, '}');
656 792294 : if (tok_end == NULL) {
657 0 : free_extra_tokens(extra_tokens);
658 0 : if (*ppath_out)
659 0 : free(*ppath_out);
660 0 : *ppath_out = NULL;
661 0 : heim_set_error_message(context, EINVAL, "variable missing }");
662 0 : return EINVAL;
663 : }
664 :
665 792294 : if (expand_token(context, tok_begin, tok_end, extra_tokens,
666 : &append)) {
667 0 : free_extra_tokens(extra_tokens);
668 0 : if (*ppath_out)
669 0 : free(*ppath_out);
670 0 : *ppath_out = NULL;
671 0 : return EINVAL;
672 : }
673 :
674 792294 : path_left = tok_end + 1;
675 : } else {
676 :
677 3317074 : append = strdup(path_left);
678 3317074 : path_left = NULL;
679 :
680 : }
681 :
682 4109368 : if (append == NULL) {
683 :
684 0 : free_extra_tokens(extra_tokens);
685 0 : if (*ppath_out)
686 0 : free(*ppath_out);
687 0 : *ppath_out = NULL;
688 0 : return heim_enomem(context);
689 :
690 : }
691 :
692 : {
693 4109368 : size_t append_len = strlen(append);
694 4109368 : char * new_str = realloc(*ppath_out, len + append_len + 1);
695 :
696 4109368 : if (new_str == NULL) {
697 0 : free_extra_tokens(extra_tokens);
698 0 : free(append);
699 0 : if (*ppath_out)
700 0 : free(*ppath_out);
701 0 : *ppath_out = NULL;
702 0 : return heim_enomem(context);
703 : }
704 :
705 4109368 : *ppath_out = new_str;
706 4109368 : memcpy(*ppath_out + len, append, append_len + 1);
707 4109368 : len = len + append_len;
708 4109368 : free(append);
709 : }
710 : }
711 :
712 : #ifdef _WIN32
713 : /* Also deal with slashes */
714 : if (filepath && *ppath_out) {
715 : char * c;
716 :
717 : for (c = *ppath_out; *c; c++)
718 : if (*c == '/')
719 : *c = '\\';
720 : }
721 : #endif
722 :
723 3317074 : free_extra_tokens(extra_tokens);
724 3317074 : return 0;
725 : }
|