add missing files
[heimdal.git] / lib / krb5 / expand_path.c
blobc8a48847ddd10f37800c97a349644f6a4804c6a2
2 /***********************************************************************
3 * Copyright (c) 2009, Secure Endpoints Inc.
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
31 **********************************************************************/
33 #include "krb5_locl.h"
35 #include <stdarg.h>
37 typedef int PTYPE;
39 #ifdef _WIN32
40 #include <shlobj.h>
41 #include <sddl.h>
44 * Expand a %{TEMP} token
46 * The %{TEMP} token expands to the temporary path for the current
47 * user as returned by GetTempPath().
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.
54 static krb5_error_code
55 _expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
57 TCHAR tpath[MAX_PATH];
58 size_t len;
60 if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
61 if (context)
62 krb5_set_error_message(context, EINVAL,
63 "Failed to get temporary path (GLE=%d)",
64 GetLastError());
65 return EINVAL;
68 len = strlen(tpath);
70 if (len > 0 && tpath[len - 1] == '\\')
71 tpath[len - 1] = '\0';
73 *ret = strdup(tpath);
75 if (*ret == NULL)
76 return krb5_enomem(context);
78 return 0;
81 extern HINSTANCE _krb5_hInstance;
84 * Expand a %{BINDIR} token
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 * krb5.dll is located.
91 static krb5_error_code
92 _expand_bin_dir(krb5_context context, PTYPE param, const char *postfix, char **ret)
94 TCHAR path[MAX_PATH];
95 TCHAR *lastSlash;
96 DWORD nc;
98 nc = GetModuleFileName(_krb5_hInstance, path, sizeof(path)/sizeof(path[0]));
99 if (nc == 0 ||
100 nc == sizeof(path)/sizeof(path[0])) {
101 return EINVAL;
104 lastSlash = strrchr(path, '\\');
105 if (lastSlash != NULL) {
106 TCHAR *fslash = strrchr(lastSlash, '/');
108 if (fslash != NULL)
109 lastSlash = fslash;
111 *lastSlash = '\0';
114 if (postfix) {
115 if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
116 return EINVAL;
119 *ret = strdup(path);
120 if (*ret == NULL)
121 return krb5_enomem(context);
123 return 0;
127 * Expand a %{USERID} token
129 * The %{USERID} token expands to the string representation of the
130 * user's SID. The user account that will be used is the account
131 * corresponding to the current thread's security token. This means
132 * that:
134 * - If the current thread token has the anonymous impersonation
135 * level, the call will fail.
137 * - If the current thread is impersonating a token at
138 * SecurityIdentification level the call will fail.
141 static krb5_error_code
142 _expand_userid(krb5_context context, PTYPE param, const char *postfix, char **ret)
144 int rv = EINVAL;
145 HANDLE hThread = NULL;
146 HANDLE hToken = NULL;
147 PTOKEN_OWNER pOwner = NULL;
148 DWORD len = 0;
149 LPTSTR strSid = NULL;
151 hThread = GetCurrentThread();
153 if (!OpenThreadToken(hThread, TOKEN_QUERY,
154 FALSE, /* Open the thread token as the
155 current thread user. */
156 &hToken)) {
158 DWORD le = GetLastError();
160 if (le == ERROR_NO_TOKEN) {
161 HANDLE hProcess = GetCurrentProcess();
163 le = 0;
164 if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
165 le = GetLastError();
168 if (le != 0) {
169 if (context)
170 krb5_set_error_message(context, rv,
171 "Can't open thread token (GLE=%d)", le);
172 goto _exit;
176 if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
177 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
178 if (context)
179 krb5_set_error_message(context, rv,
180 "Unexpected error reading token information (GLE=%d)",
181 GetLastError());
182 goto _exit;
185 if (len == 0) {
186 if (context)
187 krb5_set_error_message(context, rv,
188 "GetTokenInformation() returned truncated buffer");
189 goto _exit;
192 pOwner = malloc(len);
193 if (pOwner == NULL) {
194 if (context)
195 krb5_set_error_message(context, rv, "Out of memory");
196 goto _exit;
198 } else {
199 if (context)
200 krb5_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
201 goto _exit;
204 if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
205 if (context)
206 krb5_set_error_message(context, rv, "GetTokenInformation() failed. GLE=%d", GetLastError());
207 goto _exit;
210 if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
211 if (context)
212 krb5_set_error_message(context, rv, "Can't convert SID to string. GLE=%d", GetLastError());
213 goto _exit;
216 *ret = strdup(strSid);
217 if (*ret == NULL && context)
218 krb5_set_error_message(context, rv, "Out of memory");
220 rv = 0;
222 _exit:
223 if (hToken != NULL)
224 CloseHandle(hToken);
226 if (pOwner != NULL)
227 free (pOwner);
229 if (strSid != NULL)
230 LocalFree(strSid);
232 return rv;
236 * Expand a folder identified by a CSIDL
239 static krb5_error_code
240 _expand_csidl(krb5_context context, PTYPE folder, const char *postfix, char **ret)
242 TCHAR path[MAX_PATH];
243 size_t len;
245 if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
246 if (context)
247 krb5_set_error_message(context, EINVAL, "Unable to determine folder path");
248 return EINVAL;
251 len = strlen(path);
253 if (len > 0 && path[len - 1] == '\\')
254 path[len - 1] = '\0';
256 if (postfix &&
257 strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
258 return krb5_enomem(context);
260 *ret = strdup(path);
261 if (*ret == NULL)
262 return krb5_enomem(context);
263 return 0;
266 #else
268 static krb5_error_code
269 _expand_path(krb5_context context, PTYPE param, const char *postfix, char **ret)
271 *ret = strdup(postfix);
272 if (*ret == NULL)
273 return krb5_enomem(context);
274 return 0;
277 static krb5_error_code
278 _expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
280 const char *p = NULL;
282 if (issuid())
283 p = getenv("TEMP");
284 if (p)
285 *ret = strdup(p);
286 else
287 *ret = strdup("/tmp");
288 if (*ret == NULL)
289 return krb5_enomem(context);
290 return 0;
293 static krb5_error_code
294 _expand_userid(krb5_context context, PTYPE param, const char *postfix, char **str)
296 int ret = asprintf(str, "%ld", (unsigned long)getuid());
297 if (ret < 0 || *str == NULL)
298 return krb5_enomem(context);
299 return 0;
303 #endif /* _WIN32 */
306 * Expand an extra token
309 static krb5_error_code
310 _expand_extra_token(krb5_context context, const char *value, char **ret)
312 *ret = strdup(value);
313 if (*ret == NULL)
314 return krb5_enomem(context);
315 return 0;
319 * Expand a %{null} token
321 * The expansion of a %{null} token is always the empty string.
324 static krb5_error_code
325 _expand_null(krb5_context context, PTYPE param, const char *postfix, char **ret)
327 *ret = strdup("");
328 if (*ret == NULL)
329 return krb5_enomem(context);
330 return 0;
334 static const struct token {
335 const char * tok;
336 int ftype;
337 #define FTYPE_CSIDL 0
338 #define FTYPE_SPECIAL 1
340 PTYPE param;
341 const char * postfix;
343 int (*exp_func)(krb5_context, PTYPE, const char *, char **);
345 #define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
346 #define SPECIAL(f) SPECIALP(f, NULL)
348 } tokens[] = {
349 #ifdef _WIN32
350 #define CSIDLP(C,P) FTYPE_CSIDL, C, P, _expand_csidl
351 #define CSIDL(C) CSIDLP(C, NULL)
353 {"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */
354 {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */
355 {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */
356 {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */
357 {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */
358 {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */
359 {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */
360 {"LIBDIR", SPECIAL(_expand_bin_dir)},
361 {"BINDIR", SPECIAL(_expand_bin_dir)},
362 {"LIBEXEC", SPECIAL(_expand_bin_dir)},
363 {"SBINDIR", SPECIAL(_expand_bin_dir)},
364 #else
365 {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, _expand_path},
366 {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, _expand_path},
367 {"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, _expand_path},
368 {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, _expand_path},
369 #endif
370 {"TEMP", SPECIAL(_expand_temp_folder)},
371 {"USERID", SPECIAL(_expand_userid)},
372 {"uid", SPECIAL(_expand_userid)},
373 {"null", SPECIAL(_expand_null)}
376 static krb5_error_code
377 _expand_token(krb5_context context,
378 const char *token,
379 const char *token_end,
380 char **extra_tokens,
381 char **ret)
383 size_t i;
384 char **p;
386 *ret = NULL;
388 if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
389 token_end - token <= 2) {
390 if (context)
391 krb5_set_error_message(context, EINVAL,"Invalid token.");
392 return EINVAL;
395 for (p = extra_tokens; p && p[0]; p += 2) {
396 if (strncmp(token+2, p[0], (token_end - token) - 2) == 0)
397 return _expand_extra_token(context, p[1], ret);
400 for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++) {
401 if (!strncmp(token+2, tokens[i].tok, (token_end - token) - 2))
402 return tokens[i].exp_func(context, tokens[i].param,
403 tokens[i].postfix, ret);
406 if (context)
407 krb5_set_error_message(context, EINVAL, "Invalid token.");
408 return EINVAL;
412 * Internal function to expand tokens in paths.
414 * Inputs:
416 * @context A krb5_context
417 * @path_in The path to expand tokens from
419 * Outputs:
421 * @ppath_out Path with expanded tokens (caller must free() this)
423 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
424 _krb5_expand_path_tokens(krb5_context context,
425 const char *path_in,
426 char **ppath_out)
428 return _krb5_expand_path_tokensv(context, path_in, ppath_out, NULL);
431 static void
432 free_extra_tokens(char **extra_tokens)
434 char **p;
436 for (p = extra_tokens; p && *p; p++)
437 free(*p);
438 free(extra_tokens);
442 * Internal function to expand tokens in paths.
444 * Inputs:
446 * @context A krb5_context
447 * @path_in The path to expand tokens from
448 * @ppath_out The expanded path
449 * @... Variable number of pairs of strings, the first of each
450 * being a token (e.g., "luser") and the second a string to
451 * replace it with. The list is terminated by a NULL.
453 * Outputs:
455 * @ppath_out Path with expanded tokens (caller must free() this)
457 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
458 _krb5_expand_path_tokensv(krb5_context context,
459 const char *path_in,
460 char **ppath_out, ...)
462 char *tok_begin, *tok_end, *append;
463 char **extra_tokens = NULL;
464 const char *path_left;
465 const char *s;
466 size_t nargs = 0;
467 size_t len = 0;
468 va_list ap;
470 if (path_in == NULL || *path_in == '\0') {
471 *ppath_out = strdup("");
472 return 0;
475 *ppath_out = NULL;
477 va_start(ap, ppath_out);
478 while ((s = va_arg(ap, const char *))) {
479 nargs++;
480 s = va_arg(ap, const char *);
482 va_end(ap);
483 nargs *= 2;
485 /* Get extra tokens */
486 if (nargs) {
487 size_t i;
489 extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens));
490 if (extra_tokens == NULL)
491 return krb5_enomem(context);
492 va_start(ap, ppath_out);
493 for (i = 0; i < nargs; i++) {
494 s = va_arg(ap, const char *); /* token key */
495 if (s == NULL)
496 break;
497 extra_tokens[i] = strdup(s);
498 if (extra_tokens[i++] == NULL) {
499 free_extra_tokens(extra_tokens);
500 return krb5_enomem(context);
502 s = va_arg(ap, const char *); /* token value */
503 if (s == NULL)
504 s = "";
505 extra_tokens[i] = strdup(s);
506 if (extra_tokens[i] == NULL) {
507 free_extra_tokens(extra_tokens);
508 return krb5_enomem(context);
511 va_end(ap);
514 for (path_left = path_in; path_left && *path_left; ) {
516 tok_begin = strstr(path_left, "%{");
518 if (tok_begin && tok_begin != path_left) {
520 append = malloc((tok_begin - path_left) + 1);
521 if (append) {
522 memcpy(append, path_left, tok_begin - path_left);
523 append[tok_begin - path_left] = '\0';
525 path_left = tok_begin;
527 } else if (tok_begin) {
529 tok_end = strchr(tok_begin, '}');
530 if (tok_end == NULL) {
531 free_extra_tokens(extra_tokens);
532 if (*ppath_out)
533 free(*ppath_out);
534 *ppath_out = NULL;
535 if (context)
536 krb5_set_error_message(context, EINVAL, "variable missing }");
537 return EINVAL;
540 if (_expand_token(context, tok_begin, tok_end, extra_tokens,
541 &append)) {
542 free_extra_tokens(extra_tokens);
543 if (*ppath_out)
544 free(*ppath_out);
545 *ppath_out = NULL;
546 return EINVAL;
549 path_left = tok_end + 1;
550 } else {
552 append = strdup(path_left);
553 path_left = NULL;
557 if (append == NULL) {
559 free_extra_tokens(extra_tokens);
560 if (*ppath_out)
561 free(*ppath_out);
562 *ppath_out = NULL;
563 return krb5_enomem(context);
568 size_t append_len = strlen(append);
569 char * new_str = realloc(*ppath_out, len + append_len + 1);
571 if (new_str == NULL) {
572 free_extra_tokens(extra_tokens);
573 free(append);
574 if (*ppath_out)
575 free(*ppath_out);
576 *ppath_out = NULL;
577 return krb5_enomem(context);
580 *ppath_out = new_str;
581 memcpy(*ppath_out + len, append, append_len + 1);
582 len = len + append_len;
583 free(append);
587 #ifdef _WIN32
588 /* Also deal with slashes */
589 if (*ppath_out) {
590 char * c;
591 for (c = *ppath_out; *c; c++)
592 if (*c == '/')
593 *c = '\\';
595 #endif
597 free_extra_tokens(extra_tokens);
598 return 0;