1 /***********************************************************************
2 * Copyright (c) 2009, Secure Endpoints Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * 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 ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 **********************************************************************/
32 #include "krb5_locl.h"
39 * Expand a %{TEMP} token
41 * The %{TEMP} token expands to the temporary path for the current
42 * user as returned by GetTempPath().
44 * @note: Since the GetTempPath() function relies on the TMP or TEMP
45 * environment variables, this function will failover to the system
46 * temporary directory until the user profile is loaded. In addition,
47 * the returned path may or may not exist.
50 _expand_temp_folder(krb5_context context
, PTYPE param
, const char * postfix
, char ** ret
)
52 TCHAR tpath
[MAX_PATH
];
55 if (!GetTempPath(sizeof(tpath
)/sizeof(tpath
[0]), tpath
)) {
57 krb5_set_error_string(context
, "Failed to get temporary path (GLE=%d)",
64 if (len
> 0 && tpath
[len
- 1] == '\\')
65 tpath
[len
- 1] = '\0';
71 krb5_set_error_string(context
, "strdup - Out of memory");
78 extern HINSTANCE _krb5_hInstance
;
81 * Expand a %{BINDIR} token
83 * This is also used to expand a few other tokens on Windows, since
84 * most of the executable binaries end up in the same directory. The
85 * "bin" directory is considered to be the directory in which the
86 * krb5.dll is located.
89 _expand_bin_dir(krb5_context context
, PTYPE param
, const char * postfix
, char ** ret
)
95 nc
= GetModuleFileName(_krb5_hInstance
, path
, sizeof(path
)/sizeof(path
[0]));
97 nc
== sizeof(path
)/sizeof(path
[0])) {
101 lastSlash
= strrchr(path
, '\\');
102 if (lastSlash
!= NULL
) {
103 TCHAR
*fslash
= strrchr(lastSlash
, '/');
112 if (strlcat(path
, postfix
, sizeof(path
)/sizeof(path
[0])) >= sizeof(path
)/sizeof(path
[0]))
124 * Expand a %{USERID} token
126 * The %{USERID} token expands to the string representation of the
127 * user's SID. The user account that will be used is the account
128 * corresponding to the current thread's security token. This means
131 * - If the current thread token has the anonymous impersonation
132 * level, the call will fail.
134 * - If the current thread is impersonating a token at
135 * SecurityIdentification level the call will fail.
139 _expand_userid(krb5_context context
, PTYPE param
, const char * postfix
, char ** ret
)
142 HANDLE hThread
= NULL
;
143 HANDLE hToken
= NULL
;
144 PTOKEN_OWNER pOwner
= NULL
;
146 LPTSTR strSid
= NULL
;
148 hThread
= GetCurrentThread();
150 if (!OpenThreadToken(hThread
, TOKEN_QUERY
,
151 FALSE
, /* Open the thread token as the
152 current thread user. */
155 DWORD le
= GetLastError();
157 if (le
== ERROR_NO_TOKEN
) {
158 HANDLE hProcess
= GetCurrentProcess();
161 if (!OpenProcessToken(hProcess
, TOKEN_QUERY
, &hToken
))
167 krb5_set_error_string(context
, "Can't open thread token (GLE=%d)", le
);
172 if (!GetTokenInformation(hToken
, TokenOwner
, NULL
, 0, &len
)) {
173 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
) {
175 krb5_set_error_string(context
, "Unexpected error reading token information (GLE=%d)",
182 krb5_set_error_string(context
, "GetTokenInformation() returned truncated buffer");
186 pOwner
= malloc(len
);
187 if (pOwner
== NULL
) {
189 krb5_set_error_string(context
, "Out of memory");
194 krb5_set_error_string(context
, "GetTokenInformation() returned truncated buffer");
198 if (!GetTokenInformation(hToken
, TokenOwner
, pOwner
, len
, &len
)) {
200 krb5_set_error_string(context
, "GetTokenInformation() failed. GLE=%d", GetLastError());
204 if (!ConvertSidToStringSid(pOwner
->Owner
, &strSid
)) {
206 krb5_set_error_string(context
, "Can't convert SID to string. GLE=%d", GetLastError());
210 *ret
= strdup(strSid
);
211 if (*ret
== NULL
&& context
)
212 krb5_set_error_string(context
, "Out of memory");
230 * Expand a %{null} token
232 * The expansion of a %{null} token is always the empty string.
235 _expand_null(krb5_context context
, PTYPE param
, const char * postfix
, char ** ret
)
240 krb5_set_error_string(context
, "Out of memory");
248 * Expand a folder identified by a CSIDL
252 * @param[in] folder A CSIDL value identifying the folder to be
256 _expand_csidl(krb5_context context
, PTYPE folder
, const char * postfix
, char ** ret
)
258 TCHAR path
[MAX_PATH
];
261 if (SHGetFolderPath(NULL
, folder
, NULL
, SHGFP_TYPE_CURRENT
, path
) != S_OK
) {
263 krb5_set_error_string(context
, "Unable to determine folder path");
269 if (len
> 0 && path
[len
- 1] == '\\')
270 path
[len
- 1] = '\0';
273 strlcat(path
, postfix
, sizeof(path
)/sizeof(path
[0])) >= sizeof(path
)/sizeof(path
[0])) {
280 krb5_set_error_string(context
, "Out of memory");
286 static const struct token
{
289 #define FTYPE_CSIDL 0
290 #define FTYPE_SPECIAL 1
293 const char * postfix
;
295 int (*exp_func
)(krb5_context
, PTYPE
, const char *, char **);
297 #define CSIDLP(C,P) FTYPE_CSIDL, C, P, _expand_csidl
298 #define CSIDL(C) CSIDLP(C, NULL)
300 #define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
301 #define SPECIAL(f) SPECIALP(f, NULL)
304 /* Windows only -- */
305 {"APPDATA", CSIDL(CSIDL_APPDATA
)},
306 /* Roaming application data (for current user) */
308 {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA
)},
309 /* Application data (all users) */
311 {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA
)},
312 /* Local application data (for current user) */
314 {"SYSTEM", CSIDL(CSIDL_SYSTEM
)},
315 /* Windows System folder (e.g. %WINDIR%\System32) */
317 {"WINDOWS", CSIDL(CSIDL_WINDOWS
)},
319 /* -- end Windows only */
321 {"USERCONFIG", CSIDLP(CSIDL_APPDATA
, "\\" PACKAGE
)},
322 /* Per user Heimdal configuration file path */
324 {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA
, "\\" PACKAGE
)},
325 /* Common Heimdal configuration file path */
327 {"LIBDIR", SPECIAL(_expand_bin_dir
)},
328 /* Expands to the "lib" directory. On
329 Windows this is treated the same as
330 the "bin" directory. */
332 {"BINDIR", SPECIAL(_expand_bin_dir
)},
333 /* Expands to the "bin" directory. On
334 Windows this is treated the same as
335 the "bin" directory. */
337 {"LIBEXEC", SPECIAL(_expand_bin_dir
)},
338 /* Expands to the "libexec"
339 directory. On Windows, this is
340 treated the same as the "bin"
343 {"SBINDIR", SPECIAL(_expand_bin_dir
)},
344 /* Expands to the "sbin" directory.
345 On Windows, this is treated the
346 same as the "bin" directory. */
348 {"TEMP", SPECIAL(_expand_temp_folder
)},
349 /* Temporary files folder */
351 {"USERID", SPECIAL(_expand_userid
)},
352 /* User ID (On Windows, this expands
353 to the user's string SID */
355 {"uid", SPECIAL(_expand_userid
)},
356 /* Alias for USERID */
358 {"null", SPECIAL(_expand_null
)},
359 /* Empty string. For compatibility. */
364 _expand_token(krb5_context context
, const char * token
, const char * token_end
,
371 if (token
[0] != '%' || token
[1] != '{' || token_end
[0] != '}' ||
372 token_end
- token
<= 2) {
374 krb5_set_error_string(context
, "Invalid token.");
378 for (i
=0; i
< sizeof(tokens
)/sizeof(tokens
[0]); i
++) {
379 if (!strncmp(token
+2, tokens
[i
].tok
, (token_end
- token
) - 2))
380 return tokens
[i
].exp_func(context
, tokens
[i
].param
, tokens
[i
].postfix
, ret
);
384 krb5_set_error_string(context
, "Invalid token.");
388 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
389 _krb5_free_path(krb5_context context
,
399 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
400 _krb5_expand_path_tokens(krb5_context context
,
401 const char * path_in
,
405 char *tok_begin
, *tok_end
, *append
;
406 const char *path_left
;
410 for (path_left
= path_in
; path_left
&& *path_left
; ) {
412 tok_begin
= strstr(path_left
, "%{");
414 if (tok_begin
&& tok_begin
!= path_left
) {
416 append
= malloc((tok_begin
- path_left
) + 1);
418 memcpy(append
, path_left
, tok_begin
- path_left
);
419 append
[tok_begin
- path_left
] = '\0';
421 path_left
= tok_begin
;
423 } else if (tok_begin
) {
425 tok_end
= strchr(tok_begin
, '}');
426 if (tok_end
== NULL
) {
431 krb5_set_error_string(context
, "variable missing }");
435 if (_expand_token(context
, tok_begin
, tok_end
, &append
)) {
442 path_left
= tok_end
+ 1;
445 append
= strdup(path_left
);
450 if (append
== NULL
) {
456 krb5_set_error_string(context
, "malloc - out of memory");
462 size_t append_len
= strlen(append
);
463 char * new_str
= realloc(*ppath_out
, len
+ append_len
+ 1);
465 if (new_str
== NULL
) {
471 krb5_set_error_string(context
, "malloc - out of memory");
475 *ppath_out
= new_str
;
476 memcpy(*ppath_out
+ len
, append
, append_len
+ 1);
477 len
= len
+ append_len
;
482 /* Also deal with slashes */
485 for (c
= *ppath_out
; *c
; c
++)