Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/lib/krb5/expand_path.c
34878 views
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
_expand_path(krb5_context context, PTYPE param, const char *postfix, char **ret)
275
{
276
*ret = strdup(postfix);
277
if (*ret == NULL) {
278
krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
279
return ENOMEM;
280
}
281
return 0;
282
}
283
284
static int
285
_expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
286
{
287
const char *p = NULL;
288
289
if (issuid())
290
p = getenv("TEMP");
291
if (p)
292
*ret = strdup(p);
293
else
294
*ret = strdup("/tmp");
295
if (*ret == NULL)
296
return ENOMEM;
297
return 0;
298
}
299
300
static int
301
_expand_userid(krb5_context context, PTYPE param, const char *postfix, char **str)
302
{
303
int ret = asprintf(str, "%ld", (unsigned long)getuid());
304
if (ret < 0 || *str == NULL)
305
return ENOMEM;
306
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
_expand_null(krb5_context context, PTYPE param, const char *postfix, char **ret)
320
{
321
*ret = strdup("");
322
if (*ret == NULL) {
323
if (context)
324
krb5_set_error_message(context, ENOMEM, "Out of memory");
325
return ENOMEM;
326
}
327
return 0;
328
}
329
330
331
static const struct token {
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
_expand_token(krb5_context context,
375
const char *token,
376
const char *token_end,
377
char **ret)
378
{
379
size_t i;
380
381
*ret = NULL;
382
383
if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
384
token_end - token <= 2) {
385
if (context)
386
krb5_set_error_message(context, EINVAL,"Invalid token.");
387
return EINVAL;
388
}
389
390
for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++) {
391
if (!strncmp(token+2, tokens[i].tok, (token_end - token) - 2))
392
return tokens[i].exp_func(context, tokens[i].param,
393
tokens[i].postfix, ret);
394
}
395
396
if (context)
397
krb5_set_error_message(context, EINVAL, "Invalid token.");
398
return EINVAL;
399
}
400
401
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
402
_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
size_t len = 0;
409
410
if (path_in == NULL || *path_in == '\0') {
411
*ppath_out = strdup("");
412
return 0;
413
}
414
415
*ppath_out = NULL;
416
417
for (path_left = path_in; path_left && *path_left; ) {
418
419
tok_begin = strstr(path_left, "%{");
420
421
if (tok_begin && tok_begin != path_left) {
422
423
append = malloc((tok_begin - path_left) + 1);
424
if (append) {
425
memcpy(append, path_left, tok_begin - path_left);
426
append[tok_begin - path_left] = '\0';
427
}
428
path_left = tok_begin;
429
430
} else if (tok_begin) {
431
432
tok_end = strchr(tok_begin, '}');
433
if (tok_end == NULL) {
434
if (*ppath_out)
435
free(*ppath_out);
436
*ppath_out = NULL;
437
if (context)
438
krb5_set_error_message(context, EINVAL, "variable missing }");
439
return EINVAL;
440
}
441
442
if (_expand_token(context, tok_begin, tok_end, &append)) {
443
if (*ppath_out)
444
free(*ppath_out);
445
*ppath_out = NULL;
446
return EINVAL;
447
}
448
449
path_left = tok_end + 1;
450
} else {
451
452
append = strdup(path_left);
453
path_left = NULL;
454
455
}
456
457
if (append == NULL) {
458
459
if (*ppath_out)
460
free(*ppath_out);
461
*ppath_out = NULL;
462
if (context)
463
krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
464
return ENOMEM;
465
466
}
467
468
{
469
size_t append_len = strlen(append);
470
char * new_str = realloc(*ppath_out, len + append_len + 1);
471
472
if (new_str == NULL) {
473
free(append);
474
if (*ppath_out)
475
free(*ppath_out);
476
*ppath_out = NULL;
477
if (context)
478
krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
479
return ENOMEM;
480
}
481
482
*ppath_out = new_str;
483
memcpy(*ppath_out + len, append, append_len + 1);
484
len = len + append_len;
485
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
return 0;
500
}
501
502