Path: blob/main/crypto/heimdal/lib/krb5/expand_path.c
34878 views
1/***********************************************************************2* Copyright (c) 2009, Secure Endpoints Inc.3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8*9* - Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* - Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in14* the documentation and/or other materials provided with the15* distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS18* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT19* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS20* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE21* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,22* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES23* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR24* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)25* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,26* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)27* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED28* OF THE POSSIBILITY OF SUCH DAMAGE.29*30**********************************************************************/3132#include "krb5_locl.h"3334typedef int PTYPE;3536#ifdef _WIN3237#include <shlobj.h>38#include <sddl.h>3940/*41* Expand a %{TEMP} token42*43* The %{TEMP} token expands to the temporary path for the current44* user as returned by GetTempPath().45*46* @note: Since the GetTempPath() function relies on the TMP or TEMP47* environment variables, this function will failover to the system48* temporary directory until the user profile is loaded. In addition,49* the returned path may or may not exist.50*/51static int52_expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)53{54TCHAR tpath[MAX_PATH];55size_t len;5657if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {58if (context)59krb5_set_error_message(context, EINVAL,60"Failed to get temporary path (GLE=%d)",61GetLastError());62return EINVAL;63}6465len = strlen(tpath);6667if (len > 0 && tpath[len - 1] == '\\')68tpath[len - 1] = '\0';6970*ret = strdup(tpath);7172if (*ret == NULL) {73if (context)74krb5_set_error_message(context, ENOMEM, "strdup - Out of memory");75return ENOMEM;76}7778return 0;79}8081extern HINSTANCE _krb5_hInstance;8283/*84* Expand a %{BINDIR} token85*86* This is also used to expand a few other tokens on Windows, since87* most of the executable binaries end up in the same directory. The88* "bin" directory is considered to be the directory in which the89* krb5.dll is located.90*/91static int92_expand_bin_dir(krb5_context context, PTYPE param, const char *postfix, char **ret)93{94TCHAR path[MAX_PATH];95TCHAR *lastSlash;96DWORD nc;9798nc = GetModuleFileName(_krb5_hInstance, path, sizeof(path)/sizeof(path[0]));99if (nc == 0 ||100nc == sizeof(path)/sizeof(path[0])) {101return EINVAL;102}103104lastSlash = strrchr(path, '\\');105if (lastSlash != NULL) {106TCHAR *fslash = strrchr(lastSlash, '/');107108if (fslash != NULL)109lastSlash = fslash;110111*lastSlash = '\0';112}113114if (postfix) {115if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))116return EINVAL;117}118119*ret = strdup(path);120if (*ret == NULL)121return ENOMEM;122123return 0;124}125126/*127* Expand a %{USERID} token128*129* The %{USERID} token expands to the string representation of the130* user's SID. The user account that will be used is the account131* corresponding to the current thread's security token. This means132* that:133*134* - If the current thread token has the anonymous impersonation135* level, the call will fail.136*137* - If the current thread is impersonating a token at138* SecurityIdentification level the call will fail.139*140*/141static int142_expand_userid(krb5_context context, PTYPE param, const char *postfix, char **ret)143{144int rv = EINVAL;145HANDLE hThread = NULL;146HANDLE hToken = NULL;147PTOKEN_OWNER pOwner = NULL;148DWORD len = 0;149LPTSTR strSid = NULL;150151hThread = GetCurrentThread();152153if (!OpenThreadToken(hThread, TOKEN_QUERY,154FALSE, /* Open the thread token as the155current thread user. */156&hToken)) {157158DWORD le = GetLastError();159160if (le == ERROR_NO_TOKEN) {161HANDLE hProcess = GetCurrentProcess();162163le = 0;164if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))165le = GetLastError();166}167168if (le != 0) {169if (context)170krb5_set_error_message(context, rv,171"Can't open thread token (GLE=%d)", le);172goto _exit;173}174}175176if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {177if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {178if (context)179krb5_set_error_message(context, rv,180"Unexpected error reading token information (GLE=%d)",181GetLastError());182goto _exit;183}184185if (len == 0) {186if (context)187krb5_set_error_message(context, rv,188"GetTokenInformation() returned truncated buffer");189goto _exit;190}191192pOwner = malloc(len);193if (pOwner == NULL) {194if (context)195krb5_set_error_message(context, rv, "Out of memory");196goto _exit;197}198} else {199if (context)200krb5_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");201goto _exit;202}203204if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {205if (context)206krb5_set_error_message(context, rv, "GetTokenInformation() failed. GLE=%d", GetLastError());207goto _exit;208}209210if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {211if (context)212krb5_set_error_message(context, rv, "Can't convert SID to string. GLE=%d", GetLastError());213goto _exit;214}215216*ret = strdup(strSid);217if (*ret == NULL && context)218krb5_set_error_message(context, rv, "Out of memory");219220rv = 0;221222_exit:223if (hToken != NULL)224CloseHandle(hToken);225226if (pOwner != NULL)227free (pOwner);228229if (strSid != NULL)230LocalFree(strSid);231232return rv;233}234235/*236* Expand a folder identified by a CSIDL237*/238239static int240_expand_csidl(krb5_context context, PTYPE folder, const char *postfix, char **ret)241{242TCHAR path[MAX_PATH];243size_t len;244245if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {246if (context)247krb5_set_error_message(context, EINVAL, "Unable to determine folder path");248return EINVAL;249}250251len = strlen(path);252253if (len > 0 && path[len - 1] == '\\')254path[len - 1] = '\0';255256if (postfix &&257strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0])) {258return ENOMEM;259}260261*ret = strdup(path);262if (*ret == NULL) {263if (context)264krb5_set_error_message(context, ENOMEM, "Out of memory");265return ENOMEM;266}267return 0;268}269270#else271272static int273_expand_path(krb5_context context, PTYPE param, const char *postfix, char **ret)274{275*ret = strdup(postfix);276if (*ret == NULL) {277krb5_set_error_message(context, ENOMEM, "malloc - out of memory");278return ENOMEM;279}280return 0;281}282283static int284_expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)285{286const char *p = NULL;287288if (issuid())289p = getenv("TEMP");290if (p)291*ret = strdup(p);292else293*ret = strdup("/tmp");294if (*ret == NULL)295return ENOMEM;296return 0;297}298299static int300_expand_userid(krb5_context context, PTYPE param, const char *postfix, char **str)301{302int ret = asprintf(str, "%ld", (unsigned long)getuid());303if (ret < 0 || *str == NULL)304return ENOMEM;305return 0;306}307308309#endif /* _WIN32 */310311/**312* Expand a %{null} token313*314* The expansion of a %{null} token is always the empty string.315*/316317static int318_expand_null(krb5_context context, PTYPE param, const char *postfix, char **ret)319{320*ret = strdup("");321if (*ret == NULL) {322if (context)323krb5_set_error_message(context, ENOMEM, "Out of memory");324return ENOMEM;325}326return 0;327}328329330static const struct token {331const char * tok;332int ftype;333#define FTYPE_CSIDL 0334#define FTYPE_SPECIAL 1335336PTYPE param;337const char * postfix;338339int (*exp_func)(krb5_context, PTYPE, const char *, char **);340341#define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f342#define SPECIAL(f) SPECIALP(f, NULL)343344} tokens[] = {345#ifdef _WIN32346#define CSIDLP(C,P) FTYPE_CSIDL, C, P, _expand_csidl347#define CSIDL(C) CSIDLP(C, NULL)348349{"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */350{"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */351{"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */352{"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */353{"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */354{"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */355{"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */356{"LIBDIR", SPECIAL(_expand_bin_dir)},357{"BINDIR", SPECIAL(_expand_bin_dir)},358{"LIBEXEC", SPECIAL(_expand_bin_dir)},359{"SBINDIR", SPECIAL(_expand_bin_dir)},360#else361{"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, _expand_path},362{"BINDIR", FTYPE_SPECIAL, 0, BINDIR, _expand_path},363{"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, _expand_path},364{"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, _expand_path},365#endif366{"TEMP", SPECIAL(_expand_temp_folder)},367{"USERID", SPECIAL(_expand_userid)},368{"uid", SPECIAL(_expand_userid)},369{"null", SPECIAL(_expand_null)}370};371372static int373_expand_token(krb5_context context,374const char *token,375const char *token_end,376char **ret)377{378size_t i;379380*ret = NULL;381382if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||383token_end - token <= 2) {384if (context)385krb5_set_error_message(context, EINVAL,"Invalid token.");386return EINVAL;387}388389for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++) {390if (!strncmp(token+2, tokens[i].tok, (token_end - token) - 2))391return tokens[i].exp_func(context, tokens[i].param,392tokens[i].postfix, ret);393}394395if (context)396krb5_set_error_message(context, EINVAL, "Invalid token.");397return EINVAL;398}399400KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL401_krb5_expand_path_tokens(krb5_context context,402const char *path_in,403char **ppath_out)404{405char *tok_begin, *tok_end, *append;406const char *path_left;407size_t len = 0;408409if (path_in == NULL || *path_in == '\0') {410*ppath_out = strdup("");411return 0;412}413414*ppath_out = NULL;415416for (path_left = path_in; path_left && *path_left; ) {417418tok_begin = strstr(path_left, "%{");419420if (tok_begin && tok_begin != path_left) {421422append = malloc((tok_begin - path_left) + 1);423if (append) {424memcpy(append, path_left, tok_begin - path_left);425append[tok_begin - path_left] = '\0';426}427path_left = tok_begin;428429} else if (tok_begin) {430431tok_end = strchr(tok_begin, '}');432if (tok_end == NULL) {433if (*ppath_out)434free(*ppath_out);435*ppath_out = NULL;436if (context)437krb5_set_error_message(context, EINVAL, "variable missing }");438return EINVAL;439}440441if (_expand_token(context, tok_begin, tok_end, &append)) {442if (*ppath_out)443free(*ppath_out);444*ppath_out = NULL;445return EINVAL;446}447448path_left = tok_end + 1;449} else {450451append = strdup(path_left);452path_left = NULL;453454}455456if (append == NULL) {457458if (*ppath_out)459free(*ppath_out);460*ppath_out = NULL;461if (context)462krb5_set_error_message(context, ENOMEM, "malloc - out of memory");463return ENOMEM;464465}466467{468size_t append_len = strlen(append);469char * new_str = realloc(*ppath_out, len + append_len + 1);470471if (new_str == NULL) {472free(append);473if (*ppath_out)474free(*ppath_out);475*ppath_out = NULL;476if (context)477krb5_set_error_message(context, ENOMEM, "malloc - out of memory");478return ENOMEM;479}480481*ppath_out = new_str;482memcpy(*ppath_out + len, append, append_len + 1);483len = len + append_len;484free(append);485}486}487488#ifdef _WIN32489/* Also deal with slashes */490if (*ppath_out) {491char * c;492for (c = *ppath_out; *c; c++)493if (*c == '/')494*c = '\\';495}496#endif497498return 0;499}500501502