1226031Sstas 2226031Sstas/*********************************************************************** 3226031Sstas * Copyright (c) 2009, Secure Endpoints Inc. 4226031Sstas * All rights reserved. 5226031Sstas * 6226031Sstas * Redistribution and use in source and binary forms, with or without 7226031Sstas * modification, are permitted provided that the following conditions 8226031Sstas * are met: 9226031Sstas * 10226031Sstas * - Redistributions of source code must retain the above copyright 11226031Sstas * notice, this list of conditions and the following disclaimer. 12226031Sstas * 13226031Sstas * - Redistributions in binary form must reproduce the above copyright 14226031Sstas * notice, this list of conditions and the following disclaimer in 15226031Sstas * the documentation and/or other materials provided with the 16226031Sstas * distribution. 17226031Sstas * 18226031Sstas * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19226031Sstas * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20226031Sstas * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21226031Sstas * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22226031Sstas * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23226031Sstas * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24226031Sstas * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25226031Sstas * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 27226031Sstas * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28226031Sstas * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 29226031Sstas * OF THE POSSIBILITY OF SUCH DAMAGE. 30226031Sstas * 31226031Sstas **********************************************************************/ 32226031Sstas 33226031Sstas#include "krb5_locl.h" 34226031Sstas 35226031Sstastypedef int PTYPE; 36226031Sstas 37226031Sstas#ifdef _WIN32 38226031Sstas#include <shlobj.h> 39226031Sstas#include <sddl.h> 40226031Sstas 41226031Sstas/* 42226031Sstas * Expand a %{TEMP} token 43226031Sstas * 44226031Sstas * The %{TEMP} token expands to the temporary path for the current 45226031Sstas * user as returned by GetTempPath(). 46226031Sstas * 47226031Sstas * @note: Since the GetTempPath() function relies on the TMP or TEMP 48226031Sstas * environment variables, this function will failover to the system 49226031Sstas * temporary directory until the user profile is loaded. In addition, 50226031Sstas * the returned path may or may not exist. 51226031Sstas */ 52226031Sstasstatic int 53226031Sstas_expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret) 54226031Sstas{ 55226031Sstas TCHAR tpath[MAX_PATH]; 56226031Sstas size_t len; 57226031Sstas 58226031Sstas if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) { 59226031Sstas if (context) 60226031Sstas krb5_set_error_message(context, EINVAL, 61226031Sstas "Failed to get temporary path (GLE=%d)", 62226031Sstas GetLastError()); 63226031Sstas return EINVAL; 64226031Sstas } 65226031Sstas 66226031Sstas len = strlen(tpath); 67226031Sstas 68226031Sstas if (len > 0 && tpath[len - 1] == '\\') 69226031Sstas tpath[len - 1] = '\0'; 70226031Sstas 71226031Sstas *ret = strdup(tpath); 72226031Sstas 73226031Sstas if (*ret == NULL) { 74226031Sstas if (context) 75226031Sstas krb5_set_error_message(context, ENOMEM, "strdup - Out of memory"); 76226031Sstas return ENOMEM; 77226031Sstas } 78226031Sstas 79226031Sstas return 0; 80226031Sstas} 81226031Sstas 82226031Sstasextern HINSTANCE _krb5_hInstance; 83226031Sstas 84226031Sstas/* 85226031Sstas * Expand a %{BINDIR} token 86226031Sstas * 87226031Sstas * This is also used to expand a few other tokens on Windows, since 88226031Sstas * most of the executable binaries end up in the same directory. The 89226031Sstas * "bin" directory is considered to be the directory in which the 90226031Sstas * krb5.dll is located. 91226031Sstas */ 92226031Sstasstatic int 93226031Sstas_expand_bin_dir(krb5_context context, PTYPE param, const char *postfix, char **ret) 94226031Sstas{ 95226031Sstas TCHAR path[MAX_PATH]; 96226031Sstas TCHAR *lastSlash; 97226031Sstas DWORD nc; 98226031Sstas 99226031Sstas nc = GetModuleFileName(_krb5_hInstance, path, sizeof(path)/sizeof(path[0])); 100226031Sstas if (nc == 0 || 101226031Sstas nc == sizeof(path)/sizeof(path[0])) { 102226031Sstas return EINVAL; 103226031Sstas } 104226031Sstas 105226031Sstas lastSlash = strrchr(path, '\\'); 106226031Sstas if (lastSlash != NULL) { 107226031Sstas TCHAR *fslash = strrchr(lastSlash, '/'); 108226031Sstas 109226031Sstas if (fslash != NULL) 110226031Sstas lastSlash = fslash; 111226031Sstas 112226031Sstas *lastSlash = '\0'; 113226031Sstas } 114226031Sstas 115226031Sstas if (postfix) { 116226031Sstas if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0])) 117226031Sstas return EINVAL; 118226031Sstas } 119226031Sstas 120226031Sstas *ret = strdup(path); 121226031Sstas if (*ret == NULL) 122226031Sstas return ENOMEM; 123226031Sstas 124226031Sstas return 0; 125226031Sstas} 126226031Sstas 127226031Sstas/* 128226031Sstas * Expand a %{USERID} token 129226031Sstas * 130226031Sstas * The %{USERID} token expands to the string representation of the 131226031Sstas * user's SID. The user account that will be used is the account 132226031Sstas * corresponding to the current thread's security token. This means 133226031Sstas * that: 134226031Sstas * 135226031Sstas * - If the current thread token has the anonymous impersonation 136226031Sstas * level, the call will fail. 137226031Sstas * 138226031Sstas * - If the current thread is impersonating a token at 139226031Sstas * SecurityIdentification level the call will fail. 140226031Sstas * 141226031Sstas */ 142226031Sstasstatic int 143226031Sstas_expand_userid(krb5_context context, PTYPE param, const char *postfix, char **ret) 144226031Sstas{ 145226031Sstas int rv = EINVAL; 146226031Sstas HANDLE hThread = NULL; 147226031Sstas HANDLE hToken = NULL; 148226031Sstas PTOKEN_OWNER pOwner = NULL; 149226031Sstas DWORD len = 0; 150226031Sstas LPTSTR strSid = NULL; 151226031Sstas 152226031Sstas hThread = GetCurrentThread(); 153226031Sstas 154226031Sstas if (!OpenThreadToken(hThread, TOKEN_QUERY, 155226031Sstas FALSE, /* Open the thread token as the 156226031Sstas current thread user. */ 157226031Sstas &hToken)) { 158226031Sstas 159226031Sstas DWORD le = GetLastError(); 160226031Sstas 161226031Sstas if (le == ERROR_NO_TOKEN) { 162226031Sstas HANDLE hProcess = GetCurrentProcess(); 163226031Sstas 164226031Sstas le = 0; 165226031Sstas if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) 166226031Sstas le = GetLastError(); 167226031Sstas } 168226031Sstas 169226031Sstas if (le != 0) { 170226031Sstas if (context) 171226031Sstas krb5_set_error_message(context, rv, 172226031Sstas "Can't open thread token (GLE=%d)", le); 173226031Sstas goto _exit; 174226031Sstas } 175226031Sstas } 176226031Sstas 177226031Sstas if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) { 178226031Sstas if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { 179226031Sstas if (context) 180226031Sstas krb5_set_error_message(context, rv, 181226031Sstas "Unexpected error reading token information (GLE=%d)", 182226031Sstas GetLastError()); 183226031Sstas goto _exit; 184226031Sstas } 185226031Sstas 186226031Sstas if (len == 0) { 187226031Sstas if (context) 188226031Sstas krb5_set_error_message(context, rv, 189226031Sstas "GetTokenInformation() returned truncated buffer"); 190226031Sstas goto _exit; 191226031Sstas } 192226031Sstas 193226031Sstas pOwner = malloc(len); 194226031Sstas if (pOwner == NULL) { 195226031Sstas if (context) 196226031Sstas krb5_set_error_message(context, rv, "Out of memory"); 197226031Sstas goto _exit; 198226031Sstas } 199226031Sstas } else { 200226031Sstas if (context) 201226031Sstas krb5_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer"); 202226031Sstas goto _exit; 203226031Sstas } 204226031Sstas 205226031Sstas if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) { 206226031Sstas if (context) 207226031Sstas krb5_set_error_message(context, rv, "GetTokenInformation() failed. GLE=%d", GetLastError()); 208226031Sstas goto _exit; 209226031Sstas } 210226031Sstas 211226031Sstas if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) { 212226031Sstas if (context) 213226031Sstas krb5_set_error_message(context, rv, "Can't convert SID to string. GLE=%d", GetLastError()); 214226031Sstas goto _exit; 215226031Sstas } 216226031Sstas 217226031Sstas *ret = strdup(strSid); 218226031Sstas if (*ret == NULL && context) 219226031Sstas krb5_set_error_message(context, rv, "Out of memory"); 220226031Sstas 221226031Sstas rv = 0; 222226031Sstas 223226031Sstas _exit: 224226031Sstas if (hToken != NULL) 225226031Sstas CloseHandle(hToken); 226226031Sstas 227226031Sstas if (pOwner != NULL) 228226031Sstas free (pOwner); 229226031Sstas 230226031Sstas if (strSid != NULL) 231226031Sstas LocalFree(strSid); 232226031Sstas 233226031Sstas return rv; 234226031Sstas} 235226031Sstas 236226031Sstas/* 237226031Sstas * Expand a folder identified by a CSIDL 238226031Sstas */ 239226031Sstas 240226031Sstasstatic int 241226031Sstas_expand_csidl(krb5_context context, PTYPE folder, const char *postfix, char **ret) 242226031Sstas{ 243226031Sstas TCHAR path[MAX_PATH]; 244226031Sstas size_t len; 245226031Sstas 246226031Sstas if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) { 247226031Sstas if (context) 248226031Sstas krb5_set_error_message(context, EINVAL, "Unable to determine folder path"); 249226031Sstas return EINVAL; 250226031Sstas } 251226031Sstas 252226031Sstas len = strlen(path); 253226031Sstas 254226031Sstas if (len > 0 && path[len - 1] == '\\') 255226031Sstas path[len - 1] = '\0'; 256226031Sstas 257226031Sstas if (postfix && 258226031Sstas strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0])) { 259226031Sstas return ENOMEM; 260226031Sstas } 261226031Sstas 262226031Sstas *ret = strdup(path); 263226031Sstas if (*ret == NULL) { 264226031Sstas if (context) 265226031Sstas krb5_set_error_message(context, ENOMEM, "Out of memory"); 266226031Sstas return ENOMEM; 267226031Sstas } 268226031Sstas return 0; 269226031Sstas} 270226031Sstas 271226031Sstas#else 272226031Sstas 273226031Sstasstatic int 274226031Sstas_expand_path(krb5_context context, PTYPE param, const char *postfix, char **ret) 275226031Sstas{ 276226031Sstas *ret = strdup(postfix); 277226031Sstas if (*ret == NULL) { 278226031Sstas krb5_set_error_message(context, ENOMEM, "malloc - out of memory"); 279226031Sstas return ENOMEM; 280226031Sstas } 281226031Sstas return 0; 282226031Sstas} 283226031Sstas 284226031Sstasstatic int 285226031Sstas_expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret) 286226031Sstas{ 287226031Sstas const char *p = NULL; 288226031Sstas 289226031Sstas if (issuid()) 290226031Sstas p = getenv("TEMP"); 291226031Sstas if (p) 292226031Sstas *ret = strdup(p); 293226031Sstas else 294226031Sstas *ret = strdup("/tmp"); 295226031Sstas if (*ret == NULL) 296226031Sstas return ENOMEM; 297226031Sstas return 0; 298226031Sstas} 299226031Sstas 300226031Sstasstatic int 301226031Sstas_expand_userid(krb5_context context, PTYPE param, const char *postfix, char **str) 302226031Sstas{ 303226031Sstas int ret = asprintf(str, "%ld", (unsigned long)getuid()); 304226031Sstas if (ret < 0 || *str == NULL) 305226031Sstas return ENOMEM; 306226031Sstas return 0; 307226031Sstas} 308226031Sstas 309226031Sstas 310226031Sstas#endif /* _WIN32 */ 311226031Sstas 312226031Sstas/** 313226031Sstas * Expand a %{null} token 314226031Sstas * 315226031Sstas * The expansion of a %{null} token is always the empty string. 316226031Sstas */ 317226031Sstas 318226031Sstasstatic int 319226031Sstas_expand_null(krb5_context context, PTYPE param, const char *postfix, char **ret) 320226031Sstas{ 321226031Sstas *ret = strdup(""); 322226031Sstas if (*ret == NULL) { 323226031Sstas if (context) 324226031Sstas krb5_set_error_message(context, ENOMEM, "Out of memory"); 325226031Sstas return ENOMEM; 326226031Sstas } 327226031Sstas return 0; 328226031Sstas} 329226031Sstas 330226031Sstas 331226031Sstasstatic const struct token { 332226031Sstas const char * tok; 333226031Sstas int ftype; 334226031Sstas#define FTYPE_CSIDL 0 335226031Sstas#define FTYPE_SPECIAL 1 336226031Sstas 337226031Sstas PTYPE param; 338226031Sstas const char * postfix; 339226031Sstas 340226031Sstas int (*exp_func)(krb5_context, PTYPE, const char *, char **); 341226031Sstas 342226031Sstas#define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f 343226031Sstas#define SPECIAL(f) SPECIALP(f, NULL) 344226031Sstas 345226031Sstas} tokens[] = { 346226031Sstas#ifdef _WIN32 347226031Sstas#define CSIDLP(C,P) FTYPE_CSIDL, C, P, _expand_csidl 348226031Sstas#define CSIDL(C) CSIDLP(C, NULL) 349226031Sstas 350226031Sstas {"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */ 351226031Sstas {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */ 352226031Sstas {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */ 353226031Sstas {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */ 354226031Sstas {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */ 355226031Sstas {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */ 356226031Sstas {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */ 357226031Sstas {"LIBDIR", SPECIAL(_expand_bin_dir)}, 358226031Sstas {"BINDIR", SPECIAL(_expand_bin_dir)}, 359226031Sstas {"LIBEXEC", SPECIAL(_expand_bin_dir)}, 360226031Sstas {"SBINDIR", SPECIAL(_expand_bin_dir)}, 361226031Sstas#else 362226031Sstas {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, _expand_path}, 363226031Sstas {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, _expand_path}, 364226031Sstas {"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, _expand_path}, 365226031Sstas {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, _expand_path}, 366226031Sstas#endif 367226031Sstas {"TEMP", SPECIAL(_expand_temp_folder)}, 368226031Sstas {"USERID", SPECIAL(_expand_userid)}, 369226031Sstas {"uid", SPECIAL(_expand_userid)}, 370226031Sstas {"null", SPECIAL(_expand_null)} 371226031Sstas}; 372226031Sstas 373226031Sstasstatic int 374226031Sstas_expand_token(krb5_context context, 375226031Sstas const char *token, 376226031Sstas const char *token_end, 377226031Sstas char **ret) 378226031Sstas{ 379226031Sstas size_t i; 380226031Sstas 381226031Sstas *ret = NULL; 382226031Sstas 383226031Sstas if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' || 384226031Sstas token_end - token <= 2) { 385226031Sstas if (context) 386226031Sstas krb5_set_error_message(context, EINVAL,"Invalid token."); 387226031Sstas return EINVAL; 388226031Sstas } 389226031Sstas 390226031Sstas for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++) { 391226031Sstas if (!strncmp(token+2, tokens[i].tok, (token_end - token) - 2)) 392226031Sstas return tokens[i].exp_func(context, tokens[i].param, 393226031Sstas tokens[i].postfix, ret); 394226031Sstas } 395226031Sstas 396226031Sstas if (context) 397226031Sstas krb5_set_error_message(context, EINVAL, "Invalid token."); 398226031Sstas return EINVAL; 399226031Sstas} 400226031Sstas 401226031SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 402226031Sstas_krb5_expand_path_tokens(krb5_context context, 403226031Sstas const char *path_in, 404226031Sstas char **ppath_out) 405226031Sstas{ 406226031Sstas char *tok_begin, *tok_end, *append; 407226031Sstas const char *path_left; 408226031Sstas size_t len = 0; 409226031Sstas 410226031Sstas if (path_in == NULL || *path_in == '\0') { 411226031Sstas *ppath_out = strdup(""); 412226031Sstas return 0; 413226031Sstas } 414226031Sstas 415226031Sstas *ppath_out = NULL; 416226031Sstas 417226031Sstas for (path_left = path_in; path_left && *path_left; ) { 418226031Sstas 419226031Sstas tok_begin = strstr(path_left, "%{"); 420226031Sstas 421226031Sstas if (tok_begin && tok_begin != path_left) { 422226031Sstas 423226031Sstas append = malloc((tok_begin - path_left) + 1); 424226031Sstas if (append) { 425226031Sstas memcpy(append, path_left, tok_begin - path_left); 426226031Sstas append[tok_begin - path_left] = '\0'; 427226031Sstas } 428226031Sstas path_left = tok_begin; 429226031Sstas 430226031Sstas } else if (tok_begin) { 431226031Sstas 432226031Sstas tok_end = strchr(tok_begin, '}'); 433226031Sstas if (tok_end == NULL) { 434226031Sstas if (*ppath_out) 435226031Sstas free(*ppath_out); 436226031Sstas *ppath_out = NULL; 437226031Sstas if (context) 438226031Sstas krb5_set_error_message(context, EINVAL, "variable missing }"); 439226031Sstas return EINVAL; 440226031Sstas } 441226031Sstas 442226031Sstas if (_expand_token(context, tok_begin, tok_end, &append)) { 443226031Sstas if (*ppath_out) 444226031Sstas free(*ppath_out); 445226031Sstas *ppath_out = NULL; 446226031Sstas return EINVAL; 447226031Sstas } 448226031Sstas 449226031Sstas path_left = tok_end + 1; 450226031Sstas } else { 451226031Sstas 452226031Sstas append = strdup(path_left); 453226031Sstas path_left = NULL; 454226031Sstas 455226031Sstas } 456226031Sstas 457226031Sstas if (append == NULL) { 458226031Sstas 459226031Sstas if (*ppath_out) 460226031Sstas free(*ppath_out); 461226031Sstas *ppath_out = NULL; 462226031Sstas if (context) 463226031Sstas krb5_set_error_message(context, ENOMEM, "malloc - out of memory"); 464226031Sstas return ENOMEM; 465226031Sstas 466226031Sstas } 467226031Sstas 468226031Sstas { 469226031Sstas size_t append_len = strlen(append); 470226031Sstas char * new_str = realloc(*ppath_out, len + append_len + 1); 471226031Sstas 472226031Sstas if (new_str == NULL) { 473226031Sstas free(append); 474226031Sstas if (*ppath_out) 475226031Sstas free(*ppath_out); 476226031Sstas *ppath_out = NULL; 477226031Sstas if (context) 478226031Sstas krb5_set_error_message(context, ENOMEM, "malloc - out of memory"); 479226031Sstas return ENOMEM; 480226031Sstas } 481226031Sstas 482226031Sstas *ppath_out = new_str; 483226031Sstas memcpy(*ppath_out + len, append, append_len + 1); 484226031Sstas len = len + append_len; 485226031Sstas free(append); 486226031Sstas } 487226031Sstas } 488226031Sstas 489226031Sstas#ifdef _WIN32 490226031Sstas /* Also deal with slashes */ 491226031Sstas if (*ppath_out) { 492226031Sstas char * c; 493226031Sstas for (c = *ppath_out; *c; c++) 494226031Sstas if (*c == '/') 495226031Sstas *c = '\\'; 496226031Sstas } 497226031Sstas#endif 498226031Sstas 499226031Sstas return 0; 500226031Sstas} 501