2 /***********************************************************************
3 * Copyright (c) 2009, Secure Endpoints Inc.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 **********************************************************************/
33 #include "krb5_locl.h"
44 * Expand a %{TEMP} token
46 * The %{TEMP} token expands to the temporary path for the current
47 * user as returned by GetTempPath().
49 * @note: Since the GetTempPath() function relies on the TMP or TEMP
50 * environment variables, this function will failover to the system
51 * temporary directory until the user profile is loaded. In addition,
52 * the returned path may or may not exist.
54 static krb5_error_code
55 _expand_temp_folder(krb5_context context
, PTYPE param
, const char *postfix
, char **ret
)
57 TCHAR tpath
[MAX_PATH
];
60 if (!GetTempPath(sizeof(tpath
)/sizeof(tpath
[0]), tpath
)) {
62 krb5_set_error_message(context
, EINVAL
,
63 "Failed to get temporary path (GLE=%d)",
70 if (len
> 0 && tpath
[len
- 1] == '\\')
71 tpath
[len
- 1] = '\0';
76 return krb5_enomem(context
);
81 extern HINSTANCE _krb5_hInstance
;
84 * Expand a %{BINDIR} token
86 * This is also used to expand a few other tokens on Windows, since
87 * most of the executable binaries end up in the same directory. The
88 * "bin" directory is considered to be the directory in which the
89 * krb5.dll is located.
91 static krb5_error_code
92 _expand_bin_dir(krb5_context context
, PTYPE param
, const char *postfix
, char **ret
)
98 nc
= GetModuleFileName(_krb5_hInstance
, path
, sizeof(path
)/sizeof(path
[0]));
100 nc
== sizeof(path
)/sizeof(path
[0])) {
104 lastSlash
= strrchr(path
, '\\');
105 if (lastSlash
!= NULL
) {
106 TCHAR
*fslash
= strrchr(lastSlash
, '/');
115 if (strlcat(path
, postfix
, sizeof(path
)/sizeof(path
[0])) >= sizeof(path
)/sizeof(path
[0]))
121 return krb5_enomem(context
);
127 * Expand a %{USERID} token
129 * The %{USERID} token expands to the string representation of the
130 * user's SID. The user account that will be used is the account
131 * corresponding to the current thread's security token. This means
134 * - If the current thread token has the anonymous impersonation
135 * level, the call will fail.
137 * - If the current thread is impersonating a token at
138 * SecurityIdentification level the call will fail.
141 static krb5_error_code
142 _expand_userid(krb5_context context
, PTYPE param
, const char *postfix
, char **ret
)
145 HANDLE hThread
= NULL
;
146 HANDLE hToken
= NULL
;
147 PTOKEN_OWNER pOwner
= NULL
;
149 LPTSTR strSid
= NULL
;
151 hThread
= GetCurrentThread();
153 if (!OpenThreadToken(hThread
, TOKEN_QUERY
,
154 FALSE
, /* Open the thread token as the
155 current thread user. */
158 DWORD le
= GetLastError();
160 if (le
== ERROR_NO_TOKEN
) {
161 HANDLE hProcess
= GetCurrentProcess();
164 if (!OpenProcessToken(hProcess
, TOKEN_QUERY
, &hToken
))
170 krb5_set_error_message(context
, rv
,
171 "Can't open thread token (GLE=%d)", le
);
176 if (!GetTokenInformation(hToken
, TokenOwner
, NULL
, 0, &len
)) {
177 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
) {
179 krb5_set_error_message(context
, rv
,
180 "Unexpected error reading token information (GLE=%d)",
187 krb5_set_error_message(context
, rv
,
188 "GetTokenInformation() returned truncated buffer");
192 pOwner
= malloc(len
);
193 if (pOwner
== NULL
) {
195 krb5_set_error_message(context
, rv
, "Out of memory");
200 krb5_set_error_message(context
, rv
, "GetTokenInformation() returned truncated buffer");
204 if (!GetTokenInformation(hToken
, TokenOwner
, pOwner
, len
, &len
)) {
206 krb5_set_error_message(context
, rv
, "GetTokenInformation() failed. GLE=%d", GetLastError());
210 if (!ConvertSidToStringSid(pOwner
->Owner
, &strSid
)) {
212 krb5_set_error_message(context
, rv
, "Can't convert SID to string. GLE=%d", GetLastError());
216 *ret
= strdup(strSid
);
217 if (*ret
== NULL
&& context
)
218 krb5_set_error_message(context
, rv
, "Out of memory");
236 * Expand a folder identified by a CSIDL
239 static krb5_error_code
240 _expand_csidl(krb5_context context
, PTYPE folder
, const char *postfix
, char **ret
)
242 TCHAR path
[MAX_PATH
];
245 if (SHGetFolderPath(NULL
, folder
, NULL
, SHGFP_TYPE_CURRENT
, path
) != S_OK
) {
247 krb5_set_error_message(context
, EINVAL
, "Unable to determine folder path");
253 if (len
> 0 && path
[len
- 1] == '\\')
254 path
[len
- 1] = '\0';
257 strlcat(path
, postfix
, sizeof(path
)/sizeof(path
[0])) >= sizeof(path
)/sizeof(path
[0]))
258 return krb5_enomem(context
);
262 return krb5_enomem(context
);
268 static krb5_error_code
269 _expand_path(krb5_context context
, PTYPE param
, const char *postfix
, char **ret
)
271 *ret
= strdup(postfix
);
273 return krb5_enomem(context
);
277 static krb5_error_code
278 _expand_temp_folder(krb5_context context
, PTYPE param
, const char *postfix
, char **ret
)
280 const char *p
= NULL
;
287 *ret
= strdup("/tmp");
289 return krb5_enomem(context
);
293 static krb5_error_code
294 _expand_userid(krb5_context context
, PTYPE param
, const char *postfix
, char **str
)
296 int ret
= asprintf(str
, "%ld", (unsigned long)getuid());
297 if (ret
< 0 || *str
== NULL
)
298 return krb5_enomem(context
);
306 * Expand an extra token
309 static krb5_error_code
310 _expand_extra_token(krb5_context context
, const char *value
, char **ret
)
312 *ret
= strdup(value
);
314 return krb5_enomem(context
);
319 * Expand a %{null} token
321 * The expansion of a %{null} token is always the empty string.
324 static krb5_error_code
325 _expand_null(krb5_context context
, PTYPE param
, const char *postfix
, char **ret
)
329 return krb5_enomem(context
);
334 static const struct token
{
337 #define FTYPE_CSIDL 0
338 #define FTYPE_SPECIAL 1
341 const char * postfix
;
343 int (*exp_func
)(krb5_context
, PTYPE
, const char *, char **);
345 #define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
346 #define SPECIAL(f) SPECIALP(f, NULL)
350 #define CSIDLP(C,P) FTYPE_CSIDL, C, P, _expand_csidl
351 #define CSIDL(C) CSIDLP(C, NULL)
353 {"APPDATA", CSIDL(CSIDL_APPDATA
)}, /* Roaming application data (for current user) */
354 {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA
)}, /* Application data (all users) */
355 {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA
)}, /* Local application data (for current user) */
356 {"SYSTEM", CSIDL(CSIDL_SYSTEM
)}, /* Windows System folder (e.g. %WINDIR%\System32) */
357 {"WINDOWS", CSIDL(CSIDL_WINDOWS
)}, /* Windows folder */
358 {"USERCONFIG", CSIDLP(CSIDL_APPDATA
, "\\" PACKAGE
)}, /* Per user Heimdal configuration file path */
359 {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA
, "\\" PACKAGE
)}, /* Common Heimdal configuration file path */
360 {"LIBDIR", SPECIAL(_expand_bin_dir
)},
361 {"BINDIR", SPECIAL(_expand_bin_dir
)},
362 {"LIBEXEC", SPECIAL(_expand_bin_dir
)},
363 {"SBINDIR", SPECIAL(_expand_bin_dir
)},
365 {"LIBDIR", FTYPE_SPECIAL
, 0, LIBDIR
, _expand_path
},
366 {"BINDIR", FTYPE_SPECIAL
, 0, BINDIR
, _expand_path
},
367 {"LIBEXEC", FTYPE_SPECIAL
, 0, LIBEXECDIR
, _expand_path
},
368 {"SBINDIR", FTYPE_SPECIAL
, 0, SBINDIR
, _expand_path
},
370 {"TEMP", SPECIAL(_expand_temp_folder
)},
371 {"USERID", SPECIAL(_expand_userid
)},
372 {"uid", SPECIAL(_expand_userid
)},
373 {"null", SPECIAL(_expand_null
)}
376 static krb5_error_code
377 _expand_token(krb5_context context
,
379 const char *token_end
,
388 if (token
[0] != '%' || token
[1] != '{' || token_end
[0] != '}' ||
389 token_end
- token
<= 2) {
391 krb5_set_error_message(context
, EINVAL
,"Invalid token.");
395 for (p
= extra_tokens
; p
&& p
[0]; p
+= 2) {
396 if (strncmp(token
+2, p
[0], (token_end
- token
) - 2) == 0)
397 return _expand_extra_token(context
, p
[1], ret
);
400 for (i
= 0; i
< sizeof(tokens
)/sizeof(tokens
[0]); i
++) {
401 if (!strncmp(token
+2, tokens
[i
].tok
, (token_end
- token
) - 2))
402 return tokens
[i
].exp_func(context
, tokens
[i
].param
,
403 tokens
[i
].postfix
, ret
);
407 krb5_set_error_message(context
, EINVAL
, "Invalid token.");
412 * Internal function to expand tokens in paths.
416 * @context A krb5_context
417 * @path_in The path to expand tokens from
421 * @ppath_out Path with expanded tokens (caller must free() this)
423 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
424 _krb5_expand_path_tokens(krb5_context context
,
428 return _krb5_expand_path_tokensv(context
, path_in
, ppath_out
, NULL
);
432 free_extra_tokens(char **extra_tokens
)
436 for (p
= extra_tokens
; p
&& *p
; p
++)
442 * Internal function to expand tokens in paths.
446 * @context A krb5_context
447 * @path_in The path to expand tokens from
448 * @ppath_out The expanded path
449 * @... Variable number of pairs of strings, the first of each
450 * being a token (e.g., "luser") and the second a string to
451 * replace it with. The list is terminated by a NULL.
455 * @ppath_out Path with expanded tokens (caller must free() this)
457 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
458 _krb5_expand_path_tokensv(krb5_context context
,
460 char **ppath_out
, ...)
462 char *tok_begin
, *tok_end
, *append
;
463 char **extra_tokens
= NULL
;
464 const char *path_left
;
470 if (path_in
== NULL
|| *path_in
== '\0') {
471 *ppath_out
= strdup("");
477 va_start(ap
, ppath_out
);
478 while ((s
= va_arg(ap
, const char *))) {
480 s
= va_arg(ap
, const char *);
485 /* Get extra tokens */
489 extra_tokens
= calloc(nargs
+ 1, sizeof (*extra_tokens
));
490 if (extra_tokens
== NULL
)
491 return krb5_enomem(context
);
492 va_start(ap
, ppath_out
);
493 for (i
= 0; i
< nargs
; i
++) {
494 s
= va_arg(ap
, const char *); /* token key */
497 extra_tokens
[i
] = strdup(s
);
498 if (extra_tokens
[i
++] == NULL
) {
499 free_extra_tokens(extra_tokens
);
500 return krb5_enomem(context
);
502 s
= va_arg(ap
, const char *); /* token value */
505 extra_tokens
[i
] = strdup(s
);
506 if (extra_tokens
[i
] == NULL
) {
507 free_extra_tokens(extra_tokens
);
508 return krb5_enomem(context
);
514 for (path_left
= path_in
; path_left
&& *path_left
; ) {
516 tok_begin
= strstr(path_left
, "%{");
518 if (tok_begin
&& tok_begin
!= path_left
) {
520 append
= malloc((tok_begin
- path_left
) + 1);
522 memcpy(append
, path_left
, tok_begin
- path_left
);
523 append
[tok_begin
- path_left
] = '\0';
525 path_left
= tok_begin
;
527 } else if (tok_begin
) {
529 tok_end
= strchr(tok_begin
, '}');
530 if (tok_end
== NULL
) {
531 free_extra_tokens(extra_tokens
);
536 krb5_set_error_message(context
, EINVAL
, "variable missing }");
540 if (_expand_token(context
, tok_begin
, tok_end
, extra_tokens
,
542 free_extra_tokens(extra_tokens
);
549 path_left
= tok_end
+ 1;
552 append
= strdup(path_left
);
557 if (append
== NULL
) {
559 free_extra_tokens(extra_tokens
);
563 return krb5_enomem(context
);
568 size_t append_len
= strlen(append
);
569 char * new_str
= realloc(*ppath_out
, len
+ append_len
+ 1);
571 if (new_str
== NULL
) {
572 free_extra_tokens(extra_tokens
);
577 return krb5_enomem(context
);
580 *ppath_out
= new_str
;
581 memcpy(*ppath_out
+ len
, append
, append_len
+ 1);
582 len
= len
+ append_len
;
588 /* Also deal with slashes */
591 for (c
= *ppath_out
; *c
; c
++)
597 free_extra_tokens(extra_tokens
);