#include "krb5_locl.h"
typedef int PTYPE;
#ifdef _WIN32
#include <shlobj.h>
#include <sddl.h>
static int
_expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
{
TCHAR tpath[MAX_PATH];
size_t len;
if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
if (context)
krb5_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) {
if (context)
krb5_set_error_message(context, ENOMEM, "strdup - Out of memory");
return ENOMEM;
}
return 0;
}
extern HINSTANCE _krb5_hInstance;
static int
_expand_bin_dir(krb5_context context, PTYPE param, const char *postfix, char **ret)
{
TCHAR path[MAX_PATH];
TCHAR *lastSlash;
DWORD nc;
nc = GetModuleFileName(_krb5_hInstance, 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 ENOMEM;
return 0;
}
static int
_expand_userid(krb5_context context, PTYPE param, const char *postfix, 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,
&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) {
if (context)
krb5_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) {
if (context)
krb5_set_error_message(context, rv,
"Unexpected error reading token information (GLE=%d)",
GetLastError());
goto _exit;
}
if (len == 0) {
if (context)
krb5_set_error_message(context, rv,
"GetTokenInformation() returned truncated buffer");
goto _exit;
}
pOwner = malloc(len);
if (pOwner == NULL) {
if (context)
krb5_set_error_message(context, rv, "Out of memory");
goto _exit;
}
} else {
if (context)
krb5_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
goto _exit;
}
if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
if (context)
krb5_set_error_message(context, rv, "GetTokenInformation() failed. GLE=%d", GetLastError());
goto _exit;
}
if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
if (context)
krb5_set_error_message(context, rv, "Can't convert SID to string. GLE=%d", GetLastError());
goto _exit;
}
*ret = strdup(strSid);
if (*ret == NULL && context)
krb5_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;
}
static int
_expand_csidl(krb5_context context, PTYPE folder, const char *postfix, char **ret)
{
TCHAR path[MAX_PATH];
size_t len;
if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
if (context)
krb5_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 ENOMEM;
}
*ret = strdup(path);
if (*ret == NULL) {
if (context)
krb5_set_error_message(context, ENOMEM, "Out of memory");
return ENOMEM;
}
return 0;
}
#else
static int
_expand_path(krb5_context context, PTYPE param, const char *postfix, char **ret)
{
*ret = strdup(postfix);
if (*ret == NULL) {
krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
return ENOMEM;
}
return 0;
}
static int
_expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
{
const char *p = NULL;
if (!issuid())
p = getenv("TEMP");
if (p)
*ret = strdup(p);
else
*ret = strdup("/tmp");
if (*ret == NULL)
return ENOMEM;
return 0;
}
static int
_expand_userid(krb5_context context, PTYPE param, const char *postfix, char **str)
{
int ret = asprintf(str, "%ld", (unsigned long)getuid());
if (ret < 0 || *str == NULL)
return ENOMEM;
return 0;
}
#ifdef __APPLE__
#include <bsm/audit_session.h>
#endif
static int
_expand_asid(krb5_context context, PTYPE param, const char *postfix, char **str)
{
unsigned long asid = 0;
#ifdef __APPLE__
auditinfo_addr_t aia = { .ai_asid = 0 };
int ret;
if (!getaudit_addr(&aia, sizeof(aia))) {
ret = errno;
krb5_set_error_message(context, ret, "cant get audit information for the session");
return ret;
}
asid = aia.ai_asid;
#endif
ret = asprintf(str, "%ld", asid);
if (ret < 0 || *str == NULL)
return krb5_enomem(context);
return 0;
}
#endif
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
static int
_expand_resources(krb5_context context, PTYPE param, const char *postfix, char **str)
{
char path[MAXPATHLEN];
CFBundleRef appBundle = CFBundleGetMainBundle();
if (appBundle == NULL)
return KRB5_CONFIG_BADFORMAT;
CFDictionaryRef infoPlist = CFBundleGetInfoDictionary(appBundle);
if (infoPlist == NULL || CFDictionaryGetCount(infoPlist) == 0)
return KRB5_CONFIG_BADFORMAT;
CFURLRef resourcesDir = CFBundleCopyResourcesDirectoryURL(appBundle);
if (resourcesDir == NULL)
return KRB5_CONFIG_BADFORMAT;
if (!CFURLGetFileSystemRepresentation(resourcesDir, true, (UInt8 *)path, sizeof(path))) {
CFRelease(resourcesDir);
return ENOMEM;
}
CFRelease(resourcesDir);
*str = strdup(path);
if (*str == NULL)
return ENOMEM;
return 0;
}
static int
_expand_env(krb5_context context, PTYPE param, const char *postfix, char **str)
{
const char *env = getenv(postfix);
if (env)
*str = strdup(env);
else
*str = strdup("");
if (*str == NULL)
return ENOMEM;
return 0;
}
#endif
static int
_expand_null(krb5_context context, PTYPE param, const char *postfix, char **ret)
{
*ret = strdup("");
if (*ret == NULL) {
if (context)
krb5_set_error_message(context, ENOMEM, "Out of memory");
return ENOMEM;
}
return 0;
}
static const struct token {
const char * tok;
int ftype;
#define FTYPE_CSIDL 0
#define FTYPE_SPECIAL 1
PTYPE param;
const char * postfix;
int (*exp_func)(krb5_context, PTYPE, const char *, char **);
#define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
#define SPECIAL(f) SPECIALP(f, NULL)
} tokens[] = {
#ifdef __APPLE__
{"ApplicationResources", SPECIAL(_expand_resources) },
{"IPHONE_SIMULATOR_ROOT", SPECIALP(_expand_env, "IPHONE_SIMULATOR_ROOT") },
#endif
#ifdef _WIN32
#define CSIDLP(C,P) FTYPE_CSIDL, C, P, _expand_csidl
#define CSIDL(C) CSIDLP(C, NULL)
{"APPDATA", CSIDL(CSIDL_APPDATA)},
{"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)},
{"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)},
{"SYSTEM", CSIDL(CSIDL_SYSTEM)},
{"WINDOWS", CSIDL(CSIDL_WINDOWS)},
{"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)},
{"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)},
{"LIBDIR", SPECIAL(_expand_bin_dir)},
{"BINDIR", SPECIAL(_expand_bin_dir)},
{"LIBEXEC", SPECIAL(_expand_bin_dir)},
{"SBINDIR", SPECIAL(_expand_bin_dir)},
#else
{"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},
#endif
{"TEMP", SPECIAL(_expand_temp_folder)},
{"USERID", SPECIAL(_expand_userid)},
{"uid", SPECIAL(_expand_userid)},
{"asid", SPECIAL(_expand_asid)},
{"null", SPECIAL(_expand_null)}
};
static int
_expand_token(krb5_context context,
const char *token,
const char *token_end,
char **ret)
{
size_t token_len = (token_end - token);
size_t i;
*ret = NULL;
if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
token_end - token <= 2) {
if (context)
krb5_set_error_message(context, EINVAL,"Invalid token: %.*s", (int)token_len, token);
return EINVAL;
}
for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++) {
if (!strncmp(token+2, tokens[i].tok, token_len - 2))
return tokens[i].exp_func(context, tokens[i].param,
tokens[i].postfix, ret);
}
if (context)
krb5_set_error_message(context, EINVAL,"Invalid token: %.*s", (int)token_len, token);
return EINVAL;
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_expand_path_tokens(krb5_context context,
const char *path_in,
char **ppath_out)
{
char *tok_begin, *tok_end, *append;
krb5_error_code ret;
const char *path_left;
size_t len = 0;
if (path_in == NULL || *path_in == '\0') {
*ppath_out = strdup("");
return 0;
}
*ppath_out = NULL;
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) {
if (*ppath_out)
free(*ppath_out);
*ppath_out = NULL;
if (context)
krb5_set_error_message(context, EINVAL, "variable missing }");
return EINVAL;
}
ret = _expand_token(context, tok_begin, tok_end, &append);
if (ret) {
if (*ppath_out)
free(*ppath_out);
*ppath_out = NULL;
return ret;
}
path_left = tok_end + 1;
} else {
append = strdup(path_left);
path_left = NULL;
}
if (append == NULL) {
if (*ppath_out)
free(*ppath_out);
*ppath_out = NULL;
if (context)
krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
return ENOMEM;
}
{
size_t append_len = strlen(append);
char * new_str = realloc(*ppath_out, len + append_len + 1);
if (new_str == NULL) {
free(append);
if (*ppath_out)
free(*ppath_out);
*ppath_out = NULL;
if (context)
krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
return ENOMEM;
}
*ppath_out = new_str;
memcpy(*ppath_out + len, append, append_len + 1);
len = len + append_len;
free(append);
}
}
#ifdef _WIN32
if (*ppath_out) {
char * c;
for (c = *ppath_out; *c; c++)
if (*c == '/')
*c = '\\';
}
#endif
return 0;
}