485 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			485 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***********************************************************************
 | |
|  * Copyright (c) 2009, 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 "krb5_locl.h"
 | |
| #include <shlobj.h>
 | |
| #include <sddl.h>
 | |
| 
 | |
| typedef int PTYPE;
 | |
| 
 | |
| /**
 | |
|  * 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 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_string(context, "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_string(context, "strdup - Out of memory");
 | |
| 	return ENOMEM;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| extern HINSTANCE _krb5_hInstance;
 | |
| 
 | |
| /**
 | |
|  * 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
 | |
|  * krb5.dll is located.
 | |
|  */
 | |
| 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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  *  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 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,	/* 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) {
 | |
| 	    if (context)
 | |
| 		krb5_set_error_string(context, "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_string(context, "Unexpected error reading token information (GLE=%d)",
 | |
| 				      GetLastError());
 | |
| 	    goto _exit;
 | |
| 	}
 | |
| 
 | |
| 	if (len == 0) {
 | |
| 	    if (context)
 | |
| 		krb5_set_error_string(context, "GetTokenInformation() returned truncated buffer");
 | |
| 	    goto _exit;
 | |
| 	}
 | |
| 
 | |
| 	pOwner = malloc(len);
 | |
| 	if (pOwner == NULL) {
 | |
| 	    if (context)
 | |
| 		krb5_set_error_string(context, "Out of memory");
 | |
| 	    goto _exit;
 | |
| 	}
 | |
|     } else {
 | |
| 	if (context)
 | |
| 	    krb5_set_error_string(context, "GetTokenInformation() returned truncated buffer");
 | |
| 	goto _exit;
 | |
|     }
 | |
| 
 | |
|     if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
 | |
| 	if (context)
 | |
| 	    krb5_set_error_string(context, "GetTokenInformation() failed. GLE=%d", GetLastError());
 | |
| 	goto _exit;
 | |
|     }
 | |
| 
 | |
|     if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
 | |
| 	if (context)
 | |
| 	    krb5_set_error_string(context, "Can't convert SID to string. GLE=%d", GetLastError());
 | |
| 	goto _exit;
 | |
|     }
 | |
| 
 | |
|     *ret = strdup(strSid);
 | |
|     if (*ret == NULL && context)
 | |
| 	krb5_set_error_string(context, "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 %{null} token
 | |
|  *
 | |
|  * The expansion of a %{null} token is always the empty string.
 | |
|  */
 | |
| static int
 | |
| _expand_null(krb5_context context, PTYPE param, const char * postfix, char ** ret)
 | |
| {
 | |
|     *ret = strdup("");
 | |
|     if (*ret == NULL) {
 | |
| 	if (context)
 | |
| 	    krb5_set_error_string(context, "Out of memory");
 | |
| 	return ENOMEM;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Expand a folder identified by a CSIDL
 | |
|  *
 | |
|  * Parameters:
 | |
|  *
 | |
|  * @param[in] folder A CSIDL value identifying the folder to be
 | |
|  *     returned.
 | |
|  */
 | |
| 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_string(context, "Unable to determine folder path");
 | |
| 	return 1;
 | |
|     } 
 | |
| 
 | |
|     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_string(context, "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 CSIDLP(C,P) FTYPE_CSIDL, C, P, _expand_csidl
 | |
| #define CSIDL(C) CSIDLP(C, NULL)
 | |
| 
 | |
| #define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
 | |
| #define SPECIAL(f) SPECIALP(f, NULL)
 | |
| 
 | |
| } tokens[] = {
 | |
|     /* Windows only -- */
 | |
|     {"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 */
 | |
|     /* -- end Windows only */
 | |
| 
 | |
|     {"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)},
 | |
| 				/* Expands to the "lib" directory.  On
 | |
| 				   Windows this is treated the same as
 | |
| 				   the "bin" directory. */
 | |
| 
 | |
|     {"BINDIR", SPECIAL(_expand_bin_dir)},
 | |
| 				/* Expands to the "bin" directory. On
 | |
| 				   Windows this is treated the same as
 | |
| 				   the "bin" directory. */
 | |
| 
 | |
|     {"LIBEXEC", SPECIAL(_expand_bin_dir)},
 | |
| 				/* Expands to the "libexec"
 | |
| 				   directory. On Windows, this is
 | |
| 				   treated the same as the "bin"
 | |
| 				   directory. */
 | |
| 
 | |
|     {"SBINDIR", SPECIAL(_expand_bin_dir)},
 | |
| 				/* Expands to the "sbin" directory.
 | |
| 				   On Windows, this is treated the
 | |
| 				   same as the "bin" directory. */
 | |
| 
 | |
|     {"TEMP", SPECIAL(_expand_temp_folder)},
 | |
| 				/* Temporary files folder */
 | |
| 
 | |
|     {"USERID", SPECIAL(_expand_userid)},
 | |
| 				/* User ID (On Windows, this expands
 | |
| 				   to the user's string SID */
 | |
| 
 | |
|     {"uid", SPECIAL(_expand_userid)},
 | |
| 				/* Alias for USERID */
 | |
| 
 | |
|     {"null", SPECIAL(_expand_null)},
 | |
| 				/* Empty string.  For compatibility. */
 | |
| 
 | |
| };
 | |
| 
 | |
| static int
 | |
| _expand_token(krb5_context context, const char * token, const char * token_end,
 | |
| 	      char ** ret)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     *ret = NULL;
 | |
| 
 | |
|     if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
 | |
| 	token_end - token <= 2) {
 | |
| 	if (context)
 | |
| 	    krb5_set_error_string(context, "Invalid token.");
 | |
| 	return EINVAL;
 | |
|     }
 | |
| 
 | |
|     for (i=0; i < sizeof(tokens)/sizeof(tokens[0]); i++) {
 | |
| 	if (!strncmp(token+2, tokens[i].tok, (token_end - token) - 2))
 | |
| 	    return tokens[i].exp_func(context, tokens[i].param, tokens[i].postfix, ret);
 | |
|     }
 | |
| 
 | |
|     if (context)
 | |
| 	krb5_set_error_string(context, "Invalid token.");
 | |
|     return EINVAL;
 | |
| }
 | |
| 
 | |
| KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 | |
| _krb5_free_path(krb5_context context,
 | |
| 		char * path)
 | |
| {
 | |
|     if (path == NULL)
 | |
| 	return EINVAL;
 | |
| 
 | |
|     free(path);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 | |
| _krb5_expand_path_tokens(krb5_context context,
 | |
| 			 const char * path_in,
 | |
| 			 char ** ppath_out)
 | |
| {
 | |
|     size_t len = 0;
 | |
|     char *tok_begin, *tok_end, *append;
 | |
|     const char *path_left;
 | |
| 
 | |
|     *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_string(context, "variable missing }");
 | |
| 		return EINVAL;
 | |
| 	    }
 | |
| 
 | |
| 	    if (_expand_token(context, tok_begin, tok_end, &append)) {
 | |
| 		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) {
 | |
| 
 | |
| 	    if (*ppath_out)
 | |
| 		free(*ppath_out);
 | |
| 	    *ppath_out = NULL;
 | |
| 	    if (context)
 | |
| 		krb5_set_error_string(context, "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_string(context, "malloc - out of memory");
 | |
| 		return ENOMEM;
 | |
| 	    }
 | |
| 
 | |
| 	    *ppath_out = new_str;
 | |
| 	    memcpy(*ppath_out + len, append, append_len + 1);
 | |
| 	    len = len + append_len;
 | |
| 	    free(append);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | 
