1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/expand_path.c - Parameterized path expansion facility */
4 * Copyright (c) 2009, Secure Endpoints Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
43 * Expand a %{TEMP} token
45 * The %{TEMP} token expands to the temporary path for the current
46 * user as returned by GetTempPath().
48 * @note: Since the GetTempPath() function relies on the TMP or TEMP
49 * environment variables, this function will failover to the system
50 * temporary directory until the user profile is loaded. In addition,
51 * the returned path may or may not exist.
53 static krb5_error_code
54 expand_temp_folder(krb5_context context, PTYPE param, const char *postfix,
57 TCHAR tpath[MAX_PATH];
60 if (!GetTempPath(sizeof(tpath) / sizeof(tpath[0]), tpath)) {
61 k5_setmsg(context, EINVAL, "Failed to get temporary path (GLE=%d)",
68 if (len > 0 && tpath[len - 1] == '\\')
69 tpath[len - 1] = '\0';
80 * Expand a %{BINDIR} token
82 * This is also used to expand a few other tokens on Windows, since
83 * most of the executable binaries end up in the same directory. The
84 * "bin" directory is considered to be the directory in which the
85 * krb5.dll is located.
87 static krb5_error_code
88 expand_bin_dir(krb5_context context, PTYPE param, const char *postfix,
95 nc = GetModuleFileName(get_lib_instance(), path,
96 sizeof(path) / sizeof(path[0]));
98 nc == sizeof(path) / sizeof(path[0])) {
102 lastSlash = strrchr(path, '\\');
103 if (lastSlash != NULL) {
104 TCHAR *fslash = strrchr(lastSlash, '/');
113 if (strlcat(path, postfix, sizeof(path) / sizeof(path[0])) >=
114 sizeof(path) / sizeof(path[0]))
126 * Expand a %{USERID} token
128 * The %{USERID} token expands to the string representation of the
129 * user's SID. The user account that will be used is the account
130 * corresponding to the current thread's security token. This means
133 * - If the current thread token has the anonymous impersonation
134 * level, the call will fail.
136 * - If the current thread is impersonating a token at
137 * SecurityIdentification level the call will fail.
140 static krb5_error_code
141 expand_userid(krb5_context context, PTYPE param, const char *postfix,
145 HANDLE hThread = NULL;
146 HANDLE hToken = NULL;
147 PTOKEN_OWNER pOwner = NULL;
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. */
158 DWORD le = GetLastError();
160 if (le == ERROR_NO_TOKEN) {
161 HANDLE hProcess = GetCurrentProcess();
164 if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
169 k5_setmsg(context, rv, "Can't open thread token (GLE=%d)", le);
174 if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
175 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
176 k5_setmsg(context, rv,
177 "Unexpected error reading token information (GLE=%d)",
183 k5_setmsg(context, rv,
184 "GetTokenInformation() returned truncated buffer");
188 pOwner = malloc(len);
189 if (pOwner == NULL) {
194 k5_setmsg(context, rv,
195 "GetTokenInformation() returned truncated buffer");
199 if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
200 k5_setmsg(context, rv,
201 "GetTokenInformation() failed. GLE=%d", GetLastError());
205 if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
206 k5_setmsg(context, rv,
207 "Can't convert SID to string. GLE=%d", GetLastError());
211 *ret = strdup(strSid);
233 * Expand a folder identified by a CSIDL
235 static krb5_error_code
236 expand_csidl(krb5_context context, PTYPE folder, const char *postfix,
239 TCHAR path[MAX_PATH];
242 if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT,
244 k5_setmsg(context, EINVAL, "Unable to determine folder path");
250 if (len > 0 && path[len - 1] == '\\')
251 path[len - 1] = '\0';
254 strlcat(path, postfix, sizeof(path) / sizeof(path[0])) >=
255 sizeof(path)/sizeof(path[0]))
264 #else /* not _WIN32 */
267 static krb5_error_code
268 expand_path(krb5_context context, PTYPE param, const char *postfix, char **ret)
270 *ret = strdup(postfix);
276 static krb5_error_code
277 expand_temp_folder(krb5_context context, PTYPE param, const char *postfix,
280 const char *p = NULL;
282 if (context == NULL || !context->profile_secure)
283 p = getenv("TMPDIR");
284 *ret = strdup((p != NULL) ? p : "/tmp");
290 static krb5_error_code
291 expand_userid(krb5_context context, PTYPE param, const char *postfix,
294 if (asprintf(str, "%lu", (unsigned long)getuid()) < 0)
299 static krb5_error_code
300 expand_euid(krb5_context context, PTYPE param, const char *postfix, char **str)
302 if (asprintf(str, "%lu", (unsigned long)geteuid()) < 0)
307 static krb5_error_code
308 expand_username(krb5_context context, PTYPE param, const char *postfix,
311 uid_t euid = geteuid();
312 struct passwd *pw, pwx;
315 if (k5_getpwuid_r(euid, &pwx, pwbuf, sizeof(pwbuf), &pw) != 0) {
316 k5_setmsg(context, ENOENT, _("Can't find username for uid %lu"),
317 (unsigned long)euid);
320 *str = strdup(pw->pw_name);
326 #endif /* not _WIN32 */
329 * Expand an extra token
331 static krb5_error_code
332 expand_extra_token(krb5_context context, const char *value, char **ret)
334 *ret = strdup(value);
341 * Expand a %{null} token
343 * The expansion of a %{null} token is always the empty string.
345 static krb5_error_code
346 expand_null(krb5_context context, PTYPE param, const char *postfix, char **ret)
354 static const struct {
358 int (*exp_func)(krb5_context, PTYPE, const char *, char **);
361 /* Roaming application data (for current user) */
362 {"APPDATA", CSIDL_APPDATA, NULL, expand_csidl},
363 /* Application data (all users) */
364 {"COMMON_APPDATA", CSIDL_COMMON_APPDATA, NULL, expand_csidl},
365 /* Local application data (for current user) */
366 {"LOCAL_APPDATA", CSIDL_LOCAL_APPDATA, NULL, expand_csidl},
367 /* Windows System folder (e.g. %WINDIR%\System32) */
368 {"SYSTEM", CSIDL_SYSTEM, NULL, expand_csidl},
370 {"WINDOWS", CSIDL_WINDOWS, NULL, expand_csidl},
371 /* Per user MIT krb5 configuration file directory */
372 {"USERCONFIG", CSIDL_APPDATA, "\\MIT\\Kerberos5",
374 /* Common MIT krb5 configuration file directory */
375 {"COMMONCONFIG", CSIDL_COMMON_APPDATA, "\\MIT\\Kerberos5",
377 {"LIBDIR", 0, NULL, expand_bin_dir},
378 {"BINDIR", 0, NULL, expand_bin_dir},
379 {"SBINDIR", 0, NULL, expand_bin_dir},
380 {"euid", 0, NULL, expand_userid},
382 {"LIBDIR", 0, LIBDIR, expand_path},
383 {"BINDIR", 0, BINDIR, expand_path},
384 {"SBINDIR", 0, SBINDIR, expand_path},
385 {"euid", 0, NULL, expand_euid},
386 {"username", 0, NULL, expand_username},
388 {"TEMP", 0, NULL, expand_temp_folder},
389 {"USERID", 0, NULL, expand_userid},
390 {"uid", 0, NULL, expand_userid},
391 {"null", 0, NULL, expand_null}
394 static krb5_error_code
395 expand_token(krb5_context context, const char *token, const char *token_end,
396 char **extra_tokens, char **ret)
403 if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
404 token_end - token <= 2) {
405 k5_setmsg(context, EINVAL, _("Invalid token"));
409 for (p = extra_tokens; p != NULL && *p != NULL; p += 2) {
410 if (strncmp(token + 2, *p, (token_end - token) - 2) == 0)
411 return expand_extra_token(context, p[1], ret);
414 for (i = 0; i < sizeof(tokens) / sizeof(tokens[0]); i++) {
415 if (!strncmp(token + 2, tokens[i].tok, (token_end - token) - 2)) {
416 return tokens[i].exp_func(context, tokens[i].param,
417 tokens[i].postfix, ret);
421 k5_setmsg(context, EINVAL, _("Invalid token"));
426 * Expand tokens in path_in to produce *path_out. The caller should free
427 * *path_out with free().
430 k5_expand_path_tokens(krb5_context context, const char *path_in,
433 return k5_expand_path_tokens_extra(context, path_in, path_out, NULL);
437 free_extra_tokens(char **extra_tokens)
441 for (p = extra_tokens; p != NULL && *p != NULL; p++)
447 * Expand tokens in path_in to produce *path_out. Arguments after path_out are
448 * pairs of extra token names and replacement values, terminated by a NULL.
449 * The caller should free *path_out with free().
452 k5_expand_path_tokens_extra(krb5_context context, const char *path_in,
453 char **path_out, ...)
457 char *tok_begin, *tok_end, *tok_val, **extra_tokens = NULL;
458 const char *path_left;
464 k5_buf_init_dynamic(&buf);
466 /* Count extra tokens. */
467 va_start(ap, path_out);
468 while (va_arg(ap, const char *) != NULL)
474 /* Get extra tokens. */
476 extra_tokens = k5calloc(nargs + 1, sizeof(char *), &ret);
477 if (extra_tokens == NULL)
479 va_start(ap, path_out);
480 for (i = 0; i < nargs; i++) {
481 extra_tokens[i] = strdup(va_arg(ap, const char *));
482 if (extra_tokens[i] == NULL) {
493 /* Find the next token in path_left and add the literal text up to it.
494 * If there are no more tokens, we can finish up. */
495 tok_begin = strstr(path_left, "%{");
496 if (tok_begin == NULL) {
497 k5_buf_add(&buf, path_left);
500 k5_buf_add_len(&buf, path_left, tok_begin - path_left);
502 /* Find the end of this token. */
503 tok_end = strchr(tok_begin, '}');
504 if (tok_end == NULL) {
506 k5_setmsg(context, ret, _("variable missing }"));
510 /* Expand this token and add its value. */
511 ret = expand_token(context, tok_begin, tok_end, extra_tokens,
515 k5_buf_add(&buf, tok_val);
517 path_left = tok_end + 1;
520 ret = k5_buf_status(&buf);
525 /* Also deal with slashes. */
528 for (p = buf.data; *p != '\0'; p++) {
534 *path_out = buf.data;
535 memset(&buf, 0, sizeof(buf));
539 free_extra_tokens(extra_tokens);