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