summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/base/expand_path.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/lib/base/expand_path.c')
-rw-r--r--third_party/heimdal/lib/base/expand_path.c725
1 files changed, 725 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/base/expand_path.c b/third_party/heimdal/lib/base/expand_path.c
new file mode 100644
index 0000000..cf24991
--- /dev/null
+++ b/third_party/heimdal/lib/base/expand_path.c
@@ -0,0 +1,725 @@
+
+/***********************************************************************
+ * Copyright (c) 2009-2020, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include "baselocl.h"
+
+#include <stdarg.h>
+
+typedef int PTYPE;
+
+#ifdef _WIN32
+#include <shlobj.h>
+#include <sddl.h>
+
+/*
+ * Expand a %{TEMP} token
+ *
+ * The %{TEMP} token expands to the temporary path for the current
+ * user as returned by GetTempPath().
+ *
+ * @note: Since the GetTempPath() function relies on the TMP or TEMP
+ * environment variables, this function will failover to the system
+ * temporary directory until the user profile is loaded. In addition,
+ * the returned path may or may not exist.
+ */
+static heim_error_code
+expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ TCHAR tpath[MAX_PATH];
+ size_t len;
+
+ if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
+ heim_set_error_message(context, EINVAL,
+ "Failed to get temporary path (GLE=%d)",
+ GetLastError());
+ return EINVAL;
+ }
+
+ len = strlen(tpath);
+
+ if (len > 0 && tpath[len - 1] == '\\')
+ tpath[len - 1] = '\0';
+
+ *ret = strdup(tpath);
+
+ if (*ret == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+EXTERN_C IMAGE_DOS_HEADER __ImageBase;
+
+/*
+ * Expand a %{BINDIR} token
+ *
+ * This is also used to expand a few other tokens on Windows, since
+ * most of the executable binaries end up in the same directory. The
+ * "bin" directory is considered to be the directory in which the
+ * containing DLL is located.
+ */
+static heim_error_code
+expand_bin_dir(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ TCHAR path[MAX_PATH];
+ TCHAR *lastSlash;
+ DWORD nc;
+
+ nc = GetModuleFileName((HINSTANCE)&__ImageBase, path,
+ sizeof(path)/sizeof(path[0]));
+ if (nc == 0 ||
+ nc == sizeof(path)/sizeof(path[0])) {
+ return EINVAL;
+ }
+
+ lastSlash = strrchr(path, '\\');
+ if (lastSlash != NULL) {
+ TCHAR *fslash = strrchr(lastSlash, '/');
+
+ if (fslash != NULL)
+ lastSlash = fslash;
+
+ *lastSlash = '\0';
+ }
+
+ if (postfix) {
+ if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
+ return EINVAL;
+ }
+
+ *ret = strdup(path);
+ if (*ret == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+/*
+ * Expand a %{USERID} token
+ *
+ * The %{USERID} token expands to the string representation of the
+ * user's SID. The user account that will be used is the account
+ * corresponding to the current thread's security token. This means
+ * that:
+ *
+ * - If the current thread token has the anonymous impersonation
+ * level, the call will fail.
+ *
+ * - If the current thread is impersonating a token at
+ * SecurityIdentification level the call will fail.
+ *
+ */
+static heim_error_code
+expand_userid(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ int rv = EINVAL;
+ HANDLE hThread = NULL;
+ HANDLE hToken = NULL;
+ PTOKEN_OWNER pOwner = NULL;
+ DWORD len = 0;
+ LPTSTR strSid = NULL;
+
+ hThread = GetCurrentThread();
+
+ if (!OpenThreadToken(hThread, TOKEN_QUERY,
+ FALSE, /* Open the thread token as the
+ current thread user. */
+ &hToken)) {
+
+ DWORD le = GetLastError();
+
+ if (le == ERROR_NO_TOKEN) {
+ HANDLE hProcess = GetCurrentProcess();
+
+ le = 0;
+ if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
+ le = GetLastError();
+ }
+
+ if (le != 0) {
+ heim_set_error_message(context, rv,
+ "Can't open thread token (GLE=%d)", le);
+ goto _exit;
+ }
+ }
+
+ if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ heim_set_error_message(context, rv,
+ "Unexpected error reading token information (GLE=%d)",
+ GetLastError());
+ goto _exit;
+ }
+
+ if (len == 0) {
+ heim_set_error_message(context, rv,
+ "GetTokenInformation() returned truncated buffer");
+ goto _exit;
+ }
+
+ pOwner = malloc(len);
+ if (pOwner == NULL) {
+ heim_set_error_message(context, rv, "Out of memory");
+ goto _exit;
+ }
+ } else {
+ heim_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
+ goto _exit;
+ }
+
+ if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
+ heim_set_error_message(context, rv,
+ "GetTokenInformation() failed. GLE=%d",
+ GetLastError());
+ goto _exit;
+ }
+
+ if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
+ heim_set_error_message(context, rv,
+ "Can't convert SID to string. GLE=%d",
+ GetLastError());
+ goto _exit;
+ }
+
+ *ret = strdup(strSid);
+ if (*ret == NULL)
+ heim_set_error_message(context, rv, "Out of memory");
+
+ rv = 0;
+
+ _exit:
+ if (hToken != NULL)
+ CloseHandle(hToken);
+
+ if (pOwner != NULL)
+ free (pOwner);
+
+ if (strSid != NULL)
+ LocalFree(strSid);
+
+ return rv;
+}
+
+/*
+ * Expand a folder identified by a CSIDL
+ */
+
+static heim_error_code
+expand_csidl(heim_context context, PTYPE folder, const char *postfix,
+ const char *arg, char **ret)
+{
+ TCHAR path[MAX_PATH];
+ size_t len;
+
+ if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
+ heim_set_error_message(context, EINVAL, "Unable to determine folder path");
+ return EINVAL;
+ }
+
+ len = strlen(path);
+
+ if (len > 0 && path[len - 1] == '\\')
+ path[len - 1] = '\0';
+
+ if (postfix &&
+ strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
+ return heim_enomem(context);
+
+ *ret = strdup(path);
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+#else
+
+static heim_error_code
+expand_path(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ *ret = strdup(postfix);
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ const char *p = NULL;
+
+ p = secure_getenv("TEMP");
+
+ if (p)
+ *ret = strdup(p);
+ else
+ *ret = strdup("/tmp");
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_userid(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ int ret = asprintf(str, "%ld", (unsigned long)getuid());
+ if (ret < 0 || *str == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_euid(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ int ret = asprintf(str, "%ld", (unsigned long)geteuid());
+ if (ret < 0 || *str == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+#endif /* _WIN32 */
+
+static heim_error_code
+expand_home(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ char homedir[MAX_PATH];
+ int ret;
+
+ if (roken_get_homedir(homedir, sizeof(homedir)))
+ ret = asprintf(str, "%s", homedir);
+ else
+ ret = asprintf(str, "/unknown");
+ if (ret < 0 || *str == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+static heim_error_code
+expand_username(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ char user[128];
+ const char *username = roken_get_username(user, sizeof(user));
+
+ if (username == NULL) {
+ heim_set_error_message(context, ENOTTY,
+ N_("unable to figure out current principal",
+ ""));
+ return ENOTTY; /* XXX */
+ }
+
+ *str = strdup(username);
+ if (*str == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+static heim_error_code
+expand_loginname(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **str)
+{
+ char user[128];
+ const char *username = roken_get_loginname(user, sizeof(user));
+
+ if (username == NULL) {
+ heim_set_error_message(context, ENOTTY,
+ N_("unable to figure out current principal",
+ ""));
+ return ENOTTY; /* XXX */
+ }
+
+ *str = strdup(username);
+ if (*str == NULL)
+ return heim_enomem(context);
+
+ return 0;
+}
+
+static heim_error_code
+expand_strftime(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ size_t len;
+ time_t t;
+ char buf[1024];
+
+ t = time(NULL);
+ len = strftime(buf, sizeof(buf), arg, localtime(&t));
+ if (len == 0 || len >= sizeof(buf))
+ return heim_enomem(context);
+ *ret = strdup(buf);
+ return 0;
+}
+
+/**
+ * Expand an extra token
+ */
+
+static heim_error_code
+expand_extra_token(heim_context context, const char *value, char **ret)
+{
+ *ret = strdup(value);
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+/**
+ * Expand a %{null} token
+ *
+ * The expansion of a %{null} token is always the empty string.
+ */
+
+static heim_error_code
+expand_null(heim_context context, PTYPE param, const char *postfix,
+ const char *arg, char **ret)
+{
+ *ret = strdup("");
+ if (*ret == NULL)
+ return heim_enomem(context);
+ return 0;
+}
+
+
+static const struct {
+ const char * tok;
+ int ftype;
+#define FTYPE_CSIDL 0
+#define FTYPE_SPECIAL 1
+
+ PTYPE param;
+ const char * postfix;
+
+ int (*exp_func)(heim_context, PTYPE, const char *, const char *, char **);
+
+#define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
+#define SPECIAL(f) SPECIALP(f, NULL)
+
+} tokens[] = {
+#ifdef _WIN32
+#define CSIDLP(C,P) FTYPE_CSIDL, C, P, expand_csidl
+#define CSIDL(C) CSIDLP(C, NULL)
+
+ {"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */
+ {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */
+ {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */
+ {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */
+ {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */
+ {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */
+ {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */
+ {"LIBDIR", SPECIAL(expand_bin_dir)},
+ {"BINDIR", SPECIAL(expand_bin_dir)},
+ {"LIBEXEC", SPECIAL(expand_bin_dir)},
+ {"SBINDIR", SPECIAL(expand_bin_dir)},
+#else
+ {"LOCALSTATEDIR", FTYPE_SPECIAL, 0, LOCALSTATEDIR, expand_path},
+ {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, expand_path},
+ {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, expand_path},
+ {"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, expand_path},
+ {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, expand_path},
+ {"USERCONFIG", SPECIAL(expand_home)}, /* same as %{HOME} on not-Windows */
+ {"euid", SPECIAL(expand_euid)},
+ {"ruid", SPECIAL(expand_userid)},
+ {"loginname", SPECIAL(expand_loginname)},
+#endif
+ {"username", SPECIAL(expand_username)},
+ {"TEMP", SPECIAL(expand_temp_folder)},
+ {"USERID", SPECIAL(expand_userid)},
+ {"uid", SPECIAL(expand_userid)},
+ {"null", SPECIAL(expand_null)},
+ {"strftime", SPECIAL(expand_strftime)},
+ {"HOME", SPECIAL(expand_home)},
+};
+
+static heim_error_code
+expand_token(heim_context context,
+ const char *token,
+ const char *token_end,
+ char **extra_tokens,
+ char **ret)
+{
+ heim_error_code errcode;
+ size_t i;
+ char **p;
+ const char *colon;
+
+ *ret = NULL;
+
+ if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
+ token_end - token <= 2) {
+ heim_set_error_message(context, EINVAL,"Invalid token.");
+ return EINVAL;
+ }
+
+ for (p = extra_tokens; p && p[0]; p += 2) {
+ if (strncmp(token+2, p[0], (token_end - token) - 2) == 0)
+ return expand_extra_token(context, p[1], ret);
+ }
+
+ for (colon=token+2; colon < token_end; colon++)
+ if (*colon == ':')
+ break;
+
+ for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++)
+ if (!strncmp(token+2, tokens[i].tok, (colon - token) - 2)) {
+ char *arg = NULL;
+
+ errcode = 0;
+ if (*colon == ':') {
+ int asprintf_ret = asprintf(&arg, "%.*s",
+ (int)(token_end - colon - 1),
+ colon + 1);
+ if (asprintf_ret < 0 || !arg)
+ errcode = ENOMEM;
+ }
+ if (!errcode)
+ errcode = tokens[i].exp_func(context, tokens[i].param,
+ tokens[i].postfix, arg, ret);
+ free(arg);
+ return errcode;
+ }
+
+ heim_set_error_message(context, EINVAL, "Invalid token.");
+ return EINVAL;
+}
+
+/**
+ * Internal function to expand tokens in paths.
+ *
+ * Params:
+ *
+ * @context A heim_context
+ * @path_in The path to expand tokens from
+ * @filepath True if this is a filesystem path (converts slashes to
+ * backslashes on Windows)
+ * @ppath_out The expanded path
+ * @... Variable number of pairs of strings, the first of each
+ * being a token (e.g., "luser") and the second a string to
+ * replace it with. The list is terminated by a NULL.
+ */
+heim_error_code
+heim_expand_path_tokens(heim_context context,
+ const char *path_in,
+ int filepath,
+ char **ppath_out,
+ ...)
+{
+ heim_error_code ret;
+ va_list ap;
+
+ va_start(ap, ppath_out);
+ ret = heim_expand_path_tokensv(context, path_in, filepath, ppath_out, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+static void
+free_extra_tokens(char **extra_tokens)
+{
+ char **p;
+
+ for (p = extra_tokens; p && *p; p++)
+ free(*p);
+ free(extra_tokens);
+}
+
+/**
+ * Internal function to expand tokens in paths.
+ *
+ * Inputs:
+ *
+ * @context A heim_context
+ * @path_in The path to expand tokens from
+ * @filepath True if this is a filesystem path (converts slashes to
+ * backslashes on Windows)
+ * @ppath_out The expanded path
+ * @ap A NULL-terminated va_list of pairs of strings, the first of each
+ * being a token (e.g., "luser") and the second a string to replace
+ * it with.
+ *
+ * Outputs:
+ *
+ * @ppath_out Path with expanded tokens (caller must free() this)
+ */
+heim_error_code
+heim_expand_path_tokensv(heim_context context,
+ const char *path_in,
+ int filepath,
+ char **ppath_out, va_list ap)
+{
+ char *tok_begin, *tok_end, *append;
+ char **extra_tokens = NULL;
+ const char *path_left;
+ size_t nargs = 0;
+ size_t len = 0;
+ va_list ap2;
+
+ if (path_in == NULL || *path_in == '\0') {
+ *ppath_out = strdup("");
+ return 0;
+ }
+
+ *ppath_out = NULL;
+
+#if defined(_MSC_VER)
+ ap2 = ap; /* Come on! See SO #558223 */
+#else
+ va_copy(ap2, ap);
+#endif
+ while (va_arg(ap2, const char *)) {
+ nargs++;
+ va_arg(ap2, const char *);
+ }
+ va_end(ap2);
+ nargs *= 2;
+
+ /* Get extra tokens */
+ if (nargs) {
+ size_t i;
+
+ extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens));
+ if (extra_tokens == NULL)
+ return heim_enomem(context);
+ for (i = 0; i < nargs; i++) {
+ const char *s = va_arg(ap, const char *); /* token key */
+ if (s == NULL)
+ break;
+ extra_tokens[i] = strdup(s);
+ if (extra_tokens[i++] == NULL) {
+ free_extra_tokens(extra_tokens);
+ return heim_enomem(context);
+ }
+ s = va_arg(ap, const char *); /* token value */
+ if (s == NULL)
+ s = "";
+ extra_tokens[i] = strdup(s);
+ if (extra_tokens[i] == NULL) {
+ free_extra_tokens(extra_tokens);
+ return heim_enomem(context);
+ }
+ }
+ }
+
+ for (path_left = path_in; path_left && *path_left; ) {
+
+ tok_begin = strstr(path_left, "%{");
+
+ if (tok_begin && tok_begin != path_left) {
+
+ append = malloc((tok_begin - path_left) + 1);
+ if (append) {
+ memcpy(append, path_left, tok_begin - path_left);
+ append[tok_begin - path_left] = '\0';
+ }
+ path_left = tok_begin;
+
+ } else if (tok_begin) {
+
+ tok_end = strchr(tok_begin, '}');
+ if (tok_end == NULL) {
+ free_extra_tokens(extra_tokens);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ heim_set_error_message(context, EINVAL, "variable missing }");
+ return EINVAL;
+ }
+
+ if (expand_token(context, tok_begin, tok_end, extra_tokens,
+ &append)) {
+ free_extra_tokens(extra_tokens);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ return EINVAL;
+ }
+
+ path_left = tok_end + 1;
+ } else {
+
+ append = strdup(path_left);
+ path_left = NULL;
+
+ }
+
+ if (append == NULL) {
+
+ free_extra_tokens(extra_tokens);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ return heim_enomem(context);
+
+ }
+
+ {
+ size_t append_len = strlen(append);
+ char * new_str = realloc(*ppath_out, len + append_len + 1);
+
+ if (new_str == NULL) {
+ free_extra_tokens(extra_tokens);
+ free(append);
+ if (*ppath_out)
+ free(*ppath_out);
+ *ppath_out = NULL;
+ return heim_enomem(context);
+ }
+
+ *ppath_out = new_str;
+ memcpy(*ppath_out + len, append, append_len + 1);
+ len = len + append_len;
+ free(append);
+ }
+ }
+
+#ifdef _WIN32
+ /* Also deal with slashes */
+ if (filepath && *ppath_out) {
+ char * c;
+
+ for (c = *ppath_out; *c; c++)
+ if (*c == '/')
+ *c = '\\';
+ }
+#endif
+
+ free_extra_tokens(extra_tokens);
+ return 0;
+}