etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / lib / krb5 / expand_path.c
blobc5d234fcbfef5419b0f0ee8dc91fd6b41baac169
1 /* $NetBSD: expand_path.c,v 1.1.1.2 2014/04/24 12:45:50 pettai Exp $ */
4 /***********************************************************************
5 * Copyright (c) 2009, Secure Endpoints Inc.
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
33 **********************************************************************/
35 #include "krb5_locl.h"
37 typedef int PTYPE;
39 #ifdef _WIN32
40 #include <shlobj.h>
41 #include <sddl.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 int
55 _expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
57 TCHAR tpath[MAX_PATH];
58 size_t len;
60 if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
61 if (context)
62 krb5_set_error_message(context, EINVAL,
63 "Failed to get temporary path (GLE=%d)",
64 GetLastError());
65 return EINVAL;
68 len = strlen(tpath);
70 if (len > 0 && tpath[len - 1] == '\\')
71 tpath[len - 1] = '\0';
73 *ret = strdup(tpath);
75 if (*ret == NULL) {
76 if (context)
77 krb5_set_error_message(context, ENOMEM, "strdup - Out of memory");
78 return ENOMEM;
81 return 0;
84 extern HINSTANCE _krb5_hInstance;
87 * Expand a %{BINDIR} token
89 * This is also used to expand a few other tokens on Windows, since
90 * most of the executable binaries end up in the same directory. The
91 * "bin" directory is considered to be the directory in which the
92 * krb5.dll is located.
94 static int
95 _expand_bin_dir(krb5_context context, PTYPE param, const char *postfix, char **ret)
97 TCHAR path[MAX_PATH];
98 TCHAR *lastSlash;
99 DWORD nc;
101 nc = GetModuleFileName(_krb5_hInstance, path, sizeof(path)/sizeof(path[0]));
102 if (nc == 0 ||
103 nc == sizeof(path)/sizeof(path[0])) {
104 return EINVAL;
107 lastSlash = strrchr(path, '\\');
108 if (lastSlash != NULL) {
109 TCHAR *fslash = strrchr(lastSlash, '/');
111 if (fslash != NULL)
112 lastSlash = fslash;
114 *lastSlash = '\0';
117 if (postfix) {
118 if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
119 return EINVAL;
122 *ret = strdup(path);
123 if (*ret == NULL)
124 return ENOMEM;
126 return 0;
130 * Expand a %{USERID} token
132 * The %{USERID} token expands to the string representation of the
133 * user's SID. The user account that will be used is the account
134 * corresponding to the current thread's security token. This means
135 * that:
137 * - If the current thread token has the anonymous impersonation
138 * level, the call will fail.
140 * - If the current thread is impersonating a token at
141 * SecurityIdentification level the call will fail.
144 static int
145 _expand_userid(krb5_context context, PTYPE param, const char *postfix, char **ret)
147 int rv = EINVAL;
148 HANDLE hThread = NULL;
149 HANDLE hToken = NULL;
150 PTOKEN_OWNER pOwner = NULL;
151 DWORD len = 0;
152 LPTSTR strSid = NULL;
154 hThread = GetCurrentThread();
156 if (!OpenThreadToken(hThread, TOKEN_QUERY,
157 FALSE, /* Open the thread token as the
158 current thread user. */
159 &hToken)) {
161 DWORD le = GetLastError();
163 if (le == ERROR_NO_TOKEN) {
164 HANDLE hProcess = GetCurrentProcess();
166 le = 0;
167 if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
168 le = GetLastError();
171 if (le != 0) {
172 if (context)
173 krb5_set_error_message(context, rv,
174 "Can't open thread token (GLE=%d)", le);
175 goto _exit;
179 if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
180 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
181 if (context)
182 krb5_set_error_message(context, rv,
183 "Unexpected error reading token information (GLE=%d)",
184 GetLastError());
185 goto _exit;
188 if (len == 0) {
189 if (context)
190 krb5_set_error_message(context, rv,
191 "GetTokenInformation() returned truncated buffer");
192 goto _exit;
195 pOwner = malloc(len);
196 if (pOwner == NULL) {
197 if (context)
198 krb5_set_error_message(context, rv, "Out of memory");
199 goto _exit;
201 } else {
202 if (context)
203 krb5_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
204 goto _exit;
207 if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
208 if (context)
209 krb5_set_error_message(context, rv, "GetTokenInformation() failed. GLE=%d", GetLastError());
210 goto _exit;
213 if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
214 if (context)
215 krb5_set_error_message(context, rv, "Can't convert SID to string. GLE=%d", GetLastError());
216 goto _exit;
219 *ret = strdup(strSid);
220 if (*ret == NULL && context)
221 krb5_set_error_message(context, rv, "Out of memory");
223 rv = 0;
225 _exit:
226 if (hToken != NULL)
227 CloseHandle(hToken);
229 if (pOwner != NULL)
230 free (pOwner);
232 if (strSid != NULL)
233 LocalFree(strSid);
235 return rv;
239 * Expand a folder identified by a CSIDL
242 static int
243 _expand_csidl(krb5_context context, PTYPE folder, const char *postfix, char **ret)
245 TCHAR path[MAX_PATH];
246 size_t len;
248 if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
249 if (context)
250 krb5_set_error_message(context, EINVAL, "Unable to determine folder path");
251 return EINVAL;
254 len = strlen(path);
256 if (len > 0 && path[len - 1] == '\\')
257 path[len - 1] = '\0';
259 if (postfix &&
260 strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0])) {
261 return ENOMEM;
264 *ret = strdup(path);
265 if (*ret == NULL) {
266 if (context)
267 krb5_set_error_message(context, ENOMEM, "Out of memory");
268 return ENOMEM;
270 return 0;
273 #else
275 static int
276 _expand_path(krb5_context context, PTYPE param, const char *postfix, char **ret)
278 *ret = strdup(postfix);
279 if (*ret == NULL) {
280 krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
281 return ENOMEM;
283 return 0;
286 static int
287 _expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
289 const char *p = NULL;
291 if (issuid())
292 p = getenv("TEMP");
293 if (p)
294 *ret = strdup(p);
295 else
296 *ret = strdup("/tmp");
297 if (*ret == NULL)
298 return ENOMEM;
299 return 0;
302 static int
303 _expand_userid(krb5_context context, PTYPE param, const char *postfix, char **str)
305 int ret = asprintf(str, "%ld", (unsigned long)getuid());
306 if (ret < 0 || *str == NULL)
307 return ENOMEM;
308 return 0;
312 #endif /* _WIN32 */
315 * Expand a %{null} token
317 * The expansion of a %{null} token is always the empty string.
320 static int
321 _expand_null(krb5_context context, PTYPE param, const char *postfix, char **ret)
323 *ret = strdup("");
324 if (*ret == NULL) {
325 if (context)
326 krb5_set_error_message(context, ENOMEM, "Out of memory");
327 return ENOMEM;
329 return 0;
333 static const struct token {
334 const char * tok;
335 int ftype;
336 #define FTYPE_CSIDL 0
337 #define FTYPE_SPECIAL 1
339 PTYPE param;
340 const char * postfix;
342 int (*exp_func)(krb5_context, PTYPE, const char *, char **);
344 #define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
345 #define SPECIAL(f) SPECIALP(f, NULL)
347 } tokens[] = {
348 #ifdef _WIN32
349 #define CSIDLP(C,P) FTYPE_CSIDL, C, P, _expand_csidl
350 #define CSIDL(C) CSIDLP(C, NULL)
352 {"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */
353 {"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */
354 {"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */
355 {"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */
356 {"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */
357 {"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */
358 {"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */
359 {"LIBDIR", SPECIAL(_expand_bin_dir)},
360 {"BINDIR", SPECIAL(_expand_bin_dir)},
361 {"LIBEXEC", SPECIAL(_expand_bin_dir)},
362 {"SBINDIR", SPECIAL(_expand_bin_dir)},
363 #else
364 {"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, _expand_path},
365 {"BINDIR", FTYPE_SPECIAL, 0, BINDIR, _expand_path},
366 {"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, _expand_path},
367 {"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, _expand_path},
368 #endif
369 {"TEMP", SPECIAL(_expand_temp_folder)},
370 {"USERID", SPECIAL(_expand_userid)},
371 {"uid", SPECIAL(_expand_userid)},
372 {"null", SPECIAL(_expand_null)}
375 static int
376 _expand_token(krb5_context context,
377 const char *token,
378 const char *token_end,
379 char **ret)
381 size_t i;
383 *ret = NULL;
385 if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
386 token_end - token <= 2) {
387 if (context)
388 krb5_set_error_message(context, EINVAL,"Invalid token.");
389 return EINVAL;
392 for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++) {
393 if (!strncmp(token+2, tokens[i].tok, (token_end - token) - 2))
394 return tokens[i].exp_func(context, tokens[i].param,
395 tokens[i].postfix, ret);
398 if (context)
399 krb5_set_error_message(context, EINVAL, "Invalid token.");
400 return EINVAL;
403 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
404 _krb5_expand_path_tokens(krb5_context context,
405 const char *path_in,
406 char **ppath_out)
408 char *tok_begin, *tok_end, *append;
409 const char *path_left;
410 size_t len = 0;
412 if (path_in == NULL || *path_in == '\0') {
413 *ppath_out = strdup("");
414 return 0;
417 *ppath_out = NULL;
419 for (path_left = path_in; path_left && *path_left; ) {
421 tok_begin = strstr(path_left, "%{");
423 if (tok_begin && tok_begin != path_left) {
425 append = malloc((tok_begin - path_left) + 1);
426 if (append) {
427 memcpy(append, path_left, tok_begin - path_left);
428 append[tok_begin - path_left] = '\0';
430 path_left = tok_begin;
432 } else if (tok_begin) {
434 tok_end = strchr(tok_begin, '}');
435 if (tok_end == NULL) {
436 if (*ppath_out)
437 free(*ppath_out);
438 *ppath_out = NULL;
439 if (context)
440 krb5_set_error_message(context, EINVAL, "variable missing }");
441 return EINVAL;
444 if (_expand_token(context, tok_begin, tok_end, &append)) {
445 if (*ppath_out)
446 free(*ppath_out);
447 *ppath_out = NULL;
448 return EINVAL;
451 path_left = tok_end + 1;
452 } else {
454 append = strdup(path_left);
455 path_left = NULL;
459 if (append == NULL) {
461 if (*ppath_out)
462 free(*ppath_out);
463 *ppath_out = NULL;
464 if (context)
465 krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
466 return ENOMEM;
471 size_t append_len = strlen(append);
472 char * new_str = realloc(*ppath_out, len + append_len + 1);
474 if (new_str == NULL) {
475 free(append);
476 if (*ppath_out)
477 free(*ppath_out);
478 *ppath_out = NULL;
479 if (context)
480 krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
481 return ENOMEM;
484 *ppath_out = new_str;
485 memcpy(*ppath_out + len, append, append_len + 1);
486 len = len + append_len;
487 free(append);
491 #ifdef _WIN32
492 /* Also deal with slashes */
493 if (*ppath_out) {
494 char * c;
495 for (c = *ppath_out; *c; c++)
496 if (*c == '/')
497 *c = '\\';
499 #endif
501 return 0;