Line data Source code
1 :
2 : /***********************************************************************
3 : * Copyright (c) 2009, Secure Endpoints Inc.
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : *
10 : * - Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : *
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
16 : * distribution.
17 : *
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.
30 : *
31 : **********************************************************************/
32 :
33 : #include "krb5_locl.h"
34 :
35 : typedef int PTYPE;
36 :
37 : #ifdef _WIN32
38 : #include <shlobj.h>
39 : #include <sddl.h>
40 :
41 : /*
42 : * Expand a %{TEMP} token
43 : *
44 : * The %{TEMP} token expands to the temporary path for the current
45 : * user as returned by GetTempPath().
46 : *
47 : * @note: Since the GetTempPath() function relies on the TMP or TEMP
48 : * environment variables, this function will failover to the system
49 : * temporary directory until the user profile is loaded. In addition,
50 : * the returned path may or may not exist.
51 : */
52 : static int
53 : _expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
54 : {
55 : TCHAR tpath[MAX_PATH];
56 : size_t len;
57 :
58 : if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
59 : if (context)
60 : krb5_set_error_message(context, EINVAL,
61 : "Failed to get temporary path (GLE=%d)",
62 : GetLastError());
63 : return EINVAL;
64 : }
65 :
66 : len = strlen(tpath);
67 :
68 : if (len > 0 && tpath[len - 1] == '\\')
69 : tpath[len - 1] = '\0';
70 :
71 : *ret = strdup(tpath);
72 :
73 : if (*ret == NULL) {
74 : if (context)
75 : krb5_set_error_message(context, ENOMEM, "strdup - Out of memory");
76 : return ENOMEM;
77 : }
78 :
79 : return 0;
80 : }
81 :
82 : extern HINSTANCE _krb5_hInstance;
83 :
84 : /*
85 : * Expand a %{BINDIR} token
86 : *
87 : * This is also used to expand a few other tokens on Windows, since
88 : * most of the executable binaries end up in the same directory. The
89 : * "bin" directory is considered to be the directory in which the
90 : * krb5.dll is located.
91 : */
92 : static int
93 : _expand_bin_dir(krb5_context context, PTYPE param, const char *postfix, char **ret)
94 : {
95 : TCHAR path[MAX_PATH];
96 : TCHAR *lastSlash;
97 : DWORD nc;
98 :
99 : nc = GetModuleFileName(_krb5_hInstance, path, sizeof(path)/sizeof(path[0]));
100 : if (nc == 0 ||
101 : nc == sizeof(path)/sizeof(path[0])) {
102 : return EINVAL;
103 : }
104 :
105 : lastSlash = strrchr(path, '\\');
106 : if (lastSlash != NULL) {
107 : TCHAR *fslash = strrchr(lastSlash, '/');
108 :
109 : if (fslash != NULL)
110 : lastSlash = fslash;
111 :
112 : *lastSlash = '\0';
113 : }
114 :
115 : if (postfix) {
116 : if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
117 : return EINVAL;
118 : }
119 :
120 : *ret = strdup(path);
121 : if (*ret == NULL)
122 : return ENOMEM;
123 :
124 : return 0;
125 : }
126 :
127 : /*
128 : * Expand a %{USERID} token
129 : *
130 : * The %{USERID} token expands to the string representation of the
131 : * user's SID. The user account that will be used is the account
132 : * corresponding to the current thread's security token. This means
133 : * that:
134 : *
135 : * - If the current thread token has the anonymous impersonation
136 : * level, the call will fail.
137 : *
138 : * - If the current thread is impersonating a token at
139 : * SecurityIdentification level the call will fail.
140 : *
141 : */
142 : static int
143 : _expand_userid(krb5_context context, PTYPE param, const char *postfix, char **ret)
144 : {
145 : int rv = EINVAL;
146 : HANDLE hThread = NULL;
147 : HANDLE hToken = NULL;
148 : PTOKEN_OWNER pOwner = NULL;
149 : DWORD len = 0;
150 : LPTSTR strSid = NULL;
151 :
152 : hThread = GetCurrentThread();
153 :
154 : if (!OpenThreadToken(hThread, TOKEN_QUERY,
155 : FALSE, /* Open the thread token as the
156 : current thread user. */
157 : &hToken)) {
158 :
159 : DWORD le = GetLastError();
160 :
161 : if (le == ERROR_NO_TOKEN) {
162 : HANDLE hProcess = GetCurrentProcess();
163 :
164 : le = 0;
165 : if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
166 : le = GetLastError();
167 : }
168 :
169 : if (le != 0) {
170 : if (context)
171 : krb5_set_error_message(context, rv,
172 : "Can't open thread token (GLE=%d)", le);
173 : goto _exit;
174 : }
175 : }
176 :
177 : if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
178 : if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
179 : if (context)
180 : krb5_set_error_message(context, rv,
181 : "Unexpected error reading token information (GLE=%d)",
182 : GetLastError());
183 : goto _exit;
184 : }
185 :
186 : if (len == 0) {
187 : if (context)
188 : krb5_set_error_message(context, rv,
189 : "GetTokenInformation() returned truncated buffer");
190 : goto _exit;
191 : }
192 :
193 : pOwner = malloc(len);
194 : if (pOwner == NULL) {
195 : if (context)
196 : krb5_set_error_message(context, rv, "Out of memory");
197 : goto _exit;
198 : }
199 : } else {
200 : if (context)
201 : krb5_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
202 : goto _exit;
203 : }
204 :
205 : if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
206 : if (context)
207 : krb5_set_error_message(context, rv, "GetTokenInformation() failed. GLE=%d", GetLastError());
208 : goto _exit;
209 : }
210 :
211 : if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
212 : if (context)
213 : krb5_set_error_message(context, rv, "Can't convert SID to string. GLE=%d", GetLastError());
214 : goto _exit;
215 : }
216 :
217 : *ret = strdup(strSid);
218 : if (*ret == NULL && context)
219 : krb5_set_error_message(context, rv, "Out of memory");
220 :
221 : rv = 0;
222 :
223 : _exit:
224 : if (hToken != NULL)
225 : CloseHandle(hToken);
226 :
227 : if (pOwner != NULL)
228 : free (pOwner);
229 :
230 : if (strSid != NULL)
231 : LocalFree(strSid);
232 :
233 : return rv;
234 : }
235 :
236 : /*
237 : * Expand a folder identified by a CSIDL
238 : */
239 :
240 : static int
241 : _expand_csidl(krb5_context context, PTYPE folder, const char *postfix, char **ret)
242 : {
243 : TCHAR path[MAX_PATH];
244 : size_t len;
245 :
246 : if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
247 : if (context)
248 : krb5_set_error_message(context, EINVAL, "Unable to determine folder path");
249 : return EINVAL;
250 : }
251 :
252 : len = strlen(path);
253 :
254 : if (len > 0 && path[len - 1] == '\\')
255 : path[len - 1] = '\0';
256 :
257 : if (postfix &&
258 : strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0])) {
259 : return ENOMEM;
260 : }
261 :
262 : *ret = strdup(path);
263 : if (*ret == NULL) {
264 : if (context)
265 : krb5_set_error_message(context, ENOMEM, "Out of memory");
266 : return ENOMEM;
267 : }
268 : return 0;
269 : }
270 :
271 : #else
272 :
273 : static int
274 0 : _expand_path(krb5_context context, PTYPE param, const char *postfix, char **ret)
275 : {
276 0 : *ret = strdup(postfix);
277 0 : if (*ret == NULL) {
278 0 : krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
279 0 : return ENOMEM;
280 : }
281 0 : return 0;
282 : }
283 :
284 : static int
285 0 : _expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
286 : {
287 0 : const char *p = NULL;
288 :
289 0 : if (issuid())
290 0 : p = getenv("TEMP");
291 0 : if (p)
292 0 : *ret = strdup(p);
293 : else
294 0 : *ret = strdup("/tmp");
295 0 : if (*ret == NULL)
296 0 : return ENOMEM;
297 0 : return 0;
298 : }
299 :
300 : static int
301 1 : _expand_userid(krb5_context context, PTYPE param, const char *postfix, char **str)
302 : {
303 1 : int ret = asprintf(str, "%ld", (unsigned long)getuid());
304 1 : if (ret < 0 || *str == NULL)
305 0 : return ENOMEM;
306 1 : return 0;
307 : }
308 :
309 :
310 : #endif /* _WIN32 */
311 :
312 : /**
313 : * Expand a %{null} token
314 : *
315 : * The expansion of a %{null} token is always the empty string.
316 : */
317 :
318 : static int
319 0 : _expand_null(krb5_context context, PTYPE param, const char *postfix, char **ret)
320 : {
321 0 : *ret = strdup("");
322 0 : if (*ret == NULL) {
323 0 : if (context)
324 0 : krb5_set_error_message(context, ENOMEM, "Out of memory");
325 0 : return ENOMEM;
326 : }
327 0 : return 0;
328 : }
329 :
330 :
331 : static const struct {
332 : const char * tok;
333 : int ftype;
334 : #define FTYPE_CSIDL 0
335 : #define FTYPE_SPECIAL 1
336 :
337 : PTYPE param;
338 : const char * postfix;
339 :
340 : int (*exp_func)(krb5_context, PTYPE, const char *, char **);
341 :
342 : #define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
343 : #define SPECIAL(f) SPECIALP(f, NULL)
344 :
345 : } tokens[] = {
346 : #ifdef _WIN32
347 : #define CSIDLP(C,P) FTYPE_CSIDL, C, P, _expand_csidl
348 : #define CSIDL(C) CSIDLP(C, NULL)
349 :
350 : {"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */
351 : {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */
352 : {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */
353 : {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */
354 : {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */
355 : {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */
356 : {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */
357 : {"LIBDIR", SPECIAL(_expand_bin_dir)},
358 : {"BINDIR", SPECIAL(_expand_bin_dir)},
359 : {"LIBEXEC", SPECIAL(_expand_bin_dir)},
360 : {"SBINDIR", SPECIAL(_expand_bin_dir)},
361 : #else
362 : {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, _expand_path},
363 : {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, _expand_path},
364 : {"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, _expand_path},
365 : {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, _expand_path},
366 : #endif
367 : {"TEMP", SPECIAL(_expand_temp_folder)},
368 : {"USERID", SPECIAL(_expand_userid)},
369 : {"uid", SPECIAL(_expand_userid)},
370 : {"null", SPECIAL(_expand_null)}
371 : };
372 :
373 : static int
374 1 : _expand_token(krb5_context context,
375 : const char *token,
376 : const char *token_end,
377 : char **ret)
378 : {
379 : size_t i;
380 :
381 1 : *ret = NULL;
382 :
383 2 : if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
384 1 : token_end - token <= 2) {
385 0 : if (context)
386 0 : krb5_set_error_message(context, EINVAL,"Invalid token.");
387 0 : return EINVAL;
388 : }
389 :
390 7 : for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++) {
391 7 : if (!strncmp(token+2, tokens[i].tok, (token_end - token) - 2))
392 1 : return tokens[i].exp_func(context, tokens[i].param,
393 : tokens[i].postfix, ret);
394 : }
395 :
396 0 : if (context)
397 0 : krb5_set_error_message(context, EINVAL, "Invalid token.");
398 0 : return EINVAL;
399 : }
400 :
401 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
402 265404 : _krb5_expand_path_tokens(krb5_context context,
403 : const char *path_in,
404 : char **ppath_out)
405 : {
406 : char *tok_begin, *tok_end, *append;
407 : const char *path_left;
408 265404 : size_t len = 0;
409 :
410 265404 : if (path_in == NULL || *path_in == '\0') {
411 0 : *ppath_out = strdup("");
412 0 : return 0;
413 : }
414 :
415 265404 : *ppath_out = NULL;
416 :
417 796213 : for (path_left = path_in; path_left && *path_left; ) {
418 :
419 265405 : tok_begin = strstr(path_left, "%{");
420 :
421 265405 : if (tok_begin && tok_begin != path_left) {
422 :
423 1 : append = malloc((tok_begin - path_left) + 1);
424 1 : if (append) {
425 1 : memcpy(append, path_left, tok_begin - path_left);
426 1 : append[tok_begin - path_left] = '\0';
427 : }
428 1 : path_left = tok_begin;
429 :
430 265404 : } else if (tok_begin) {
431 :
432 1 : tok_end = strchr(tok_begin, '}');
433 1 : if (tok_end == NULL) {
434 0 : if (*ppath_out)
435 0 : free(*ppath_out);
436 0 : *ppath_out = NULL;
437 0 : if (context)
438 0 : krb5_set_error_message(context, EINVAL, "variable missing }");
439 0 : return EINVAL;
440 : }
441 :
442 1 : if (_expand_token(context, tok_begin, tok_end, &append)) {
443 0 : if (*ppath_out)
444 0 : free(*ppath_out);
445 0 : *ppath_out = NULL;
446 0 : return EINVAL;
447 : }
448 :
449 1 : path_left = tok_end + 1;
450 : } else {
451 :
452 265403 : append = strdup(path_left);
453 265403 : path_left = NULL;
454 :
455 : }
456 :
457 265405 : if (append == NULL) {
458 :
459 0 : if (*ppath_out)
460 0 : free(*ppath_out);
461 0 : *ppath_out = NULL;
462 0 : if (context)
463 0 : krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
464 0 : return ENOMEM;
465 :
466 : }
467 :
468 : {
469 265405 : size_t append_len = strlen(append);
470 265405 : char * new_str = realloc(*ppath_out, len + append_len + 1);
471 :
472 265405 : if (new_str == NULL) {
473 0 : free(append);
474 0 : if (*ppath_out)
475 0 : free(*ppath_out);
476 0 : *ppath_out = NULL;
477 0 : if (context)
478 0 : krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
479 0 : return ENOMEM;
480 : }
481 :
482 265405 : *ppath_out = new_str;
483 265836 : memcpy(*ppath_out + len, append, append_len + 1);
484 265405 : len = len + append_len;
485 265405 : free(append);
486 : }
487 : }
488 :
489 : #ifdef _WIN32
490 : /* Also deal with slashes */
491 : if (*ppath_out) {
492 : char * c;
493 : for (c = *ppath_out; *c; c++)
494 : if (*c == '/')
495 : *c = '\\';
496 : }
497 : #endif
498 :
499 264973 : return 0;
500 : }
|