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